📄 digi_acceleport.c
字号:
.num_bulk_out = 4, .num_ports = 3, .open = digi_open, .close = digi_close, .write = digi_write, .write_room = digi_write_room, .write_bulk_callback = digi_write_bulk_callback, .read_bulk_callback = digi_read_bulk_callback, .chars_in_buffer = digi_chars_in_buffer, .throttle = digi_rx_throttle, .unthrottle = digi_rx_unthrottle, .ioctl = digi_ioctl, .set_termios = digi_set_termios, .break_ctl = digi_break_ctl, .tiocmget = digi_tiocmget, .tiocmset = digi_tiocmset, .attach = digi_startup, .shutdown = digi_shutdown,};static struct usb_serial_driver digi_acceleport_4_device = { .driver = { .owner = THIS_MODULE, .name = "digi_4", }, .description = "Digi 4 port USB adapter", .id_table = id_table_4, .num_interrupt_in = 0, .num_bulk_in = 5, .num_bulk_out = 5, .num_ports = 4, .open = digi_open, .close = digi_close, .write = digi_write, .write_room = digi_write_room, .write_bulk_callback = digi_write_bulk_callback, .read_bulk_callback = digi_read_bulk_callback, .chars_in_buffer = digi_chars_in_buffer, .throttle = digi_rx_throttle, .unthrottle = digi_rx_unthrottle, .ioctl = digi_ioctl, .set_termios = digi_set_termios, .break_ctl = digi_break_ctl, .tiocmget = digi_tiocmget, .tiocmset = digi_tiocmset, .attach = digi_startup, .shutdown = digi_shutdown,};/* Functions *//** Cond Wait Interruptible Timeout Irqrestore** Do spin_unlock_irqrestore and interruptible_sleep_on_timeout* so that wake ups are not lost if they occur between the unlock* and the sleep. In other words, spin_unlock_irqrestore and* interruptible_sleep_on_timeout are "atomic" with respect to* wake ups. This is used to implement condition variables.** interruptible_sleep_on_timeout is deprecated and has been replaced* with the equivalent code.*/static inline long cond_wait_interruptible_timeout_irqrestore( wait_queue_head_t *q, long timeout, spinlock_t *lock, unsigned long flags ){ DEFINE_WAIT(wait); prepare_to_wait(q, &wait, TASK_INTERRUPTIBLE); spin_unlock_irqrestore(lock, flags); timeout = schedule_timeout(timeout); finish_wait(q, &wait); return timeout;}/** Digi Wakeup Write** Wake up port, line discipline, and tty processes sleeping* on writes.*/static void digi_wakeup_write_lock(void *arg){ struct usb_serial_port *port = arg; unsigned long flags; struct digi_port *priv = usb_get_serial_port_data(port); spin_lock_irqsave( &priv->dp_port_lock, flags ); digi_wakeup_write( port ); spin_unlock_irqrestore( &priv->dp_port_lock, flags );}static void digi_wakeup_write( struct usb_serial_port *port ){ struct tty_struct *tty = port->tty; /* wake up port processes */ wake_up_interruptible( &port->write_wait ); /* wake up line discipline */ tty_wakeup(tty);}/** Digi Write OOB Command** Write commands on the out of band port. Commands are 4* bytes each, multiple commands can be sent at once, and* no command will be split across USB packets. Returns 0* if successful, -EINTR if interrupted while sleeping and* the interruptible flag is true, or a negative error* returned by usb_submit_urb.*/static int digi_write_oob_command( struct usb_serial_port *port, unsigned char *buf, int count, int interruptible ){ int ret = 0; int len; struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port; struct digi_port *oob_priv = usb_get_serial_port_data(oob_port); unsigned long flags = 0;dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_priv->dp_port_num, count ); spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); while( count > 0 ) { while( oob_port->write_urb->status == -EINPROGRESS || oob_priv->dp_write_urb_in_use ) { cond_wait_interruptible_timeout_irqrestore( &oob_port->write_wait, DIGI_RETRY_TIMEOUT, &oob_priv->dp_port_lock, flags ); if( interruptible && signal_pending(current) ) { return( -EINTR ); } spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); } /* len must be a multiple of 4, so commands are not split */ len = min(count, oob_port->bulk_out_size ); if( len > 4 ) len &= ~3; memcpy( oob_port->write_urb->transfer_buffer, buf, len ); oob_port->write_urb->transfer_buffer_length = len; oob_port->write_urb->dev = port->serial->dev; if( (ret=usb_submit_urb(oob_port->write_urb, GFP_ATOMIC)) == 0 ) { oob_priv->dp_write_urb_in_use = 1; count -= len; buf += len; } } spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags ); if( ret ) { err("%s: usb_submit_urb failed, ret=%d", __FUNCTION__, ret ); } return( ret );}/** Digi Write In Band Command** Write commands on the given port. Commands are 4* bytes each, multiple commands can be sent at once, and* no command will be split across USB packets. If timeout* is non-zero, write in band command will return after* waiting unsuccessfully for the URB status to clear for* timeout ticks. Returns 0 if successful, or a negative* error returned by digi_write.*/static int digi_write_inb_command( struct usb_serial_port *port, unsigned char *buf, int count, unsigned long timeout ){ int ret = 0; int len; struct digi_port *priv = usb_get_serial_port_data(port); unsigned char *data = port->write_urb->transfer_buffer; unsigned long flags = 0;dbg( "digi_write_inb_command: TOP: port=%d, count=%d", priv->dp_port_num,count ); if( timeout ) timeout += jiffies; else timeout = ULONG_MAX; spin_lock_irqsave( &priv->dp_port_lock, flags ); while( count > 0 && ret == 0 ) { while( (port->write_urb->status == -EINPROGRESS || priv->dp_write_urb_in_use) && time_before(jiffies, timeout)) { cond_wait_interruptible_timeout_irqrestore( &port->write_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags ); if( signal_pending(current) ) { return( -EINTR ); } spin_lock_irqsave( &priv->dp_port_lock, flags ); } /* len must be a multiple of 4 and small enough to */ /* guarantee the write will send buffered data first, */ /* so commands are in order with data and not split */ len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len ); if( len > 4 ) len &= ~3; /* write any buffered data first */ if( priv->dp_out_buf_len > 0 ) { data[0] = DIGI_CMD_SEND_DATA; data[1] = priv->dp_out_buf_len; memcpy( data+2, priv->dp_out_buf, priv->dp_out_buf_len ); memcpy( data+2+priv->dp_out_buf_len, buf, len ); port->write_urb->transfer_buffer_length = priv->dp_out_buf_len+2+len; } else { memcpy( data, buf, len ); port->write_urb->transfer_buffer_length = len; } port->write_urb->dev = port->serial->dev; if( (ret=usb_submit_urb(port->write_urb, GFP_ATOMIC)) == 0 ) { priv->dp_write_urb_in_use = 1; priv->dp_out_buf_len = 0; count -= len; buf += len; } } spin_unlock_irqrestore( &priv->dp_port_lock, flags ); if( ret ) { err("%s: usb_submit_urb failed, ret=%d, port=%d", __FUNCTION__, ret, priv->dp_port_num ); } return( ret );}/** Digi Set Modem Signals** Sets or clears DTR and RTS on the port, according to the* modem_signals argument. Use TIOCM_DTR and TIOCM_RTS flags* for the modem_signals argument. Returns 0 if successful,* -EINTR if interrupted while sleeping, or a non-zero error* returned by usb_submit_urb.*/static int digi_set_modem_signals( struct usb_serial_port *port, unsigned int modem_signals, int interruptible ){ int ret; struct digi_port *port_priv = usb_get_serial_port_data(port); struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port; struct digi_port *oob_priv = usb_get_serial_port_data(oob_port); unsigned char *data = oob_port->write_urb->transfer_buffer; unsigned long flags = 0;dbg( "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x",port_priv->dp_port_num, modem_signals ); spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); spin_lock( &port_priv->dp_port_lock ); while( oob_port->write_urb->status == -EINPROGRESS || oob_priv->dp_write_urb_in_use ) { spin_unlock( &port_priv->dp_port_lock ); cond_wait_interruptible_timeout_irqrestore( &oob_port->write_wait, DIGI_RETRY_TIMEOUT, &oob_priv->dp_port_lock, flags ); if( interruptible && signal_pending(current) ) { return( -EINTR ); } spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); spin_lock( &port_priv->dp_port_lock ); } data[0] = DIGI_CMD_SET_DTR_SIGNAL; data[1] = port_priv->dp_port_num; data[2] = (modem_signals&TIOCM_DTR) ? DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE; data[3] = 0; data[4] = DIGI_CMD_SET_RTS_SIGNAL; data[5] = port_priv->dp_port_num; data[6] = (modem_signals&TIOCM_RTS) ? DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE; data[7] = 0; oob_port->write_urb->transfer_buffer_length = 8; oob_port->write_urb->dev = port->serial->dev; if( (ret=usb_submit_urb(oob_port->write_urb, GFP_ATOMIC)) == 0 ) { oob_priv->dp_write_urb_in_use = 1; port_priv->dp_modem_signals = (port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS)) | (modem_signals&(TIOCM_DTR|TIOCM_RTS)); } spin_unlock( &port_priv->dp_port_lock ); spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags ); if( ret ) { err("%s: usb_submit_urb failed, ret=%d", __FUNCTION__, ret ); } return( ret );}/** Digi Transmit Idle** Digi transmit idle waits, up to timeout ticks, for the transmitter* to go idle. It returns 0 if successful or a negative error.** There are race conditions here if more than one process is calling* digi_transmit_idle on the same port at the same time. However, this* is only called from close, and only one process can be in close on a* port at a time, so its ok.*/static int digi_transmit_idle( struct usb_serial_port *port, unsigned long timeout ){ int ret; unsigned char buf[2]; struct digi_port *priv = usb_get_serial_port_data(port); unsigned long flags = 0; spin_lock_irqsave( &priv->dp_port_lock, flags ); priv->dp_transmit_idle = 0; spin_unlock_irqrestore( &priv->dp_port_lock, flags ); buf[0] = DIGI_CMD_TRANSMIT_IDLE; buf[1] = 0; timeout += jiffies; if( (ret=digi_write_inb_command( port, buf, 2, timeout-jiffies )) != 0 ) return( ret ); spin_lock_irqsave( &priv->dp_port_lock, flags ); while( time_before(jiffies, timeout) && !priv->dp_transmit_idle ) { cond_wait_interruptible_timeout_irqrestore( &priv->dp_transmit_idle_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags ); if( signal_pending(current) ) { return( -EINTR ); } spin_lock_irqsave( &priv->dp_port_lock, flags ); } priv->dp_transmit_idle = 0; spin_unlock_irqrestore( &priv->dp_port_lock, flags ); return( 0 );}static void digi_rx_throttle( struct usb_serial_port *port ){ unsigned long flags; struct digi_port *priv = usb_get_serial_port_data(port);dbg( "digi_rx_throttle: TOP: port=%d", priv->dp_port_num ); /* stop receiving characters by not resubmitting the read urb */ spin_lock_irqsave( &priv->dp_port_lock, flags ); priv->dp_throttled = 1; priv->dp_throttle_restart = 0; priv->dp_in_buf_len = 0; spin_unlock_irqrestore( &priv->dp_port_lock, flags );}static void digi_rx_unthrottle( struct usb_serial_port *port ){ int ret = 0; int len; unsigned long flags; struct digi_port *priv = usb_get_serial_port_data(port); struct tty_struct *tty = port->tty;dbg( "digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num ); spin_lock_irqsave( &priv->dp_port_lock, flags ); /* send any buffered chars from throttle time on to tty subsystem */ len = min(priv->dp_in_buf_len, TTY_FLIPBUF_SIZE - tty->flip.count ); if( len > 0 ) { memcpy( tty->flip.char_buf_ptr, priv->dp_in_buf, len ); memcpy( tty->flip.flag_buf_ptr, priv->dp_in_flag_buf, len ); tty->flip.char_buf_ptr += len; tty->flip.flag_buf_ptr += len; tty->flip.count += len; tty_flip_buffer_push( tty ); } /* restart read chain */ if( priv->dp_throttle_restart ) { port->read_urb->dev = port->serial->dev; ret = usb_submit_urb( port->read_urb, GFP_ATOMIC ); } /* turn throttle off */ priv->dp_throttled = 0; priv->dp_in_buf_len = 0; priv->dp_throttle_restart = 0; spin_unlock_irqrestore( &priv->dp_port_lock, flags ); if( ret ) { err("%s: usb_submit_urb failed, ret=%d, port=%d", __FUNCTION__, ret, priv->dp_port_num ); }}static void digi_set_termios( struct usb_serial_port *port, struct termios *old_termios ){ struct digi_port *priv = usb_get_serial_port_data(port); unsigned int iflag = port->tty->termios->c_iflag; unsigned int cflag = port->tty->termios->c_cflag; unsigned int old_iflag = old_termios->c_iflag; unsigned int old_cflag = old_termios->c_cflag; unsigned char buf[32]; unsigned int modem_signals; int arg,ret; int i = 0;dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", priv->dp_port_num, iflag, old_iflag, cflag, old_cflag ); /* set baud rate */ if( (cflag&CBAUD) != (old_cflag&CBAUD) ) { arg = -1; /* reassert DTR and (maybe) RTS on transition from B0 */ if( (old_cflag&CBAUD) == B0 ) { /* don't set RTS if using hardware flow control */ /* and throttling input */ modem_signals = TIOCM_DTR; if( !(port->tty->termios->c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &port->tty->flags) ) { modem_signals |= TIOCM_RTS; } digi_set_modem_signals( port, modem_signals, 1 ); } switch( (cflag&CBAUD) ) { /* drop DTR and RTS on transition to B0 */ case B0: digi_set_modem_signals( port, 0, 1 ); break; case B50: arg = DIGI_BAUD_50; break; case B75: arg = DIGI_BAUD_75; break; case B110: arg = DIGI_BAUD_110; break; case B150: arg = DIGI_BAUD_150; break; case B200: arg = DIGI_BAUD_200; break; case B300: arg = DIGI_BAUD_300; break; case B600: arg = DIGI_BAUD_600; break; case B1200: arg = DIGI_BAUD_1200; break; case B1800: arg = DIGI_BAUD_1800; break; case B2400: arg = DIGI_BAUD_2400; break; case B4800: arg = DIGI_BAUD_4800; break; case B9600: arg = DIGI_BAUD_9600; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -