etisserant@448: /* etisserant@448: This file is part of CanFestival, a library implementing CanOpen Stack. etisserant@448: etisserant@448: Copyright (C): James Steward etisserant@448: etisserant@448: See COPYING file for copyrights details. etisserant@448: etisserant@448: This library is free software; you can redistribute it and/or etisserant@448: modify it under the terms of the GNU Lesser General Public etisserant@448: License as published by the Free Software Foundation; either etisserant@448: version 2.1 of the License, or (at your option) any later version. etisserant@448: etisserant@448: This library is distributed in the hope that it will be useful, etisserant@448: but WITHOUT ANY WARRANTY; without even the implied warranty of etisserant@448: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU etisserant@448: Lesser General Public License for more details. etisserant@448: etisserant@448: You should have received a copy of the GNU Lesser General Public etisserant@448: License along with this library; if not, write to the Free Software etisserant@448: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA etisserant@448: */ etisserant@448: etisserant@448: /** etisserant@448: Pseudo CAN hub application. etisserant@448: */ etisserant@448: etisserant@448: #define _GNU_SOURCE //for asprintf() etisserant@448: etisserant@448: #include etisserant@448: #include etisserant@448: #include etisserant@448: #include etisserant@448: #include etisserant@448: #include etisserant@448: #include etisserant@448: #include etisserant@448: etisserant@448: #define NEED_PRINT_MESSAGE etisserant@448: etisserant@448: #ifdef DLL_CALL etisserant@448: #undef DLL_CALL etisserant@448: #endif etisserant@448: etisserant@448: #define DLL_CALL(funcname) (* funcname##_driver) etisserant@448: etisserant@448: #include "canfestival.h" etisserant@448: etisserant@448: int DLL_CALL(canfd)(CAN_HANDLE)FCT_PTR_INIT; etisserant@448: etisserant@448: #ifdef DLL_CALL etisserant@448: #undef DLL_CALL etisserant@448: #endif etisserant@448: etisserant@448: #define DLL_CALL(funcname) funcname##_driver etisserant@448: etisserant@448: #define DLSYM(name)\ etisserant@448: *(void **) (&name##_driver) = dlsym(handle, #name"_driver");\ etisserant@448: if ((error = dlerror()) != NULL) {\ etisserant@448: fprintf (stderr, "%s\n", error);\ etisserant@448: UnLoadCanDriver(handle);\ etisserant@448: return NULL;\ etisserant@448: } etisserant@448: etisserant@448: #define MAX_HUB_PORTS 16 etisserant@448: etisserant@448: typedef struct { etisserant@448: int fd; etisserant@448: struct termios old_termio, new_termio; etisserant@448: char *name; etisserant@448: } port; etisserant@448: etisserant@448: port hub_ports[MAX_HUB_PORTS + 1]; //An extra for a CAN driver port etisserant@448: etisserant@448: s_BOARD bus_if = { "/dev/ttyS0", "125K" }; etisserant@448: etisserant@448: /* Receive a CAN message from a hub (pseudo) port */ etisserant@448: int hub_receive(port *p, Message *m) etisserant@448: { etisserant@448: int rv, N, n = 0; etisserant@448: fd_set rfds; etisserant@448: struct timeval tv; etisserant@448: etisserant@448: N = 4; //initially try to read 4 bytes, including the length byte etisserant@448: etisserant@448: retry: etisserant@448: rv = read(p->fd, &((char *)m)[n], N - n); etisserant@448: etisserant@448: if (rv == -1) { etisserant@448: fprintf(stderr, "read: %d, %s\n", p->fd, strerror(errno)); etisserant@448: return -1; etisserant@448: } etisserant@448: etisserant@448: n += rv; etisserant@448: etisserant@448: if (n == 4) { etisserant@448: N = (4 + m->len); etisserant@448: etisserant@448: if (m->len > 8) { etisserant@448: fprintf(stderr, "Warning: invalid message length %d\n", etisserant@448: m->len); etisserant@448: etisserant@448: //try to resync etisserant@448: return 0; etisserant@448: } etisserant@448: etisserant@448: } etisserant@448: etisserant@448: if (n < N) { etisserant@448: etisserant@448: FD_ZERO(&rfds); etisserant@448: FD_SET(p->fd, &rfds); etisserant@448: etisserant@448: tv.tv_sec = 0; etisserant@448: tv.tv_usec=100000; etisserant@448: etisserant@448: rv = select(p->fd + 1, &rfds, NULL, NULL, &tv); etisserant@448: if (rv == 0 || rv == -1) { etisserant@448: fprintf(stderr, "select: %s\n", strerror(errno)); etisserant@448: return 0; etisserant@448: } etisserant@448: etisserant@448: goto retry; etisserant@448: } etisserant@448: etisserant@448: return 1; etisserant@448: } etisserant@448: etisserant@448: /* send a CAN message to one of the hub ports */ etisserant@448: UNS8 hub_send(port *p, Message *m) etisserant@448: { etisserant@448: int rv; etisserant@448: etisserant@448: rv = write(p->fd, m, 4 + m->len); etisserant@448: etisserant@448: if (rv != 4 + m->len) { etisserant@448: return 1; etisserant@448: } etisserant@448: etisserant@448: return 0; etisserant@448: } etisserant@448: etisserant@448: /* Open a hub port */ etisserant@448: int hub_open(port *p) etisserant@448: { etisserant@448: if (p->fd != -1) { etisserant@448: fprintf(stderr, "Warning, port %s is already open, fd %d!\n", etisserant@448: p->name, p->fd); etisserant@448: } etisserant@448: etisserant@448: p->fd = open(p->name, O_RDWR); etisserant@448: etisserant@448: if (p->fd < 0) { etisserant@448: fprintf(stderr, "open: %s, %s\n", p->name, strerror(errno)); etisserant@448: goto exit_here; etisserant@448: } etisserant@448: etisserant@448: if (tcgetattr(p->fd, &p->old_termio) != 0) { etisserant@448: fprintf(stderr, "tcgetattr: %s, %s\n", etisserant@448: p->name, strerror(errno)); etisserant@448: close(p->fd); etisserant@448: p->fd = -1; etisserant@448: goto exit_here; etisserant@448: } etisserant@448: etisserant@448: memcpy(&p->new_termio, &p->old_termio, sizeof(p->old_termio)); etisserant@448: cfmakeraw(&p->new_termio); etisserant@448: cfsetispeed(&p->new_termio, B115200); etisserant@448: cfsetospeed(&p->new_termio, B115200); etisserant@448: tcsetattr(p->fd, TCSANOW, &p->new_termio); etisserant@448: etisserant@448: exit_here: etisserant@448: return p->fd; etisserant@448: } etisserant@448: etisserant@448: /* Close a hub port*/ etisserant@448: int hub_close(port *p) etisserant@448: { etisserant@448: if (p->fd >= 0) { etisserant@448: tcsetattr(p->fd, TCSANOW, &p->old_termio); etisserant@448: close(p->fd); etisserant@448: p->fd = -1; etisserant@448: } etisserant@448: etisserant@448: return 0; etisserant@448: } etisserant@448: etisserant@448: /** Read from the port index rd_port, and write to all other ports. */ etisserant@448: int read_write(int rd_port, port *p, CAN_HANDLE h, fd_set *wfds, int max_fd) etisserant@448: { etisserant@448: Message m; etisserant@448: int rv, i; etisserant@448: fd_set wfds_copy; etisserant@451: struct timeval tv = {.tv_sec = 0, .tv_usec = 0}; //wait 0 msec etisserant@448: etisserant@448: if (rd_port == MAX_HUB_PORTS) { etisserant@448: rv = DLL_CALL(canReceive)(h, &m); etisserant@448: etisserant@448: if (rv == 1) { etisserant@448: return 0; etisserant@448: } etisserant@448: } else { etisserant@448: rv = hub_receive(&p[rd_port], &m); etisserant@448: etisserant@448: if (rv != 1) { etisserant@448: return rv; etisserant@448: } etisserant@448: } etisserant@448: etisserant@448: memcpy(&wfds_copy, wfds, sizeof(fd_set)); etisserant@448: etisserant@448: rv = select(max_fd + 1, NULL, &wfds_copy, NULL, &tv); etisserant@448: etisserant@448: if (rv <= 0) { etisserant@448: return 0; etisserant@448: } etisserant@448: etisserant@448: for (i = 0; i < MAX_HUB_PORTS + 1; i++) { etisserant@448: etisserant@448: if (i == rd_port) { etisserant@448: fprintf(stderr, "[%d] ", i); etisserant@448: continue; etisserant@448: } etisserant@448: etisserant@451: if (p[i].fd < 0 || !FD_ISSET(p[i].fd, &wfds_copy)) { etisserant@448: fprintf(stderr, "{%d} ", i); etisserant@448: continue; etisserant@448: } etisserant@448: etisserant@448: fprintf(stderr, "<%d> ", i); etisserant@448: etisserant@448: if (i == MAX_HUB_PORTS && h) { etisserant@448: DLL_CALL(canSend)(h, &m); etisserant@448: } else { etisserant@448: hub_send(&p[i], &m); etisserant@448: } etisserant@448: } etisserant@448: etisserant@448: print_message(&m); etisserant@448: etisserant@448: return 0; etisserant@448: } etisserant@448: etisserant@448: void help(void) etisserant@448: { etisserant@448: printf("\n\n"); etisserant@448: printf("This is a software hub for the CANFestival library, \n"); etisserant@448: printf("based on the *nix pseudo tty. It supports up to 16\n"); etisserant@448: printf("connections from clients, and a connection to a CANFestival\n"); etisserant@448: printf("driver, for connection to the outside world.\n"); etisserant@448: printf("\n"); etisserant@448: printf("Basic use is simply to run can_hub. Without arguments, it\n"); etisserant@448: printf("will use /dev/ptya[0..f] ptys. You should then run your\n"); etisserant@448: printf("linux CANFestival app using libcanfestival_can_serial.so\n"); etisserant@448: printf("with the bus name /dev/ttyaX, where X is 0..f and unused.\n"); etisserant@448: printf("\n"); etisserant@448: printf("You can alter the pty base with -p /dev/ptyx .\n"); etisserant@448: printf("\n"); etisserant@448: printf("If you want to interface with some other CAN driver, supply\n"); etisserant@448: printf("the option -l /path/to/libcanfestival_can_foo.so .\n"); etisserant@448: printf("The default bus name and baud are /dev/ttyS0 and 125k.\n"); etisserant@448: printf("These can be overridden with -b /dev/{bus name} and -s {baud}.\n"); etisserant@448: } etisserant@448: etisserant@448: /*UnLoads the dll*/ etisserant@448: UNS8 UnLoadCanDriver(LIB_HANDLE handle) etisserant@448: { etisserant@448: if(handle!=NULL) etisserant@448: { etisserant@448: dlclose(handle); etisserant@448: etisserant@448: handle=NULL; etisserant@448: return 0; etisserant@448: } etisserant@448: return -1; etisserant@448: } etisserant@448: etisserant@448: /*Loads the dll and get funcs ptr*/ etisserant@448: LIB_HANDLE LoadCanDriver(char* driver_name) etisserant@448: { etisserant@448: LIB_HANDLE handle = NULL; etisserant@448: char *error; etisserant@448: etisserant@448: if(handle==NULL) etisserant@448: { etisserant@448: handle = dlopen(driver_name, RTLD_LAZY); etisserant@448: } etisserant@448: etisserant@448: if (!handle) { etisserant@448: fprintf (stderr, "%s\n", dlerror()); etisserant@448: return NULL; etisserant@448: } etisserant@448: etisserant@448: /*Get function ptr*/ etisserant@448: DLSYM(canReceive) etisserant@448: DLSYM(canSend) etisserant@448: DLSYM(canOpen) etisserant@448: DLSYM(canChangeBaudRate) etisserant@448: DLSYM(canClose) etisserant@448: DLSYM(canfd) etisserant@448: etisserant@448: return handle; etisserant@448: } etisserant@448: etisserant@448: /** etisserant@448: */ etisserant@448: int main(int argc, char **argv) etisserant@448: { etisserant@448: int i, rv, max_fd = 0, ret = 0; etisserant@448: fd_set rfds, rfds_copy; etisserant@448: CAN_HANDLE can_h = NULL; etisserant@448: LIB_HANDLE lib_h = NULL; etisserant@448: etisserant@448: int c; etisserant@448: extern char *optarg; etisserant@448: etisserant@448: char *can_drv = NULL; etisserant@448: char *pty_base = "/dev/ptya"; etisserant@448: etisserant@448: while ((c = getopt(argc, argv, "-b:s:l:p:h")) != EOF) { etisserant@448: switch (c) { etisserant@448: case 'b': etisserant@448: if (optarg[0] == 0) { etisserant@448: help(); etisserant@448: exit(1); etisserant@448: } etisserant@448: bus_if.busname = optarg; etisserant@448: break; etisserant@448: case 's': etisserant@448: if (optarg[0] == 0) { etisserant@448: help(); etisserant@448: exit(1); etisserant@448: } etisserant@448: bus_if.baudrate = optarg; etisserant@448: break; etisserant@448: case 'l': etisserant@448: if (optarg[0] == 0) { etisserant@448: help(); etisserant@448: exit(1); etisserant@448: } etisserant@448: can_drv = optarg; etisserant@448: break; etisserant@448: case 'p': etisserant@448: if (optarg[0] == 0) { etisserant@448: help(); etisserant@448: exit(1); etisserant@448: } etisserant@448: pty_base = optarg; etisserant@448: break; etisserant@448: case 'h': etisserant@448: help(); etisserant@448: exit(1); etisserant@448: break; etisserant@448: default: etisserant@448: help(); etisserant@448: exit(1); etisserant@448: } etisserant@448: } etisserant@448: etisserant@448: FD_ZERO(&rfds); etisserant@448: etisserant@448: hub_ports[MAX_HUB_PORTS].fd = -1; etisserant@448: etisserant@448: if (can_drv) { etisserant@448: lib_h = LoadCanDriver(can_drv); etisserant@448: if (lib_h == NULL) { etisserant@448: printf("Unable to load library: %s\n", can_drv); etisserant@448: exit(1); etisserant@448: } etisserant@448: etisserant@448: can_h = DLL_CALL(canOpen)(&bus_if); etisserant@448: if(!can_h) { etisserant@448: fprintf(stderr,"canOpen : failed\n"); etisserant@448: exit(1); etisserant@448: } etisserant@448: etisserant@448: hub_ports[MAX_HUB_PORTS].fd = DLL_CALL(canfd)(can_h); etisserant@448: etisserant@448: FD_SET(hub_ports[MAX_HUB_PORTS].fd, &rfds); etisserant@448: etisserant@448: if (hub_ports[MAX_HUB_PORTS].fd > max_fd) { etisserant@448: max_fd = hub_ports[MAX_HUB_PORTS].fd; etisserant@448: } etisserant@448: etisserant@448: } etisserant@448: etisserant@448: for (i = 0; i < MAX_HUB_PORTS; i++) { etisserant@448: etisserant@448: hub_ports[i].fd = -1; etisserant@448: hub_ports[i].name = NULL; etisserant@448: etisserant@448: rv = asprintf(&hub_ports[i].name, "%s%x", pty_base, i); etisserant@448: etisserant@448: if (rv < 0) { etisserant@448: fprintf(stderr, "asprintf: %s\n", strerror(errno)); etisserant@448: ret = 1; etisserant@448: break; etisserant@448: } etisserant@448: etisserant@448: rv = hub_open(&hub_ports[i]); etisserant@448: etisserant@448: if (rv < 0) { etisserant@448: ret = 1; etisserant@448: break; etisserant@448: } etisserant@448: etisserant@448: FD_SET(rv, &rfds); etisserant@448: etisserant@448: if (rv > max_fd) { etisserant@448: max_fd = rv; etisserant@448: } etisserant@448: } etisserant@448: etisserant@448: if (ret) { etisserant@448: return ret; etisserant@448: } etisserant@448: etisserant@448: while (!ret) { etisserant@448: memcpy(&rfds_copy, &rfds, sizeof(rfds)); etisserant@448: etisserant@448: rv = select(max_fd + 1, &rfds_copy, NULL, NULL, NULL); etisserant@448: etisserant@448: if (rv < 0) { etisserant@448: //select error etisserant@448: fprintf(stderr, "select: %s\n", strerror(errno)); etisserant@448: ret = 1; etisserant@448: continue; etisserant@448: } etisserant@448: etisserant@448: //as timeout is NULL, must be a rfds set. etisserant@448: for (i = 0; i < MAX_HUB_PORTS + 1; i++) { etisserant@451: if (hub_ports[i].fd >= 0 && etisserant@451: FD_ISSET(hub_ports[i].fd, &rfds_copy)) { etisserant@448: etisserant@448: rv = read_write(i, hub_ports, can_h, &rfds, max_fd); etisserant@448: etisserant@448: if (rv < 0 && i < MAX_HUB_PORTS) { etisserant@448: etisserant@448: FD_CLR(hub_ports[i].fd, &rfds); etisserant@448: etisserant@448: hub_close(&hub_ports[i]); etisserant@451: } etisserant@451: } etisserant@451: etisserant@451: if (hub_ports[i].fd < 0 && i < MAX_HUB_PORTS) { etisserant@451: rv = hub_open(&hub_ports[i]); etisserant@451: etisserant@451: if (rv >= 0) { etisserant@448: FD_SET(rv, &rfds); etisserant@448: etisserant@448: if (rv > max_fd) { etisserant@448: max_fd = rv; etisserant@451: } etisserant@451: } etisserant@448: } etisserant@448: } etisserant@448: } etisserant@448: etisserant@448: for (i = 0; i < MAX_HUB_PORTS; i++) { etisserant@448: hub_close(&hub_ports[i]); etisserant@448: } etisserant@448: etisserant@448: if (hub_ports[MAX_HUB_PORTS].fd >= 0) { etisserant@448: DLL_CALL(canClose)(&bus_if); etisserant@448: } etisserant@448: etisserant@448: return ret; etisserant@448: } etisserant@448: