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; +} +