Sample code/C/Serial Daemon

Packet Forwarding
This program allows the user to connect a serial port (ttyS*) to a socket (http://localhost:port) and/or to a pseudo terminal (pty). Communication should be bidirectional but hasn't been exhaustively tested.

Possible connections: /dev/ttyS1 < = > 127.0.0.0:5000 /dev/ttyS1 < = > /dev/pts/0

Compiling with GCC: gcc -c -fPIC serialdaemon.c gcc -o serialdaemon -L. -lutil serialdaemon.o

Giving the help option produces the following output: -- SerialDaemon -- Usage: ./serialdaemon [options] [arguments] -serial [Use to indicate which serial port to connect to. E.G. /dev/ttyS1] -port  [Use to indicate which TCP/IP port of the local host to connect to. E.G. 5000] -pty   [Create a pseudo terminal for the serial port to connect to.] -baud  [Serial port baudrate.] 115200               38400                19200                9600        -strip  [Strip the endline character and replace with a space.] -debug [Set the verbose debug mode for help.] -help  [For this help screen.]
 * 1) ./serialdaemon -help

Example Usage: ./serialdaemon -serial /dev/ttyS1 -baud 115200 -pty -port 5000 This will link ttyS1 to localhost:5000 and ttyS1 to a pseudo terminal. The connection to ttyS1 will have a baudrate of 115200.

/******************************************************************************** * Written by Rich Ketcham February 25, 2008                                    * * Modified from serialdaemon.c by the FlockBots                                * * http://cs.gmu.edu/~eclab/projects/robots/flockbots/pmwiki.php?n=Main.Gumstix * *                                                                              * * This program is free software; you can redistribute it and/or modify          * * it under the terms of the GNU General Public License as published by         * * the Free Software Foundation; either version 2 of the License, or            * * (at your option) any later version. * *                                                                              * * This program 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 General Public License for more details. * *                                                                              * * You should have received a copy of the GNU General Public License             * * along with this program; if not, write to the Free Software                  * * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ********************************************************************************/


 * 1) include 
 * 2) include 
 * 3) include 
 * 4) include 
 * 5) include  // baudrate settings are defined in , which is included by 
 * 6) include 
 * 7) include 
 * 8) include 
 * 9) include 
 * 10) include 
 * 11) include 

//pseudoTY
 * 1) include 
 * 2) include 
 * 3) include 

//For getIP
 * 1) include <sys/ioctl.h>
 * 2) include <net/if.h>
 * 3) include <arpa/inet.h>


 * 1) define BUFFER 1024

//getIP// //Returns the IP address of the localhost// //---//

char *getIP{ int i;	int s = socket (PF_INET, SOCK_STREAM, 0); char *IP; for (i=1;;i++) {		struct ifreq ifr; struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; char *ip; ifr.ifr_ifindex = i;		if (ioctl (s, SIOCGIFNAME, &ifr) < 0) break; /* now ifr.ifr_name is set */ if (ioctl (s, SIOCGIFADDR, &ifr) < 0) continue; ip = inet_ntoa (sin->sin_addr); IP =ip; }	close (s); return IP; }

//makeSocket--// //Creates a socket for com over ethernet	// //Returns a file descriptor for the local socket// //--// int makeSocket(int port) //Make the socket {	int   sockfd,sd,childpid; struct sockaddr_in serv_addr; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { //Create the socket file descriptor fprintf(stderr,"Server Error: Can't open stream socket.\n"); return -1; }	bzero((char*) &serv_addr, sizeof(serv_addr)); //Zeros... 	serv_addr.sin_family       =AF_INET; //Address Family Internet Sockets serv_addr.sin_addr.s_addr  =htonl(INADDR_ANY);  //Address serv_addr.sin_port         =htons(port);  //Port if (bind(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr))<0) { //Bind a name to a socket (socket, address, length) fprintf(stderr,"Server Error: Can't bind to local address.\n"); return -1; }

listen(sockfd,5); //Listen for socket connections and limit the queue to 5

//Make socket non-blocking (will return immediately from accept) if (fcntl(sockfd, F_SETFL, O_NDELAY) < 0) { perror("Can't set socket to non-blocking"); return -1; }	return sockfd; }

//connectSerial-// //Create a connection the serial port located on port    // //Returns a file descriptor for the serial port		 // ////

int connectSerial(char port[],int baud) { //Opens serial port int fd;  //File Descriptor struct termios newtio; fd = open(port,O_RDWR | O_NONBLOCK | O_NOCTTY); // open up the port on read / write mode and nonblocking if (fd == -1){ perror("ERROR: Couldn't open serial port!\nOPEN RETURNED"); return fd; // Opps. We just had an error }	/* Save the current serial port settings */ tcgetattr(fd, &newtio); /* Set the input/output baud rates for this device */ cfsetospeed(&newtio, baud); //change the baud cfsetispeed(&newtio, baud); //Change the baud /* CLOCAL:     Local connection (no modem control) */ /* CREAD:      Enable the receiver */ newtio.c_cflag |= (CLOCAL | CREAD); /* PARENB:     Use NO parity */ /* CSTOPB:     Use 1 stop bit */ /* CSIZE:      Next two constants: */ /* CS8:        Use 8 data bits */ newtio.c_cflag &= ~PARENB; newtio.c_cflag &= ~CSTOPB; newtio.c_cflag &= ~CSIZE; newtio.c_cflag |= CS8; /* Disable hardware flow control */ // BAD: newtio.c_cflag &= ~(CRTSCTS); /* ICANON:     Disable Canonical mode */ /* ECHO:       Disable echoing of input characters */ /* ECHOE:      Echo erase characters as BS-SP-BS */ /* ISIG:       Disable status signals */ // BAD: newtio.c_lflag = (ECHOK); /* IGNPAR:     Ignore bytes with parity errors */ /* ICRNL:      Map CR to NL (otherwise a CR input on the other computer will not terminate input) */ // BAD: newtio.c_iflag |= (IGNPAR | ICRNL); newtio.c_iflag |= (IGNPAR | IGNBRK); /* NO FLAGS AT ALL FOR OUTPUT CONTROL -- Sean */ newtio.c_oflag = 0; newtio.c_oflag &= ~(ONLCR); /* IXON:       Disable software flow control (incoming) */ /* IXOFF:      Disable software flow control (outgoing) */ /* IXANY:      Disable software flow control (any character can start flow control */	newtio.c_iflag &= ~(IXON | IXOFF | IXANY);	/* NO FLAGS AT ALL FOR LFLAGS  -- Sean*/	newtio.c_lflag = 0;	/*** The following settings are deprecated and we are no longer using them (~Peter) ****/	// cam_data.newtio.c_lflag &= ~(ICANON && ECHO && ECHOE && ISIG); 	// cam_data.newtio.c_lflag = (ECHO);	// cam_data.newtio.c_iflag = (IXON | IXOFF);	/* Raw output */	// cam_data.newtio.c_oflag &= ~OPOST;	//Timeouts	//newtio.c_cc[VMIN]=0; //If x = 0, it is non-blocking	//newtio.c_cc[VTIME]=20; //Inter-Character Timer -- i.e. timeout=x*.1 s	/* Clean the modem line and activate new port settings */	tcflush(fd, TCIOFLUSH);	tcsetattr(fd, TCSANOW, &newtio);	return fd; }

//waitOnSocket--// //Check to see if there are connections pending  // //This will return the handle for the connection // //This is set for non-blocking, so the function  // //should return right away. // //// int waitOnSocket(int sockfd) { struct sockaddr_in cli_addr; int clilen = sizeof(cli_addr); int sd;

sd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); //Accept a connection and return the handle for that connection if (sd < -1 ) { fprintf(stderr,"Server Error: Accept error.\n"); return -1; }	return sd; }

//max--// //Returns the largest value of the two inputs// //---// int max(int a, int b) { return (a > b ? a : b); }

//pseudoPTY--// //Create a psuedo terminal			  // //Return the path and name of the PTY port        // //Returns a file descriptor for the pseudo terminal// //-// int pseudoTY(char** PTY) {	//char *letters; int master; int slave; pid_t pid = openpty(&master,&slave,NULL,NULL,NULL);//Create a pseudo terminal //Use ttyname on the file descriptor to find the name of the terminal //That is: slave name = ttyname(slave);

if(pid == -1){//Openpty failed perror("Openpty Failed! :"); return pid; } 	if(pid == 0){//Openpty successful (*PTY) = ttyname(slave); // Ensure that the echo is switched off struct termios orig_termios; if (tcgetattr (master, &orig_termios) < 0) { perror ("ERROR getting current terminal's attributes"); return -1; }		orig_termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); orig_termios.c_oflag &= ~(ONLCR); if (tcsetattr (master, TCSANOW, &orig_termios) < 0) { perror ("ERROR setting current terminal's attributes"); return -1; }		return master; //Return the file descriptor } }

int main(int argc, char *argv[]) { fd_set rset; struct timeval timeout; char * IP; //IP address of socket char * PTY; //Pseudo Terminal Name unsigned char cBuff[BUFFER]; //char c[BUFFER]; int csize; int sd1 = -1; //Socket File Descriptor int sd2 = -1; //Serial Port File Descriptor int pty = -1; //Pseudo Terminal File Descriptor int sockfd1, sockfd2 = -1; int x;	int args = 0; int tmp; char argSerial[] = "-serial"; char argHelp[]  = "-help"; char argPort[]  = "-port"; char argPTY[]   = "-pty"; char argStrip[] = "-strip"; char argBaud[]  = "-baud"; char argDebug[] = "-debug"; int SOCKET_PORT, BAUD, STRIP, DEBUG = 0; char SERIAL[100];

for (x = 0; x < argc; x++){//Cycle through the command line arguments if (!strcmp(argSerial,argv[x])) {//Look for the -serial option strcpy(SERIAL,argv[x+1]); //Copies the port to SERIAL if(BAUD>0){ //If the baud option has been passed sockfd2 = connectSerial(SERIAL, BAUD); //Open the serial port and return the file descriptor if (sockfd2 < 0) { close(sockfd2); if(sockfd1>=0) close(sockfd1); if(pty>=0) close(pty); return -1; }else{ args+=3; }			}else{ args+=3; }		}		else if (!strcmp(argPort,argv[x])) { //Look for -port  option SOCKET_PORT = atoi(argv[x+1]); //Convert string address into int sockfd1 = makeSocket(SOCKET_PORT); //Make the socket and return the file descriptor if (sockfd1 < 0) { close(sockfd1); if(sockfd2>=0) close(sockfd2); if(pty>=0) close(pty); return -1; }else{ args+=5; }		}		else if (!strcmp(argPTY,argv[x])) { //Look for -pty  option pty = pseudoTY(&PTY); if(pty<0){ close(pty); if(sockfd2>=0) close(sockfd2); if(sockfd1>=0) close(sockfd1); return -1; }else{ args+=7; }		}		else if (!strcmp(argBaud,argv[x])) { //Look for -baud option tmp = atoi(argv[x+1]); //Convert string baud rate to int switch (tmp) { //Make sure the value is supported case 115200: BAUD = B115200; break; case 38400: BAUD = B38400; break; case 19200: BAUD = B19200; break; case 9600: BAUD = B9600; break; default: printf("ERROR!: Unknown baud rate.\n"); return -1; break; }			if(strlen(SERIAL) != 0) //If we got the tag for a serial port, create the serial port sockfd2 = connectSerial(SERIAL, BAUD); //Open the serial port and return the file descriptor args+=1; }		else if ( (args != 9 && args != 11 && args !=16 && x == argc-1) || (!strcmp(argHelp,argv[x]))) { //If not enough arguments, output usage directions and exit // Serial <=> Socket w/ baud  = 9 // Serial <=> PTY w/ baud = 11 // Serial <=> Socket, Serial <=> PTY w/ baud = 16 printf("--\n"); printf(" SerialDaemon \n"); printf("--\n"); printf("Usage:"); printf("\t./serialdaemon [options] [arguments]\n"); printf("\t-serial [Use to indicate which serial port to connect to. E.G. /dev/ttyS1]\n"); printf("\t-port  [Use to indicate which TCP/IP port of the local host to connect to. E.G. 5000]\n"); printf("\t-pty   [Create a pseudo terminal for the serial port to connect to.]\n"); printf("\t-baud  [Serial port baudrate.]\n"); printf("\t\t115200\n"); printf("\t\t38400\n"); printf("\t\t19200\n"); printf("\t\t9600\n"); printf("\t-strip [Strip the endline character and replace with a space.]\n"); printf("\t-debug [Set the verbose debug mode for help.]\n"); printf("\t-help  [For this help screen.]\n\n"); printf("Example Usage:\t./serialdaemon -serial /dev/ttyS1 -baud 115200 -pty -port 5000\n"); printf("This will link ttyS1 to localhost:5000 and ttyS1 to a pseudo terminal. The connection to ttyS1 will have a baudrate of 115200.\n"); return -1; }		else if (!strcmp(argStrip,argv[x])){STRIP = 1;} //Look for the -strip option else if (!strcmp(argDebug,argv[x])){DEBUG = 1; printf ("DEBUG: debug mode on!\n");} //Look for the -debug option }	IP = getIP; //Get the local IP address

if(args == 9)//Serial to Socket printf("Connections made: \n\t\t\t%s < = > http://%s:%d\n",SERIAL,IP,SOCKET_PORT); else if(args == 11)//Serial to PTY printf("Connections made: \n\t\t\t%s < = > %s\n",SERIAL,PTY); else if(args == 16)//Serial to PTY &  Serial to Socket printf("Connections made: \n\t\t\t%s < = > http://%s:%d\n\t\t\t%s < = > %s\n",SERIAL,IP,SOCKET_PORT,SERIAL,PTY);

sd1 = waitOnSocket(sockfd1); //Check for a connection to the socket

while(1){ /* Select on sockets */ if(sd1 > 0){ //If There is a connection to the socket then potentially set up these connections: tty < = > socket and tty < = > pty if (DEBUG) printf("DEBUG: New client socket opened.\n"); if (sd1 < 0) { //Error in creating connection close(sd1); return -1; }			sd2 = sockfd2; //File descriptor of the serial port if (sd2 < 0) { //If an error close(sd1); close(sd2); return -1; }			FD_ZERO(&rset); //Clear file descriptors in the rset set while(1) { FD_SET(sd1,&rset);//Set sd1 in rset FD_SET(sd2,&rset);//Set sd2 in rset if(pty!=-1) {//If a virtual port is requested FD_SET(pty,&rset); select(max(max(sd1,sd2),pty)+1,&rset,NULL,NULL,NULL); //Select specifies which of the file descriptors is ready for reading or writing }else { select(max(sd1,sd2)+1,&rset,NULL,NULL,NULL); //Select tests file descriptors in the range of 0 to nfds-1 or in this case 0 to max(sd1,sd2). }				//Check The Socket For Data -- if (FD_ISSET(sd1,&rset)) { //Is there stuff to read from the socket /* There's stuff to read */ if ((csize= read(sd1, &cBuff, BUFFER)) >= 1) { //If there's something worth reading if (STRIP==1) { //Remove endline characters and replace with space for(x = 0 ; x < csize; x++) { if (cBuff[x] == '\n' ) { cBuff[x] = ' '; if (DEBUG) printf ("DEBUG: **STRIPPED**\n"); }							}						}						if (DEBUG) { //Replace &cBuff and cBuff with c							//cBuff[csize] = '\0'; printf("\nDEBUG: %s <== ",SERIAL); for(x=0; x<csize;x++){ printf("%#.2x ",cBuff[x]); }							printf("\n"); }						write(sd2, &cBuff, csize);//Write data from sd1 to sd2 }else{break;}// Failed -- port closed }				//Check The Serial Port For Data -- if (FD_ISSET(sd2,&rset)) {//Is there stuff to read from the serial port if ((csize = read(sd2, &cBuff, BUFFER)) >= 1) {//If there is something worth reading from the serial port write(sd1, &cBuff, csize); //Write this data to the socket if(pty != -1){write(pty,&cBuff,csize);} //Write this data to the virtual com port if (DEBUG) { //Replace &cBuff and cBuff with c							//cBuff[csize] = '\0'; printf("DEBUG: http://%s:%d <== ",IP,SOCKET_PORT); for(x=0; x<csize;x++){ printf("%#.2x ",cBuff[x]); }							printf("\n"); if(pty !=-1){ printf("DEBUG: %s <== ",PTY); for(x=0; x<csize;x++){ printf("%#.2x ",cBuff[x]); }								printf("\n"); }						}					}					//else break;          /* Failed */ }				//Check The PTY Port For Data -- if (pty != -1 && FD_ISSET(pty,&rset)) {//If there is a virtual port, and data is ready, write data from if ((csize = read(pty, &cBuff, BUFFER)) >= 1) {//If there is something worth reading from the serial port write(sd2, &cBuff, csize); //Write this data to the serial port if (DEBUG) { //Replace &cBuff and cBuff with c							//cBuff[csize] = '\0'; printf("\nDEBUG: %s <== ",SERIAL); for(x=0; x<csize;x++){ printf("%#.2x ",cBuff[x]); }							printf("\n"); }					}					//else break;          /* Failed */ }			}			printf("Restarting\n"); close(sd1);/* clean up */ sd1 = waitOnSocket(sockfd1); //Check for a connection to the socket

}else if(pty != -1) {//Else, if there is a virtual port then tty <=> pty sd2 = sockfd2; //File descriptor of the serial port if (sd2 < 0) { //If an error close(sd2); close(pty); return -1; }			FD_ZERO(&rset); //Clear file descriptors in the rset set while(1) { FD_SET(sd2,&rset);//Set sd2 in rset FD_SET(pty,&rset);//Set pty in rset sd1 = waitOnSocket(sockfd1); if(sd1 >= 0){break;} //Check for socket connection, if there is, break out of this loop. select(max(sd2,pty)+1,&rset,NULL,NULL,NULL); // Specifies which of the file descriptors is ready for reading or writing if (FD_ISSET(pty,&rset)) { //If there is a virtual port, and data is ready, write data from if ((csize = read(pty, &cBuff, BUFFER)) >= 1) {//If there is something worth reading from the serial port write(sd2, &cBuff, csize); //Write this data to the serial port if (DEBUG) { //Replace &cBuff and cBuff with c							//cBuff[csize] = '\0'; printf("\nDEBUG: %s <== ",SERIAL); for(x=0; x<csize;x++){ printf("%#.2x ",cBuff[x]); }							printf("\n"); }					}					//else break;          // Failed }

if (FD_ISSET(sd2,&rset)) {//Is there stuff to read from the serial port if ((csize = read(sd2, &cBuff, BUFFER)) >= 1) { //If there is something worth reading from the serial port write(pty, &cBuff, csize); //Write this data to the virtual com port if (DEBUG) { //Replace &cBuff and cBuff with c							//cBuff[csize] = '\0'; printf("DEBUG: %s <== ",PTY); for(x=0; x<csize;x++){ printf("%#.2x ",cBuff[x]); }							printf("\n"); }					}					//else break;          /* Failed */ }

}		}else {//If there isn't a pty, then check the socket for a connection once a second if(DEBUG) printf("\rWaiting on the socket connection...\n"); sd1 = waitOnSocket(sockfd1); //Check for a connection to the socket sleep(1); }	} }