How to integrate 3964(R) protocol driver into TS100 (c) 2003 Pichler Engeneering GmbH Author: Ernst Mosinski ; Jens Reimann ; This document is part of the 3964(R) protocol daemon. It can be freely distributed in terms with the GNU General Public License. CONTENTS 0. REVISION RECORD 1. INTRODUCTION 2. 3964(R) DRIVER 3. KERNEL INIT CODE 4. 3964(R) DRIVER VERIFICATION 5. ADD 3964(R) PROTOCOL TO RUNTIME CONFIGURATION 6. 3964(R) DAEMON 7. INETD SUPER DAEMON 8. VERIFY 3964rd DAEMON AVAILABILITY 9. CONFIGURATION A. 3964 DRIVER CODE B. 3964 DAEMON CODE C. OTHER ISSUES 0. REVISION RECORD 1.0 - 2003-02-10 - Ernst Mosinski: Initial Version 1.1 - 2003-10-11 - Jens Reimann: Updated to new CDK Version 1.3.8 and new configuration options for 3964(R) 1.3 - 2003-10-22 - Jens Reimann: Cleaned up 3964R daemon and enhanced to support both standalone and inetd modes. Also a multicast mode was added. 1. INTRODUCTION The TS100 Serial Server from Cyclades is a PowerPC based system runing a LINUX kernel. The kernel is of version 2.2.14 and is a normal kernel with all drivers embeded. The standard system of the TS100 does not support the 3964(R) protocol on the serial line. But, there is a driver available for this protocol since kernel 2.0. The driver was not standard part of the kernel befor version 2.4. This document describes hot to integrate the 3964(R) driver into the TS100 based on Cyclades system version 1.3.8 (with kernel 2.2.14) using the appropriate CDK. The description asumes, that the CDK and all tools are already installed and, at least a test build of the complete system has been done. All code used and additionally written is licensend under the GNU General Public License and freely distributable. 2. 3964(R) DRIVER The driver code used is based on version 1.6 of the 3964(R) driver. It is lightly modified. First at all it is modified to run as a build in driver, because the original driver is only designed to run as a modul. Second, the driver needed a modification to run correctly with the serial driver of the TS100 (uart.c). The listing of the complete driver is included in appendix A for reference. The driver and his include file have to be copied to the right places in the kernel code. The drivers code in file n_r3964.c has to be copied to directory: /usr/devkit/tslinux/linux/drivers/char The include file n_r3964.h has to be copied to directory: /usr/devkit/tslinux/linux/include/linux Now the driver is in the kernel tree, but it will not be build, because this needs some modification in the build files. First the configuration file has to be modified. This is: /usr/devkit/tslinux/linux/drivers/char/Config.in In this file search for the configuration of "/dev/nvram support". Just after this block add the line: tristate 'Siemens R3964 Line discipline' CONFIG_R3964 This will enable the configuration of the 3964(R) support. Next the makefile has to be changed. This is: /usr/devkit/tslinux/linux/drivers/char/Makefile In this file search for CONFIG_NVRAM. Just after this definition add the following definition for the 3964(R) driver: ifeq ($(CONFIG_R3964),y) L_OBJS += n_r3964.o else ifeq ($(CONFIG_R3964),m) M_OBJS += n_r3964.o endif endif This will add the compilation of the driver to the makefile. At last the configuration file for the TS100 needs to be extended to include the 3964(R) driver into the configuration. This file is: /usr/devkit/tslinux/linux/.config.ts1h In this file search for CONFIG_RTC and add the following line just after it: CONFIG_R3964=y This completes the configuration for the 3964(R) driver and it is now possible to build the driver into the kernel. 3. KERNEL INIT CODE Of course it is now possible to build the driver, but the kernel will not activate it, because the initialisation code is not executed. For this, the driver initialisation of the kernel needs to be extended. The driver initialisation for serial drivers is down in file tty_io.c. It's complete path is: /usr/devkit/tslinux/linux/drivers/char/tty_io.c First of all the tty driver needs to now about the 3964(R) drivers init code entry. This requires a declaration if this function in this driver. In tty_io.c search for '#include '. After this line include the following declaration: #ifdef CONFIG_R3964 extern int r3964_init(void); #endif Next it is required to call the 3964(R) drivers init code out of the initialisation function of the tty driver. Go to the end of the file to function 'int tty_init(void)'. At the end of this function after the CONFIG_VT section but befor the 'return 0' include the following: #ifdef CONFIG_R3964 r3964_init(); #endif This will call the 3964(R) drivers init code and make the line discipline available. 4. 3964(R) DRIVER VERIFICATION If the kernel is build and loaded into the TS100, the activation of the line disciplin can be verified (Building and loading a kernel for TS100 is not part of this document. Refer to the CDK README and the TS100 Manual for this). Login in as root to the TS100 and execute the following command: cat /proc/tty/ldiscs This will display all loaded line disciplines and its corresponding ID. You should see an output like this: n_tty 0 slip 1 ppp 3 R3964 9 The last line indicates the 3964(R) driver is loaded as line discipline with ID number 9 (This is the ID hard coded assigned to the driver). 5. ADD 3964(R) PROTOCOL TO RUNTIME CONFIGURATION The 3964(R) protocol is now available, but it can not yet be configured in the runtime configuration of the TS100 (pslave.conf). The cyclades applications need to be extended to be able to notify the 3964(R) protocol setup. Only litle modifications are required, because the main communication part is done in a seperate daemon. First we need a definition for the new protocol. This is to be done in file: /usr/devkit/tslinux/ras2000-12-25/src/auth.h In this file search for the definition '#define P_MODBUS'. Just after this line include the following new definitions: #define P_3964 '3' /*EM 10-Feb-03 to Support 3964 protocol */ #define P_3964R '4' /* with BCC */ This adds two definition for the 3964(R) protocol. The first adds a definition for the 3964 protocol without BCC. The second definition is for the 3964(R) protocol with additional BCC check. The configuration file parser has to be extended to be aware of the new protocols. This is the file: /usr/devkit/tslinux/ras2000-12-25/src/rwconf.c In this file search for P_MODBUS in struct prlst[]. Just after the modus definition add the following two lines: { "3964", P_3964 }, { "3964R", P_3964R }, This enables the awareness of the new protocols in the configuration file. Next the configuration must be extended to support the 3964 special configuration settings. Just after the following line in struct conf line_cfg[]: { "modbus_smode", C_LIST, mbsmlst, OFFSET_LINE(modbus_smode) }, add the following lines: { "3964_zvz", C_INT, NULL, OFFSET_LINE(r3964_to_zvz) }, //[JR]Sep/10/03 - 3964(R) Protocol { "3964_qvz", C_INT, NULL, OFFSET_LINE(r3964_to_qvz) }, //[JR]Sep/10/03 - 3964(R) Protocol { "3964_max_retries",C_INT, NULL, OFFSET_LINE(r3964_max_retries) }, //[JR]Sep/10/03 - 3964(R) Protocol At last the cy_ras program has to be modified to react on the new protocols. The only thing the cy_ras program has to do, is to do nothing if the 3964(R) protocol is defined. The following file has to be modified: /usr/devkit/tslinux/ras2000-12-25/src/cy_ras.c Again search for P_MODBUS in this file and add the following two lines just after the found line: lineconf[i].protocol != P_3964 && lineconf[i].protocol != P_3964R && In addition the configuration wizard can be extended to be able to configure the new 3964(R) protocol. The file is: /usr/devkit/tslinux/ras2000-12-25/src/wiz.c Search for first 'modbus' entry in file. Extend the line to the following: "modbus", "3964", "3964R" Go done 21 line to the entry: static struct combolist protocoll = {protocollist, 16}; and modify it to: static struct combolist protocoll = {protocollist, 18}; JR 2003-09-08: For CDK version 1.3.4B this was 15 to 17! Search further for 'modbus' and come to line 2379 (JR 2003-09-08: CDK 1.3.4B was 2199). The following line is to be modified: "\r\nssh1/ssh2, raw data, or modbus." Change it to: "\r\nssh1/ssh2, raw data, modbus, 3964 or 3964R." Go done to line: "\r\nwidely used for industrial automation, etc.)", portFlag ? port : ALL); Change and extend it to: "\r\nwidely used for industrial automation, etc," "\r\n3964(R) -a line protocol from Siemens for reliable" "\r\ndata transmission of serial line, 3964R will use additionl" "\r\nBCC for error checking.)" ,portFlag ? port : ALL); Now the wizard can be used to configure the 3964(R) protocol. 6. 3964(R) DAEMON The connection to the serial line using the 3964(R) protocol is done by an own daemon. The daemon will serve a socket port to which a client can connect and send or receive data througout the serial line using the 3964(R) line protocol. The daemon consist of the files 3964r.h and 3964rd.c. Both files have to be copied to directory: /usr/devkit/tslinux/ras2000-12-25/src The contens of both files can be seen in appendix B for reference. To let the daemon part of the standard build process, some files have to be modified. Modify the following file: /usr/devkit/tslinux/ras2000-12-25/ex_config Search for 'modbusd' in the file. You will find two lines yust one after another. Extend the lines to the following: ... cy_alarm modbusd 3964rd pu billing" Next modify the file: /usr/devkit/tslinux/ras2000-12-25/src/Makefile Search for the entry 'modbusd:'. After the modbusd target definition add the new target for the 3964rd daemon: 3964rd: 3964rd.c 3964r.h rot_shm.o \ rot_buf.h ../pslave_cfg.h rwconf.h cy_shm.h rot_shm.h \ Makefile ../Makefile $(CC) -I../../include $(CFLAGS) $(LDFALGS) -o 3964rd 3964rd.c rot_shm.c $(LIBS) 7. INETD SUPER DAEMON The 3964(R) daemon is started by the inetd super daemon. To make the inetd daemon able to start the 3964rd daemon entries in two files are required. The first file to modify is: /usr/devkit/tslinux/cyclades/etc/services In this file search for the entry 'modbus'. After the modbus entry add the following line: r3964 503/tcp # 3964(R) to TCP Next the following files need to be modified: /usr/devkit/tslinux/cyclades/etc/inetd.conf.ts1h /usr/devkit/tslinux/cyclades/etc/inetd.conf.ts1hbb <-- JR 2003-09-08: This file is missing in CDK 1.3.8 Both files need the same addition. Go to the end of the file and add after the modbus entry the following entry: # # 3964(R) Protocol to TCP # r3964 stream tcp nowait.1000 root /sbin/3964rd 3964rd This enables the super daemon to start the 3964 protocol daemon. 8. VERIFY 3964rd DAEMON AVAILABILITY On a runing TS100 system login as root and execute the following command: netstat -a This will produce an output like the following (only part): Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State . . . tcp 0 0 *:ssh *:* LISTEN tcp 0 0 *:https *:* LISTEN tcp 0 0 *:www *:* LISTEN tcp 0 0 *:r3964 *:* LISTEN tcp 0 0 *:modbus *:* LISTEN tcp 0 0 *:telnet *:* LISTEN tcp 0 0 *:daytime *:* LISTEN udp 0 0 TST-TSx000:1024 ns1.pe-muc.de:ntp ESTABLISHED udp 0 0 *:daytime *:* An entry like the fourth line indicates, the 3964rd is ready to receive connections. 9. CONFIGURATION The 3964(R) protocol has to be configured, like all other protocols too, through the file: /etc/portslave/pslave.conf This file resides on the TS100. The following entries are evaluated by the 3964rd daemon: all.speed all.datasize all.stopbits all.parity all.flow all.protocol all.idletimeout all.media For configuration of this parameters see the Cyclades-TS User Guide. The parameter all.protocol now accepts two additional values. This are: 3964 3964R The first parameter enables the 3964 protocol without BCC. The second parameter enabkes 3964R protocol, which is the 3964 protocol with an additional BCC check character. A. 3964 DRIVER CODE n_r3964.h /* r3964 linediscipline for linux * * ----------------------------------------------------------- * Copyright by * Philips Automation Projects * Kassel (Germany) * http://www.pap-philips.de * ----------------------------------------------------------- * This software may be used and distributed according to the terms of * the GNU General Public License, incorporated herein by reference. * * Author: * L. Haag * J. Reimann , * * $Log: r3964.h,v $ * * Revision 1.4 2003/09/10 00:00:00 ctron@dentrassi.de * - Fixed driver in order to let it compile from 2.2 up to 2.6 kernels * - Added configurable timeouts and max retries * * Revision 1.3 2001/03/18 13:02:24 dwmw2 * Fix timer usage, use spinlocks properly. * * Revision 1.2 2001/03/18 12:53:15 dwmw2 * Merge changes in 2.4.2 * * Revision 1.1.1.1 1998/10/13 16:43:14 dwmw2 * This'll screw the version control * * Revision 1.6 1998/09/30 00:40:38 dwmw2 * Updated to use kernel's N_R3964 if available * * Revision 1.4 1998/04/02 20:29:44 lhaag * select, blocking, ... * * Revision 1.3 1998/02/12 18:58:43 root * fixed some memory leaks * calculation of checksum characters * * Revision 1.2 1998/02/07 13:03:17 root * ioctl read_telegram * * Revision 1.1 1998/02/06 19:19:43 root * Initial revision * * */ #define USEOLDTIMER 1 #ifndef __LINUX_N_R3964_H__ #define __LINUX_N_R3964_H__ #ifdef __KERNEL__ /* line disciplines for r3964 protocol */ #include /* * Common ascii handshake characters: */ #define STX 0x02 #define ETX 0x03 #define DLE 0x10 #define NAK 0x15 /* * JR: These will work as default values now! * Timeouts (msecs/10 msecs per timer interrupt): */ #define R3964_TO_QVZ 550/10 #define R3964_TO_ZVZ 220/10 #define R3964_TO_NO_BUF 400/10 #define R3964_NO_TX_ROOM 100/10 #define R3964_TO_RX_PANIC 4000/10 #define R3964_MAX_RETRIES 5 #endif /* * Ioctl-commands */ #define R3964_ENABLE_SIGNALS 0x5301 #define R3964_SETPRIORITY 0x5302 #define R3964_USE_BCC 0x5303 #define R3964_READ_TELEGRAM 0x5304 /* JR: new values for our configurable protocol */ #define R3964_SET_QVZ 0x5405 #define R3964_SET_ZVZ 0x5407 #define R3964_SET_MAX_RETRIES 0x5409 /* Options for R3964_SETPRIORITY */ #define R3964_MASTER 0 #define R3964_SLAVE 1 /* Options for R3964_ENABLE_SIGNALS */ #define R3964_SIG_ACK 0x0001 #define R3964_SIG_DATA 0x0002 #define R3964_SIG_ALL 0x000f #define R3964_SIG_NONE 0x0000 #define R3964_USE_SIGIO 0x1000 /* * r3964 operation states: */ #ifdef __KERNEL__ enum { R3964_IDLE, R3964_TX_REQUEST, R3964_TRANSMITTING, R3964_WAIT_ZVZ_BEFORE_TX_RETRY, R3964_WAIT_FOR_TX_ACK, R3964_WAIT_FOR_RX_BUF, R3964_RECEIVING, R3964_WAIT_FOR_BCC, R3964_WAIT_FOR_RX_REPEAT }; /* * All open file-handles are 'clients' and are stored in a linked list: */ struct r3964_message; struct r3964_client_info { spinlock_t lock; pid_t pid; unsigned int sig_flags; struct r3964_client_info *next; struct r3964_message *first_msg; struct r3964_message *last_msg; struct r3964_block_header *next_block_to_read; int msg_count; }; #endif /* types for msg_id: */ enum {R3964_MSG_ACK=1, R3964_MSG_DATA }; #define R3964_MAX_MSG_COUNT 32 /* error codes for client messages */ #define R3964_OK 0 /* no error. */ #define R3964_TX_FAIL -1 /* transmission error, block NOT sent */ #define R3964_OVERFLOW -2 /* msg queue overflow */ /* the client gets this struct when calling read(fd,...): */ struct r3964_client_message { int msg_id; int arg; int error_code; }; #define R3964_MTU 256 #ifdef __KERNEL__ struct r3964_block_header; /* internal version of client_message: */ struct r3964_message { int msg_id; int arg; int error_code; struct r3964_block_header *block; struct r3964_message *next; }; /* * Header of received block in rx_buf/tx_buf: */ struct r3964_block_header { unsigned int length; /* length in chars without header */ unsigned char *data; /* usually data is located immediately behind this struct */ unsigned int locks; /* only used in rx_buffer */ struct r3964_block_header *next; struct r3964_client_info *owner; /* =NULL in rx_buffer */ }; /* * If rx_buf hasn't enough space to store R3964_MTU chars, * we will reject all incoming STX-requests by sending NAK. */ #define RX_BUF_SIZE 4000 #define TX_BUF_SIZE 4000 #define R3964_MAX_BLOCKS_IN_RX_QUEUE 100 #define R3964_PARITY 0x0001 #define R3964_FRAME 0x0002 #define R3964_OVERRUN 0x0004 #define R3964_UNKNOWN 0x0008 #define R3964_BREAK 0x0010 #define R3964_CHECKSUM 0x0020 #define R3964_ERROR 0x003f #define R3964_BCC 0x4000 #define R3964_DEBUG 0x8000 struct r3964_info { spinlock_t lock; struct tty_struct *tty; unsigned char priority; unsigned char *rx_buf; /* ring buffer */ unsigned char *tx_buf; #if LINUX_VERSION_CODE, * * $Log: n_r3964.c,v $ * * Revision 1.11 2003/09/10 00:00:00 ctron@dentrassi.de * - Fixed driver in order to let it compile from 2.2 up to 2.6 kernels * - Added configurable timeouts and max retries * * Revision 1.10 2001/03/18 13:02:24 dwmw2 * Fix timer usage, use spinlocks properly. * * Revision 1.9 2001/03/18 12:52:14 dwmw2 * Merge changes in 2.4.2 * * Revision 1.8 2000/03/23 14:14:54 dwmw2 * Fix race in sleeping in r3964_read() * * Revision 1.7 1999/28/08 11:41:50 dwmw2 * Port to 2.3 kernel * * Revision 1.6 1998/09/30 00:40:40 dwmw2 * Fixed compilation on 2.0.x kernels * Updated to newly registered tty-ldisc number 9 * * Revision 1.5 1998/09/04 21:57:36 dwmw2 * Signal handling bug fixes, port to 2.1.x. * * Revision 1.4 1998/04/02 20:26:59 lhaag * select, blocking, ... * * Revision 1.3 1998/02/12 18:58:43 root * fixed some memory leaks * calculation of checksum characters * * Revision 1.2 1998/02/07 13:03:34 root * ioctl read_telegram * * Revision 1.1 1998/02/06 19:21:03 root * Initial revision * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* used in new tty drivers */ #include /* used in new tty drivers */ #include #include #include #include #include #include #include #include //#define DEBUG_QUEUE /* Log successful handshake and protocol operations */ //#define DEBUG_PROTO_S /* Log handshake and protocol errors: */ //#define DEBUG_PROTO_E /* Log Linediscipline operations (open, close, read, write...): */ //#define DEBUG_LDISC /* Log module and memory operations (init, cleanup; kmalloc, kfree): */ //#define DEBUG_MODUL /* Macro helpers for debug output: */ #define TRACE(format, args...) printk("r3964: " format "\n" , ## args); #ifdef DEBUG_MODUL #define TRACE_M(format, args...) printk("r3964: " format "\n" , ## args); #else #define TRACE_M(fmt, arg...) /**/ #endif #ifdef DEBUG_PROTO_S #define TRACE_PS(format, args...) printk("r3964: " format "\n" , ## args); #else #define TRACE_PS(fmt, arg...) /**/ #endif #ifdef DEBUG_PROTO_E #define TRACE_PE(format, args...) printk("r3964: " format "\n" , ## args); #else #define TRACE_PE(fmt, arg...) /**/ #endif #ifdef DEBUG_LDISC #define TRACE_L(format, args...) printk("r3964: " format "\n" , ## args); #else #define TRACE_L(fmt, arg...) /**/ #endif #ifdef DEBUG_QUEUE #define TRACE_Q(format, args...) printk("r3964: " format "\n" , ## args); #else #define TRACE_Q(fmt, arg...) /**/ #endif #ifdef USEOLDTIMER static void on_timer_1(void*); static void on_timer_2(void*); #else #endif static void add_tx_queue(struct r3964_info *, struct r3964_block_header *); static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code); static void put_char(struct r3964_info *pInfo, unsigned char ch); static void trigger_transmit(struct r3964_info *pInfo); static void retry_transmit(struct r3964_info *pInfo); static void transmit_block(struct r3964_info *pInfo); static void receive_char(struct r3964_info *pInfo, const unsigned char c); static void receive_error(struct r3964_info *pInfo, const char flag); static void on_timeout(unsigned long priv); static int enable_signals(struct r3964_info *pInfo, pid_t pid, int arg); static int read_telegram(struct r3964_info *pInfo, pid_t pid, unsigned char *buf); static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg, int error_code, struct r3964_block_header *pBlock); static struct r3964_message* remove_msg(struct r3964_info *pInfo, struct r3964_client_info *pClient); static void remove_client_block(struct r3964_info *pInfo, struct r3964_client_info *pClient); static int r3964_open(struct tty_struct *tty); static void r3964_close(struct tty_struct *tty); static ssize_t r3964_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr); static ssize_t r3964_write(struct tty_struct * tty, struct file * file, const unsigned char * buf, size_t nr); static int r3964_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg); static void r3964_set_termios(struct tty_struct *tty, struct termios * old); static unsigned int r3964_poll(struct tty_struct * tty, struct file * file, struct poll_table_struct *wait); static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count); static int r3964_receive_room(struct tty_struct *tty); static struct tty_ldisc tty_ldisc_N_R3964 = { TTY_LDISC_MAGIC, /* magic */ "R3964", /* name */ 0, /* num */ 0, /* flags */ r3964_open, /* open */ r3964_close, /* close */ 0, /* flush_buffer */ 0, /* chars_in_buffer */ r3964_read, /* read */ r3964_write, /* write */ r3964_ioctl, /* ioctl */ r3964_set_termios, /* set_termios */ r3964_poll, /* poll */ r3964_receive_buf, /* receive_buf */ r3964_receive_room, /* receive_room */ 0 /* write_wakeup */ }; static void dump_block(const unsigned char *block, unsigned int length) { unsigned int i,j; char linebuf[16*3+1]; for(i=0;icount_down) { if(!--pInfo->count_down) { on_timeout(pInfo); } } queue_task(&pInfo->bh_2, &tq_timer); } static void on_timer_2(void *arg) { struct r3964_info *pInfo = (struct r3964_info *)arg; if(pInfo->count_down) { if(!--pInfo->count_down) { on_timeout(pInfo); } } queue_task(&pInfo->bh_1, &tq_timer); } #else #endif static void add_tx_queue(struct r3964_info *pInfo, struct r3964_block_header *pHeader) { unsigned long flags; spin_lock_irqsave(&pInfo->lock, flags); pHeader->next = NULL; if(pInfo->tx_last == NULL) { pInfo->tx_first = pInfo->tx_last = pHeader; } else { pInfo->tx_last->next = pHeader; pInfo->tx_last = pHeader; } spin_unlock_irqrestore(&pInfo->lock, flags); TRACE_Q("add_tx_queue %x, length %d, tx_first = %x", (int)pHeader, pHeader->length, (int)pInfo->tx_first ); } static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code) { struct r3964_block_header *pHeader; unsigned long flags; #ifdef DEBUG_QUEUE struct r3964_block_header *pDump; #endif pHeader = pInfo->tx_first; if(pHeader==NULL) return; #ifdef DEBUG_QUEUE printk("r3964: remove_from_tx_queue: %x, length %d - ", (int)pHeader, (int)pHeader->length ); for(pDump=pHeader;pDump;pDump=pDump->next) printk("%x ", (int)pDump); printk("\n"); #endif if(pHeader->owner) { if(error_code) { add_msg(pHeader->owner, R3964_MSG_ACK, 0, error_code, NULL); } else { add_msg(pHeader->owner, R3964_MSG_ACK, pHeader->length, error_code, NULL); } wake_up_interruptible (&pInfo->read_wait); } spin_lock_irqsave(&pInfo->lock, flags); pInfo->tx_first = pHeader->next; if(pInfo->tx_first==NULL) { pInfo->tx_last = NULL; } spin_unlock_irqrestore(&pInfo->lock, flags); kfree(pHeader); TRACE_M("remove_from_tx_queue - kfree %x",(int)pHeader); TRACE_Q("remove_from_tx_queue: tx_first = %x, tx_last = %x", (int)pInfo->tx_first, (int)pInfo->tx_last ); } static void add_rx_queue(struct r3964_info *pInfo, struct r3964_block_header *pHeader) { unsigned long flags; spin_lock_irqsave(&pInfo->lock, flags); pHeader->next = NULL; if(pInfo->rx_last == NULL) { pInfo->rx_first = pInfo->rx_last = pHeader; } else { pInfo->rx_last->next = pHeader; pInfo->rx_last = pHeader; } pInfo->blocks_in_rx_queue++; spin_unlock_irqrestore(&pInfo->lock, flags); TRACE_Q("add_rx_queue: %x, length = %d, rx_first = %x, count = %d", (int)pHeader, pHeader->length, (int)pInfo->rx_first, pInfo->blocks_in_rx_queue); } static void remove_from_rx_queue(struct r3964_info *pInfo, struct r3964_block_header *pHeader) { unsigned long flags; struct r3964_block_header *pFind; if(pHeader==NULL) return; TRACE_Q("remove_from_rx_queue: rx_first = %x, rx_last = %x, count = %d", (int)pInfo->rx_first, (int)pInfo->rx_last, pInfo->blocks_in_rx_queue ); TRACE_Q("remove_from_rx_queue: %x, length %d", (int)pHeader, (int)pHeader->length ); spin_lock_irqsave(&pInfo->lock, flags); if(pInfo->rx_first == pHeader) { /* Remove the first block in the linked list: */ pInfo->rx_first = pHeader->next; if(pInfo->rx_first==NULL) { pInfo->rx_last = NULL; } pInfo->blocks_in_rx_queue--; } else { /* Find block to remove: */ for(pFind=pInfo->rx_first; pFind; pFind=pFind->next) { if(pFind->next == pHeader) { /* Got it. */ pFind->next = pHeader->next; pInfo->blocks_in_rx_queue--; if(pFind->next==NULL) { /* Oh, removed the last one! */ pInfo->rx_last = pFind; } break; } } } spin_unlock_irqrestore(&pInfo->lock, flags); kfree(pHeader); TRACE_M("remove_from_rx_queue - kfree %x",(int)pHeader); TRACE_Q("remove_from_rx_queue: rx_first = %x, rx_last = %x, count = %d", (int)pInfo->rx_first, (int)pInfo->rx_last, pInfo->blocks_in_rx_queue ); } static void put_char(struct r3964_info *pInfo, unsigned char ch) { struct tty_struct *tty = pInfo->tty; if(tty==NULL) return; if(tty->driver.put_char) { tty->driver.put_char(tty, ch); } pInfo->bcc ^= ch; } static void flush(struct r3964_info *pInfo) { struct tty_struct *tty = pInfo->tty; if(tty==NULL) return; if(tty->driver.flush_chars) { tty->driver.flush_chars(tty); } } static void trigger_transmit(struct r3964_info *pInfo) { unsigned long flags; spin_lock_irqsave(&pInfo->lock, flags); if((pInfo->state == R3964_IDLE) && (pInfo->tx_first!=NULL)) { pInfo->state = R3964_TX_REQUEST; pInfo->nRetry=0; pInfo->flags &= ~R3964_ERROR; #ifdef USEOLDTIMER pInfo->count_down = pInfo->to_qvz; #else mod_timer(&pInfo->tmr, jiffies + pInfo->to_qvz); #endif spin_unlock_irqrestore(&pInfo->lock, flags); TRACE_PS("trigger_transmit - sent STX"); put_char(pInfo, STX); flush(pInfo); pInfo->bcc = 0; } else { spin_unlock_irqrestore(&pInfo->lock, flags); } } static void retry_transmit(struct r3964_info *pInfo) { if(pInfo->nRetrymax_retries) { TRACE_PE("transmission failed. Retry #%d", pInfo->nRetry); pInfo->bcc = 0; put_char(pInfo, STX); flush(pInfo); pInfo->state = R3964_TX_REQUEST; pInfo->nRetry++; #ifdef USEOLDTIMER pInfo->count_down = pInfo->to_qvz; #else mod_timer(&pInfo->tmr, jiffies + pInfo->to_qvz); #endif } else { TRACE_PE("transmission failed after %d retries", pInfo->max_retries); remove_from_tx_queue(pInfo, R3964_TX_FAIL); put_char(pInfo, NAK); flush(pInfo); pInfo->state = R3964_IDLE; trigger_transmit(pInfo); } } static void transmit_block(struct r3964_info *pInfo) { struct tty_struct *tty = pInfo->tty; struct r3964_block_header *pBlock = pInfo->tx_first; int room=0; if((tty==NULL) || (pBlock==NULL)) { return; } if(tty->driver.write_room) room=tty->driver.write_room(tty); TRACE_PS("transmit_block %x, room %d, length %d", (int)pBlock, room, pBlock->length); while(pInfo->tx_position < pBlock->length) { if(room<2) break; if(pBlock->data[pInfo->tx_position]==DLE) { /* send additional DLE char: */ put_char(pInfo, DLE); } put_char(pInfo, pBlock->data[pInfo->tx_position++]); room--; } if((pInfo->tx_position == pBlock->length) && (room>=3)) { put_char(pInfo, DLE); put_char(pInfo, ETX); if(pInfo->flags & R3964_BCC) { put_char(pInfo, pInfo->bcc); } pInfo->state = R3964_WAIT_FOR_TX_ACK; #ifdef USEOLDTIMER pInfo->count_down = pInfo->to_qvz; #else mod_timer(&pInfo->tmr, jiffies + pInfo->to_qvz); #endif } flush(pInfo); } static void on_receive_block(struct r3964_info *pInfo) { unsigned int length; struct r3964_client_info *pClient; struct r3964_block_header *pBlock; length=pInfo->rx_position; /* compare byte checksum characters: */ if(pInfo->flags & R3964_BCC) { if(pInfo->bcc!=pInfo->last_rx) { TRACE_PE("checksum error - got %x but expected %x", pInfo->last_rx, pInfo->bcc); pInfo->flags |= R3964_CHECKSUM; } } /* check for errors (parity, overrun,...): */ if(pInfo->flags & R3964_ERROR) { TRACE_PE("on_receive_block - transmission failed error %x", pInfo->flags & R3964_ERROR); put_char(pInfo, NAK); flush(pInfo); if(pInfo->nRetrymax_retries) { pInfo->state=R3964_WAIT_FOR_RX_REPEAT; pInfo->nRetry++; #ifdef USEOLDTIMER pInfo->count_down = R3964_TO_RX_PANIC; #else mod_timer(&pInfo->tmr, jiffies + R3964_TO_RX_PANIC); #endif } else { TRACE_PE("on_receive_block - failed after max retries"); pInfo->state=R3964_IDLE; } return; } /* received block; submit DLE: */ put_char(pInfo, DLE); flush(pInfo); #ifdef USEOLDTIMER pInfo->count_down=0; #else #if LINUX_VERSION_CODEtmr); #else del_timer_sync(&pInfo->tmr); #endif #endif TRACE_PS(" rx success: got %d chars", length); /* prepare struct r3964_block_header: */ pBlock = kmalloc(length+sizeof(struct r3964_block_header), GFP_KERNEL); TRACE_M("on_receive_block - kmalloc %x",(int)pBlock); if(pBlock==NULL) return; pBlock->length = length; pBlock->data = ((unsigned char*)pBlock)+sizeof(struct r3964_block_header); pBlock->locks = 0; pBlock->next = NULL; pBlock->owner = NULL; memcpy(pBlock->data, pInfo->rx_buf, length); /* queue block into rx_queue: */ add_rx_queue(pInfo, pBlock); /* notify attached client processes: */ for(pClient=pInfo->firstClient; pClient; pClient=pClient->next) { if(pClient->sig_flags & R3964_SIG_DATA) { add_msg(pClient, R3964_MSG_DATA, length, R3964_OK, pBlock); } } wake_up_interruptible (&pInfo->read_wait); pInfo->state = R3964_IDLE; trigger_transmit(pInfo); } static void receive_char(struct r3964_info *pInfo, const unsigned char c) { TRACE_L("receive_char: state = %d, c = %c [%x]", pInfo->state, c > ' ' ? c : ' ', (unsigned int)c); switch(pInfo->state) { case R3964_TX_REQUEST: if(c==DLE) { TRACE_PS("TX_REQUEST - got DLE"); pInfo->state = R3964_TRANSMITTING; pInfo->tx_position = 0; transmit_block(pInfo); } else if(c==STX) { if(pInfo->nRetry==0) { TRACE_PE("TX_REQUEST - init conflict"); if(pInfo->priority == R3964_SLAVE) { goto start_receiving; } } else { TRACE_PE("TX_REQUEST - secondary init conflict!?" " Switching to SLAVE mode for next rx."); goto start_receiving; } } else { TRACE_PE("TX_REQUEST - char != DLE: %x", c); retry_transmit(pInfo); } break; case R3964_TRANSMITTING: if(c==NAK) { TRACE_PE("TRANSMITTING - got NAK"); retry_transmit(pInfo); } else { TRACE_PE("TRANSMITTING - got illegal char"); pInfo->state = R3964_WAIT_ZVZ_BEFORE_TX_RETRY; #ifdef USEOLDTIMER pInfo->count_down = pInfo->to_zvz; #else mod_timer(&pInfo->tmr, jiffies + pInfo->to_zvz); #endif } break; case R3964_WAIT_FOR_TX_ACK: if(c==DLE) { TRACE_PS("WAIT_FOR_TX_ACK - got DLE"); remove_from_tx_queue(pInfo, R3964_OK); pInfo->state = R3964_IDLE; trigger_transmit(pInfo); } else { retry_transmit(pInfo); } break; case R3964_WAIT_FOR_RX_REPEAT: /* FALLTROUGH */ case R3964_IDLE: if(c==STX) { /* Prevent rx_queue from overflow: */ if(pInfo->blocks_in_rx_queue >= R3964_MAX_BLOCKS_IN_RX_QUEUE) { TRACE_PE("IDLE - got STX but no space in rx_queue!"); pInfo->state=R3964_WAIT_FOR_RX_BUF; #ifdef USEOLDTIMER pInfo->count_down = R3964_TO_NO_BUF; #else mod_timer(&pInfo->tmr, R3964_TO_NO_BUF); #endif break; } start_receiving: /* Ok, start receiving: */ TRACE_PS("IDLE - got STX"); pInfo->rx_position = 0; pInfo->last_rx = 0; pInfo->flags &= ~R3964_ERROR; pInfo->state=R3964_RECEIVING; #ifdef USEOLDTIMER pInfo->count_down = pInfo->to_zvz; #else mod_timer(&pInfo->tmr, pInfo->to_zvz); #endif pInfo->nRetry = 0; put_char(pInfo, DLE); flush(pInfo); pInfo->bcc = 0; } break; case R3964_RECEIVING: if(pInfo->rx_position < RX_BUF_SIZE) { pInfo->bcc ^= c; if(c==DLE) { if(pInfo->last_rx==DLE) { pInfo->last_rx = 0; goto char_to_buf; } pInfo->last_rx = DLE; break; } else if((c==ETX) && (pInfo->last_rx==DLE)) { if(pInfo->flags & R3964_BCC) { pInfo->state = R3964_WAIT_FOR_BCC; #ifdef USEOLDTIMER pInfo->count_down = pInfo->to_zvz; #else mod_timer(&pInfo->tmr, pInfo->to_zvz); #endif } else { on_receive_block(pInfo); } } else { pInfo->last_rx = c; char_to_buf: pInfo->rx_buf[pInfo->rx_position++] = c; #ifdef USEOLDTIMER pInfo->count_down = pInfo->to_zvz; #else mod_timer(&pInfo->tmr, pInfo->to_zvz); #endif } } /* else: overflow-msg? BUF_SIZE>MTU; should not happen? */ break; case R3964_WAIT_FOR_BCC: pInfo->last_rx = c; on_receive_block(pInfo); break; } } static void receive_error(struct r3964_info *pInfo, const char flag) { switch (flag) { case TTY_NORMAL: break; case TTY_BREAK: TRACE_PE("received break") pInfo->flags |= R3964_BREAK; break; case TTY_PARITY: TRACE_PE("parity error") pInfo->flags |= R3964_PARITY; break; case TTY_FRAME: TRACE_PE("frame error") pInfo->flags |= R3964_FRAME; break; case TTY_OVERRUN: TRACE_PE("frame overrun") pInfo->flags |= R3964_OVERRUN; break; default: TRACE_PE("receive_error - unknown flag %d", flag); pInfo->flags |= R3964_UNKNOWN; break; } } static void on_timeout(unsigned long priv) { struct r3964_info *pInfo = (void *)priv; switch(pInfo->state) { case R3964_TX_REQUEST: TRACE_PE("TX_REQUEST - timeout"); retry_transmit(pInfo); break; case R3964_WAIT_ZVZ_BEFORE_TX_RETRY: put_char(pInfo, NAK); flush(pInfo); retry_transmit(pInfo); break; case R3964_WAIT_FOR_TX_ACK: TRACE_PE("WAIT_FOR_TX_ACK - timeout"); retry_transmit(pInfo); break; case R3964_WAIT_FOR_RX_BUF: TRACE_PE("WAIT_FOR_RX_BUF - timeout"); put_char(pInfo, NAK); flush(pInfo); pInfo->state=R3964_IDLE; break; case R3964_RECEIVING: TRACE_PE("RECEIVING - timeout after %d chars", pInfo->rx_position); put_char(pInfo, NAK); flush(pInfo); pInfo->state=R3964_IDLE; break; case R3964_WAIT_FOR_RX_REPEAT: TRACE_PE("WAIT_FOR_RX_REPEAT - timeout"); pInfo->state=R3964_IDLE; break; case R3964_WAIT_FOR_BCC: TRACE_PE("WAIT_FOR_BCC - timeout"); put_char(pInfo, NAK); flush(pInfo); pInfo->state=R3964_IDLE; break; } } static struct r3964_client_info *findClient( struct r3964_info *pInfo, pid_t pid) { struct r3964_client_info *pClient; for(pClient=pInfo->firstClient; pClient; pClient=pClient->next) { if(pClient->pid == pid) { return pClient; } } return NULL; } static int enable_signals(struct r3964_info *pInfo, pid_t pid, int arg) { struct r3964_client_info *pClient; struct r3964_client_info **ppClient; struct r3964_message *pMsg; if((arg & R3964_SIG_ALL)==0) { /* Remove client from client list */ for(ppClient=&pInfo->firstClient; *ppClient; ppClient=&(*ppClient)->next) { pClient = *ppClient; if(pClient->pid == pid) { TRACE_PS("removing client %d from client list", pid); *ppClient = pClient->next; while(pClient->msg_count) { pMsg=remove_msg(pInfo, pClient); if(pMsg) { kfree(pMsg); TRACE_M("enable_signals - msg kfree %x",(int)pMsg); } } kfree(pClient); TRACE_M("enable_signals - kfree %x",(int)pClient); return 0; } } return -EINVAL; } else { pClient=findClient(pInfo, pid); if(pClient) { /* update signal options */ pClient->sig_flags=arg; } else { /* add client to client list */ pClient=kmalloc(sizeof(struct r3964_client_info), GFP_KERNEL); TRACE_M("enable_signals - kmalloc %x",(int)pClient); if(pClient==NULL) return -ENOMEM; TRACE_PS("add client %d to client list", pid); spin_lock_init(&pClient->lock); pClient->sig_flags=arg; pClient->pid = pid; pClient->next=pInfo->firstClient; pClient->first_msg = NULL; pClient->last_msg = NULL; pClient->next_block_to_read = NULL; pClient->msg_count = 0; pInfo->firstClient=pClient; } } return 0; } static int read_telegram(struct r3964_info *pInfo, pid_t pid, unsigned char *buf) { struct r3964_client_info *pClient; struct r3964_block_header *block; if(!buf) { return -EINVAL; } pClient=findClient(pInfo,pid); if(pClient==NULL) { return -EINVAL; } block=pClient->next_block_to_read; if(!block) { return 0; } else { if (copy_to_user (buf, block->data, block->length)) return -EFAULT; remove_client_block(pInfo, pClient); return block->length; } return -EINVAL; } static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg, int error_code, struct r3964_block_header *pBlock) { struct r3964_message *pMsg; unsigned long flags; if(pClient->msg_countlock, flags); pMsg->msg_id = msg_id; pMsg->arg = arg; pMsg->error_code = error_code; pMsg->block = pBlock; pMsg->next = NULL; if(pClient->last_msg==NULL) { pClient->first_msg=pClient->last_msg=pMsg; } else { pClient->last_msg->next = pMsg; pClient->last_msg=pMsg; } pClient->msg_count++; if(pBlock!=NULL) { pBlock->locks++; } spin_unlock_irqrestore(&pClient->lock, flags); } else { if((pClient->last_msg->msg_id == R3964_MSG_ACK) && (pClient->last_msg->error_code==R3964_OVERFLOW)) { pClient->last_msg->arg++; TRACE_PE("add_msg - inc prev OVERFLOW-msg"); } else { msg_id = R3964_MSG_ACK; arg = 0; error_code = R3964_OVERFLOW; pBlock = NULL; TRACE_PE("add_msg - queue OVERFLOW-msg"); goto queue_the_message; } } /* Send SIGIO signal to client process: */ if(pClient->sig_flags & R3964_USE_SIGIO) { kill_proc(pClient->pid, SIGIO, 1); } } static struct r3964_message *remove_msg(struct r3964_info *pInfo, struct r3964_client_info *pClient) { struct r3964_message *pMsg=NULL; unsigned long flags; if(pClient->first_msg) { spin_lock_irqsave(&pClient->lock, flags); pMsg = pClient->first_msg; pClient->first_msg = pMsg->next; if(pClient->first_msg==NULL) { pClient->last_msg = NULL; } pClient->msg_count--; if(pMsg->block) { remove_client_block(pInfo, pClient); pClient->next_block_to_read = pMsg->block; } spin_unlock_irqrestore(&pClient->lock, flags); } return pMsg; } static void remove_client_block(struct r3964_info *pInfo, struct r3964_client_info *pClient) { struct r3964_block_header *block; TRACE_PS("remove_client_block PID %d", pClient->pid); block=pClient->next_block_to_read; if(block) { block->locks--; if(block->locks==0) { remove_from_rx_queue(pInfo, block); } } pClient->next_block_to_read = NULL; } /************************************************************* * Line discipline routines *************************************************************/ static int r3964_open(struct tty_struct *tty) { struct r3964_info *pInfo; MOD_INC_USE_COUNT; TRACE_L("open"); TRACE_L("tty=%x, PID=%d, disc_data=%x", (int)tty, current->pid, (int)tty->disc_data); pInfo=kmalloc(sizeof(struct r3964_info), GFP_KERNEL); TRACE_M("r3964_open - info kmalloc %x",(int)pInfo); if(!pInfo) { printk(KERN_ERR "r3964: failed to alloc info structure\n"); return -ENOMEM; } pInfo->rx_buf = kmalloc(RX_BUF_SIZE, GFP_KERNEL); TRACE_M("r3964_open - rx_buf kmalloc %x",(int)pInfo->rx_buf); if(!pInfo->rx_buf) { printk(KERN_ERR "r3964: failed to alloc receive buffer\n"); kfree(pInfo); TRACE_M("r3964_open - info kfree %x",(int)pInfo); return -ENOMEM; } pInfo->tx_buf = kmalloc(TX_BUF_SIZE, GFP_KERNEL); TRACE_M("r3964_open - tx_buf kmalloc %x",(int)pInfo->tx_buf); if(!pInfo->tx_buf) { printk(KERN_ERR "r3964: failed to alloc transmit buffer\n"); kfree(pInfo->rx_buf); TRACE_M("r3964_open - rx_buf kfree %x",(int)pInfo->rx_buf); kfree(pInfo); TRACE_M("r3964_open - info kfree %x",(int)pInfo); return -ENOMEM; } spin_lock_init(&pInfo->lock); pInfo->tty = tty; #if LINUX_VERSION_CODEread_wait = NULL; #else init_waitqueue_head (&pInfo->read_wait); #endif #ifdef USEOLDTIMER pInfo->count_down = 0; #else #endif pInfo->priority = R3964_MASTER; pInfo->rx_first = pInfo->rx_last = NULL; pInfo->tx_first = pInfo->tx_last = NULL; pInfo->rx_position = 0; pInfo->tx_position = 0; pInfo->last_rx = 0; pInfo->blocks_in_rx_queue = 0; pInfo->firstClient=NULL; pInfo->state=R3964_IDLE; pInfo->flags = R3964_DEBUG; pInfo->nRetry = 0; pInfo->max_retries = R3964_MAX_RETRIES; pInfo->to_zvz = R3964_TO_ZVZ; pInfo->to_qvz = R3964_TO_QVZ; tty->disc_data = pInfo; #ifdef USEOLDTIMER /* * Add 'on_timer' to timer task queue * (will be called from timer bh) */ pInfo->bh_1.next = NULL; pInfo->bh_1.sync = 0; pInfo->bh_1.routine = &on_timer_1; pInfo->bh_1.data = pInfo; pInfo->bh_2.next = NULL; pInfo->bh_2.sync = 0; pInfo->bh_2.routine = &on_timer_2; pInfo->bh_2.data = pInfo; queue_task(&pInfo->bh_1, &tq_timer); #else init_timer(&pInfo->tmr); pInfo->tmr.data = (unsigned long)pInfo; pInfo->tmr.function = on_timeout; #endif return 0; } static void r3964_close(struct tty_struct *tty) { #ifdef USEOLDTIMER struct tq_struct *tq, *prev; #else #endif struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; struct r3964_client_info *pClient, *pNext; struct r3964_message *pMsg; struct r3964_block_header *pHeader, *pNextHeader; unsigned long flags; TRACE_L("close"); /* * Make sure that our task queue isn't activated. If it * is, take it out of the linked list. */ #ifdef USEOLDTIMER spin_lock_irqsave(&pInfo->lock, flags); for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { if ((tq == &pInfo->bh_1) || (tq==&pInfo->bh_2)) { if (prev) prev->next = tq->next; else tq_timer = tq->next; break; } } spin_unlock_irqrestore(&pInfo->lock, flags); #else #if LINUX_VERSION_CODEtmr); #else del_timer_sync(&pInfo->tmr); #endif #endif /* Remove client-structs and message queues: */ pClient=pInfo->firstClient; while(pClient) { pNext=pClient->next; while(pClient->msg_count) { pMsg=remove_msg(pInfo, pClient); if(pMsg) { kfree(pMsg); TRACE_M("r3964_close - msg kfree %x",(int)pMsg); } } kfree(pClient); TRACE_M("r3964_close - client kfree %x",(int)pClient); pClient=pNext; } /* Remove jobs from tx_queue: */ spin_lock_irqsave(&pInfo->lock, flags); pHeader=pInfo->tx_first; pInfo->tx_first=pInfo->tx_last=NULL; spin_unlock_irqrestore(&pInfo->lock, flags); while(pHeader) { pNextHeader=pHeader->next; kfree(pHeader); pHeader=pNextHeader; } /* Free buffers: */ wake_up_interruptible(&pInfo->read_wait); kfree(pInfo->rx_buf); TRACE_M("r3964_close - rx_buf kfree %x",(int)pInfo->rx_buf); kfree(pInfo->tx_buf); TRACE_M("r3964_close - tx_buf kfree %x",(int)pInfo->tx_buf); kfree(pInfo); TRACE_M("r3964_close - info kfree %x",(int)pInfo); MOD_DEC_USE_COUNT; } static ssize_t r3964_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr) { struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; struct r3964_client_info *pClient; struct r3964_message *pMsg; struct r3964_client_message theMsg; #if LINUX_VERSION_CODEpid; int count; TRACE_L("read()"); pClient=findClient(pInfo, pid); if(pClient) { pMsg = remove_msg(pInfo, pClient); if(pMsg==NULL) { /* no messages available. */ if (file->f_flags & O_NONBLOCK) { return -EAGAIN; } /* block until there is a message: */ add_wait_queue(&pInfo->read_wait, &wait); repeat: current->state = TASK_INTERRUPTIBLE; pMsg = remove_msg(pInfo, pClient); if (!pMsg && !signal_pending(current)) { schedule(); goto repeat; } current->state = TASK_RUNNING; remove_wait_queue(&pInfo->read_wait, &wait); } /* If we still haven't got a message, we must have been signalled */ if (!pMsg) return -EINTR; /* deliver msg to client process: */ theMsg.msg_id = pMsg->msg_id; theMsg.arg = pMsg->arg; theMsg.error_code = pMsg->error_code; count = sizeof(struct r3964_client_message); kfree(pMsg); TRACE_M("r3964_read - msg kfree %x",(int)pMsg); if (copy_to_user(buf,&theMsg, count)) return -EFAULT; TRACE_PS("read - return %d", count); return count; } return -EPERM; } static ssize_t r3964_write(struct tty_struct * tty, struct file * file, const unsigned char *data, size_t count) { struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; struct r3964_block_header *pHeader; struct r3964_client_info *pClient; unsigned char *new_data; int status; int pid; TRACE_L("write request, %d characters", count); /* * Verify the pointers */ if(!pInfo) return -EIO; status = verify_area (VERIFY_READ, data, count); if (status != 0) { return status; } /* * Ensure that the caller does not wish to send too much. */ if (count > R3964_MTU) { if (pInfo->flags & R3964_DEBUG) { TRACE_L (KERN_WARNING "r3964_write: truncating user packet " "from %u to mtu %d", count, R3964_MTU); } count = R3964_MTU; } /* * Allocate a buffer for the data and fetch it from the user space. */ new_data = kmalloc (count+sizeof(struct r3964_block_header), GFP_KERNEL); TRACE_M("r3964_write - kmalloc %x",(int)new_data); if (new_data == NULL) { if (pInfo->flags & R3964_DEBUG) { printk (KERN_ERR "r3964_write: no memory\n"); } return -ENOSPC; } pHeader = (struct r3964_block_header *)new_data; pHeader->data = new_data + sizeof(struct r3964_block_header); pHeader->length = count; pHeader->locks = 0; pHeader->owner = NULL; pid=current->pid; pClient=findClient(pInfo, pid); if(pClient) { pHeader->owner = pClient; } __copy_from_user(pHeader->data, data, count); /* We already verified this */ if(pInfo->flags & R3964_DEBUG) { dump_block(pHeader->data, count); } /* * Add buffer to transmit-queue: */ add_tx_queue(pInfo, pHeader); trigger_transmit(pInfo); return 0; } static int r3964_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg) { struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; if(pInfo==NULL) return -EINVAL; switch(cmd) { case R3964_ENABLE_SIGNALS: return enable_signals(pInfo, current->pid, arg); case R3964_SETPRIORITY: if(argR3964_SLAVE) return -EINVAL; pInfo->priority = arg & 0xff; return 0; case R3964_USE_BCC: if(arg) pInfo->flags |= R3964_BCC; else pInfo->flags &= ~R3964_BCC; return 0; case R3964_READ_TELEGRAM: return read_telegram(pInfo, current->pid, (unsigned char *)arg); case R3964_SET_ZVZ: if ( arg>0 ) pInfo->to_zvz = arg/10; else pInfo->to_zvz = R3964_TO_ZVZ; TRACE_L("Set ZVZ timeout to %d msecs", pInfo->to_zvz*10 ); return 0; case R3964_SET_QVZ: if ( arg>0 ) pInfo->to_qvz = arg/10; else pInfo->to_qvz = R3964_TO_QVZ; TRACE_L("Set QVZ timeout to %d msecs", pInfo->to_qvz*10 ); return 0; case R3964_SET_MAX_RETRIES: if ( arg>0 ) pInfo->max_retries = arg; else pInfo->max_retries = R3964_MAX_RETRIES; TRACE_L("Set max tries to %d", pInfo->max_retries ); return 0; default: return -ENOIOCTLCMD; } } static void r3964_set_termios(struct tty_struct *tty, struct termios * old) { TRACE_L("set_termios"); } /* Called without the kernel lock held - fine */ static unsigned int r3964_poll(struct tty_struct * tty, struct file * file, struct poll_table_struct *wait) { struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; int pid=current->pid; struct r3964_client_info *pClient; struct r3964_message *pMsg=NULL; unsigned long flags; int result = POLLOUT; TRACE_L("POLL"); pClient=findClient(pInfo,pid); if(pClient) { poll_wait(file, &pInfo->read_wait, wait); spin_lock_irqsave(&pInfo->lock, flags); pMsg=pClient->first_msg; spin_unlock_irqrestore(&pInfo->lock, flags); if(pMsg) result |= POLLIN | POLLRDNORM; } else { result = -EINVAL; } return result; } static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; const unsigned char *p; char *f, flags = 0; int i; for (i=count, p = cp, f = fp; i; i--, p++) { if (f) flags = *f++; if(flags==TTY_NORMAL) { receive_char(pInfo, *p); } else { receive_error(pInfo, flags); } } } static int r3964_receive_room(struct tty_struct *tty) { TRACE_L("receive_room"); /* return -1;*/ return RX_BUF_SIZE; } #if LINUX_VERSION_CODE, * Jens Reimann , * * Copyright: (c) 2003 Pichler Engineering GmbH * * 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. * * Date: 2003-10-11 * * Comments: * */ #ifndef _3964R_H #define _3964R_H /**************************************************/ /********** Constant Definitions **********/ /**************************************************/ #undef TRUE #define TRUE 1 #undef FALSE #define FALSE (!TRUE) #undef NULL #define NULL ((void*)0) #define PARITY_NONE 1 #define PARITY_ODD 2 #define PARITY_EVEN 3 #define FLOW_NONE 0 #define FLOW_HARD 1 #define FLOW_SOFT 2 #define INHSOCK 0 /** * Structure describing the serial line. * @author Ernst Mosinski */ struct SerialConfigurationType { int speed; int dataSize; int parity; int stopBits; int flowControl; int media; }; /** * Structure describing configuration for 3964(R) * protocol. * @author Ernst Mosinski */ struct ConfigurationType { struct SerialConfigurationType serial; int protocol; int idleTimeout; int bcc; int priority; char * tty; unsigned int max_retries; unsigned int to_zvz; unsigned int to_qvz; }; #endif /* _3964R_H */ 3964rd.c /* * Module: r3964d.c (3964(R)/TCP deamon fpr TS100) * * Author: Ernst Mosinski , * Jens Reimann , * * Copyright: (c) 2003 Pichler Engineering GmbH * * 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. * * Date: 2003-10-11 * * Comments: * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rot_buf.h" #include "../pslave_cfg.h" #include "rwconf.h" #include "cy_shm.h" #include "auth.h" #include "rot_shm.h" #include "3964r.h" /* * Some constants */ static const char * const C_Null = "(NULL)"; static const char * const C_Version = "1.0.0"; /* * Some global variables for debugging. */ static int G_DoDebug = FALSE; static int G_DoSyslog = FALSE; static int G_DebugFd = -1; /** * Log a message to debug output and syslog output. * This function will write a message to either debug output, * syslog output, or both. * @author Ernsst Mosinski * @param level - The level of information. This is syslog priority. * @param format - Pointer to format string (printf). * @param ... - Varaible arguments to format string. * @input G_DoDebug and G_DoSyslog will determin output to go. */ static void LogMessage(int level, const char * format, ...) { va_list ap; char buffer[1024]; int length; // Debug or syslog output? if (G_DoDebug || G_DoSyslog) { // Convert level to text for output. switch (level) { case LOG_EMERG: strcpy(buffer, "LOG_EMERG: "); break; case LOG_ALERT: strcpy(buffer, "LOG_ALERT: "); break; case LOG_CRIT: strcpy(buffer, "LOG_CRIT: "); break; case LOG_ERR: strcpy(buffer, "LOG_ERR: "); break; case LOG_WARNING: strcpy(buffer, "LOG_WARNING: "); break; case LOG_NOTICE: strcpy(buffer, "LOG_NOTICE: "); break; case LOG_INFO: strcpy(buffer, "LOG_INFO: "); break; case LOG_DEBUG: strcpy(buffer, "LOG_DEBUG: "); break; } // make a nice message length = strlen(buffer); va_start(ap, format); vsnprintf(&buffer[length], sizeof(buffer) - length, format, ap); va_end(ap); } // Debug output? if (G_DoDebug) { write(G_DebugFd, buffer, strlen(buffer)); } // Syslog output? if (G_DoSyslog) { syslog(level, buffer); } } /** * Show configuration * Output configuration to debug and/or syslog output. * @author Ernst Mosinski * @param configuration - Pointer to the configuration to dump. */ static void ShowConfiguration(const struct ConfigurationType *configuration) { LogMessage(LOG_DEBUG, "configuration.serial.speed = %d\n", configuration->serial.speed); LogMessage(LOG_DEBUG, "configuration.serial.dataSize = %d\n", configuration->serial.dataSize); LogMessage(LOG_DEBUG, "configuration.serial.parity = %d\n", configuration->serial.parity); LogMessage(LOG_DEBUG, "configuration.serial.stopBits = %d\n", configuration->serial.stopBits); LogMessage(LOG_DEBUG, "configuration.serial.flowControl = %d\n", configuration->serial.flowControl); LogMessage(LOG_DEBUG, "configuration.serial.media = %d\n", configuration->serial.media); LogMessage(LOG_DEBUG, "configuration.protocol = %d\n", configuration->protocol); LogMessage(LOG_DEBUG, "configuration.idleTimeout = %d\n", configuration->idleTimeout); LogMessage(LOG_DEBUG, "configuration.bcc = %d\n", configuration->bcc); LogMessage(LOG_DEBUG, "configuration.priority = %d\n", configuration->priority); LogMessage(LOG_DEBUG, "configuration.to_zvz = %d\n", configuration->to_zvz); LogMessage(LOG_DEBUG, "configuration.to_qvz = %d\n", configuration->to_qvz); LogMessage(LOG_DEBUG, "configuration.max_retries = %d\n", configuration->max_retries); if (configuration->tty == NULL) { LogMessage(LOG_DEBUG, "configuration.tty = NULL\n"); } else { LogMessage(LOG_DEBUG, "configuration.tty = %s\n", configuration->tty); } } /** * Get Peer Network Address * The function will return the peer network address as string. * This function will only work with IP4! * @author Ernst Mosinski * @return A pointer to a string with the peer's network address. * In case of error a null string "(NULL)" is returned. */ static char * GetPeerNetworkAddress() { int ret; unsigned short port; char portStr[16]; static char str[128]; // Unix domain is largest struct sockaddr_in client; socklen_t length = sizeof(client); // Get the peer name ret = getpeername(0, (struct sockaddr *)&client, &length); if (ret < 0) { LogMessage(LOG_ERR, "error getting peer name! errno = %d\n", errno); strcpy(str, C_Null); return str; } // Convert peer to a network number if (inet_ntop(AF_INET, &client.sin_addr, str, sizeof(str)) == NULL) { LogMessage(LOG_ERR, "error getting peer presentation! errno = %d\n", errno); strcpy(str, C_Null); return str; } // Convert port number to host byte order. port = ntohs(client.sin_port); if (port == 0) { LogMessage(LOG_ERR, "error converting port number! port = %hu\n", client.sin_port); return str; } // Convert port number to string snprintf(portStr, sizeof(portStr), ":%hu", port); strcat(str, portStr); return str; } /** * Signal handler. * Handle some signals. * @author Ernst Mosinski * @param signal - The releasing signal. */ static void SignalHandler(int signal) { LogMessage(LOG_NOTICE, "got signal # %d\n", signal); if ((signal == SIGINT) || (signal == SIGQUIT)) { exit(0); } } /** * Setup signal handlers. * This function will setup a signal handler for some signals. * @author Ernst Mosinski */ static void SetupSignals(void) { struct sigaction sa; sa.sa_handler = SignalHandler; __sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_restorer = NULL; sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGALRM, &sa, NULL); sigaction(SIGTERM, &sa, NULL); } /** * Open and configure 3964 port. * The function will open the serial line and configures * it to work in 3964 line discipline. * The function will not return on any error. * @author Ernst Mosinski * @param configuration - Pointer to the configuration. * @return The file descriptor of the port. */ static int Open3964Port(const struct ConfigurationType * configuration) { int fd; int ret; int media; speed_t speed; int ldisc = N_R3964; struct termios theTermio; // Open the serial device fd = open(configuration->tty, O_RDWR, 0); if (fd < 0) { LogMessage(LOG_ERR, "unable to open serial device %s\n", configuration->tty); exit(1); } // Get the attributes ret = tcgetattr(fd, &theTermio); if (ret < 0) { LogMessage(LOG_ERR, "error getting attributes of device %s\n", configuration->tty); exit(1); } // Set speed switch (configuration->serial.speed) { case 50: speed = B50; break; case 75: speed = B75; break; case 110: speed = B110; break; case 134: speed = B134; break; case 150: speed = B150; break; case 200: speed = B200; break; case 300: speed = B300; break; case 600: speed = B600; break; case 1200: speed = B1200; break; case 1800: speed = B1800; break; case 2400: speed = B2400; break; case 4800: speed = B4800; break; case 14400: speed = B14400; break; case 19200: speed = B19200; break; case 28800: speed = B28800; break; case 38400: speed = B38400; break; case 57600: speed = B57600; break; case 76800: speed = B76800; break; case 115200: speed = B115200; break; case 230400: speed = B230400; break; case 460800: speed = B460800; break; case 921600: speed = B921600; break; case 9600: default: speed = B9600; break; } ret = cfsetospeed(&theTermio, speed); if (ret < 0) { LogMessage(LOG_ERR, "error setting output speed for device %s to %d\n", configuration->tty, configuration->serial.speed); exit(1); } ret = cfsetispeed(&theTermio, speed); if (ret < 0) { LogMessage(LOG_ERR, "error setting input speed for device %s to %d\n", configuration->tty, configuration->serial.speed); exit(1); } // Set data size theTermio.c_cflag &= ~CSIZE; // Clear bits switch (configuration->serial.dataSize) { case 5: theTermio.c_cflag |= CS5; break; case 6: theTermio.c_cflag |= CS6; break; case 7: theTermio.c_cflag |= CS7; break; case 8: default: theTermio.c_cflag |= CS8; break; } // Set parity switch (configuration->serial.parity) { case PARITY_ODD: theTermio.c_cflag |= (PARENB | PARODD); break; case PARITY_EVEN: theTermio.c_cflag |= PARENB; theTermio.c_cflag &= ~PARODD; break; default: theTermio.c_cflag &= ~PARENB; // Disable parity break; } // Set flow control switch (configuration->serial.flowControl) { case FLOW_HARD: theTermio.c_iflag &= ~(IXON | IXOFF); theTermio.c_cflag |= CRTSCTS; break; case FLOW_SOFT: theTermio.c_iflag |= (IXON | IXOFF); theTermio.c_cflag &= CRTSCTS; break; default: // Disable flow control theTermio.c_iflag &= ~(IXON | IXOFF); theTermio.c_cflag &= ~CRTSCTS; break; } // No canonical mode theTermio.c_lflag &= ~ICANON; // Disable echo theTermio.c_lflag &= ~(ECHO | ECHONL); // Disable modem signals theTermio.c_cflag |= CLOCAL; // Flush the port ret = tcflush(fd, TCIOFLUSH); if (ret < 0) { LogMessage(LOG_ERR, "error flushing device %s\n", configuration->tty); exit(1); } LogMessage(LOG_DEBUG, "cflag = %x\n", theTermio.c_cflag); // Set the port configuration ret = tcsetattr(fd, TCSANOW, &theTermio); if (ret < 0) { LogMessage(LOG_ERR, "error setting configuration for device %s\n", configuration->tty); exit(1); } // Now, and not before, we can do the ioctl() // Set media type media = configuration->serial.media; ret = ioctl(fd, TIOCSERCONFIG, &media); if (ret < 0) { LogMessage(LOG_ERR, "error setting media type for device %s to %d\n", configuration->tty, configuration->serial.media); exit(1); } // Set line discipline to 3964 ret = ioctl(fd, TIOCSETD, (caddr_t)&ldisc); if (ret < 0) { LogMessage(LOG_ERR, "error setting line disciplin to 3964 for device %s\n", configuration->tty); exit(1); } ret = ioctl(fd, R3964_SETPRIORITY, configuration->priority); if (ret < 0) { LogMessage(LOG_ERR, "error setting priority for device %s to %d\n", configuration->tty, configuration->priority); exit(1); } ret = ioctl(fd, R3964_USE_BCC, configuration->bcc); if (ret < 0) { LogMessage(LOG_ERR, "error setting BCC mode for device %s to %d\n", configuration->tty, configuration->bcc); exit(1); } if ( configuration->max_retries > 0 ) ret = ioctl(fd, R3964_SET_MAX_RETRIES, configuration->max_retries); if (ret < 0) { LogMessage(LOG_ERR, "unable to set max retries for device %s to %d\n", configuration->tty, configuration->max_retries); exit(1); } if ( configuration->to_zvz > 0 ) ret = ioctl(fd, R3964_SET_ZVZ, configuration->to_zvz); if (ret < 0) { LogMessage(LOG_ERR, "unable to set timeout ZVZ for device %s to %d\n", configuration->tty, configuration->to_zvz); exit(1); } if ( configuration->to_qvz > 0 ) ret = ioctl(fd, R3964_SET_QVZ, configuration->to_qvz); if (ret < 0) { LogMessage(LOG_ERR, "unable to set timeout QVZ for device %s to %d\n", configuration->tty, configuration->to_qvz); exit(1); } /* Important: Notification on succes/error on sending * and receiving packages. */ ret = ioctl(fd, R3964_ENABLE_SIGNALS, R3964_SIG_ALL); if (ret < 0) { LogMessage(LOG_ERR, "error setting signal mode for device %s\n", configuration->tty); exit(1); } LogMessage(LOG_INFO, "setting up 3964 mode for device %s done!\n", configuration->tty); return fd; } /** * Exit daemon. * This function will close all files and exit the daemon. * It never will return. * @author Ernst Mosinski * @param tty_fd - The file descriptor of the serial device. * @param socket_fd - The file descriptor of the communication socket */ static void DoExit(int tty_fd, int socket_fd) { int ret; LogMessage(LOG_NOTICE, "3964 daemon exiting!\n"); // Close serial device ret = close(tty_fd); if (ret < 0) { LogMessage(LOG_ERR, "error closing serial device!\n"); exit(1); } ret = close(socket_fd); if (ret < 0) { LogMessage(LOG_ERR, "error closing communication socket!\n"); exit(1); } // Close debug output if (G_DoDebug) { ret = close(G_DebugFd); G_DoDebug = FALSE; // No more debugging if (ret < 0) { // Maybe output to syslog LogMessage(LOG_ERR, "error closing debug file\n"); exit(1); } } // Close syslog if (G_DoSyslog) { closelog(); } exit(0); } /** * Read socket. * The function will read data from the socket and forward it to * the serial line. * @author Ernst Mosinski * @param socket_fd - File descriptor of the socket. * @param tty_fd - File descriptor of the serial line. */ static void ReadSocket(int socket_fd, int tty_fd) { int received; int ret; char tcpBuffer[4096]; received = recv(socket_fd, tcpBuffer, sizeof(tcpBuffer), 0); if (received <= 0) { LogMessage(LOG_ERR, "error reading socket. return = %d, errno = %d\n", received, errno); DoExit(tty_fd, socket_fd); } LogMessage(LOG_DEBUG, "received %d bytes from socket.\n", received); ret = write(tty_fd, tcpBuffer, received); if (ret < 0) { LogMessage(LOG_ERR, "error sending data to serial line. errno = %d\n", errno); } } /** * Read serial line. * The function will read data from the serial line and forward it * the network connection. * @author Ernst Mosinski * @param tty_fd - File descriptor of the serial line. * @param socket_fd - File descriptor of the socket. */ static void ReadSerialLine(int tty_fd, int socket_fd) { ssize_t ret; struct r3964_client_message r3964Message; char buffer[1024]; ret = read(tty_fd, &r3964Message, sizeof(r3964Message)); if (ret < 0) { LogMessage(LOG_ERR, "error reading serial line. return = %d, errno = %d\n", ret, errno); return; } switch (r3964Message.msg_id) { case R3964_MSG_ACK: // Got message LogMessage(LOG_DEBUG, "message R3964_MSG_ACK msg_id = %d, arg = %d," " error_code = %d\n", r3964Message.msg_id, r3964Message.arg, r3964Message.error_code); // check if error happened if (r3964Message.error_code != R3964_OK) { LogMessage(LOG_ERR, "error on serial line! terminating!"); DoExit(tty_fd, socket_fd); } break; case R3964_MSG_DATA: // Got data ret = ioctl(tty_fd, R3964_READ_TELEGRAM, buffer); if (ret < 0) { LogMessage(LOG_ERR, "error getting serial line data. return = %d," " errno = %d\n", ret, errno); return; } LogMessage(LOG_DEBUG, "message R3964_MSG_DATA msg_id = %d, arg = %d," " erro_code = %d\n", r3964Message.msg_id, r3964Message.arg, r3964Message.error_code); // check if error happened if (r3964Message.error_code != R3964_OK) { LogMessage(LOG_ERR, "error on serial line receiving data! terminating!"); DoExit(tty_fd, socket_fd); } ret = send(socket_fd, buffer, r3964Message.arg, 0); if (ret < 0) { LogMessage(LOG_ERR, "error sending data to network. errno = %d\n", errno); } break; } } /** * 3964(R) daemon entry point. * This daemon will connect a TCP connection to the serial line. * It will use the 3964(R) data link protocol in communication over the * serial line. The protocol is implemented by a line discipline driver * in the kernel. The task of the daemon is to handle this properly. * @author Ernst Mosinski * @param argc - The number of arguments on the command line. * @param argv - An array of the arguments * @return something greater 0 on error. */ int main(int argc, char **argv) { int argument; int argumentError = FALSE; int ttyFd = -1; fd_set inputs; int fdReady; const char * const logFileName = "/root/3964rd.log"; struct ConfigurationType configuration; struct timeval timeout; // Check arguments for (argument = 1; argument < argc; argument++) { if (strcmp(argv[argument], "-debug") == 0) { G_DoDebug = TRUE; } else if (strcmp(argv[argument], "-syslog") == 0) { G_DoSyslog = TRUE; } else { argumentError = TRUE; } } if (argumentError) { printf("3964R daemon usage: %s [-debug | -syslog]\n", argv[0]); exit(1); } // Open debug file, if required. if (G_DoDebug) { G_DebugFd = open(logFileName, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC); if (G_DebugFd <= 0) { printf("3964R daemon can not open log file %s\n", logFileName); exit(1); } } // Open syslog, if required. if (G_DoSyslog) { openlog(argv[0], LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); } LogMessage(LOG_INFO, "3964R daemon starting up! Version %s\n", C_Version); LogMessage(LOG_NOTICE, "network connection from %s\n", GetPeerNetworkAddress()); // Initialize Shared Memory if (shm_init(-1) < 0) { LogMessage(LOG_ERR, "3964R deamon can not connet to shared memory!\n"); exit(1); } while (shm_get_set_val(0, &cy_shm->st_cy_ras, 0, CY_SEM_CONFIG) == 0) { sleep(1); } LogMessage(LOG_INFO, "shared memory initialized!\n"); // Prepare to catch some signals. SetupSignals(); // Copy configuration information configuration.serial.speed = cy_shm->line_conf[0].speed; configuration.serial.dataSize = cy_shm->line_conf[0].DataSize; configuration.serial.parity = cy_shm->line_conf[0].Parity; configuration.serial.stopBits = cy_shm->line_conf[0].StopBits; configuration.serial.flowControl = cy_shm->line_conf[0].flow; configuration.serial.media = cy_shm->line_conf[0].media; configuration.protocol = cy_shm->line_conf[0].protocol; configuration.idleTimeout = cy_shm->line_conf[0].idletimeout; configuration.tty = "/dev/ttyS0"; // We support only this line configuration.to_zvz = cy_shm->line_conf[0].r3964_to_zvz; configuration.to_qvz = cy_shm->line_conf[0].r3964_to_qvz; configuration.max_retries = cy_shm->line_conf[0].r3964_max_retries; configuration.priority = R3964_MASTER; if ((configuration.protocol == P_3964) || (configuration.protocol == P_3964R)) { configuration.bcc = configuration.protocol == P_3964R; } else { // Wrong protocol definied configuration.bcc = 0; ShowConfiguration(&configuration); LogMessage(LOG_ERR, "wrong protocol definition %d, should be %d (3964)" " or %d (3964R)\n", configuration.protocol, (int)P_3964, (int)P_3964R); exit(1); } // Output this for debugging purpose. ShowConfiguration(&configuration); // Open and configure the serial line // Don't need to check the fd, because funktion will not come back on error. ttyFd = Open3964Port(&configuration); LogMessage(LOG_DEBUG, "file descriptor for serial line = %d\n", ttyFd); FD_ZERO(&inputs); // main loop while (TRUE) { // Don't forget to set file descriptor we monitor FD_SET(INHSOCK, &inputs); FD_SET(ttyFd, &inputs); timeout.tv_sec = configuration.idleTimeout * 60; timeout.tv_usec = 0; if (timeout.tv_sec > 0) { // With timeout fdReady = select(ttyFd + 1, &inputs, NULL, NULL, &timeout); if (fdReady <= 0) { // Error or timeout if (fdReady == 0) { LogMessage(LOG_NOTICE, "timout during read!"); DoExit(ttyFd, INHSOCK); } else { if (errno == EINTR) // Interupted system call continue; LogMessage(LOG_ERR, "error in select(timeout)! errno = %d\n", errno); DoExit(ttyFd, INHSOCK); } } } // if timeout else { // Without timeout fdReady = select(ttyFd + 1, &inputs, NULL, NULL, NULL); if (fdReady <= 0) { if (errno == EINTR) // Interupted system call continue; LogMessage(LOG_ERR, "error in select()! errno = %d\n", errno); DoExit(ttyFd, INHSOCK); } } // else timeout LogMessage(LOG_DEBUG, "fdReady = %d\n", fdReady); // Test for network input. if (FD_ISSET(INHSOCK, &inputs)) { // Socket is readable LogMessage(LOG_DEBUG, "got data on network to read.\n"); ReadSocket(INHSOCK, ttyFd); } // Test for serial line input. if (FD_ISSET(ttyFd, &inputs)) { // Line is readable LogMessage(LOG_DEBUG, "got data on serial line to read.\n"); ReadSerialLine(ttyFd, INHSOCK); } } // while TRUE return 0; } C. OTHER ISSUES In order to compile the CDK you will need the Hard Hat development system installed. This system is packed in RPMs which can easy be installed using the RPM command. Although installing by "rpm -i *.rpm" would be fine there is one issue with that. The packet hh-xxx-filesystem.rpm will create several directories (some as sym-links). But another packet will write to one of these sym-linked directories and will therefore create the directory (real) before the sym-link can be created. The result is that the filesystem packet cannot be installed anymore since the sym-link cannot be created. The packet filesystem will not be installed and several files (/dev) will be missing in the resulting kernel image. This will cause the serial server to fail booting. Therefore it is important to install the filesystem package first! It is important that you make a _clean_ build after changing the rwconf.c otherwhise other applications based on this file (e.g. inetd) will not work anymore.