--- /dev/null
+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
--- /dev/null
+/*
+ * 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 <lisovy@gmail.cz>
+ */
+
+/*
+ Used prefixes explanation:
+ pcl_ -- PCAN-LIN (hw) related functions
+ sll_ -- sllin (tty lin implementation) related functions
+ linc_ -- LIN config general functions
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <termios.h>
+#include <stdint.h>
+#include <assert.h>
+#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] <SERIAL_INTERFACE>\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 <FILE> 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;
+}
--- /dev/null
+#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_ */
--- /dev/null
+#include <libxml/parser.h>
+#include <assert.h>
+#include <string.h>
+#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;
+}
+
--- /dev/null
+#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_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <termios.h>
+#include <stdint.h>
+#include <assert.h>
+#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;
+}
+
--- /dev/null
+#ifndef _PCL_CONFIG_H_
+#define _PCL_CONFIG_H_
+
+#include <inttypes.h>
+#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_ */
--- /dev/null
+#/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
--- /dev/null
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <linux/can.h>
+#include <linux/can/bcm.h>
+
+#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;
+}
+