From: Pavel Pisa Date: Fri, 20 Jul 2012 13:07:22 +0000 (+0200) Subject: Merge /home/pi/repo/lin/pcan_lin_config/submodule/linux-lin X-Git-Url: http://rtime.felk.cvut.cz/gitweb/linux-lin.git/commitdiff_plain/e1fc1596b4cb0ca22544c52bd364973832f48379?hp=15febc596cdee62724c16fa7a8ed8dfcb9b667bb Merge /home/pi/repo/lin/pcan_lin_config/submodule/linux-lin --- diff --git a/lin_config/examples/master_slave.pclin b/lin_config/examples/master_slave.pclin new file mode 100644 index 0000000..573e067 Binary files /dev/null and b/lin_config/examples/master_slave.pclin differ diff --git a/lin_config/src/Makefile b/lin_config/src/Makefile new file mode 100644 index 0000000..db50031 --- /dev/null +++ b/lin_config/src/Makefile @@ -0,0 +1,17 @@ +CC=gcc +CFLAGS=-std=gnu99 -Wall -pedantic $(DEBUG) `xml2-config --cflags` -I$(INCLUDE) +LIBS=`xml2-config --libs` +INCLUDE=../../linux_lin_git/sllin +DEBUG=-ggdb + +objects = linc_parse_xml.o pcl_config.o sllin_config.o lin_config.o + +lin_config: $(objects) + $(CC) $(objects) $(LIBS) -o lin_config + +%.o : %.c %.h + $(CC) $(CFLAGS) $(LIBS) -c $< -o $@ + +.PHONY: clean +clean: + rm *.o diff --git a/lin_config/src/lin_config.c b/lin_config/src/lin_config.c new file mode 100644 index 0000000..a42d3e8 --- /dev/null +++ b/lin_config/src/lin_config.c @@ -0,0 +1,143 @@ +/* + * PCAN-LIN, RS-232 to CAN/LIN converter control application + * + * This program is free software; you can distribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 of + * the License. + * + * Copyright: (c) 2012 Czech Technical University in Prague + * Authors: Rostislav Lisovy + */ + +/* + Used prefixes explanation: + pcl_ -- PCAN-LIN (hw) related functions + sll_ -- sllin (tty lin implementation) related functions + linc_ -- LIN config general functions +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "linc_parse_xml.h" +#include "pcl_config.h" +#include "sllin_config.h" +#include "lin_config.h" + +#include "linux/lin_bus.h" + +struct linc_lin_state linc_lin_state; + + +void linc_explain(int argc, char *argv[]) +{ +// FIXME what is default behaviour +// Write a warning about not using a rs232--usb converter for sllin + fprintf(stderr, "Usage: %s [OPTIONS] \n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "'lin_config' is used for configuring sllin -- " \ + "simple LIN device implemented\n" \ + " as a TTY line discipline for arbitrary UART interface.\n" \ + " This program is able to configure PCAN-LIN (RS232 configurable " \ + "LIN node) as well.\n" \ + " When invoked without any OPTIONS, it configures PCAN-LIN device\n" \ + " with configuration obtained from '"PCL_DEFAULT_CONFIG"' " \ + "file (if it exists).\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "SERIAL_INTERFACE is in format CLASS:PATH\n"); + fprintf(stderr, " CLASS defines the device class -- it is either " \ + "'sllin' or 'pcanlin'\n"); + fprintf(stderr, " PATH is path to the serial interface, e.g /dev/ttyS0\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "General options:\n"); + fprintf(stderr, " -c Path to XML configuration file in PCLIN format\n"); + fprintf(stderr, " -r Execute only Reset of a device\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "PCAN-LIN specific options:\n"); + fprintf(stderr, " -f Store the active configuration into internal " \ + "flash memory\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Sllin specific options:\n"); + fprintf(stderr, " -a Attach sllin TTY line discipline to " \ + "particular SERIAL_INTERFACE\n"); + fprintf(stderr, " -d Detach sllin TTY line discipline from " \ + "particular SERIAL_INTERFACE\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Examples:\n"); + fprintf(stderr, " %s sllin:/dev/ttyS0 (Configure the device with the " \ + "configuration from '"PCL_DEFAULT_CONFIG"')\n", argv[0]); + fprintf(stderr, " %s -r pcanlin:/dev/ttyS0 (Reset the device)\n", argv[0]); +} + +int main(int argc, char *argv[]) +{ + int ret; + int opt; + int flags = 0; + char *filename = NULL; + + while ((opt = getopt(argc, argv, "rfc:ad")) != -1) { + switch (opt) { + case 'r': + flags |= RESET_DEVICE_fl; + break; + case 'f': + flags |= FLASH_CONF_fl; + break; + case 'c': + filename = optarg; + break; + case 'a': + flags |= SLLIN_ATTACH_fl; + break; + case 'd': + flags |= SLLIN_DETACH_fl; + break; + default: + linc_explain(argc, argv); + return EXIT_FAILURE; + } + } + + /* Expected argument after options */ + if (optind >= argc) { + linc_explain(argc, argv); + exit(EXIT_FAILURE); + } + + linc_lin_state.dev = strdup(argv[optind]); + + ret = linc_parse_configuration(filename, &linc_lin_state); + if (!ret) + printf("Configuration file %s parsed correctly\n", filename); + + linc_lin_state.flags = flags; + //ret = pcl_config(&linc_lin_state); + ret = sllin_config(&linc_lin_state); + + if (ret < 0) + return EXIT_FAILURE; + if (ret == LIN_EXIT_OK) { + + return EXIT_SUCCESS; + } + + printf("Running in background ...\n"); + ret = daemon(0, 0); + if (ret < 0) { + perror("daemon()"); + return EXIT_FAILURE; + } + + // FIXME free() linc_lin_state? + /* Sleep to keep the line discipline active. */ + pause(); + + return EXIT_SUCCESS; +} diff --git a/lin_config/src/lin_config.h b/lin_config/src/lin_config.h new file mode 100644 index 0000000..6d4ea1b --- /dev/null +++ b/lin_config/src/lin_config.h @@ -0,0 +1,45 @@ +#ifndef _LIN_CONFIG_H_ +#define _LIN_CONFIG_H_ + +#define LIN_EXIT_OK 1 + +/* Flags passed to configuration functions */ +#define FLASH_CONF_fl (1 << 0) +#define RESET_DEVICE_fl (1 << 1) +#define SLLIN_ATTACH_fl (1 << 2) +#define SLLIN_DETACH_fl (1 << 3) + +#define MAX_LIN_ID 0x3F +#define PCL_DEFAULT_CONFIG "config.pclin" + +struct linc_scheduler_entry { + int lin_id; + int interval_ms; +}; + +/* Index in this array = LIN ID */ +struct linc_frame_entry { + int status; /* 1 = active; 0 = unactive */ + int data_len; + char data[8]; +}; + +struct linc_lin_state { + int is_active; /* Is LIN device active */ + int baudrate; /* LIN baudrate */ + int master_status; /* LIN node type -- Master or Slave */ + int bus_termination; /* LIN bus termination in device -- Master or Slave */ + + /* Subscriber/publisher table entries */ + struct linc_frame_entry frame_entry[MAX_LIN_ID + 1]; + /* Scheduler table entries */ + // FIXME max value + struct linc_scheduler_entry scheduler_entry[100]; + int scheduler_entries_cnt; /* No. of configured scheduler entries */ + + char *dev; /* Path to LIN device to be configured */ + int flags; /* Flags passed to configuration function + of particular device */ +}; + +#endif /* _LIN_CONFIG_H_ */ diff --git a/lin_config/src/linc_parse_xml.c b/lin_config/src/linc_parse_xml.c new file mode 100644 index 0000000..160ef84 --- /dev/null +++ b/lin_config/src/linc_parse_xml.c @@ -0,0 +1,186 @@ +#include +#include +#include +#include "lin_config.h" + +static inline int linc_xml_get_prop_int(xmlNodePtr cur, const xmlChar* str) +{ + int val; + xmlChar *attr; + + attr = xmlGetProp(cur, str); + if (!attr) + assert(0); + + val = atoi((const char *)attr); // FIXME error handling + xmlFree(attr); + + return val; +} + +static inline int linc_xml_get_element_int(xmlDocPtr doc, xmlNodePtr cur) +{ + xmlChar *key; + int val; + + key = xmlNodeListGetString(doc, cur->children, 1); + if (!key) + assert(0); + + val = atoi((const char *)key); // FIXME error handling etc. + xmlFree(key); + + return val; +} + +void linc_parse_scheduler_entries(struct linc_lin_state *linc_lin_state, xmlDocPtr doc, xmlNodePtr cur) +{ + cur = cur->children; + while (cur) { + if (!xmlStrcmp(cur->name, (const xmlChar *)"Entry")) { + int linid; + int interval; + linid = linc_xml_get_element_int(doc, cur); + interval = linc_xml_get_prop_int(cur, (const xmlChar *)"Time"); + + linc_lin_state->scheduler_entry[linc_lin_state->scheduler_entries_cnt].lin_id = linid; + linc_lin_state->scheduler_entry[linc_lin_state->scheduler_entries_cnt].interval_ms = interval; + linc_lin_state->scheduler_entries_cnt++; + + //printf("Time = %d Lin ID = %d Entry no. = %d\n", + // interval, linid, linc_lin_state->scheduler_entries_cnt-1); + } + cur = cur->next; + } +} + +void linc_parse_frame_configuration(struct linc_lin_state *linc_lin_state, xmlDocPtr doc, xmlNodePtr cur) +{ + xmlNodePtr tmp_node; + int val; + + cur = cur->children; + while (cur) { + if (!xmlStrcmp(cur->name, (const xmlChar *)"Frame")) { + tmp_node = cur->children; + /* We are able to write into the main Configuration array after + parsing of all necessary elements (especially LIN ID) -- store + parsed elements in this temporary entry -- copy the entry afterwards */ + struct linc_frame_entry tmp_fr_entry; + int linid = -1; + + while (tmp_node) { + if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"ID")) { + val = linc_xml_get_element_int(doc, tmp_node); + linid = val; + //printf("ID = %d\n", val); + } + if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"Length")) { + val = linc_xml_get_element_int(doc, tmp_node); + tmp_fr_entry.data_len = val; + //printf("Length = %d\n", val); + } + if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"Active")) { + val = linc_xml_get_element_int(doc, tmp_node); + tmp_fr_entry.status = val; + //printf("Active = %d\n", val); + } + if (!xmlStrcmp(tmp_node->name, (const xmlChar *)"Data")) { + int indx = 0; + xmlNodePtr tmp_node2; + tmp_node2 = tmp_node->children; + while (tmp_node2) { + if (!xmlStrcmp(tmp_node2->name, (const xmlChar *)"Byte")) { + // Byte indexing in XML file is wrong + //indx = linc_xml_get_prop_int(tmp_node2, + // (const xmlChar *)"Index"); + val = linc_xml_get_element_int(doc, tmp_node2); + //printf("Data = %d\n", val); + snprintf((char *)&tmp_fr_entry.data[indx], 1, "%i", val); + indx++; + } + tmp_node2 = tmp_node2->next; + } + } + tmp_node = tmp_node->next; + } + + if (linid >= 0 && linid <= MAX_LIN_ID) { + memcpy(&linc_lin_state->frame_entry[linid], &tmp_fr_entry, + sizeof(struct linc_frame_entry)); + } + } + cur = cur->next; + } +} + +int linc_parse_configuration(char *filename, struct linc_lin_state *linc_lin_state) +{ + xmlDocPtr doc; + xmlNodePtr cur_node; + + if (!filename) + filename = PCL_DEFAULT_CONFIG; + + xmlKeepBlanksDefault(1); + doc = xmlParseFile(filename); + if (doc == NULL) + return -1; + + cur_node = xmlDocGetRootElement(doc); + if (cur_node == NULL) { + fprintf(stderr, "Configuration file %s is empty\n", filename); + xmlFreeDoc(doc); + return -1; + } + + /* Check for Root element */ + if (xmlStrcmp(cur_node->name, (const xmlChar *)"PCLIN_PROFILE")) + goto exit_failure; + + /* Check for LIN element */ + cur_node = cur_node->children; + while (cur_node) { + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"LIN")) + break; + + cur_node = cur_node->next; + } + + if (!cur_node) + goto exit_failure; + + /* Process LIN configuration */ + cur_node = cur_node->children; + while (cur_node) { + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Active")) { + linc_lin_state->is_active = linc_xml_get_element_int(doc, cur_node); + } + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Baudrate")) { + linc_lin_state->baudrate = linc_xml_get_element_int(doc, cur_node); + } + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Master_Status")) { + linc_lin_state->master_status = linc_xml_get_element_int(doc, cur_node); + } + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Bus_Termination")) { + linc_lin_state->bus_termination = linc_xml_get_element_int(doc, cur_node); + } + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Scheduler_Entries")) { + linc_parse_scheduler_entries(linc_lin_state, doc, cur_node); + } + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Frame_Configuration")) { + linc_parse_frame_configuration(linc_lin_state, doc, cur_node); + } + + cur_node = cur_node->next; + } + + xmlFreeDoc(doc); + return 0; + +exit_failure: + fprintf(stderr, "Invalid configuration file\n"); + xmlFreeDoc(doc); + return -1; +} + diff --git a/lin_config/src/linc_parse_xml.h b/lin_config/src/linc_parse_xml.h new file mode 100644 index 0000000..09efa16 --- /dev/null +++ b/lin_config/src/linc_parse_xml.h @@ -0,0 +1,8 @@ +#ifndef _LINC_PARSE_H_ +#define _LINC_PARSE_H_ + +#include "lin_config.h" + +int linc_parse_configuration(char *filename, struct linc_lin_state *linc_lin_state); + +#endif /* _LINC_PARSE_H_ */ diff --git a/lin_config/src/pcl_config.c b/lin_config/src/pcl_config.c new file mode 100644 index 0000000..1b313e8 --- /dev/null +++ b/lin_config/src/pcl_config.c @@ -0,0 +1,456 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcl_config.h" +#include "lin_config.h" + +struct termios term_attr; + +static void pcl_reset_input_mode(int tty) +{ + tcsetattr(tty, TCSANOW, &term_attr); +} + +static void pcl_set_input_mode(int tty) +{ + int status; + struct termios tattr; + + /* Flush input and output queues. */ + if (tcflush(tty, TCIOFLUSH) != 0) { + perror("tcflush"); + exit(EXIT_FAILURE); + } + + /* Fetch the current terminal parameters. */ + if (!isatty(tty)) { + fprintf(stderr, "Not a terminal.\n"); + exit(EXIT_FAILURE); + } + + /* Save settings for later restoring */ + tcgetattr(tty, &term_attr); + + /* RAW mode */ + tcgetattr(tty, &tattr); + tattr.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP + | INLCR | IGNCR | ICRNL | IXON); + tattr.c_oflag &= ~OPOST; + tattr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + tattr.c_cflag &= ~(CSIZE | PARENB); + tattr.c_cflag |= CS8; + + tattr.c_cc[VMIN] = 1; + tattr.c_cc[VTIME] = 0; + + /* Set TX, RX speed */ + cfsetispeed(&tattr, B38400); + cfsetospeed(&tattr, B38400); + + status = tcsetattr(tty, TCSANOW, &tattr); + if (status == -1) + perror("tcsetattr()"); +} + +/* Transform 'logic' representation of a frame to exact byte sequence */ +int pcl_serialize(pcl_packet_t *pkt, uint8_t *pkt_raw) +{ + int i; + uint8_t checksum = 0; + + pkt_raw[0] = pkt->stx; + pkt_raw[1] = (pkt->seq_no << PCL_SEQ_NO_ofs) | + (pkt->seq_frlen << PCL_SEQ_FRLEN_ofs); + pkt_raw[2] = (pkt->ctrl_tiface << PCL_CTRL_TIFACE_ofs) | + (pkt->ctrl_comc << PCL_CTRL_COMC_ofs); + + for (i = 0; i < pkt->seq_frlen; i++) { + pkt_raw[3+i] = pkt->parms[i]; + } + + /* Calculate XOR checksum; Skip STX */ + for (i = 1; i <= pkt->seq_frlen + PCL_HEADERS_SIZE; i++) { + checksum ^= pkt_raw[i]; + } + pkt_raw[i] = checksum; + + printf("Snd: [STX] [SEQ] [PRM] [...] \n "); + for (i = 0; i < pkt->seq_frlen + PCL_PACKET_OVERHEAD + 1; i++) + printf("0x%02X ", pkt_raw[i]); + printf("\n"); + + return pkt->seq_frlen + 4; /* real packet size */ +} + +int pcl_send_frame(int tty, pcl_packet_t *pkt) +{ + int pkt_size; + int to_send; + int sent; + uint8_t pkt_buff[PCL_PKT_MAX_SIZE]; + + pkt_size = pcl_serialize(pkt, (uint8_t *) &pkt_buff); + to_send = pkt_size; + sent = 0; + + while (to_send > 0) { + sent = write(tty, pkt_buff+sent, to_send); + to_send -= sent; + } + + memset(pkt, '\0', sizeof(pcl_packet_t)); + return 0; +} + +int pcl_read_reset_response(int tty) +{ + char c; + int ret; + int i = 0; + char str_match[] = {'G', 'm', 'b', 'H'}; + + do { + ret = read(tty, &c, 1); + if (ret == -1) + perror("read()"); + + printf("%c", c); + + if ((c == str_match[i]) && (i == 3)) + i = -1; /* We are done -- Stop the loop*/ + else if (c == str_match[i]) + i++; /* Match found -- Go to the next letter */ + else + i = 0; /* Start from beginning */ + + } while (i != -1); + + printf("\n\n"); + + return 0; +} + +int pcl_read_response(int tty) +{ + int i; + int received = 0; + int to_read = 0; + int data_length = 0; + uint8_t buff[PCL_PKT_MAX_SIZE]; + //memset(buff, '\0', sizeof(buff)); + + /* Read 2 bytes of header */ + received = read(tty, &buff[0], 1); /* Read STX */ + if (received == -1) + perror("read()"); + + if (buff[0] == 0x2) { /* STX ok */ + received = read(tty, &buff[1], 1); /* Read SEQ */ + if (received == -1) + perror("read()"); + } else { + return 1; + } + + data_length = (buff[1] & PCL_SEQ_FRLEN_msk); + to_read = data_length + 1; /* +chksm */ + while (to_read > 0) { + received = read(tty, &buff[2], to_read); + to_read -= received; + } + + printf("Rcv: [STX] [SEQ] [PRM] [CHK]\n"); // FIXME add spaces before "CHKS" when + //more than 1 byte received in params + printf(" 0x%02X 0x%02X", buff[0], buff[1]); + for (i = 0; i < data_length; i++) { + printf(" 0x%02X", buff[i + 2]); + } + printf(" 0x%02X\n\n", buff[i]); + + return 0; +} + +void pcl_insert_scheduler_entries(int tty, struct linc_lin_state *linc_lin_state) +{ + pcl_packet_t pkt; + int i; + + /* Insert scheduler entry */ + for (i = 0; i < (sizeof(linc_lin_state->scheduler_entry)/sizeof(struct linc_scheduler_entry)); i++) { + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x3; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x28; + pkt.parms[0] = (linc_lin_state->scheduler_entry[i].interval_ms) & 0xFF; + pkt.parms[1] = (linc_lin_state->scheduler_entry[i].interval_ms >> 8) & 0xFF; + pkt.parms[2] = linc_lin_state->scheduler_entry[i].lin_id; /* LIN ID */ + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + } + +} + +void pcl_set_slave_id_and_data_configuration(int tty, struct linc_lin_state *linc_lin_state) +{ + pcl_packet_t pkt; + int i; + + /* Set Slave ID + Data Configuration */ + for (i = 0; i < 0x3F; i++) { + int len; + + if (linc_lin_state->frame_entry[i].status == 1) { /* Is Active */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = linc_lin_state->frame_entry[i].data_len + 2; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x29; + pkt.parms[0] = linc_lin_state->frame_entry[i].status; /* Field Status */ + pkt.parms[1] = i; /* LIN ID */ + pkt.parms[2] = linc_lin_state->frame_entry[i].data[0]; /* Data */ + pkt.parms[3] = linc_lin_state->frame_entry[i].data[1]; /* Data */ + pkt.parms[4] = linc_lin_state->frame_entry[i].data[2]; /* Data */ + pkt.parms[5] = linc_lin_state->frame_entry[i].data[3]; /* Data */ + pkt.parms[6] = linc_lin_state->frame_entry[i].data[4]; /* Data */ + pkt.parms[7] = linc_lin_state->frame_entry[i].data[5]; /* Data */ + pkt.parms[8] = linc_lin_state->frame_entry[i].data[6]; /* Data */ + pkt.parms[9] = linc_lin_state->frame_entry[i].data[7]; /* Data */ + + } else { + if (i < 0x20) + len = 4; + else if (i < 0x30) + len = 6; + else + len = 8; + + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = len; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x29; + pkt.parms[0] = 0x00; /* Field Status -- unactive */ + pkt.parms[1] = i; /* LIN ID */ + } + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + } +} + +void pcl_flash_config(int tty) +{ + pcl_packet_t pkt; + + /* Flash Current Configuration */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x0; + pkt.ctrl_tiface = PCL_PACKET_MODULE_IFACE; + pkt.ctrl_comc = 0x3; + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); +} + +void pcl_reset_device(int tty) +{ + pcl_packet_t pkt; + + /* Reset module */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x0; + pkt.ctrl_tiface = PCL_PACKET_MODULE_IFACE; + pkt.ctrl_comc = 0x4; + + pcl_send_frame(tty, &pkt); + pcl_read_reset_response(tty); +} + +int pcl_lin_init(int tty, struct linc_lin_state *linc_lin_state) +{ + pcl_packet_t pkt; +#if 0 + /* Initialization according to current parameters */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x0; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x0; + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); +#endif + + /* Reset module */ + pcl_reset_device(tty); + + /* Erase Master Scheduler List */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x0; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x33; + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + + /* Set Activation Status */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x1; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x1E; + pkt.parms[0] = linc_lin_state->is_active; + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + + /* Set bitrate */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x2; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x1F; + pkt.parms[0] = linc_lin_state->baudrate & 0xFF; + pkt.parms[1] = (linc_lin_state->baudrate >> 8) & 0xFF; + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + + /* Set Forward Mask */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x1; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x20; + pkt.parms[0] = 0x00; + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + + /* Set Filter Mask */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x1; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x21; + pkt.parms[0] = 0xFF; + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + + /* Set Filter Code */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x1; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x21; + pkt.parms[0] = 0x00; + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + + /* Set Message Transmission Timeouts */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x8; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x26; + pkt.parms[0] = 0xA0; + pkt.parms[1] = 0x0F; + pkt.parms[2] = 0x09; + pkt.parms[3] = 0x00; + pkt.parms[4] = 0x06; + pkt.parms[5] = 0x00; + pkt.parms[6] = 0x05; + pkt.parms[7] = 0x00; + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + + /* Set Message Retries */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x1; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x27; + pkt.parms[0] = 0x0; + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + + /* Set Slave ID + Data configuration */ + pcl_set_slave_id_and_data_configuration(tty, linc_lin_state); + + /* Insert scheduler entry */ + pcl_insert_scheduler_entries(tty, linc_lin_state); + + /* Set master status: Active */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x1; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x24; + pkt.parms[0] = linc_lin_state->master_status; + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + + /* Set LIN bus termination */ + pkt.stx = PCL_STX; + pkt.seq_no = 0x0; + pkt.seq_frlen = 0x1; + pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE; + pkt.ctrl_comc = 0x25; + pkt.parms[0] = linc_lin_state->bus_termination; + /* Should have the same value as "Set master status" */ + + pcl_send_frame(tty, &pkt); + pcl_read_response(tty); + + return 0; +} + +int pcl_config(struct linc_lin_state *linc_lin_state) +{ + int tty; + + tty = open(linc_lin_state->dev, O_RDWR); + if (tty < 0) { + perror("open()"); + return -4; + } + /* Configure UART */ + pcl_set_input_mode(tty); + + + if (linc_lin_state->flags & RESET_DEVICE_fl) { + pcl_reset_device(tty); + return 0; + } + + pcl_lin_init(tty, linc_lin_state); + + if (linc_lin_state->flags & FLASH_CONF_fl) { + pcl_flash_config(tty); + pcl_reset_device(tty); + } + + // FIXME add warning on unrecognized flags + //if (flags & (RESET_DEVICE_fl | FLASH_CONF_fl)) + + pcl_reset_input_mode(tty); + close(tty); + + return LIN_EXIT_OK; +} + diff --git a/lin_config/src/pcl_config.h b/lin_config/src/pcl_config.h new file mode 100644 index 0000000..061bb8d --- /dev/null +++ b/lin_config/src/pcl_config.h @@ -0,0 +1,39 @@ +#ifndef _PCL_CONFIG_H_ +#define _PCL_CONFIG_H_ + +#include +#include "lin_config.h" + +#define PCL_PKT_MAX_SIZE 16 +#define PCL_HEADERS_SIZE 2 /* There are 2 bytes of headers */ +#define PCL_CHECKSUM_SIZE 1 +#define PCL_STX_SIZE 1 +#define PCL_PACKET_OVERHEAD (PCL_HEADERS_SIZE + PCL_CHECKSUM_SIZE) + +#define PCL_STX 0x2 + +#define PCL_SEQ_NO_ofs 4 +#define PCL_SEQ_FRLEN_ofs 0 +#define PCL_CTRL_TIFACE_ofs 6 +#define PCL_CTRL_COMC_ofs 0 + +#define PCL_SEQ_FRLEN_msk 0xF + +#define PCL_PACKET_LIN_IFACE 0x2 +#define PCL_PACKET_MODULE_IFACE 0x3 + +/* Logical representation of a packet sent to PCAN-LIN converter via RS232 */ +typedef struct { + uint8_t stx; /* Start transmission; Always set to 0x2 */ + uint8_t seq_no; /* Sequence number */ + uint8_t seq_frlen; /* Frame length */ + uint8_t ctrl_tiface;/* Target interface */ + uint8_t ctrl_comc; /* Command code */ + uint8_t parms[8]; /* Parameters; Number of parameters depends + on the frame length */ + uint8_t chks; /* Checksum; Bitwise XOR of all bytes except STX */ +} pcl_packet_t; + +int pcl_config(struct linc_lin_state *linc_lin_state); + +#endif /* _PCL_CONFIG_H_ */ diff --git a/lin_config/src/run.sh b/lin_config/src/run.sh new file mode 100755 index 0000000..8bf1477 --- /dev/null +++ b/lin_config/src/run.sh @@ -0,0 +1,6 @@ +#/bin/bash + +killall lin_config +ip link set sllin0 down +./lin_config /dev/ttyS0 -c ../example_config/master_slave.pclin -a +ip link set sllin0 up diff --git a/lin_config/src/sllin_config.c b/lin_config/src/sllin_config.c new file mode 100644 index 0000000..ffb02f5 --- /dev/null +++ b/lin_config/src/sllin_config.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "lin_config.h" +#include "linux/lin_bus.h" + +#define SLLIN_LDISC 25 +struct bcm_msg { + struct bcm_msg_head msg_head; + struct can_frame frame; +}; + +struct sllin_connection { + int bcm_sock; // FIXME is necessary?? + int can_sock; + char iface[IFNAMSIZ+1]; +}; + +void sllin_ms_to_timeval(int ms, struct timeval *tv) +{ + tv->tv_sec = (int) ms/1000; + tv->tv_usec = (ms % 1000) * 1000; +} + +int sllin_cache_config(struct linc_lin_state *linc_lin_state, + struct sllin_connection *sllin_connection) +{ + int i; + struct ifreq ifr; + struct sockaddr_can addr; + struct can_frame frame; + int s; + int ret; + + /* Create the socket */ + s = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (s < 0) { + perror("socket()"); + return -1; + } + + /* Locate the interface you wish to use */ + strcpy(ifr.ifr_name, sllin_connection->iface); + ioctl(s, SIOCGIFINDEX, &ifr); /* ifr.ifr_ifindex gets filled + * with that device's index */ + + /* Select that CAN interface, and bind the socket to it. */ + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + ret = bind(s, (struct sockaddr*)&addr, sizeof(addr)); + if (ret < 0) { + perror("bind()"); + return -1; + } + + for (i = 0; i < 0x3F; i++) { + if (linc_lin_state->frame_entry[i].status == 1) { /* Is active */ + frame.can_dlc = linc_lin_state->frame_entry[i].data_len; + frame.can_id = i; /* LIN ID */ + frame.data[0] = linc_lin_state->frame_entry[i].data[0]; /* Data */ + frame.data[1] = linc_lin_state->frame_entry[i].data[1]; /* Data */ + frame.data[2] = linc_lin_state->frame_entry[i].data[2]; /* Data */ + frame.data[3] = linc_lin_state->frame_entry[i].data[3]; /* Data */ + frame.data[4] = linc_lin_state->frame_entry[i].data[4]; /* Data */ + frame.data[5] = linc_lin_state->frame_entry[i].data[5]; /* Data */ + frame.data[6] = linc_lin_state->frame_entry[i].data[6]; /* Data */ + frame.data[7] = linc_lin_state->frame_entry[i].data[7]; /* Data */ + + frame.can_id |= LIN_CTRL_FRAME | LIN_CACHE_RESPONSE; + ret = write(s, &frame, sizeof(frame)); + printf("configuring frame cache; ret = %d\n", ret); + //if (ret ...) + //read_response(tty); + } + } + + close(s); + return 0; +} + +int sllin_bcm_config(struct linc_lin_state *linc_lin_state, + struct sllin_connection *sllin_connection) +{ + struct sockaddr_can caddr; + struct ifreq ifr; + struct bcm_msg msg; + int s; + int ret; + int i; + + s = socket(PF_CAN, SOCK_DGRAM, CAN_BCM); + if (s < 0) { + perror("socket(): bcmsocket"); + return -1; + } + + strcpy(ifr.ifr_name, sllin_connection->iface); + ioctl(s, SIOCGIFINDEX, &ifr); + + memset(&caddr, 0, sizeof(caddr)); + caddr.can_family = AF_CAN; + caddr.can_ifindex = ifr.ifr_ifindex; + +// ret = bind(s, (struct sockaddr*)&caddr, sizeof(caddr)); +// if (ret < 0) { +// perror("bind()"); +// return -1; +// } +// +// sllin_connection->bcm_sock = s; + + ret = connect(s, (struct sockaddr *)&caddr, sizeof(caddr)); + if (ret < 0) { + perror("connect()"); + return -1; + } + + for (i = 0; i < linc_lin_state->scheduler_entries_cnt; i++) { + struct timeval time; + memset(&msg, 0, sizeof(msg)); + + msg.msg_head.nframes = 1; + msg.msg_head.opcode = TX_SETUP; + msg.msg_head.flags |= SETTIMER | STARTTIMER; + sllin_ms_to_timeval( + linc_lin_state->scheduler_entry[i].interval_ms, &time); + msg.msg_head.ival2.tv_sec = time.tv_sec; + msg.msg_head.ival2.tv_usec = time.tv_usec; + msg.msg_head.can_id = ( + linc_lin_state->scheduler_entry[i].lin_id | CAN_RTR_FLAG); + msg.frame.can_dlc = 0; + msg.frame.can_id = msg.msg_head.can_id; + + //printf("tv_sec = %i, tv_usec = %i\n", time.tv_sec, time.tv_usec); + + sendto(s, &msg, sizeof(msg), 0, + (struct sockaddr*)&caddr, sizeof(caddr)); + //read_response(s); // FIXME + } + + /* Do not close "s" to make BCM configuration running */ + + printf("Configuration finished\n"); + return 0; +} + +int sllin_config(struct linc_lin_state *linc_lin_state) +{ + int tty; + int ldisc = SLLIN_LDISC; + int ret; + struct sllin_connection sllin_connection; + + tty = open(linc_lin_state->dev, O_WRONLY | O_NOCTTY); + if (tty < 0) { + perror("open()"); + return -1; + } + + /* Set sllin line discipline on given tty */ + if (linc_lin_state->flags & SLLIN_ATTACH_fl) { + ret = ioctl(tty, TIOCSETD, &ldisc); + if (ret < 0) { + perror("ioctl TIOCSETD"); + return -1; + } + + /* Retrieve the name of the created CAN netdevice */ + ret = ioctl(tty, SIOCGIFNAME, sllin_connection.iface); + if (ret < 0) { + perror("ioctl SIOCGIFNAME"); + return -1; + } + + printf("Attached tty %s to netdevice %s\n", + linc_lin_state->dev, sllin_connection.iface); + } + + if (linc_lin_state->flags & SLLIN_DETACH_fl) { + ldisc = N_TTY; + ret = ioctl(tty, TIOCSETD, &ldisc); + if (ret < 0) { + perror("ioctl"); + return -1; + } + + printf("Detached sllin line discipline from %s\n", + linc_lin_state->dev); + + close(tty); + return LIN_EXIT_OK; + } + + ret = sllin_bcm_config(linc_lin_state, &sllin_connection); + if (ret < 0) + return ret; + + ret = sllin_cache_config(linc_lin_state, &sllin_connection); + + /* !!! Do not close "tty" to enable newly + configured tty line discipline */ + + return ret; +} +