/************************************************************************* * mcmdriver.c * Linux kernel module, Serial port driver for MCM communications : * through RS485 with 9 bit protocol * requires RS232 / RS485 converter, associated Makefile * * under root in a console out of X: * to install: * make * insmod mcmdriver.ko SerialPortNb=1 (or 2) * mknod /dev/mcmdriver c ... (nbs answered from previous command) * to uninstall: rmmod mcmdriver , rm -f /dev/mcmdriver * to check: lsmod * to test: ./mcmtest.o (compiled from mcmtest.c program) * * 20/09/05: modif write9(), no more trans MCM error. * 01/03/06: modif for Kernel 2.6 * 16/09/2008: modif for FC9 (kernel version 2.6.25.11),it's not working fully but now I m able to get data from the device, there is some bytes lose,have to go thru the code & do the things... * 23/09/2008: Now the device driver is working fine.I used twisted pair cable instead of single line wire,twisted pair cable reduced the noise level. **************************************************************************/ //#define __KERNEL__ needed? #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include //for BASE_BAUD #include //for UART reg #include // or sys/io.h for outb inb //#include MODULE_LICENSE("Dual BSD/GPL"); uint inportb(int port); void outportb(int port, char value); void setbit(int port, char bits); void clrbit(int port, char bits); char cksum(char *buff); void ckbaud(void); void init_baud(unsigned long baud); static int write9(const char *buff_wr); irqreturn_t irq_handler(int irq, void *dev_id); void hw_init(void); /****************************************************************** * serial write and read functions with MCM * reading via interruption ******************************************************************/ static char buf_ptr0[200]; static int siz; //must be char for device_read() return value static int SerialPortNb=0, SerialPortAddr, IRQNb; DECLARE_WAIT_QUEUE_HEAD(WaitQ); module_param(SerialPortNb, int, S_IRUGO); #define UART_IER_DISABLE 0x00 /*disable interrupts, is missing from serial-reg.h*/ #define UART_FCR_DISABLE_FIFO 0x00 /*disable fifo*/ #define UART_FIFO_SETUP (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_1) #define LCR_ADDR (UART_LCR_WLEN8 | UART_LCR_PARITY | UART_LCR_SPAR) #define LCR_DATA (UART_LCR_WLEN8 | UART_LCR_PARITY | UART_LCR_SPAR | UART_LCR_EPAR) /*------------ Macros -----------------------------------------------*/ uint inportb(int port) {return (uint)(inb(SerialPortAddr + port));} void outportb(int port, char value) {outb(value, SerialPortAddr + port);} void setbit(int port, char bits) {outportb(port, (inportb(port) | bits));} void clrbit(int port, char bits) {outportb(port, inportb(port) & ~bits);} /*---------- CheckSum function----------------------------------------*/ /*char cksum(char *buff) { char length, i, cs = 0; if (siz < 3) return 1; length = *(buff) + (*(buff+1)<<8); for(i=0; i> 8) & 0xff)); clrbit(UART_LCR,UART_LCR_DLAB); }; /*------------ write function----------------------------------------*/ int write9(const char *buff_wr) { int length, i; // printk(KERN_ALERT "I M IN WRITE9"); length = *(buff_wr+1) + ((*(buff_wr+2))<<8); setbit(UART_MCR, UART_MCR_RTS); // tx_on //while (!(inportb(UART_MCR) & UART_MCR_RTS)) {;} outportb(UART_LCR,LCR_ADDR); // addr parity //while (!(inportb(UART_LCR) & LCR_ADDR)) {;} while (!(inportb(UART_LSR) & UART_LSR_THRE)) {;} outportb(UART_TX, *buff_wr); while (!(inportb(UART_LSR) & UART_LSR_TEMT)) {;} outportb(UART_LCR, LCR_DATA); // data parity //while (!(inportb(UART_LCR) & LCR_DATA)) {;} for (i=1; i<(length+1); i++) { while (!(inportb(UART_LSR) & UART_LSR_THRE)) {;} outportb(UART_TX, *(buff_wr+i)); } while (!(inportb(UART_LSR) & UART_LSR_TEMT)) {;} clrbit(UART_MCR, UART_MCR_RTS); // tx_off //while ((inportb(UART_MCR) & UART_MCR_RTS)) {;} siz = 0; /* printk(KERN_ALERT "\nier %.2x\n", inportb(UART_IER)); printk(KERN_ALERT "iir %.2x\n", inportb(UART_IIR)); printk(KERN_ALERT "fcr %.2x\n", inportb(UART_FCR)); */ return 1; } /*------------ IRQ handler function-----------------------------------*/ irqreturn_t irq_handler(int irq, void *dev_id) { volatile int lsr=0, packsiz=0; printk(KERN_ALERT "IRQ detected\n"); while(inportb(UART_IIR) & 0x04) { lsr = inportb(UART_LSR); if (lsr & 0x02) { buf_ptr0[siz] = inportb(UART_RX); printk(KERN_ALERT "%x ", buf_ptr0[siz]); } //Overrun else if (lsr & 0x08) { buf_ptr0[siz] = inportb(UART_RX); printk(KERN_ALERT "Framming Error \n"); printk(KERN_ALERT "%x ", buf_ptr0[siz]); } //Framing Error else { buf_ptr0[siz] = inportb(UART_RX);siz++; printk(KERN_ALERT "%x ", buf_ptr0[siz-1]); } } if (siz>3) { packsiz = (*(buf_ptr0+1)+(*(buf_ptr0+2)<<8)+1); if (siz > 200) {printk(KERN_ALERT "siz error, packet reset\n"); siz = 0;} else if (siz == packsiz) wake_up_interruptible(&WaitQ); } return IRQ_HANDLED; } /*----------- hardware initialisation----------------------------------*/ void hw_init(void) { printk(KERN_ALERT "I m in Hw-Init"); outportb(UART_MCR, 0x00); outportb(UART_IER, UART_IER_DISABLE); outportb(UART_FCR, UART_FCR_DISABLE_FIFO); outportb(UART_FCR, UART_FIFO_SETUP); printk(KERN_ALERT "fcrsetup %.2x\n", UART_FIFO_SETUP); printk(KERN_ALERT "fcr %.2x\n", inportb(UART_FCR)); outportb(UART_LCR, 0x00); setbit(UART_MCR, UART_MCR_OUT2 | UART_MCR_RTS); //interrupt enabled setbit(UART_IER, UART_IER_RDI | UART_IER_THRI| UART_IER_MSI ); // interrupt rx on // while (!(inportb(UART_IER) & UART_IER_RDI)) {;} while ((inportb(UART_IIR) & 0x01)==0) { inportb(UART_LSR); inportb(UART_MSR); inportb(UART_RX); } init_baud(9600); } /****************************************************************** * Module functions for /dev/char driver * redefines open, write, read and close (release) ******************************************************************/ static dev_t DevNb; static struct cdev *CharDev; // struct for the internal representation of char devices in the kernel. static int device_release(struct inode *inode, struct file *file) { //release_region(SerialPortNb, 1); free_irq(IRQNb, NULL); //MOD_DEC_USE_COUNT; return 0; } static int device_open(struct inode *inode, struct file *file) { //if (request_region(SerialPortNb, 1, "mcmdriver") == NULL) //{printk(KERN_ALERT "Requesting Port failed\n"); return -1;} // if(request_irq(IRQNb, irq_handler, SA_INTERRUPT, "serial9", NULL)) // {printk(KERN_ALERT "irq request error\n"); return -1;} printk(KERN_ALERT " I m in device_open function"); if(request_irq(IRQNb, irq_handler, IRQF_DISABLED, "serial9", NULL)) printk(KERN_ALERT " I m in device_open function"); // if(request_irq(IRQNb, irq_handler,IRQF_SHARED , "serial9", NULL)) hw_init(); //MOD_INC_USE_COUNT; siz = 0; return 0; } static ssize_t device_write(struct file *filp, const char __user *buffer, size_t length, loff_t *offset) { write9(buffer); return 1; } /*blocking read with timeout*/ static ssize_t device_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset) { int err;//, i; if (siz < 3) wait_event_interruptible_timeout(WaitQ, 0,150); // &WaitQ or WaitQ?, 1 or 0? // wait_event_interruptible(WaitQ,0); //for (i=0;i 3)) siz = -1; else err = */ copy_to_user(buffer, buf_ptr0, siz); mdelay(50); /* Hardware limitation: the driver cannot write 2 times successively in a too short period. Delay necessary for rewriting just after, optimized for a write & read loop (just 1 printk), with Set Anl Mask Cmd (the longest) and in a console outside X. Delay placed here, CS/siz problems if placed before writing ?*/ return siz; } static struct file_operations fops = { .owner = THIS_MODULE, .open = device_open, .release = device_release, .read = device_read, .write = device_write, }; static int __init device_init(void) { int err; err = alloc_chrdev_region(&DevNb, 0, 1, "mcmdriver"); if (err < 0) {printk(KERN_ALERT "Allocating DevNb failed with %d\n", err); return err;} CharDev = cdev_alloc(); cdev_init(CharDev, &fops); CharDev->owner = THIS_MODULE; CharDev->ops = &fops; err = cdev_add(CharDev, DevNb, 1); if (err) {printk(KERN_ALERT "Registering CharDev failed with %d\n", err); return err;} if (SerialPortNb == 1) {SerialPortAddr = 0x3f8; IRQNb = 4;} /* if (SerialPortNb == 1 ) {SerialPortAddr = 0xfe00; IRQNb = 16;} */ /** Changes did for USB to Serial for MCM communication, but lock the computer.......*****/ /**** if (SerialPortNb == 0) {SerialPortAddr = 0x000; IRQNb = 0;} ***/ else if (SerialPortNb == 2) {SerialPortAddr = 0x2f8; IRQNb = 3;} printk(KERN_ALERT "Serial Port %d selected (address %x)\n", SerialPortNb, SerialPortAddr); printk(KERN_ALERT "Create dev file with 'mknod /dev/mcmdriver c %d %d'.\n", MAJOR(DevNb), MINOR(DevNb)); return 0; } static void __exit device_exit(void) { cdev_del(CharDev); unregister_chrdev_region(DevNb, 1); } module_init(device_init); module_exit(device_exit);