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
29 #define PCL_DEFAULT_CONFIG "config.pclin"
31 #define PCL_PKT_MAX_SIZE 16
32 #define PCL_HEADERS_SIZE 2 /* There are 2 bytes of headers */
33 #define PCL_CHECKSUM_SIZE 1
34 #define PCL_STX_SIZE 1
35 #define PCL_PACKET_OVERHEAD (PCL_HEADERS_SIZE + PCL_CHECKSUM_SIZE)
39 #define PCL_SEQ_NO_ofs 4
40 #define PCL_SEQ_FRLEN_ofs 0
41 #define PCL_CTRL_TIFACE_ofs 6
42 #define PCL_CTRL_COMC_ofs 0
44 #define PCL_SEQ_FRLEN_msk 0xF
46 #define PCL_PACKET_LIN_IFACE 0x2
47 #define PCL_PACKET_MODULE_IFACE 0x3
49 /* Logical representation of a packet sent to PCAN-LIN converter via RS232 */
51 uint8_t stx; /* Start transmission; Always set to 0x2 */
52 uint8_t seq_no; /* Sequence number */
53 uint8_t seq_frlen; /* Frame length */
54 uint8_t ctrl_tiface;/* Target interface */
55 uint8_t ctrl_comc; /* Command code */
56 uint8_t parms[8]; /* Parameters; Number of parameters depends
57 on the frame length */
58 uint8_t chks; /* Checksum; Bitwise XOR of all bytes except STX */
61 struct pcl_scheduler_entry {
66 /* Index in this array = LIN ID */
67 struct pcl_frame_entry {
68 int status; /* 1 = active; 0 = unactive */
73 struct pcl_lin_state {
80 struct pcl_lin_state pcl_lin_state;
81 struct pcl_frame_entry pcl_frame_entry[MAX_LIN_ID];
82 struct pcl_scheduler_entry pcl_scheduler_entry[100]; // FIXME max value
83 int pcl_scheduler_entries_cnt;
85 struct termios term_attr;
86 /* ------------------------------------------------------------------------ */
88 /* Transform 'logic' representation of a frame to exact byte sequence */
89 int pcl_serialize(pcl_packet_t *pkt, uint8_t *pkt_raw)
94 pkt_raw[0] = pkt->stx;
95 pkt_raw[1] = (pkt->seq_no << PCL_SEQ_NO_ofs) |
96 (pkt->seq_frlen << PCL_SEQ_FRLEN_ofs);
97 pkt_raw[2] = (pkt->ctrl_tiface << PCL_CTRL_TIFACE_ofs) |
98 (pkt->ctrl_comc << PCL_CTRL_COMC_ofs);
100 for (i = 0; i < pkt->seq_frlen; i++) {
101 pkt_raw[3+i] = pkt->parms[i];
104 /* Calculate XOR checksum; Skip STX */
105 for (i = 1; i <= pkt->seq_frlen + PCL_HEADERS_SIZE; i++) {
106 checksum ^= pkt_raw[i];
108 pkt_raw[i] = checksum;
110 printf("Snd: [STX] [SEQ] [PRM] [...] \n ");
111 for (i = 0; i < pkt->seq_frlen + PCL_PACKET_OVERHEAD + 1; i++)
112 printf("0x%02X ", pkt_raw[i]);
115 return pkt->seq_frlen + 4; /* real packet size */
118 int pcl_send_frame(int tty, pcl_packet_t *pkt)
123 uint8_t pkt_buff[PCL_PKT_MAX_SIZE];
125 pkt_size = pcl_serialize(pkt, (uint8_t *) &pkt_buff);
129 while (to_send > 0) {
130 sent = write(tty, pkt_buff+sent, to_send);
134 memset(pkt, '\0', sizeof(pcl_packet_t));
138 int pcl_read_reset_response(int tty)
143 char str_match[] = {'G', 'm', 'b', 'H'};
146 ret = read(tty, &c, 1);
152 if ((c == str_match[i]) && (i == 3))
153 i = -1; /* We are done -- Stop the loop*/
154 else if (c == str_match[i])
155 i++; /* Match found -- Go to the next letter */
157 i = 0; /* Start from beginning */
166 int pcl_read_response(int tty)
172 uint8_t buff[PCL_PKT_MAX_SIZE];
173 //memset(buff, '\0', sizeof(buff));
175 /* Read 2 bytes of header */
176 received = read(tty, &buff[0], 1); /* Read STX */
180 if (buff[0] == 0x2) { /* STX ok */
181 received = read(tty, &buff[1], 1); /* Read SEQ */
188 data_length = (buff[1] & PCL_SEQ_FRLEN_msk);
189 to_read = data_length + 1; /* +chksm */
190 while (to_read > 0) {
191 received = read(tty, &buff[2], to_read);
195 printf("Rcv: [STX] [SEQ] [PRM] [CHK]\n"); // FIXME add spaces before "CHKS" when
196 //more than 1 byte received in params
197 printf(" 0x%02X 0x%02X", buff[0], buff[1]);
198 for (i = 0; i < data_length; i++) {
199 printf(" 0x%02X", buff[i + 2]);
201 printf(" 0x%02X\n\n", buff[i]);
206 void pcl_insert_scheduler_entries(int tty)
211 /* Insert scheduler entry */
212 for (i = 0; i < (sizeof(pcl_scheduler_entry)/sizeof(struct pcl_scheduler_entry)); i++) {
216 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
217 pkt.ctrl_comc = 0x28;
218 pkt.parms[0] = (pcl_scheduler_entry[i].interval_ms) & 0xFF;
219 pkt.parms[1] = (pcl_scheduler_entry[i].interval_ms >> 8) & 0xFF;
220 pkt.parms[2] = pcl_scheduler_entry[i].lin_id; /* LIN ID */
222 pcl_send_frame(tty, &pkt);
223 pcl_read_response(tty);
228 void pcl_set_slave_id_and_data_configuration(int tty)
233 /* Set Slave ID + Data Configuration */
234 for (i = 0; i < 0x3F; i++) {
237 if (pcl_frame_entry[i].status == PCL_ACTIVE) {
240 pkt.seq_frlen = pcl_frame_entry[i].data_len + 2;
241 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
242 pkt.ctrl_comc = 0x29;
243 pkt.parms[0] = pcl_frame_entry[i].status; /* Field Status */
244 pkt.parms[1] = i; /* LIN ID */
245 pkt.parms[2] = pcl_frame_entry[i].data[0]; /* Data */
246 pkt.parms[3] = pcl_frame_entry[i].data[1]; /* Data */
247 pkt.parms[4] = pcl_frame_entry[i].data[2]; /* Data */
248 pkt.parms[5] = pcl_frame_entry[i].data[3]; /* Data */
249 pkt.parms[6] = pcl_frame_entry[i].data[4]; /* Data */
250 pkt.parms[7] = pcl_frame_entry[i].data[5]; /* Data */
251 pkt.parms[8] = pcl_frame_entry[i].data[6]; /* Data */
252 pkt.parms[9] = pcl_frame_entry[i].data[7]; /* Data */
265 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
266 pkt.ctrl_comc = 0x29;
267 pkt.parms[0] = 0x00; /* Field Status -- unactive */
268 pkt.parms[1] = i; /* LIN ID */
271 pcl_send_frame(tty, &pkt);
272 pcl_read_response(tty);
276 void pcl_flash_config(int tty)
280 /* Flash Current Configuration */
284 pkt.ctrl_tiface = PCL_PACKET_MODULE_IFACE;
287 pcl_send_frame(tty, &pkt);
288 pcl_read_response(tty);
291 void pcl_reset_device(int tty)
299 pkt.ctrl_tiface = PCL_PACKET_MODULE_IFACE;
302 pcl_send_frame(tty, &pkt);
303 pcl_read_reset_response(tty);
306 int pcl_lin_init(int tty)
310 /* Initialization according to current parameters */
314 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
317 pcl_send_frame(tty, &pkt);
318 pcl_read_response(tty);
322 pcl_reset_device(tty);
324 /* Erase Master Scheduler List */
328 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
329 pkt.ctrl_comc = 0x33;
331 pcl_send_frame(tty, &pkt);
332 pcl_read_response(tty);
334 /* Set Activation Status */
338 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
339 pkt.ctrl_comc = 0x1E;
340 pkt.parms[0] = pcl_lin_state.is_active;
342 pcl_send_frame(tty, &pkt);
343 pcl_read_response(tty);
349 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
350 pkt.ctrl_comc = 0x1F;
352 pkt.parms[1] = 0x4B; /* 19200 kBit/s */
354 pcl_send_frame(tty, &pkt);
355 pcl_read_response(tty);
357 /* Set Forward Mask */
361 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
362 pkt.ctrl_comc = 0x20;
365 pcl_send_frame(tty, &pkt);
366 pcl_read_response(tty);
368 /* Set Filter Mask */
372 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
373 pkt.ctrl_comc = 0x21;
376 pcl_send_frame(tty, &pkt);
377 pcl_read_response(tty);
379 /* Set Filter Code */
383 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
384 pkt.ctrl_comc = 0x21;
387 pcl_send_frame(tty, &pkt);
388 pcl_read_response(tty);
390 /* Set Message Transmission Timeouts */
394 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
395 pkt.ctrl_comc = 0x26;
405 pcl_send_frame(tty, &pkt);
406 pcl_read_response(tty);
408 /* Set Message Retries */
412 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
413 pkt.ctrl_comc = 0x27;
416 pcl_send_frame(tty, &pkt);
417 pcl_read_response(tty);
419 /* Set Slave ID + Data configuration */
420 pcl_set_slave_id_and_data_configuration(tty);
422 /* Insert scheduler entry */
423 pcl_insert_scheduler_entries(tty);
425 /* Set master status: Active */
429 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
430 pkt.ctrl_comc = 0x24;
431 pkt.parms[0] = pcl_lin_state.master_status;
433 pcl_send_frame(tty, &pkt);
434 pcl_read_response(tty);
436 /* Set LIN bus termination */
440 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
441 pkt.ctrl_comc = 0x25;
442 pkt.parms[0] = pcl_lin_state.bus_termination;
443 /* Should have the same value as "Set master status" */
445 pcl_send_frame(tty, &pkt);
446 pcl_read_response(tty);
451 static void pcl_reset_input_mode(int tty)
453 tcsetattr(tty, TCSANOW, &term_attr);
456 static void pcl_set_input_mode(int tty)
459 struct termios tattr;
461 /* Flush input and output queues. */
462 if (tcflush(tty, TCIOFLUSH) != 0) {
467 /* Fetch the current terminal parameters. */
469 fprintf(stderr, "Not a terminal.\n");
473 /* Save settings for later restoring */
474 tcgetattr(tty, &term_attr);
477 tcgetattr(tty, &tattr);
478 tattr.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
479 | INLCR | IGNCR | ICRNL | IXON);
480 tattr.c_oflag &= ~OPOST;
481 tattr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
482 tattr.c_cflag &= ~(CSIZE | PARENB);
483 tattr.c_cflag |= CS8;
485 tattr.c_cc[VMIN] = 1;
486 tattr.c_cc[VTIME] = 0;
488 /* Set TX, RX speed */
489 cfsetispeed(&tattr, B38400);
490 cfsetospeed(&tattr, B38400);
492 status = tcsetattr(tty, TCSANOW, &tattr);
494 perror("tcsetattr()");
498 /****** XML PARSING ******/
500 static inline int pcl_xml_get_prop_int(xmlNodePtr cur, const xmlChar* str)
505 attr = xmlGetProp(cur, str);
509 val = atoi((const char *)attr); // FIXME error handling
515 static inline int pcl_xml_get_element_int(xmlDocPtr doc, xmlNodePtr cur)
520 key = xmlNodeListGetString(doc, cur->children, 1);
524 val = atoi((const char *)key); // FIXME error handling etc.
530 void pcl_parse_scheduler_entries(xmlDocPtr doc, xmlNodePtr cur)
534 if (!xmlStrcmp(cur->name, (const xmlChar *)"Entry")) {
537 linid = pcl_xml_get_element_int(doc, cur);
538 interval = pcl_xml_get_prop_int(cur, (const xmlChar *)"Time");
540 pcl_scheduler_entry[pcl_scheduler_entries_cnt].lin_id = linid;
541 pcl_scheduler_entry[pcl_scheduler_entries_cnt].interval_ms = interval;
542 pcl_scheduler_entries_cnt++;
544 //printf("Time = %d Entry = %d\n", interval, linid);
550 void pcl_parse_frame_configuration(xmlDocPtr doc, xmlNodePtr cur)
557 if (!xmlStrcmp(cur->name, (const xmlChar *)"Frame")) {
558 tmp_node = cur->children;
559 /* We are able to write into the main Configuration array after
560 parsing of all necessary elements (especially LIN ID) -- store
561 parsed elements in this temporary entry -- copy the entry afterwards */
562 struct pcl_frame_entry tmp_fr_entry;
566 if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"ID")) {
567 val = pcl_xml_get_element_int(doc, tmp_node);
569 //printf("ID = %d\n", val);
571 if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"Length")) {
572 val = pcl_xml_get_element_int(doc, tmp_node);
573 tmp_fr_entry.data_len = val;
574 //printf("Length = %d\n", val);
576 if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"Active")) {
577 val = pcl_xml_get_element_int(doc, tmp_node);
578 tmp_fr_entry.status = val;
579 //printf("Active = %d\n", val);
581 if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"Data")) {
583 xmlNodePtr tmp_node2;
584 tmp_node2 = tmp_node->children;
586 if (!xmlStrcmp(tmp_node2->name, (const xmlChar *)"Byte")) {
587 // Byte indexing in XML file is wrong
588 //indx = pcl_xml_get_prop_int(tmp_node2,
589 // (const xmlChar *)"Index");
590 val = pcl_xml_get_element_int(doc, tmp_node2);
591 //printf("Data = %d\n", val);
592 snprintf((char *)&tmp_fr_entry.data[indx], 1, "%i", val);
595 tmp_node2 = tmp_node2->next;
598 tmp_node = tmp_node->next;
602 memcpy(&pcl_frame_entry[linid], &tmp_fr_entry,
603 sizeof(struct pcl_frame_entry));
610 int pcl_parse_configuration(char *filename)
616 filename = PCL_DEFAULT_CONFIG;
618 xmlKeepBlanksDefault(1);
619 doc = xmlParseFile(filename);
623 cur_node = xmlDocGetRootElement(doc);
624 if (cur_node == NULL) {
625 fprintf(stderr, "Configuration file %s is empty\n", filename);
630 /* Check for Root element */
631 if (xmlStrcmp(cur_node->name, (const xmlChar *)"PCLIN_PROFILE"))
634 /* Check for LIN element */
635 cur_node = cur_node->children;
637 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"LIN"))
640 cur_node = cur_node->next;
646 /* Process LIN configuration */
647 cur_node = cur_node->children;
649 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Active")) {
650 pcl_lin_state.is_active = pcl_xml_get_element_int(doc, cur_node);
652 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Baudrate")) {
653 pcl_lin_state.baudrate = pcl_xml_get_element_int(doc, cur_node);
655 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Master_Status")) {
656 pcl_lin_state.master_status = pcl_xml_get_element_int(doc, cur_node);
658 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Bus_Termination")) {
659 pcl_lin_state.bus_termination = pcl_xml_get_element_int(doc, cur_node);
661 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Scheduler_Entries")) {
662 pcl_parse_scheduler_entries(doc, cur_node);
664 if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Frame_Configuration")) {
665 pcl_parse_frame_configuration(doc, cur_node);
668 cur_node = cur_node->next;
675 fprintf(stderr, "Invalid configuration file\n");
680 void pcl_explain(int argc, char *argv[])
682 fprintf(stderr, "Usage: %s [OPTIONS] <SERIAL_INTERFACE>\n", argv[0]);
683 fprintf(stderr, "\n");
684 fprintf(stderr, "'pcan_lin_config' Is used for configuring PEAK PCAN-LIN device.\n");
685 fprintf(stderr, " When invoked without any OPTIONS, it configures PCAN-LIN device\n");
686 fprintf(stderr, " with configuration obtained from '"PCL_DEFAULT_CONFIG"' file (if it exists).\n");
687 fprintf(stderr, " The PCAN-LIN module enables CAN, LIN and serial participants to communicate.\n");
688 fprintf(stderr, "\n");
689 fprintf(stderr, "Options:\n");
690 fprintf(stderr, " -r Execute only Reset of a device\n");
691 fprintf(stderr, " -f Flash the active configuration\n");
692 fprintf(stderr, " -c <FILE> Path to XML configuration file\n");
693 fprintf(stderr, "\n");
694 fprintf(stderr, "Examples:\n");
695 fprintf(stderr, " %s /dev/ttyS0 (Configure the device with the configuration from '"PCL_DEFAULT_CONFIG"')\n",
697 fprintf(stderr, " %s -r /dev/ttyS0 (Reset the device)\n", argv[0]);
700 int main(int argc, char *argv[])
702 char dev[32]; // FIXME
706 int pcl_reset_device_fl = false;
707 int pcl_flash_config_fl = false;
708 char *filename = NULL;
710 while ((opt = getopt(argc, argv, "rfc:")) != -1) {
713 pcl_reset_device_fl = true;
716 pcl_flash_config_fl = true;
722 pcl_explain(argc, argv);
727 /* Expected argument after options */
728 if (optind >= argc) {
729 pcl_explain(argc, argv);
733 strncpy((char *) &dev, argv[optind], 32);
734 tty = open(dev, O_RDWR);
740 ret = pcl_parse_configuration(filename);
742 printf("Configuration file %s parsed correctly\n", filename);
745 pcl_set_input_mode(tty);
747 if (pcl_reset_device_fl) {
748 pcl_reset_device(tty);
754 if (pcl_flash_config_fl) {
755 pcl_flash_config(tty);
756 pcl_reset_device(tty);
760 pcl_reset_input_mode(tty);