📄 ussp.c
字号:
/* -linux-c; c-basic-offset 8; *//* ussp.c -- driver to allow a userspace daemon to handle a serial port. * * This driver was written to allow Linux to communicate with the * Perle RAS server products. * * * (C) 2000 R.E.Wolff@BitWizard.nl, patrick@BitWizard.nl * * Please read the documentation ussp.txt (In the Documentation directory). * * Perle Systems paid for the development of this driver. For more * information point your web browser to www.perle.com . * * * * 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. * * Revision history: * - Initial version released in 2000 * - Added TIOCMSET function that report modem signal change to the daemon * October 9th, 2003 Tuukka Karvonen (tkarvone@iki.fi) * - Ported to 2.6 kernel * January 20th, 2004 Vasya Novikov (vn@hotbox.ru) * * - Ported to 2.6.10 kernel * - Data no longer to be copied from user space, as * ussp_tty_write no longer has the from_user parameter * - removed %d from ussp_driver.name * - removed a couple of compiler warnings * - DumpBuffer procedure for debugging added * - more debugging statements * - added support for make clean in Makefile * March 8th, 2004 J鴕gen Thomsen (jth@jth.net) * * */#define RCS_ID "$Id: ussp.c,v 1.5 2005/03/08 22:00:02 jth Exp $"#define RCS_REV "$Revision: 1.5 $"#include <linux/config.h>#include <linux/ctype.h>#if 0#ifdef CONFIG_MODVERSIONS#ifndef MODVERSIONS#define MODVERSIONS#endif#include <linux/modversions.h>#endif#endif#include <linux/module.h>#include <linux/kdev_t.h>#include <asm/io.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/ioport.h>#include <linux/interrupt.h>#include <linux/errno.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/mm.h>#include <linux/serial.h>#include <linux/fcntl.h>#include <linux/major.h>#include <linux/delay.h>//#include <linux/tqueue.h>#include <linux/version.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/miscdevice.h>#include <linux/tty_ldisc.h>//#include <linux/compatmac.h>#include <linux/poll.h>#include "ussp.h"/* During development this is -1, to reduce nkeystrokes. Later this should be 0. Beta testers: Remind me to set this to 0 before release -- REW *//* Set default to zero to make the perle guys happy -- pvdl */int ussp_debug = 0;/* This parameter makes this driver set "lowlatency". For nomal serial ports the handling of serial characters is aggregated for a lower CPU overhead. In this driver however, that won't buy you anything, as the deamon will already packetize and aggregate the data. Clear this if you feel that this is not true. -- REW */int ussp_set_lowlatency = 1;/* I don't think that this driver can handle more than 256 ports on one machine. -- REW *//* Configurable options: (Don't be too sure that it'll work if you toggle them) */MODULE_PARM(ussp_debug, "i");MODULE_PARM(ussp_set_lowlatency, "i");MODULE_AUTHOR("R.E.Wolff@BitWizard.nl, patrick@BitWizard.nl");MODULE_DESCRIPTION("User Space Serial Ports");#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 9)MODULE_LICENSE("GPL");#endifstatic int ussp_ctl_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);static int ussp_ctl_open(struct inode *inode, struct file *filp);static ssize_t ussp_ctl_read(struct file * filp, char * buf, size_t count, loff_t *ppos);static ssize_t ussp_ctl_write(struct file * filp, const char * buf, size_t count, loff_t *ppos);static int ussp_ctl_close(struct inode *, struct file *);static unsigned int ussp_ctl_poll (struct file *, poll_table *);static struct file_operations ussp_ctl_fops = { read: ussp_ctl_read, write: ussp_ctl_write, poll: ussp_ctl_poll, ioctl: ussp_ctl_ioctl, open: ussp_ctl_open, release: ussp_ctl_close};struct miscdevice ussp_ctl_device = { USSPCTL_MISC_MINOR, "ussp_ctl", &ussp_ctl_fops};#define DEBUG_FLOW 0x0001#define DEBUG_WRITE_TO_CTL 0x0002#define DEBUG_CLOSE 0x0004#define DEBUG_IOCTL 0x0008#define DEBUG_CIB 0x0010#define DEBUG_OPEN 0x0020#define DEBUG_BUFFERS 0x0040#define DEBUG_READ_CTL 0x0080#define DEBUG_INFO 0x0100//#define DEBUG 1#ifdef DEBUG#define ussp_dprintk(f, str...) if (ussp_debug & f) printk (str)#else#define ussp_dprintk(f, str...) /* nothing */#endif#define func_enter() ussp_dprintk (DEBUG_FLOW, "ussp: enter %s\n", __func__)#define func_exit() ussp_dprintk (DEBUG_FLOW, "ussp: exit %s\n", __func__)//#define func_enter()//#define func_exit()/* Dumps a buffer */void DumpBuffer(const unsigned char *bufin, int bufinsize){#ifdef DEBUG int i,j=0,len=16; unsigned char buffer[400]; if (bufinsize == 0) {ussp_dprintk(DEBUG_BUFFERS,"buflen=%d\n", bufinsize); return; } ussp_dprintk(DEBUG_BUFFERS,"buflen=%d\n", bufinsize); memset(buffer,0x20,sizeof(buffer)); buffer[len*5-1]=0; for (i = 0; i < bufinsize; i++) { sprintf(buffer+j*4,"%02X",bufin[i]); buffer[j*4+2] = 0x20; if (isprint(bufin[i]) && bufin[i]!=0x09) { if (j != len-1) buffer[j*4+2] = bufin[i]; buffer[(len-1)*4+j+3] = bufin[i]; } else { buffer[(len-1)*4+j+3] = '.'; } if (j != len-1 && i != bufinsize-1) buffer[j*4+3] = '|'; if (j == len-1) { ussp_dprintk(DEBUG_BUFFERS, "%s\n", buffer); memset(buffer,0x20,sizeof(buffer)); buffer[len*5-1]=0; j = 0; } else { j++; } } if (j != 0) ussp_dprintk(DEBUG_BUFFERS, "%s\n", buffer);#endif}/* * TTY callbacks */static int ussp_tty_write (struct tty_struct * tty, const unsigned char *buf, int count);static int ussp_tty_ioctl (struct tty_struct *, struct file *, unsigned int, unsigned long);static int ussp_tty_open (struct tty_struct *, struct file * filp);static void ussp_tty_close (struct tty_struct *, struct file *);static int ussp_tty_write_room (struct tty_struct * );static int ussp_tty_chars_in_buffer (struct tty_struct * );static void ussp_tty_put_char (struct tty_struct *, unsigned char);void ussp_tty_flush_buffer (struct tty_struct *tty);void ussp_tty_flush_chars (struct tty_struct *tty);void ussp_tty_stop (struct tty_struct *tty);void ussp_tty_start (struct tty_struct *tty);void ussp_tty_set_termios (struct tty_struct * tty, struct termios * old_termios);void ussp_tty_hangup (struct tty_struct *tty);struct ussp_port ussp_ports[USSP_MAX_PORTS];int ussp_nports = USSP_MAX_PORTS;//int ussp_refcount;static struct tty_driver ussp_driver;static struct tty_struct * ussp_table[USSP_MAX_PORTS] = { NULL, };static struct termios * ussp_termios[USSP_MAX_PORTS];static struct termios * ussp_termios_locked[USSP_MAX_PORTS];#define D_DATA_AVAILABLE(port) ((port->daemon_head - port->daemon_tail) & (PAGE_SIZE-1))#define TTY_DATA_AVAILABLE(port) ((port->tty_head - port->tty_tail) & (PAGE_SIZE-1))#define SPACE_IN_BUFFER(head, tail, bufsz) \ ((((tail) - (head)) <= 0)?(tail) - (head) - 1 + (bufsz):((tail)-(head) - 1))void ussp_tty_hangup (struct tty_struct *tty){ func_enter(); /* nothing special for us to do? Oh well. -- REW */ func_exit();}void ussp_tty_flush_buffer (struct tty_struct *tty){ struct ussp_port *port = tty->driver_data; func_enter (); /* Clear the (output) buffer. But the buffer may contain interesting data (e.g. a baud rate change.). For now just let things be... -- REW */ if (port && port->tty) { if (port->tty->stopped || port->tty->hw_stopped || !port->tty_buffer) { func_exit (); return; } wake_up_interruptible (&port->tty_wait); } func_exit();}void ussp_tty_flush_chars (struct tty_struct *tty){ func_enter(); func_exit();}void ussp_tty_stop (struct tty_struct *tty){ func_enter(); func_exit();}void ussp_tty_start (struct tty_struct *tty){ func_enter(); func_exit();}static void ussp_dummy(struct tty_struct *tty){ func_enter(); func_exit(); return;}int ussp_init (void){ int status; func_enter(); ussp_dprintk (DEBUG_INFO, "USSP: " __DATE__ "/" __TIME__ " version 0.10\n"); memset(&ussp_driver, 0, sizeof(ussp_driver)); ussp_driver.magic = TTY_DRIVER_MAGIC; ussp_driver.driver_name = "userspace_serial";#ifdef CONFIG_DEVFS_FS ussp_driver.name = "ttu/%d";#else ussp_driver.name = "ttyU";#endif ussp_driver.major = USSP_NORMAL_MAJOR; ussp_driver.num = 4; ussp_driver.type = TTY_DRIVER_TYPE_SERIAL; ussp_driver.subtype = SERIAL_TYPE_NORMAL; ussp_driver.init_termios = tty_std_termios; ussp_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; ussp_driver.flags = TTY_DRIVER_REAL_RAW;// ussp_driver.refcount = &ussp_refcount; ussp_driver.ttys = ussp_table; ussp_driver.termios = ussp_termios; ussp_driver.termios_locked = ussp_termios_locked; ussp_driver.open = ussp_tty_open; ussp_driver.close = ussp_tty_close; ussp_driver.write = ussp_tty_write; ussp_driver.put_char = ussp_tty_put_char; ussp_driver.flush_chars = ussp_tty_flush_chars; ussp_driver.write_room = ussp_tty_write_room; ussp_driver.chars_in_buffer = ussp_tty_chars_in_buffer; ussp_driver.flush_buffer = ussp_tty_flush_buffer; ussp_driver.ioctl = ussp_tty_ioctl; ussp_driver.throttle = ussp_dummy; ussp_driver.unthrottle = ussp_dummy; ussp_driver.set_termios = ussp_tty_set_termios; ussp_driver.stop = ussp_tty_stop; ussp_driver.start = ussp_tty_start; ussp_driver.hangup = ussp_tty_hangup; status = tty_register_driver (&ussp_driver); //printk (KERN_INFO "Return value registering: %d\n", status); if(status == 0) printk(KERN_INFO "USSP driver registered.\n"); else printk(KERN_ERR "error registering driver: %d\n", status); if (misc_register(&ussp_ctl_device) < 0) { printk(KERN_ERR "USSP: Unable to register control device.\n"); return -EIO; } func_exit(); return status;}#if 0void ussp_tty_receive_chars (struct ussp_port *port){ int count; func_enter (); count = TTY_COUNTER(port); memcpy(port->tty->flip.char_buf_ptr, port->tty_buffer+port->tty_head, count); memset(port->tty->flip.flag_buf_ptr, TTY_NORMAL, count); port->tty->flip.count += count; port->tty->flip.char_buf_ptr += count; port->tty->flip.flag_buf_ptr += count; port->tty_tail = port->tty_head; tty_flip_buffer_push (port->tty); func_exit ();}#endifint copy_to_circular_buffer (char *buffer, int bufsz, int *head, int *tail, const void *data, int count, int from_user){ int rc; int c, r, s; func_enter (); s = SPACE_IN_BUFFER(*head, *tail, bufsz); if (count > s){ ussp_dprintk (DEBUG_BUFFERS, "ctcb: No space in buffer: count: %d s: %d\n", count, s); return 0; } ussp_dprintk (DEBUG_BUFFERS, "data: %p buffer: %p count: %d buffer size: %d head: %d tail %d from_user %d\n", data, buffer, count, bufsz, *head, *tail, from_user); r = 0; while (count > 0) { c = count; if (c > (bufsz - *head)) c = bufsz - *head; if (from_user) rc = copy_from_user (buffer + *head, data, c); else memcpy (buffer + *head, data, c); data += c; *head += c; if (*head >= bufsz) *head = 0; count -= c; r += c; } ussp_dprintk (DEBUG_BUFFERS, "head: %d tail %d from_user %d\n", *head, *tail, from_user); func_exit (); return r;}int copy_from_circular_buffer (char *buffer, int bufsz, int *head, int *tail, void *data, int count, int to_user){ int rc; int c, s; func_enter (); s = *head - *tail; if (s < 0) s += bufsz; if (count > s) { ussp_dprintk (DEBUG_BUFFERS, "cfcb: Not enough data in buffer for requested amount. %d s: %d\n", count, s); return -1; } ussp_dprintk (DEBUG_BUFFERS, "data: %p buffer: %p count: %d buffer size: %d head: %d tail %d\n", data, buffer, count, bufsz, *head, *tail); while (count > 0) { c = count; if (c > (bufsz - *tail)) c = bufsz - *tail; if (to_user) rc = copy_to_user (data, buffer + *tail, c); else memcpy (data, buffer + *tail, c); data += c; *tail += c; if (*tail >= bufsz) *tail = 0; count -= c; } func_exit (); return 0;}static ssize_t ussp_write_to_ctl(struct tty_struct * tty, int from_user, const unsigned char *buf, int count, int operation){ struct ussp_operation info; struct ussp_port *port; int sib, r; func_enter(); if (count <= 0){ printk ("Count is smaller than 0!\n"); func_exit (); return 0; } port = tty->driver_data; if (!(port->flags & USSP_DEAMON_PRESENT)) { printk("no deamon. Flags: %x\n", port->flags); func_exit (); return -ENODEV; } ussp_dprintk(DEBUG_WRITE_TO_CTL, "Port: %p, buf %p, count %d. ", port, buf, count); if (!port->daemon_buffer) { printk(KERN_ERR "ERROR!! port->daemon_buf is NULL!!!\n"); return -1; } sib = SPACE_IN_BUFFER (port->daemon_head, port->daemon_tail, PAGE_SIZE); if (count > (int)(sib - sizeof (info))) count = sib - sizeof (info); ussp_dprintk (DEBUG_WRITE_TO_CTL, "sib: %d count: %d\n", sib, count); if (count <= 0) return 0; info.op = operation; info.len = count; info.arg = 0; r = copy_to_circular_buffer (port->daemon_buffer, PAGE_SIZE, &port->daemon_head, &port->daemon_tail, &info, sizeof (info), 0); ussp_dprintk(DEBUG_WRITE_TO_CTL, "daemon_buffer: head: %d tail: %d\n", port->daemon_head, port->daemon_tail); r += copy_to_circular_buffer (port->daemon_buffer, PAGE_SIZE, &port->daemon_head, &port->daemon_tail, buf, count, from_user); if (r != (count + sizeof(info))) { ussp_dprintk (DEBUG_WRITE_TO_CTL, "Not enough space in buffer: r: %d, count: %d\n", r, count); return 0; } ussp_dprintk(DEBUG_WRITE_TO_CTL, "info->op: %d len: %ld\n", info.op, info.len); ussp_dprintk(DEBUG_WRITE_TO_CTL, "buffer: %*s from_user: %d daemon_buffer: %p head: %d tail: %d\n", count, buf, from_user, port->daemon_buffer, port->daemon_head, port->daemon_tail); ussp_dprintk (DEBUG_WRITE_TO_CTL, "Waking up queue: %p\n", &port->daemon_wait); wake_up(&port->daemon_wait); func_exit(); return count;}void ussp_tty_set_termios (struct tty_struct * tty, struct termios * old_termios){ func_enter (); ussp_dprintk (USSP_SET_TERMIOS, "c_cflag: %x\n", tty->termios->c_cflag); if (ussp_write_to_ctl (tty, 0, (char*)tty->termios, sizeof (struct termios), USSP_SET_TERMIOS) <= 0) ussp_dprintk (DEBUG_WRITE_TO_CTL, "XXX Fix set termios: wait for space in buffer...\n"); func_exit ();}static ssize_t ussp_tty_write(struct tty_struct * tty, const unsigned char *buf, int count){ int ret; struct ussp_port *port; func_enter (); if (tty) { port = tty->driver_data; if (port) port->stats.txcount += count; } DumpBuffer(buf, count); ret = ussp_write_to_ctl (tty, 0, buf, count, USSP_WRITE); func_exit (); return ret;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -