CAN over Serial link (TTY) interface, with serial hub software. Thanks to James Steward.
authoretisserant
Fri, 25 Apr 2008 12:18:23 +0200
changeset 448 732c33c2d8a7
parent 447 c9d01296d6d9
child 449 85f6acbb9d54
CAN over Serial link (TTY) interface, with serial hub software. Thanks to James Steward.
doc/doxygen/Doxyfile
doc/manual/en/manual.tex
drivers/can_serial/Makefile.in
drivers/can_serial/can_serial.c
drivers/can_serial/can_serial_hub.c
--- 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 
 
 
  
--- 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
 
--- /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
--- /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 <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <termios.h>
+#include <stdlib.h>
+
+#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;
+}
+
--- /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 <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <termios.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#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;
+}
+