# HG changeset patch # User etisserant # Date 1209118703 -7200 # Node ID 732c33c2d8a788bab8ad01a544a5fc6f38feee60 # Parent c9d01296d6d912437a9a30a33cf795ebcd314c90 CAN over Serial link (TTY) interface, with serial hub software. Thanks to James Steward. diff -r c9d01296d6d9 -r 732c33c2d8a7 doc/doxygen/Doxyfile --- a/doc/doxygen/Doxyfile Thu Apr 24 14:08:34 2008 +0200 +++ b/doc/doxygen/Doxyfile Fri Apr 25 12:18:23 2008 +0200 @@ -72,7 +72,7 @@ #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- -INPUT = ../../src ../../drivers/can_lincan ../../drivers/can_peak_linux ../../drivers/can_peak_win32 ../../drivers/can_socket ../../drivers/can_uvccm_win32 ../../drivers/can_virtual ../../drivers/hcs12 ../../drivers/timers_unix ../../drivers/timers_xeno ../../drivers/unix ../../drivers/win32 ../../examples/gene_SYNC_HCS12 ../../examples/TestMasterSlave ../../examples/TestMasterMicroMod ../../examples/win32test ../../include +INPUT = ../../src ../../drivers/can_lincan ../../drivers/can_peak_linux ../../drivers/can_peak_win32 ../../drivers/can_socket ../../drivers/can_uvccm_win32 ../../drivers/can_virtual ../../drivers/hcs12 ../../drivers/timers_unix ../../drivers/timers_xeno ../../drivers/unix ../../drivers/win32 ../../examples/gene_SYNC_HCS12 ../../examples/TestMasterSlave ../../examples/TestMasterMicroMod ../../examples/win32test ../../include ../../drivers/can_serial diff -r c9d01296d6d9 -r 732c33c2d8a7 doc/manual/en/manual.tex --- a/doc/manual/en/manual.tex Thu Apr 24 14:08:34 2008 +0200 +++ b/doc/manual/en/manual.tex Fri Apr 25 12:18:23 2008 +0200 @@ -194,6 +194,7 @@ ./drivers/timers_kernel Linux kernel timer/threads ./drivers/timers_unix Posix timers/threads (Linux, Cygwin) ./drivers/can_virtual_kernel Fake CAN network (kernel space) +./drivers/can_serial Serial point to point and PTY hub (*nix only) ./drivers/can_peak_linux PeakSystem CAN library interface ./drivers/can_peak_win32 PeakSystem PCAN-Light interface ./drivers/can_uvccm_win32 Acacetus's RS232 CAN-uVCCM interface @@ -381,6 +382,21 @@ ./configure --can=socket \end{verbatim} +\paragraph{Serial} + + +\begin{verbatim} + ./configure --can=serial +\end{verbatim} + +The CAN serial driver implements a 1:1 serial connection between 2 CAN devices. +For example, you can connect 2 CANFestival applications via a NULL modem cable. + +Also with this driver comes a software hub, for up to 16 CANFestival apps to +be connected on a single PC, with an optional connection to another CAN driver. +Note that only the serial driver is supported at this time. The hub uses ptys +(pseudo ttys) available on a *nix like system. + \paragraph{LinCan} @@ -1193,7 +1209,7 @@ about that. -\subsubsection{How to fit the library to an other microcontrôler ?} +\subsubsection{How to fit the library to an other microcontr�ler ?} First, be sure that you have at least 40K bytes of program memory, and about 2k of RAM. @@ -1492,11 +1508,11 @@ \includegraphics[width=10cm]{Pictures/1000020100000258000000832C6FFAB4} \par\end{center} -Unité mixte de recherche INRETS -LCPC - -sur les Interractions Véhicule -Infrastructure -Conducteur - -14, route de la minière +Unit� mixte de recherche INRETS -LCPC + +sur les Interractions V�hicule -Infrastructure -Conducteur + +14, route de la mini�re 78000 Versailles @@ -1542,7 +1558,7 @@ Raphael ZULLIGER -David DUMINY (sté A6R) +David DUMINY (st� A6R) Zakaria BELAMRI diff -r c9d01296d6d9 -r 732c33c2d8a7 drivers/can_serial/Makefile.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/can_serial/Makefile.in Fri Apr 25 12:18:23 2008 +0200 @@ -0,0 +1,79 @@ +#! gmake + +# +# Copyright (C) 2006 Laurent Bessard +# +# This file is part of canfestival, a library implementing the canopen +# stack +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +CC = SUB_CC +OPT_CFLAGS = -O2 +CFLAGS = SUB_OPT_CFLAGS +PROG_CFLAGS = SUB_PROG_CFLAGS +PREFIX = SUB_PREFIX +TARGET = SUB_TARGET +CAN_DRIVER = SUB_CAN_DRIVER +TIMERS_DRIVER = SUB_TIMERS_DRIVER +ENABLE_DLL_DRIVERS=SUB_ENABLE_DLL_DRIVERS +CAN_DLL_CFLAGS=SUB_CAN_DLL_CFLAGS + +INCLUDES = -I../../include -I../../include/$(TARGET) -I../../include/$(CAN_DRIVER) +INCLUDES += -I../../include/$(TIMERS_DRIVER) + +OBJS = $(CAN_DRIVER).o + +ifeq ($(ENABLE_DLL_DRIVERS),1) +CFLAGS += -fPIC +DRIVER = libcanfestival_$(CAN_DRIVER).so +else +DRIVER = $(OBJS) +endif + +HUB=can_serial_hub + +TARGET_SOFILES = $(PREFIX)/lib/$(DRIVER) +TARGET_BINFILES = $(PREFIX)/bin/$(HUB) + +all: driver $(HUB) + +$(HUB): $(HUB).c + $(CC) -O2 -Wall -fno-strict-aliasing $(INCLUDES) -o $(HUB) $(HUB).c -ldl + +driver: $(DRIVER) $(HUB) + +%o: %c + $(CC) $(CFLAGS) $(PROG_CFLAGS) ${PROGDEFINES} $(INCLUDES) -o $@ -c $< + +libcanfestival_$(CAN_DRIVER).so: $(OBJS) + $(CC) -shared -Wl,-soname,libcanfestival_$(CAN_DRIVER).so $(CAN_DLL_CFLAGS) -o $@ $< + +install: libcanfestival_$(CAN_DRIVER).so $(HUB) + mkdir -p $(PREFIX)/lib/ + cp libcanfestival_$(CAN_DRIVER).so $(PREFIX)/lib/ + mkdir -p $(PREFIX)/bin/ + cp $(HUB) $(PREFIX)/bin/ + +uninstall: + rm -f $(TARGET_SOFILES) + rm -f $(TARGET_BINFILES) + +clean: + rm -f $(OBJS) + rm -f $(HUB) + +mrproper: clean diff -r c9d01296d6d9 -r 732c33c2d8a7 drivers/can_serial/can_serial.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/can_serial/can_serial.c Fri Apr 25 12:18:23 2008 +0200 @@ -0,0 +1,216 @@ +/* +This file is part of CanFestival, a library implementing CanOpen Stack. + +Copyright (C): Edouard TISSERANT and Francis DUPIN + +Ruthlessly butchered by James Steward to produce a serial (tty) port +driver. + +See COPYING file for copyrights details. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + Single serial port CAN driver. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define NEED_PRINT_MESSAGE +#include "can_driver.h" +#include "def.h" + +typedef struct { + int fd; + struct termios old_termio, new_termio; +} CANPort; + +/*********functions which permit to communicate with the board****************/ +UNS8 canReceive_driver(CAN_HANDLE fd0, Message * m) +{ + int rv, N, n = 0; + fd_set rfds; + struct timeval tv; + + N = 4; //initially try to read 4 bytes, including the length byte + +retry: + rv = read(((CANPort *) fd0)->fd, &((char *)m)[n], N - n); + + if (rv == -1) { + fprintf(stderr, "read: %s\n", strerror(errno)); + return 1; + } + + n += rv; + + if (n == 4) { + N = (4 + m->len); + + if (m->len > 8) { + fprintf(stderr, "Warning: invalid message length %d\n", + m->len); + + //try to resync + n = 0; + N = 4; + } + + } + + if (n < N) { + + FD_ZERO(&rfds); + FD_SET(((CANPort *) fd0)->fd, &rfds); + + tv.tv_sec = 0; + tv.tv_usec=100000; + + rv = select(((CANPort *) fd0)->fd + 1, &rfds, NULL, NULL, &tv); + if (rv == -1) { + fprintf(stderr, "select: %s\n", strerror(errno)); + return 1; + } else if (rv == 0) { + n = 0; + } + + goto retry; + } + + print_message(m); + + return 0; +} + +/***************************************************************************/ +UNS8 canSend_driver(CAN_HANDLE fd0, Message * m) +{ + int rv; + + print_message(m); + + // Send to serial port + rv = write(((CANPort *) fd0)->fd, m, 4 + m->len); + + if (rv != 4 + m->len) { + return 1; + } + + return 0; +} + +/***************************************************************************/ +int TranslateBaudRate(char *optarg) +{ + if (!strcmp(optarg, "1M")) + return (int) 1000; + if (!strcmp(optarg, "500K")) + return (int) 500; + if (!strcmp(optarg, "250K")) + return (int) 250; + if (!strcmp(optarg, "125K")) + return (int) 125; + if (!strcmp(optarg, "100K")) + return (int) 100; + if (!strcmp(optarg, "50K")) + return (int) 50; + if (!strcmp(optarg, "20K")) + return (int) 20; + if (!strcmp(optarg, "10K")) + return (int) 10; + if (!strcmp(optarg, "5K")) + return (int) 5; + if (!strcmp(optarg, "none")) + return 0; + return 0x0000; +} + +UNS8 canChangeBaudRate_driver(CAN_HANDLE fd0, char *baud) +{ + printf("Faked changing to baud rate %s[%d]\n", + baud, TranslateBaudRate(baud)); + return 0; +} + +/***************************************************************************/ +CAN_HANDLE canOpen_driver(s_BOARD * board) +{ + int rv; + + CANPort *p; + + p = (CANPort *)calloc(1, sizeof(CANPort)); + + if (p == NULL) { + fprintf(stderr, "calloc: %s\n", strerror(errno)); + return (CAN_HANDLE) NULL; + } + + p->fd = open(board->busname, O_RDWR); + + if (p->fd < 0) { + fprintf(stderr, "open: %s, %s\n", + board->busname, strerror(errno)); + free(p); + return (CAN_HANDLE) NULL; + } + + if (tcgetattr(p->fd, &p->old_termio) != 0) { + fprintf(stderr, "tcgetattr: %s, %s\n", + board->busname, strerror(errno)); + close(p->fd); + free(p); + return (CAN_HANDLE) NULL; + } + + memcpy(&p->new_termio, &p->old_termio, + sizeof(p->old_termio)); + cfmakeraw(&p->new_termio); + cfsetispeed(&p->new_termio, B115200); + cfsetospeed(&p->new_termio, B115200); + tcsetattr(p->fd, TCSANOW, &p->new_termio); + + return (CAN_HANDLE) p; +} + +/***************************************************************************/ +int canClose_driver(CAN_HANDLE fd0) +{ + if ((CANPort *) fd0 && ((CANPort *) fd0)->fd >= 0) { + tcsetattr(((CANPort *) fd0)->fd, TCSANOW, + &((CANPort *) fd0)->old_termio); + close(((CANPort *) fd0)->fd); + free((CANPort *) fd0); + } + + return 0; +} + +int canfd_driver(CAN_HANDLE fd0) +{ + if ((CANPort *) fd0) { + return ((CANPort *) fd0)->fd; + } + + return -1; +} + diff -r c9d01296d6d9 -r 732c33c2d8a7 drivers/can_serial/can_serial_hub.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/can_serial/can_serial_hub.c Fri Apr 25 12:18:23 2008 +0200 @@ -0,0 +1,468 @@ +/* +This file is part of CanFestival, a library implementing CanOpen Stack. + +Copyright (C): James Steward + +See COPYING file for copyrights details. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** + Pseudo CAN hub application. +*/ + +#define _GNU_SOURCE //for asprintf() + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NEED_PRINT_MESSAGE + +#ifdef DLL_CALL +#undef DLL_CALL +#endif + +#define DLL_CALL(funcname) (* funcname##_driver) + +#include "canfestival.h" + +int DLL_CALL(canfd)(CAN_HANDLE)FCT_PTR_INIT; + +#ifdef DLL_CALL +#undef DLL_CALL +#endif + +#define DLL_CALL(funcname) funcname##_driver + +#define DLSYM(name)\ + *(void **) (&name##_driver) = dlsym(handle, #name"_driver");\ + if ((error = dlerror()) != NULL) {\ + fprintf (stderr, "%s\n", error);\ + UnLoadCanDriver(handle);\ + return NULL;\ + } + +#define MAX_HUB_PORTS 16 + +typedef struct { + int fd; + struct termios old_termio, new_termio; + char *name; +} port; + +port hub_ports[MAX_HUB_PORTS + 1]; //An extra for a CAN driver port + +s_BOARD bus_if = { "/dev/ttyS0", "125K" }; + +/* Receive a CAN message from a hub (pseudo) port */ +int hub_receive(port *p, Message *m) +{ + int rv, N, n = 0; + fd_set rfds; + struct timeval tv; + + N = 4; //initially try to read 4 bytes, including the length byte + +retry: + rv = read(p->fd, &((char *)m)[n], N - n); + + if (rv == -1) { + fprintf(stderr, "read: %d, %s\n", p->fd, strerror(errno)); + return -1; + } + + n += rv; + + if (n == 4) { + N = (4 + m->len); + + if (m->len > 8) { + fprintf(stderr, "Warning: invalid message length %d\n", + m->len); + + //try to resync + return 0; + } + + } + + if (n < N) { + + FD_ZERO(&rfds); + FD_SET(p->fd, &rfds); + + tv.tv_sec = 0; + tv.tv_usec=100000; + + rv = select(p->fd + 1, &rfds, NULL, NULL, &tv); + if (rv == 0 || rv == -1) { + fprintf(stderr, "select: %s\n", strerror(errno)); + return 0; + } + + goto retry; + } + + return 1; +} + +/* send a CAN message to one of the hub ports */ +UNS8 hub_send(port *p, Message *m) +{ + int rv; + + rv = write(p->fd, m, 4 + m->len); + + if (rv != 4 + m->len) { + return 1; + } + + return 0; +} + +/* Open a hub port */ +int hub_open(port *p) +{ + if (p->fd != -1) { + fprintf(stderr, "Warning, port %s is already open, fd %d!\n", + p->name, p->fd); + } + + p->fd = open(p->name, O_RDWR); + + if (p->fd < 0) { + fprintf(stderr, "open: %s, %s\n", p->name, strerror(errno)); + goto exit_here; + } + + if (tcgetattr(p->fd, &p->old_termio) != 0) { + fprintf(stderr, "tcgetattr: %s, %s\n", + p->name, strerror(errno)); + close(p->fd); + p->fd = -1; + goto exit_here; + } + + memcpy(&p->new_termio, &p->old_termio, sizeof(p->old_termio)); + cfmakeraw(&p->new_termio); + cfsetispeed(&p->new_termio, B115200); + cfsetospeed(&p->new_termio, B115200); + tcsetattr(p->fd, TCSANOW, &p->new_termio); + +exit_here: + return p->fd; +} + +/* Close a hub port*/ +int hub_close(port *p) +{ + if (p->fd >= 0) { + tcsetattr(p->fd, TCSANOW, &p->old_termio); + close(p->fd); + p->fd = -1; + } + + return 0; +} + +/** Read from the port index rd_port, and write to all other ports. */ +int read_write(int rd_port, port *p, CAN_HANDLE h, fd_set *wfds, int max_fd) +{ + Message m; + int rv, i; + fd_set wfds_copy; + struct timeval tv = {.tv_sec = 0, .tv_usec = 0}; //wait 1 msec + + if (rd_port == MAX_HUB_PORTS) { + rv = DLL_CALL(canReceive)(h, &m); + + if (rv == 1) { + return 0; + } + } else { + rv = hub_receive(&p[rd_port], &m); + + if (rv != 1) { + return rv; + } + } + + memcpy(&wfds_copy, wfds, sizeof(fd_set)); + + rv = select(max_fd + 1, NULL, &wfds_copy, NULL, &tv); + + if (rv <= 0) { + return 0; + } + + for (i = 0; i < MAX_HUB_PORTS + 1; i++) { + + if (i == rd_port) { + fprintf(stderr, "[%d] ", i); + continue; + } + + if (rv <= 0 || !FD_ISSET(p[i].fd, &wfds_copy)) { + fprintf(stderr, "{%d} ", i); + continue; + } + + fprintf(stderr, "<%d> ", i); + + if (i == MAX_HUB_PORTS && h) { + DLL_CALL(canSend)(h, &m); + } else { + hub_send(&p[i], &m); + } + } + + print_message(&m); + + return 0; +} + +void help(void) +{ + printf("\n\n"); + printf("This is a software hub for the CANFestival library, \n"); + printf("based on the *nix pseudo tty. It supports up to 16\n"); + printf("connections from clients, and a connection to a CANFestival\n"); + printf("driver, for connection to the outside world.\n"); + printf("\n"); + printf("Basic use is simply to run can_hub. Without arguments, it\n"); + printf("will use /dev/ptya[0..f] ptys. You should then run your\n"); + printf("linux CANFestival app using libcanfestival_can_serial.so\n"); + printf("with the bus name /dev/ttyaX, where X is 0..f and unused.\n"); + printf("\n"); + printf("You can alter the pty base with -p /dev/ptyx .\n"); + printf("\n"); + printf("If you want to interface with some other CAN driver, supply\n"); + printf("the option -l /path/to/libcanfestival_can_foo.so .\n"); + printf("The default bus name and baud are /dev/ttyS0 and 125k.\n"); + printf("These can be overridden with -b /dev/{bus name} and -s {baud}.\n"); +} + +/*UnLoads the dll*/ +UNS8 UnLoadCanDriver(LIB_HANDLE handle) +{ + if(handle!=NULL) + { + dlclose(handle); + + handle=NULL; + return 0; + } + return -1; +} + +/*Loads the dll and get funcs ptr*/ +LIB_HANDLE LoadCanDriver(char* driver_name) +{ + LIB_HANDLE handle = NULL; + char *error; + + if(handle==NULL) + { + handle = dlopen(driver_name, RTLD_LAZY); + } + + if (!handle) { + fprintf (stderr, "%s\n", dlerror()); + return NULL; + } + + /*Get function ptr*/ + DLSYM(canReceive) + DLSYM(canSend) + DLSYM(canOpen) + DLSYM(canChangeBaudRate) + DLSYM(canClose) + DLSYM(canfd) + + return handle; +} + +/** +*/ +int main(int argc, char **argv) +{ + int i, rv, max_fd = 0, ret = 0; + fd_set rfds, rfds_copy; + CAN_HANDLE can_h = NULL; + LIB_HANDLE lib_h = NULL; + + int c; + extern char *optarg; + + char *can_drv = NULL; + char *pty_base = "/dev/ptya"; + + while ((c = getopt(argc, argv, "-b:s:l:p:h")) != EOF) { + switch (c) { + case 'b': + if (optarg[0] == 0) { + help(); + exit(1); + } + bus_if.busname = optarg; + break; + case 's': + if (optarg[0] == 0) { + help(); + exit(1); + } + bus_if.baudrate = optarg; + break; + case 'l': + if (optarg[0] == 0) { + help(); + exit(1); + } + can_drv = optarg; + break; + case 'p': + if (optarg[0] == 0) { + help(); + exit(1); + } + pty_base = optarg; + break; + case 'h': + help(); + exit(1); + break; + default: + help(); + exit(1); + } + } + + FD_ZERO(&rfds); + + hub_ports[MAX_HUB_PORTS].fd = -1; + + if (can_drv) { + lib_h = LoadCanDriver(can_drv); + if (lib_h == NULL) { + printf("Unable to load library: %s\n", can_drv); + exit(1); + } + + can_h = DLL_CALL(canOpen)(&bus_if); + if(!can_h) { + fprintf(stderr,"canOpen : failed\n"); + exit(1); + } + + hub_ports[MAX_HUB_PORTS].fd = DLL_CALL(canfd)(can_h); + + FD_SET(hub_ports[MAX_HUB_PORTS].fd, &rfds); + + if (hub_ports[MAX_HUB_PORTS].fd > max_fd) { + max_fd = hub_ports[MAX_HUB_PORTS].fd; + } + + } + + for (i = 0; i < MAX_HUB_PORTS; i++) { + + hub_ports[i].fd = -1; + hub_ports[i].name = NULL; + + rv = asprintf(&hub_ports[i].name, "%s%x", pty_base, i); + + if (rv < 0) { + fprintf(stderr, "asprintf: %s\n", strerror(errno)); + ret = 1; + break; + } + + rv = hub_open(&hub_ports[i]); + + if (rv < 0) { + ret = 1; + break; + } + + FD_SET(rv, &rfds); + + if (rv > max_fd) { + max_fd = rv; + } + } + + if (ret) { + return ret; + } + + while (!ret) { + memcpy(&rfds_copy, &rfds, sizeof(rfds)); + + rv = select(max_fd + 1, &rfds_copy, NULL, NULL, NULL); + + if (rv < 0) { + //select error + fprintf(stderr, "select: %s\n", strerror(errno)); + ret = 1; + continue; + } + + //as timeout is NULL, must be a rfds set. + for (i = 0; i < MAX_HUB_PORTS + 1; i++) { + if (FD_ISSET(hub_ports[i].fd, &rfds_copy)) { + + rv = read_write(i, hub_ports, can_h, &rfds, max_fd); + + if (rv < 0 && i < MAX_HUB_PORTS) { + + FD_CLR(hub_ports[i].fd, &rfds); + + hub_close(&hub_ports[i]); + + rv = hub_open(&hub_ports[i]); + + if (rv < 0) { + ret = 1; + break; + } + + FD_SET(rv, &rfds); + + if (rv > max_fd) { + max_fd = rv; + } + } + } + } + } + + for (i = 0; i < MAX_HUB_PORTS; i++) { + hub_close(&hub_ports[i]); + } + + if (hub_ports[MAX_HUB_PORTS].fd >= 0) { + DLL_CALL(canClose)(&bus_if); + } + + return ret; +} +