📄 digi_acceleport.c
字号:
/** Digi AccelePort USB-4 and USB-2 Serial Converters** Copyright 2000 by Digi International** 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.** Shamelessly based on Brian Warner's keyspan_pda.c and Greg Kroah-Hartman's* usb-serial driver.** Peter Berger (pberger@brimson.com)* Al Borchers (borchers@steinerpoint.com)* * (04/08/2001) gb* Identify version on module load.** (11/01/2000) Adam J. Richter* usb_device_id table support* * (11/01/2000) pberger and borchers* -- Turned off the USB_DISABLE_SPD flag for write bulk urbs--it caused* USB 4 ports to hang on startup.* -- Serialized access to write urbs by adding the dp_write_urb_in_use* flag; otherwise, the driver caused SMP system hangs. Watching the* urb status is not sufficient.** (10/05/2000) gkh* -- Fixed bug with urb->dev not being set properly, now that the usb* core needs it.* * (8/8/2000) pberger and borchers* -- Fixed close so that * - it can timeout while waiting for transmit idle, if needed;* - it ignores interrupts when flushing the port, turning* of modem signalling, and so on;* - it waits for the flush to really complete before returning.* -- Read_bulk_callback and write_bulk_callback check for a closed* port before using the tty struct or writing to the port.* -- The two changes above fix the oops caused by interrupted closes.* -- Added interruptible args to write_oob_command and set_modem_signals* and added a timeout arg to transmit_idle; needed for fixes to* close.* -- Added code for rx_throttle and rx_unthrottle so that input flow* control works.* -- Added code to set overrun, parity, framing, and break errors* (untested).* -- Set USB_DISABLE_SPD flag for write bulk urbs, so no 0 length* bulk writes are done. These hung the Digi USB device. The* 0 length bulk writes were a new feature of usb-uhci added in* the 2.4.0-test6 kernels.* -- Fixed mod inc race in open; do mod inc before sleeping to wait* for a close to finish.** (7/31/2000) pberger* -- Fixed bugs with hardware handshaking:* - Added code to set/clear tty->hw_stopped in digi_read_oob_callback()* and digi_set_termios()* -- Added code in digi_set_termios() to* - add conditional in code handling transition from B0 to only* set RTS if RTS/CTS flow control is either not in use or if* the port is not currently throttled.* - handle turning off CRTSCTS.** (7/30/2000) borchers* -- Added support for more than one Digi USB device by moving* globals to a private structure in the pointed to from the* usb_serial structure.* -- Moved the modem change and transmit idle wait queues into* the port private structure, so each port has its own queue* rather than sharing global queues.* -- Added support for break signals.** (7/25/2000) pberger* -- Added USB-2 support. Note: the USB-2 supports 3 devices: two* serial and a parallel port. The parallel port is implemented* as a serial-to-parallel converter. That is, the driver actually* presents all three USB-2 interfaces as serial ports, but the third* one physically connects to a parallel device. Thus, for example,* one could plug a parallel printer into the USB-2's third port,* but from the kernel's (and userland's) point of view what's* actually out there is a serial device.** (7/15/2000) borchers* -- Fixed race in open when a close is in progress.* -- Keep count of opens and dec the module use count for each* outstanding open when shutdown is called (on disconnect).* -- Fixed sanity checks in read_bulk_callback and write_bulk_callback* so pointers are checked before use.* -- Split read bulk callback into in band and out of band* callbacks, and no longer restart read chains if there is* a status error or a sanity error. This fixed the seg* faults and other errors we used to get on disconnect.* -- Port->active is once again a flag as usb-serial intended it* to be, not a count. Since it was only a char it would* have been limited to 256 simultaneous opens. Now the open* count is kept in the port private structure in dp_open_count.* -- Added code for modularization of the digi_acceleport driver.** (6/27/2000) pberger and borchers* -- Zeroed out sync field in the wakeup_task before first use;* otherwise the uninitialized value might prevent the task from* being scheduled.* -- Initialized ret value to 0 in write_bulk_callback, otherwise* the uninitialized value could cause a spurious debugging message.** (6/22/2000) pberger and borchers* -- Made cond_wait_... inline--apparently on SPARC the flags arg* to spin_lock_irqsave cannot be passed to another function* to call spin_unlock_irqrestore. Thanks to Pauline Middelink.* -- In digi_set_modem_signals the inner nested spin locks use just* spin_lock() rather than spin_lock_irqsave(). The old code* mistakenly left interrupts off. Thanks to Pauline Middelink.* -- copy_from_user (which can sleep) is no longer called while a* spinlock is held. We copy to a local buffer before getting* the spinlock--don't like the extra copy but the code is simpler.* -- Printk and dbg are no longer called while a spin lock is held.** (6/4/2000) pberger and borchers* -- Replaced separate calls to spin_unlock_irqrestore and* interruptible_sleep_on_timeout with a new function* cond_wait_interruptible_timeout_irqrestore. This eliminates* the race condition where the wake up could happen after* the unlock and before the sleep.* -- Close now waits for output to drain.* -- Open waits until any close in progress is finished.* -- All out of band responses are now processed, not just the* first in a USB packet.* -- Fixed a bug that prevented the driver from working when the* first Digi port was not the first USB serial port--the driver* was mistakenly using the external USB serial port number to* try to index into its internal ports.* -- Fixed an SMP bug -- write_bulk_callback is called directly from* an interrupt, so spin_lock_irqsave/spin_unlock_irqrestore are* needed for locks outside write_bulk_callback that are also* acquired by write_bulk_callback to prevent deadlocks.* -- Fixed support for select() by making digi_chars_in_buffer()* return 256 when -EINPROGRESS is set, as the line discipline* code in n_tty.c expects.* -- Fixed an include file ordering problem that prevented debugging* messages from working.* -- Fixed an intermittent timeout problem that caused writes to* sometimes get stuck on some machines on some kernels. It turns* out in these circumstances write_chan() (in n_tty.c) was* asleep waiting for our wakeup call. Even though we call* wake_up_interruptible() in digi_write_bulk_callback(), there is* a race condition that could cause the wakeup to fail: if our* wake_up_interruptible() call occurs between the time that our* driver write routine finishes and write_chan() sets current->state* to TASK_INTERRUPTIBLE, the effect of our wakeup setting the state* to TASK_RUNNING will be lost and write_chan's subsequent call to* schedule() will never return (unless it catches a signal).* This race condition occurs because write_bulk_callback() (and thus* the wakeup) are called asynchonously from an interrupt, rather than* from the scheduler. We can avoid the race by calling the wakeup* from the scheduler queue and that's our fix: Now, at the end of* write_bulk_callback() we queue up a wakeup call on the scheduler* task queue. We still also invoke the wakeup directly since that* squeezes a bit more performance out of the driver, and any lost* race conditions will get cleaned up at the next scheduler run.** NOTE: The problem also goes away if you comment out* the two code lines in write_chan() where current->state* is set to TASK_RUNNING just before calling driver.write() and to* TASK_INTERRUPTIBLE immediately afterwards. This is why the* problem did not show up with the 2.2 kernels -- they do not* include that code.** (5/16/2000) pberger and borchers* -- Added timeouts to sleeps, to defend against lost wake ups.* -- Handle transition to/from B0 baud rate in digi_set_termios.** (5/13/2000) pberger and borchers* -- All commands now sent on out of band port, using* digi_write_oob_command.* -- Get modem control signals whenever they change, support TIOCMGET/* SET/BIS/BIC ioctls.* -- digi_set_termios now supports parity, word size, stop bits, and* receive enable.* -- Cleaned up open and close, use digi_set_termios and* digi_write_oob_command to set port parameters.* -- Added digi_startup_device to start read chains on all ports.* -- Write buffer is only used when count==1, to be sure put_char can* write a char (unless the buffer is full).** (5/10/2000) pberger and borchers* -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls on open/close.* -- Fixed problem where the first incoming character is lost on* port opens after the first close on that port. Now we keep* the read_urb chain open until shutdown.* -- Added more port conditioning calls in digi_open and digi_close.* -- Convert port->active to a use count so that we can deal with multiple* opens and closes properly.* -- Fixed some problems with the locking code.** (5/3/2000) pberger and borchers* -- First alpha version of the driver--many known limitations and bugs.*** Locking and SMP** - Each port, including the out-of-band port, has a lock used to* serialize all access to the port's private structure.* - The port lock is also used to serialize all writes and access to* the port's URB.* - The port lock is also used for the port write_wait condition* variable. Holding the port lock will prevent a wake up on the* port's write_wait; this can be used with cond_wait_... to be sure* the wake up is not lost in a race when dropping the lock and* sleeping waiting for the wakeup.* - digi_write() does not sleep, since it is sometimes called on* interrupt time.* - digi_write_bulk_callback() and digi_read_bulk_callback() are* called directly from interrupts. Hence spin_lock_irqsave()* and spin_lock_irqrestore() are used in the rest of the code* for any locks they acquire.* - digi_write_bulk_callback() gets the port lock before waking up* processes sleeping on the port write_wait. It also schedules* wake ups so they happen from the scheduler, because the tty* system can miss wake ups from interrupts.* - All sleeps use a timeout of DIGI_RETRY_TIMEOUT before looping to* recheck the condition they are sleeping on. This is defensive,* in case a wake up is lost.* - Following Documentation/DocBook/kernel-locking.pdf no spin locks* are held when calling copy_to/from_user or printk.* * $Id: digi_acceleport.c,v 1.80.1.2 2000/11/02 05:45:08 root Exp $*/#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/signal.h>#include <linux/errno.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/fcntl.h>#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/tty_flip.h>#include <linux/module.h>#include <linux/spinlock.h>#include <linux/tqueue.h>#include <linux/usb.h>#ifdef CONFIG_USB_SERIAL_DEBUG static int debug = 1;#else static int debug;#endif#include "usb-serial.h"/* Defines *//* * Version Information */#define DRIVER_VERSION "v1.80.1.2"#define DRIVER_AUTHOR "Peter Berger <pberger@brimson.com>, Al Borchers <borchers@steinerpoint.com>"#define DRIVER_DESC "Digi AccelePort USB-2/USB-4 Serial Converter driver"/* port output buffer length -- must be <= transfer buffer length - 2 *//* so we can be sure to send the full buffer in one urb */#define DIGI_OUT_BUF_SIZE 8/* port input buffer length -- must be >= transfer buffer length - 3 *//* so we can be sure to hold at least one full buffer from one urb */#define DIGI_IN_BUF_SIZE 64/* retry timeout while sleeping */#define DIGI_RETRY_TIMEOUT (HZ/10)/* timeout while waiting for tty output to drain in close *//* this delay is used twice in close, so the total delay could *//* be twice this value */#define DIGI_CLOSE_TIMEOUT (5*HZ)/* AccelePort USB Defines *//* ids */#define DIGI_VENDOR_ID 0x05c5#define DIGI_2_ID 0x0002 /* USB-2 */#define DIGI_4_ID 0x0004 /* USB-4 *//* commands * "INB": can be used on the in-band endpoint * "OOB": can be used on the out-of-band endpoint */#define DIGI_CMD_SET_BAUD_RATE 0 /* INB, OOB */#define DIGI_CMD_SET_WORD_SIZE 1 /* INB, OOB */#define DIGI_CMD_SET_PARITY 2 /* INB, OOB */#define DIGI_CMD_SET_STOP_BITS 3 /* INB, OOB */#define DIGI_CMD_SET_INPUT_FLOW_CONTROL 4 /* INB, OOB */#define DIGI_CMD_SET_OUTPUT_FLOW_CONTROL 5 /* INB, OOB */#define DIGI_CMD_SET_DTR_SIGNAL 6 /* INB, OOB */#define DIGI_CMD_SET_RTS_SIGNAL 7 /* INB, OOB */#define DIGI_CMD_READ_INPUT_SIGNALS 8 /* OOB */#define DIGI_CMD_IFLUSH_FIFO 9 /* OOB */#define DIGI_CMD_RECEIVE_ENABLE 10 /* INB, OOB */#define DIGI_CMD_BREAK_CONTROL 11 /* INB, OOB */#define DIGI_CMD_LOCAL_LOOPBACK 12 /* INB, OOB */#define DIGI_CMD_TRANSMIT_IDLE 13 /* INB, OOB */#define DIGI_CMD_READ_UART_REGISTER 14 /* OOB */#define DIGI_CMD_WRITE_UART_REGISTER 15 /* INB, OOB */#define DIGI_CMD_AND_UART_REGISTER 16 /* INB, OOB */#define DIGI_CMD_OR_UART_REGISTER 17 /* INB, OOB */#define DIGI_CMD_SEND_DATA 18 /* INB */#define DIGI_CMD_RECEIVE_DATA 19 /* INB */#define DIGI_CMD_RECEIVE_DISABLE 20 /* INB */#define DIGI_CMD_GET_PORT_TYPE 21 /* OOB *//* baud rates */#define DIGI_BAUD_50 0#define DIGI_BAUD_75 1#define DIGI_BAUD_110 2#define DIGI_BAUD_150 3#define DIGI_BAUD_200 4#define DIGI_BAUD_300 5#define DIGI_BAUD_600 6#define DIGI_BAUD_1200 7#define DIGI_BAUD_1800 8#define DIGI_BAUD_2400 9#define DIGI_BAUD_4800 10#define DIGI_BAUD_7200 11#define DIGI_BAUD_9600 12#define DIGI_BAUD_14400 13#define DIGI_BAUD_19200 14#define DIGI_BAUD_28800 15#define DIGI_BAUD_38400 16#define DIGI_BAUD_57600 17#define DIGI_BAUD_76800 18#define DIGI_BAUD_115200 19#define DIGI_BAUD_153600 20#define DIGI_BAUD_230400 21#define DIGI_BAUD_460800 22/* arguments */#define DIGI_WORD_SIZE_5 0#define DIGI_WORD_SIZE_6 1#define DIGI_WORD_SIZE_7 2#define DIGI_WORD_SIZE_8 3#define DIGI_PARITY_NONE 0#define DIGI_PARITY_ODD 1#define DIGI_PARITY_EVEN 2#define DIGI_PARITY_MARK 3#define DIGI_PARITY_SPACE 4#define DIGI_STOP_BITS_1 0#define DIGI_STOP_BITS_2 1#define DIGI_INPUT_FLOW_CONTROL_XON_XOFF 1#define DIGI_INPUT_FLOW_CONTROL_RTS 2#define DIGI_INPUT_FLOW_CONTROL_DTR 4#define DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF 1#define DIGI_OUTPUT_FLOW_CONTROL_CTS 2#define DIGI_OUTPUT_FLOW_CONTROL_DSR 4#define DIGI_DTR_INACTIVE 0#define DIGI_DTR_ACTIVE 1#define DIGI_DTR_INPUT_FLOW_CONTROL 2#define DIGI_RTS_INACTIVE 0#define DIGI_RTS_ACTIVE 1#define DIGI_RTS_INPUT_FLOW_CONTROL 2#define DIGI_RTS_TOGGLE 3#define DIGI_FLUSH_TX 1#define DIGI_FLUSH_RX 2#define DIGI_RESUME_TX 4 /* clears xoff condition */#define DIGI_TRANSMIT_NOT_IDLE 0#define DIGI_TRANSMIT_IDLE 1#define DIGI_DISABLE 0#define DIGI_ENABLE 1#define DIGI_DEASSERT 0#define DIGI_ASSERT 1/* in band status codes */#define DIGI_OVERRUN_ERROR 4#define DIGI_PARITY_ERROR 8#define DIGI_FRAMING_ERROR 16#define DIGI_BREAK_ERROR 32/* out of band status */#define DIGI_NO_ERROR 0#define DIGI_BAD_FIRST_PARAMETER 1#define DIGI_BAD_SECOND_PARAMETER 2#define DIGI_INVALID_LINE 3#define DIGI_INVALID_OPCODE 4/* input signals */#define DIGI_READ_INPUT_SIGNALS_SLOT 1#define DIGI_READ_INPUT_SIGNALS_ERR 2#define DIGI_READ_INPUT_SIGNALS_BUSY 4#define DIGI_READ_INPUT_SIGNALS_PE 8#define DIGI_READ_INPUT_SIGNALS_CTS 16#define DIGI_READ_INPUT_SIGNALS_DSR 32#define DIGI_READ_INPUT_SIGNALS_RI 64#define DIGI_READ_INPUT_SIGNALS_DCD 128/* Structures */typedef struct digi_serial { spinlock_t ds_serial_lock; struct usb_serial_port *ds_oob_port; /* out-of-band port */ int ds_oob_port_num; /* index of out-of-band port */ int ds_device_started;} digi_serial_t;typedef struct digi_port {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -