2 * PCAN-LIN, RS-232 to CAN/LIN converter control application
4 * This program is free software; you can distribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; version 2 of
9 * Copyright: (c) 2012 Czech Technical University in Prague
10 * Authors: Rostislav Lisovy <lisovy@gmail.cz>
21 #include <libxml/parser.h>
26 #define MAX_LIN_ID 0x3F
28 #define PCL_UNACTIVE 0
30 #define PCL_PKT_MAX_SIZE 16
31 #define PCL_HEADERS_SIZE 2 /* There are 2 bytes of headers */
32 #define PCL_CHECKSUM_SIZE 1
33 #define PCL_STX_SIZE 1
34 #define PCL_PACKET_OVERHEAD (PCL_HEADERS_SIZE + PCL_CHECKSUM_SIZE)
38 #define PCL_SEQ_NO_ofs 4
39 #define PCL_SEQ_FRLEN_ofs 0
40 #define PCL_CTRL_TIFACE_ofs 6
41 #define PCL_CTRL_COMC_ofs 0
43 #define PCL_SEQ_FRLEN_msk 0xF
45 #define PCL_PACKET_LIN_IFACE 0x2
46 #define PCL_PACKET_MODULE_IFACE 0x3
48 /* Logical representation of a packet sent to PCAN-LIN converter via RS232 */
50 uint8_t stx; /* Start transmission; Always set to 0x2 */
51 uint8_t seq_no; /* Sequence number */
52 uint8_t seq_frlen; /* Frame length */
53 uint8_t ctrl_tiface;/* Target interface */
54 uint8_t ctrl_comc; /* Command code */
55 uint8_t parms[8]; /* Parameters; Number of parameters depends
56 on the frame length */
57 uint8_t chks; /* Checksum; Bitwise XOR of all bytes except STX */
60 struct pcl_scheduler_entry {
65 /* Index in this array = LIN ID */
66 struct pcl_frame_entry {
67 int status; /* 1 = active; 0 = unactive */
72 struct pcl_lin_state {
79 struct pcl_lin_state pcl_lin_state;
80 struct pcl_frame_entry pcl_frame_entry[MAX_LIN_ID];
81 struct pcl_scheduler_entry pcl_scheduler_entry[100]; // FIXME max value
82 int pcl_scheduler_entries_cnt;
84 struct termios term_attr;
85 /* ------------------------------------------------------------------------ */
87 /* Transform 'logic' representation of a frame to exact byte sequence */
88 int pcl_serialize(pcl_packet_t *pkt, uint8_t *pkt_raw)
93 pkt_raw[0] = pkt->stx;
94 pkt_raw[1] = (pkt->seq_no << PCL_SEQ_NO_ofs) |
95 (pkt->seq_frlen << PCL_SEQ_FRLEN_ofs);
96 pkt_raw[2] = (pkt->ctrl_tiface << PCL_CTRL_TIFACE_ofs) |
97 (pkt->ctrl_comc << PCL_CTRL_COMC_ofs);
99 for (i = 0; i < pkt->seq_frlen; i++) {
100 pkt_raw[3+i] = pkt->parms[i];
103 /* Calculate XOR checksum; Skip STX */
104 for (i = 1; i <= pkt->seq_frlen + PCL_HEADERS_SIZE; i++) {
105 checksum ^= pkt_raw[i];
107 pkt_raw[i] = checksum;
109 printf("Snd: [STX] [SEQ] [PRM] [...] \n ");
110 for (i = 0; i < pkt->seq_frlen + PCL_PACKET_OVERHEAD + 1; i++)
111 printf("0x%02X ", pkt_raw[i]);
114 return pkt->seq_frlen + 4; /* real packet size */
117 int pcl_send_frame(int tty, pcl_packet_t *pkt)
122 uint8_t pkt_buff[PCL_PKT_MAX_SIZE];
124 pkt_size = pcl_serialize(pkt, (uint8_t *) &pkt_buff);
128 while (to_send > 0) {
129 sent = write(tty, pkt_buff+sent, to_send);
133 memset(pkt, '\0', sizeof(pcl_packet_t));
137 int pcl_read_reset_response(int tty)
142 char str_match[] = {'G', 'm', 'b', 'H'};
145 ret = read(tty, &c, 1);
151 if ((c == str_match[i]) && (i == 3))
152 i = -1; /* We are done -- Stop the loop*/
153 else if (c == str_match[i])
154 i++; /* Match found -- Go to the next letter */
156 i = 0; /* Start from beginning */
165 int pcl_read_response(int tty)
171 uint8_t buff[PCL_PKT_MAX_SIZE];
172 //memset(buff, '\0', sizeof(buff));
174 /* Read 2 bytes of header */
175 received = read(tty, &buff[0], 1); /* Read STX */
179 if (buff[0] == 0x2) { /* STX ok */
180 received = read(tty, &buff[1], 1); /* Read SEQ */
187 data_length = (buff[1] & PCL_SEQ_FRLEN_msk);
188 to_read = data_length + 1; /* +chksm */
189 while (to_read > 0) {
190 received = read(tty, &buff[2], to_read);
194 printf("Rcv: [STX] [SEQ] [PRM] [CHK]\n"); // FIXME add spaces before "CHKS" when
195 //more than 1 byte received in params
196 printf(" 0x%02X 0x%02X", buff[0], buff[1]);
197 for (i = 0; i < data_length; i++) {
198 printf(" 0x%02X", buff[i + 2]);
200 printf(" 0x%02X\n\n", buff[i]);
205 void pcl_insert_scheduler_entries(int tty)
210 /* Insert scheduler entry */
211 for (i = 0; i < (sizeof(pcl_scheduler_entry)/sizeof(struct pcl_scheduler_entry)); i++) {
215 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
216 pkt.ctrl_comc = 0x28;
217 pkt.parms[0] = (pcl_scheduler_entry[i].interval_ms) & 0xFF;
218 pkt.parms[1] = (pcl_scheduler_entry[i].interval_ms >> 8) & 0xFF;
219 pkt.parms[2] = pcl_scheduler_entry[i].lin_id; /* LIN ID */
221 pcl_send_frame(tty, &pkt);
222 pcl_read_response(tty);
227 void pcl_set_slave_id_and_data_configuration(int tty)
232 /* Set Slave ID + Data Configuration */
233 for (i = 0; i < 0x3F; i++) {
236 if (pcl_frame_entry[i].status == PCL_ACTIVE) {
239 pkt.seq_frlen = pcl_frame_entry[i].data_len + 2;
240 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
241 pkt.ctrl_comc = 0x29;
242 pkt.parms[0] = pcl_frame_entry[i].status; /* Field Status */
243 pkt.parms[1] = i; /* LIN ID */
244 pkt.parms[2] = pcl_frame_entry[i].data[0]; /* Data */
245 pkt.parms[3] = pcl_frame_entry[i].data[1]; /* Data */
246 pkt.parms[4] = pcl_frame_entry[i].data[2]; /* Data */
247 pkt.parms[5] = pcl_frame_entry[i].data[3]; /* Data */
248 pkt.parms[6] = pcl_frame_entry[i].data[4]; /* Data */
249 pkt.parms[7] = pcl_frame_entry[i].data[5]; /* Data */
250 pkt.parms[8] = pcl_frame_entry[i].data[6]; /* Data */
251 pkt.parms[9] = pcl_frame_entry[i].data[7]; /* Data */
264 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
265 pkt.ctrl_comc = 0x29;
266 pkt.parms[0] = 0x00; /* Field Status -- unactive */
267 pkt.parms[1] = i; /* LIN ID */
270 pcl_send_frame(tty, &pkt);
271 pcl_read_response(tty);
275 void pcl_flash_config(int tty)
279 /* Flash Current Configuration */
283 pkt.ctrl_tiface = PCL_PACKET_MODULE_IFACE;
286 pcl_send_frame(tty, &pkt);
287 pcl_read_response(tty);
290 void pcl_reset_device(int tty)
298 pkt.ctrl_tiface = PCL_PACKET_MODULE_IFACE;
301 pcl_send_frame(tty, &pkt);
302 pcl_read_reset_response(tty);
305 int pcl_lin_init(int tty)
309 /* Initialization according to current parameters */
313 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
316 pcl_send_frame(tty, &pkt);
317 pcl_read_response(tty);
321 pcl_reset_device(tty);
323 /* Erase Master Scheduler List */
327 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
328 pkt.ctrl_comc = 0x33;
330 pcl_send_frame(tty, &pkt);
331 pcl_read_response(tty);
333 /* Set Activation Status */
337 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
338 pkt.ctrl_comc = 0x1E;
339 pkt.parms[0] = pcl_lin_state.is_active;
341 pcl_send_frame(tty, &pkt);
342 pcl_read_response(tty);
348 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
349 pkt.ctrl_comc = 0x1F;
351 pkt.parms[1] = 0x4B; /* 19200 kBit/s */
353 pcl_send_frame(tty, &pkt);
354 pcl_read_response(tty);
356 /* Set Forward Mask */
360 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
361 pkt.ctrl_comc = 0x20;
364 pcl_send_frame(tty, &pkt);
365 pcl_read_response(tty);
367 /* Set Filter Mask */
371 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
372 pkt.ctrl_comc = 0x21;
375 pcl_send_frame(tty, &pkt);
376 pcl_read_response(tty);
378 /* Set Filter Code */
382 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
383 pkt.ctrl_comc = 0x21;
386 pcl_send_frame(tty, &pkt);
387 pcl_read_response(tty);
389 /* Set Message Transmission Timeouts */
393 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
394 pkt.ctrl_comc = 0x26;
404 pcl_send_frame(tty, &pkt);
405 pcl_read_response(tty);
407 /* Set Message Retries */
411 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
412 pkt.ctrl_comc = 0x27;
415 pcl_send_frame(tty, &pkt);
416 pcl_read_response(tty);
418 /* Set Slave ID + Data configuration */
419 pcl_set_slave_id_and_data_configuration(tty);
421 /* Insert scheduler entry */
422 pcl_insert_scheduler_entries(tty);
424 /* Set master status: Active */
428 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
429 pkt.ctrl_comc = 0x24;
430 pkt.parms[0] = pcl_lin_state.master_status;
432 pcl_send_frame(tty, &pkt);
433 pcl_read_response(tty);
435 /* Set LIN bus termination */
439 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
440 pkt.ctrl_comc = 0x25;
441 pkt.parms[0] = pcl_lin_state.bus_termination;
442 /* Should have the same value as "Set master status" */
444 pcl_send_frame(tty, &pkt);
445 pcl_read_response(tty);
450 static void pcl_reset_input_mode(int tty)
452 tcsetattr(tty, TCSANOW, &term_attr);
455 static void pcl_set_input_mode(int tty)
458 struct termios tattr;
460 /* Flush input and output queues. */
461 if (tcflush(tty, TCIOFLUSH) != 0) {
466 /* Fetch the current terminal parameters. */
468 fprintf(stderr, "Not a terminal.\n");
472 /* Save settings for later restoring */
473 tcgetattr(tty, &term_attr);
476 tcgetattr(tty, &tattr);
477 tattr.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
478 | INLCR | IGNCR | ICRNL | IXON);
479 tattr.c_oflag &= ~OPOST;
480 tattr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
481 tattr.c_cflag &= ~(CSIZE | PARENB);
482 tattr.c_cflag |= CS8;
484 tattr.c_cc[VMIN] = 1;
485 tattr.c_cc[VTIME] = 0;
487 /* Set TX, RX speed */
488 cfsetispeed(&tattr, B38400);
489 cfsetospeed(&tattr, B38400);
491 status = tcsetattr(tty, TCSANOW, &tattr);
493 perror("tcsetattr()");
497 void pcl_explain(int argc, char *argv[])
499 fprintf(stderr, "Usage: %s [OPTIONS] <SERIAL_INTERFACE>\n", argv[0]);
500 fprintf(stderr, "\n");
501 fprintf(stderr, "'pcan_lin_config' Is used for configuring PEAK PCAN-LIN device.\n");
502 fprintf(stderr, " When invoked without any OPTIONS, it configures PCAN-LIN device\n");
503 fprintf(stderr, " with default configuration from the program.\n");
504 fprintf(stderr, " The PCAN-LIN module enables CAN, LIN and serial participants to communicate.\n");
505 fprintf(stderr, "\n");
506 fprintf(stderr, "Options:\n");
507 fprintf(stderr, " -r Execute only Reset of a device\n");
508 fprintf(stderr, " -f Flash the active configuration\n");
509 fprintf(stderr, "\n");
510 fprintf(stderr, "Examples:\n");
511 fprintf(stderr, " %s /dev/ttyS0 (Configure the device with the default configuration)\n", argv[0]);
512 fprintf(stderr, " %s -r /dev/ttyS0 (Reset the device)\n", argv[0]);
515 static inline int pcl_xml_get_prop_int(xmlNodePtr cur, const xmlChar* str)
520 attr = xmlGetProp(cur, str);
524 val = atoi((const char *)attr); // FIXME error handling
530 static inline int pcl_xml_get_element_int(xmlDocPtr doc, xmlNodePtr cur)
535 key = xmlNodeListGetString(doc, cur->children, 1);
539 val = atoi((const char *)key); // FIXME error handling etc.
545 void pcl_parse_scheduler_entries(xmlDocPtr doc, xmlNodePtr cur)
549 if (!xmlStrcmp(cur->name, (const xmlChar *)"Entry")) {
552 linid = pcl_xml_get_element_int(doc, cur);
553 interval = pcl_xml_get_prop_int(cur, (const xmlChar *)"Time");
555 pcl_scheduler_entry[pcl_scheduler_entries_cnt].lin_id = linid;
556 pcl_scheduler_entry[pcl_scheduler_entries_cnt].interval_ms = interval;
557 pcl_scheduler_entries_cnt++;
559 //printf("Time = %d Entry = %d\n", interval, linid);
565 void pcl_parse_frame_configuration(xmlDocPtr doc, xmlNodePtr cur)
572 if (!xmlStrcmp(cur->name, (const xmlChar *)"Frame")) {
573 tmp_node = cur->children;
574 /* We are able to write into the main Configuration array after
575 parsing of all necessary elements (especially LIN ID) -- store
576 parsed elements in this temporary entry -- copy the entry afterwards */
577 struct pcl_frame_entry tmp_fr_entry;
581 if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"ID")) {
582 val = pcl_xml_get_element_int(doc, tmp_node);
584 //printf("ID = %d\n", val);
586 if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"Length")) {
587 val = pcl_xml_get_element_int(doc, tmp_node);
588 tmp_fr_entry.data_len = val;
589 //printf("Length = %d\n", val);
591 if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"Active")) {
592 val = pcl_xml_get_element_int(doc, tmp_node);
593 tmp_fr_entry.status = val;
594 //printf("Active = %d\n", val);
596 if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"Data")) {
598 xmlNodePtr tmp_node2;
599 tmp_node2 = tmp_node->children;
601 if (!xmlStrcmp(tmp_node2->name, (const xmlChar *)"Byte")) {
602 // Byte indexing in XML file is wrong
603 //indx = pcl_xml_get_prop_int(tmp_node2,
604 // (const xmlChar *)"Index");
605 val = pcl_xml_get_element_int(doc, tmp_node2);
606 printf("Data = %d\n", val);
607 snprintf((char *)&tmp_fr_entry.data[indx], 1, "%i", val);
610 tmp_node2 = tmp_node2->next;
613 tmp_node = tmp_node->next;
617 memcpy(&pcl_frame_entry[linid], &tmp_fr_entry,
618 sizeof(struct pcl_frame_entry));
625 int pcl_parse_configuration(char *filename)
631 filename = "config.pclin";
633 xmlKeepBlanksDefault(1);
634 doc = xmlParseFile(filename);
638 cur_node = xmlDocGetRootElement(doc);
639 if (cur_node == NULL) {
640 fprintf(stderr, "Configuration file %s is empty\n", filename);
645 /* Check for Root element */
646 if (xmlStrcmp(cur_node->name, (const xmlChar *)"PCLIN_PROFILE"))
649 /* Check for LIN element */
650 cur_node = cur_node->children;
652 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"LIN"))
655 cur_node = cur_node->next;
661 /* Process LIN configuration */
662 cur_node = cur_node->children;
664 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Active")) {
665 pcl_lin_state.is_active = pcl_xml_get_element_int(doc, cur_node);
667 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Baudrate")) {
668 pcl_lin_state.baudrate = pcl_xml_get_element_int(doc, cur_node);
670 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Master_Status")) {
671 pcl_lin_state.master_status = pcl_xml_get_element_int(doc, cur_node);
673 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Bus_Termination")) {
674 pcl_lin_state.bus_termination = pcl_xml_get_element_int(doc, cur_node);
676 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Scheduler_Entries")) {
677 pcl_parse_scheduler_entries(doc, cur_node);
679 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Frame_Configuration")) {
680 pcl_parse_frame_configuration(doc, cur_node);
683 cur_node = cur_node->next;
690 fprintf(stderr, "Invalid configuration file\n");
695 int main(int argc, char *argv[])
697 char dev[32]; // FIXME
700 int pcl_reset_device_fl = false;
701 int pcl_flash_config_fl = false;
703 while ((opt = getopt(argc, argv, "rf")) != -1) {
706 pcl_reset_device_fl = true;
709 pcl_flash_config_fl = true;
712 pcl_explain(argc, argv);
717 /* Expected argument after options */
718 if (optind >= argc) {
719 pcl_explain(argc, argv);
723 strncpy((char *) &dev, argv[optind], 32);
724 tty = open(dev, O_RDWR);
730 pcl_parse_configuration(NULL);
734 pcl_set_input_mode(tty);
736 if (pcl_reset_device_fl) {
737 pcl_reset_device(tty);
743 if (pcl_flash_config_fl) {
744 pcl_flash_config(tty);
745 pcl_reset_device(tty);
749 pcl_reset_input_mode(tty);