📄 serproto.c
字号:
/* * serial_fd/serproto.h * * Copyright (c) 2000, 2001, 2002 Lineo * Copyright (c) 2001 Hewlett Packard * * By: * Stuart Lynne <sl@lineo.com>, * Tom Rushworth <tbr@lineo.com>, * Bruce Balden <balden@lineo.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. * */#include <linux/config.h>#include <linux/module.h>#ifndef MODULE#undef GET_USE_COUNT#define GET_USE_COUNT(foo) 1#endif#include <linux/spinlock.h>#include <linux/list.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/fcntl.h>#include <linux/termios.h>#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/tty_flip.h>#include <asm/atomic.h>#include <asm/uaccess.h>#include <asm/segment.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/irq.h>#include <asm/system.h>#include "serproto.h"//#define SERIAL_TTY_MAJOR 222#define SERIAL_TTY_MAJOR 188 // re-use USB host tty dev number#define SERIAL_TTY_MINORS 1static int serial_refcount;static struct tty_struct *serial_tty[SERIAL_TTY_MINORS];static struct termios *serial_termios[SERIAL_TTY_MINORS];static struct termios *serial_termios_locked[SERIAL_TTY_MINORS];extern int usb;#define MIN(a,b) ((a>b) ? b : a)#ifndef TRUE#define TRUE 1#endif#ifndef FALSE#define FALSE 0#endifstruct serproto_dev { int number; // serial device number (index in serproto_device_array) int opencnt; // number of opens int connected; // TRUE if USB has connected. unsigned int clocal; struct tq_struct write_wakeup_task; // task queue for line discipline waking up struct tty_struct *tty; // serial tty structure struct tty_driver tty_driver; int (*xmit_data) (int, unsigned char *, int); // callback to send data int tx_size; // maximum transmit size rwlock_t rwlock; // lock changing this structure //static DECLARE_MUTEX_LOCKED(busy);// semaphore to wait if busy unsigned int max_queue_entries; // maximum queue entries unsigned int max_queue_bytes; // maximum queued data unsigned int queued_entries; // current queued entries unsigned int queued_bytes; // current queued data int blocked; int trailer;};int serproto_devices; // maximum number of interaces static struct serproto_dev **serproto_device_array; // pointer to active interacesstatic rwlock_t serproto_rwlock = RW_LOCK_UNLOCKED; // lock for changing global structures/* Debug switches ****************************************************************************** */static int dbgflg_init = 0;static int dbgflg_oc = 0;static int dbgflg_rx = 0;static int dbgflg_tx = 0;static int dbgflg_mgmt = 0;static int dbgflg_loopback = 0;static debug_option dbg_table[] = { {&dbgflg_init, NULL, "init", "initialization/termination handling"}, {&dbgflg_oc, NULL, "opcl", "open/close handling"}, {&dbgflg_rx, NULL, "rx", "receive (from host)"}, {&dbgflg_tx, NULL, "tx", "transmit (to host)"}, {&dbgflg_mgmt, NULL, "mgmt", "ioctl, termios, etc."}, {&dbgflg_loopback, NULL, "loop", "enable loopback if non-zero"}, {NULL, NULL, NULL, NULL}};#define dbg_init(lvl,fmt,args...) dbgPRINT(dbgflg_init,lvl,fmt,##args)#define dbg_oc(lvl,fmt,args...) dbgPRINT(dbgflg_oc,lvl,fmt,##args)#define dbg_rx(lvl,fmt,args...) dbgPRINT(dbgflg_rx,lvl,fmt,##args)#define dbg_tx(lvl,fmt,args...) dbgPRINT(dbgflg_tx,lvl,fmt,##args)#define dbg_mgmt(lvl,fmt,args...) dbgPRINT(dbgflg_mgmt,lvl,fmt,##args)#define dbg_loop(lvl,fmt,args...) dbgPRINT(dbgflg_loopback,lvl,fmt,##args)debug_option *serproto_get_dbg_table (void){ return (dbg_table);}/* Serial Driver Support Functions ************************************************************* *//* * * serial_open - open serial device * @tty: tty device * @filp: file structure * * Called to open serial device. */static int serial_open (struct tty_struct *tty, struct file *filp){ unsigned long flags; int n = 0, rc = 0; struct serproto_dev *device = NULL; dbg_oc (3, "tty #%p file #%p", tty, filp); if (NULL == tty || 0 > (n = MINOR (tty->device) - tty->driver.minor_start) || n >= serproto_devices || NULL == (device = serproto_device_array[n])) { dbg_oc (1, "FAIL ENODEV"); return -ENODEV; } MOD_INC_USE_COUNT; dbg_init (1, "OPEN uc=%d", GET_USE_COUNT (THIS_MODULE)); write_lock_irqsave (&device->rwlock, flags); if (1 == ++device->opencnt) { // First open tty->driver_data = device; device->tty = tty; tty->low_latency = 1; /* force low_latency on so that our tty_push actually forces the data through, * otherwise it is scheduled, and with high data rates (like with OHCI) data * can get lost. * */ tty->low_latency = 1; } else if (tty->driver_data != device || device->tty != tty) { // Second or later open, different tty/device combo rc = -EBUSY; } // XXX Should extract info from somewhere to see if receive is OK write_unlock_irqrestore (&device->rwlock, flags); if (0 != rc) { if (-EBUSY == rc) { dbg_oc (1, "2nd, conflict: old dev #%p new #%p, old tty #%p new #%p", tty->driver_data, device, device->tty, tty); } MOD_DEC_USE_COUNT; dbg_init (0, "OPEN rc=%d uc=%d", rc, GET_USE_COUNT (THIS_MODULE)); } dbg_oc (3, "->%d n=%d", rc, n); return (rc);}static void serial_close (struct tty_struct *tty, struct file *filp){ unsigned long flags; struct serproto_dev *device; int uc; uc = GET_USE_COUNT (THIS_MODULE); dbg_oc (3, "tty #%p file #%p uc=%d", tty, filp, uc); if ((device = tty->driver_data) != NULL) { write_lock_irqsave (&device->rwlock, flags); if (0 >= --device->opencnt) { // Last (or extra) close dbg_oc (1, "Last: old tty #%p new #%p oc=%d", device->tty, tty, device->opencnt); tty->driver_data = NULL; device->tty = NULL; device->opencnt = 0; } write_unlock_irqrestore (&device->rwlock, flags); } else { dbg_oc (1, "not presently connected"); } if (uc > 0) { // Should really check that uc hasn't changed since start of fn... MOD_DEC_USE_COUNT; dbg_init (1, "CLOSE uc=%d", GET_USE_COUNT (THIS_MODULE)); } dbg_oc (3, "OK"); return;}static void serial_flush (struct tty_struct *tty){ dbg_mgmt (1, "tty#%p", tty);}static int serial_write (struct tty_struct *tty, int from_user, const unsigned char *buf, int count){ // Return the number of bytes (out of count) that get written, // or negative for error. unsigned long flags; struct serproto_dev *device; int cnt = count; int size; const unsigned char *currpos = buf; unsigned char *buffer; dbg_tx (3, "count=%d", count); if ((device = tty->driver_data) == NULL) { dbg_tx (1, "not presently connected -> FAIL"); return -EINVAL; } dbgPRINTmem (dbgflg_tx, 4, buf, count); // loop on data while (cnt > 0) { int length; // send at most tx_size bytes size = MIN (device->tx_size, cnt); write_lock_irqsave (&device->rwlock, flags); // Make sure we can send. if (!device->connected || (device->max_queue_entries > 0 && (device->queued_entries >= device->max_queue_entries)) || (device->queued_bytes >= device->max_queue_bytes)) { // Can't write any more, // return the number that we did manage to send. write_unlock_irqrestore (&device->rwlock, flags); dbg_tx (2, "->%d/%d", (count - cnt), count); return (count - cnt); } size = MIN ((device->max_queue_bytes - device->queued_bytes), size); // allocate a buffer length = (device->blocked ? device->tx_size : size) + 1 + device->trailer; dbg_tx (1, "------> blocked: %d tx_size: %d size: %d trailer: %d, length: %d", device->blocked, device->tx_size, size, device->trailer, length); if ((buffer = kmalloc (length, GFP_KERNEL)) == NULL) { write_unlock_irqrestore (&device->rwlock, flags); dbg_tx (2, "->ENOMEM"); return -ENOMEM; } memset (buffer, '\0', length); // copy data if (from_user) { copy_from_user ((void *) buffer, currpos, size); } else { memcpy ((void *) buffer, currpos, size); } currpos += size; cnt -= size; device->xmit_data (device->number, buffer, size); device->queued_entries++; device->queued_bytes += size; write_unlock_irqrestore (&device->rwlock, flags); } // Everything went out. dbg_tx (5, "->%d (all)", count); return count;}static int serial_write_room (struct tty_struct *tty){ /* Return the amount of room for writing. */ unsigned long flags; struct serproto_dev *device; int n = 0; dbg_tx (7, "entered"); if ((device = tty->driver_data) == NULL) { dbg_tx (1, "not presently connected -> FAIL"); return (-EINVAL); } read_lock_irqsave (&device->rwlock, flags); if (device->connected && (device->queued_bytes < device->max_queue_bytes) && (device->max_queue_entries == 0 || device->queued_entries < device->max_queue_entries)) {#if 0 if (device->tx_size < (n = device->max_queue_bytes - device->queued_bytes)) { n = device->tx_size; }#else n = device->max_queue_bytes - device->queued_bytes;#endif } read_unlock_irqrestore (&device->rwlock, flags); // Shouldn't really access these outside the lock, but only the dbg msg can go wrong. dbg_tx (6, "c:%c b=%u/%u e=%u/%u -> %d", (device->connected ? 'T' : 'F'), device->queued_bytes, device->max_queue_bytes, device->queued_entries, device->max_queue_entries, n); return (n);}static intserial_ioctl (struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ int rc, type; type = _IOC_TYPE (cmd); dbg_mgmt (2, "type#%02x cmd#%08x arg#%08lx", type, cmd, arg); switch (cmd) { case /* TCSETATTR */ 3: rc = 0; break; default: rc = -ENOIOCTLCMD; } return (rc);}static void serial_set_termios (struct tty_struct *tty, struct termios *old){ struct serproto_dev *device = tty->driver_data; struct termios *tio = tty->termios; device->clocal = tio->c_cflag & CLOCAL; dbg_mgmt (2, "clocal->%c", (device->clocal ? 'T' : 'F')); return;}static void serial_throttle (struct tty_struct *tty){ dbg_mgmt (1, "entered"); return;}static void serial_unthrottle (struct tty_struct *tty){ dbg_mgmt (1, "entered"); return;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -