📄 keyspan.c
字号:
/* Keyspan USB to Serial Converter driver (C) Copyright (C) 2000-2001 Hugh Blemings <hugh@misc.nu> 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. See http://misc.nu/hugh/keyspan.html for more information. Code in this driver inspired by and in a number of places taken from Brian Warner's original Keyspan-PDA driver. This driver has been put together with the support of Innosys, Inc. and Keyspan, Inc the manufacturers of the Keyspan USB-serial products. Thanks Guys :) Thanks to Paulus for miscellaneous tidy ups, some largish chunks of much nicer and/or completely new code and (perhaps most uniquely) having the patience to sit down and explain why and where he'd changed stuff. Tip 'o the hat to IBM (and previously Linuxcare :) for supporting staff in their work on open source projects. Change History Mon Oct 8 14:29:00 EST 2001 hugh Fixed bug that prevented mulitport devices operating correctly if they weren't the first unit attached. Sat Oct 6 12:31:21 EST 2001 hugh Added support for USA-28XA and -28XB, misc cleanups, break support for usa26 based models thanks to David Gibson. Thu May 31 11:56:42 PDT 2001 gkh switched from using spinlock to a semaphore (04/08/2001) gb Identify version on module load. (11/01/2000) Adam J. Richter usb_device_id table support. Tue Oct 10 23:15:33 EST 2000 Hugh Merged Paul's changes with my USA-49W mods. Work in progress still... Wed Jul 19 14:00:42 EST 2000 gkh Added module_init and module_exit functions to handle the fact that this driver is a loadable module now. Tue Jul 18 16:14:52 EST 2000 Hugh Basic character input/output for USA-19 now mostly works, fixed at 9600 baud for the moment. Sat Jul 8 11:11:48 EST 2000 Hugh First public release - nothing works except the firmware upload. Tested on PPC and x86 architectures, seems to behave...*/#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/usb.h>#ifdef CONFIG_USB_SERIAL_DEBUG static int debug = 1; #define DEBUG#else static int debug; #undef DEBUG#endif#include <linux/usb.h>#include "usb-serial.h"#include "keyspan.h"/* * Version Information */#define DRIVER_VERSION "v1.1.1"#define DRIVER_AUTHOR "Hugh Blemings <hugh@misc.nu"#define DRIVER_DESC "Keyspan USB to Serial Converter Driver"#define INSTAT_BUFLEN 32#define GLOCONT_BUFLEN 64 /* Per device and per port private data */struct keyspan_serial_private { /* number of active ports */ atomic_t active_count; const keyspan_device_details *device_details; urb_t *instat_urb; char instat_buf[INSTAT_BUFLEN]; /* XXX this one probably will need a lock */ urb_t *glocont_urb; char glocont_buf[GLOCONT_BUFLEN];};struct keyspan_port_private { /* Keep track of which input & output endpoints to use */ int in_flip; int out_flip; /* Keep duplicate of device details in each port structure as well - simplifies some of the callback functions etc. */ const keyspan_device_details *device_details; /* Input endpoints and buffer for this port */ urb_t *in_urbs[2]; char in_buffer[2][64]; /* Output endpoints and buffer for this port */ urb_t *out_urbs[2]; char out_buffer[2][64]; /* Input ack endpoint */ urb_t *inack_urb; char inack_buffer[1]; /* Output control endpoint */ urb_t *outcont_urb; char outcont_buffer[64]; /* Settings for the port */ int baud; int old_baud; unsigned int cflag; enum {flow_none, flow_cts, flow_xon} flow_control; int rts_state; /* Handshaking pins (outputs) */ int dtr_state; int cts_state; /* Handshaking pins (inputs) */ int dsr_state; int dcd_state; int ri_state; int break_on; unsigned long tx_start_time[2]; int resend_cont; /* need to resend control packet */}; /* Include Keyspan message headers. All current Keyspan Adapters make use of one of three message formats which are referred to as USA-26, USA-28 and USA-49 by Keyspan and within this driver. */#include "keyspan_usa26msg.h"#include "keyspan_usa28msg.h"#include "keyspan_usa49msg.h" /* If you don't get debugging output, uncomment the following two lines to enable cheat. */#if 0 #undef dbg #define dbg printk #endif/* Functions used by new usb-serial code. */static int __init keyspan_init (void){ usb_serial_register (&keyspan_usa18x_pre_device); usb_serial_register (&keyspan_usa19_pre_device); usb_serial_register (&keyspan_usa19w_pre_device); usb_serial_register (&keyspan_usa28_pre_device); usb_serial_register (&keyspan_usa28x_pre_device); usb_serial_register (&keyspan_usa28xa_pre_device); usb_serial_register (&keyspan_usa28xb_pre_device); usb_serial_register (&keyspan_usa49w_pre_device); usb_serial_register (&keyspan_usa18x_device); usb_serial_register (&keyspan_usa19_device); usb_serial_register (&keyspan_usa19w_device); usb_serial_register (&keyspan_usa28_device); usb_serial_register (&keyspan_usa28x_device); usb_serial_register (&keyspan_usa28xa_device); /* We don't need a separate entry for the usa28xb as it appears as a 28x anyway */ usb_serial_register (&keyspan_usa49w_device); info(DRIVER_VERSION ":" DRIVER_DESC); return 0;}static void __exit keyspan_exit (void){ usb_serial_deregister (&keyspan_usa18x_pre_device); usb_serial_deregister (&keyspan_usa19_pre_device); usb_serial_deregister (&keyspan_usa19w_pre_device); usb_serial_deregister (&keyspan_usa28_pre_device); usb_serial_deregister (&keyspan_usa28x_pre_device); usb_serial_deregister (&keyspan_usa28xa_pre_device); usb_serial_deregister (&keyspan_usa28xb_pre_device); usb_serial_deregister (&keyspan_usa49w_pre_device); usb_serial_deregister (&keyspan_usa18x_device); usb_serial_deregister (&keyspan_usa19_device); usb_serial_deregister (&keyspan_usa19w_device); usb_serial_deregister (&keyspan_usa28_device); usb_serial_deregister (&keyspan_usa28x_device); usb_serial_deregister (&keyspan_usa28xa_device); /* We don't need a separate entry for the usa28xb as it appears as a 28x anyway */ usb_serial_deregister (&keyspan_usa49w_device);}module_init(keyspan_init);module_exit(keyspan_exit);static void keyspan_rx_throttle (struct usb_serial_port *port){ dbg("keyspan_rx_throttle port %d\n", port->number);}static void keyspan_rx_unthrottle (struct usb_serial_port *port){ dbg("keyspan_rx_unthrottle port %d\n", port->number);}static void keyspan_break_ctl (struct usb_serial_port *port, int break_state){ struct keyspan_port_private *p_priv; dbg("keyspan_break_ctl\n"); p_priv = (struct keyspan_port_private *)port->private; if (break_state == -1) p_priv->break_on = 1; else p_priv->break_on = 0; keyspan_send_setup(port, 0);}static void keyspan_set_termios (struct usb_serial_port *port, struct termios *old_termios){ int baud_rate; struct keyspan_port_private *p_priv; const keyspan_device_details *d_details; unsigned int cflag; dbg(__FUNCTION__ ".\n"); p_priv = (struct keyspan_port_private *)(port->private); d_details = p_priv->device_details; cflag = port->tty->termios->c_cflag; /* Baud rate calculation takes baud rate as an integer so other rates can be generated if desired. */ baud_rate = tty_get_baud_rate(port->tty); /* If no match or invalid, don't change */ if (baud_rate >= 0 && d_details->calculate_baud_rate(baud_rate, d_details->baudclk, NULL, NULL, NULL) == KEYSPAN_BAUD_RATE_OK) { /* FIXME - more to do here to ensure rate changes cleanly */ p_priv->baud = baud_rate; } /* set CTS/RTS handshake etc. */ p_priv->cflag = cflag; p_priv->flow_control = (cflag & CRTSCTS)? flow_cts: flow_none; keyspan_send_setup(port, 0);}static int keyspan_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg){ unsigned int value, set; struct keyspan_port_private *p_priv; p_priv = (struct keyspan_port_private *)(port->private); switch (cmd) { case TIOCMGET: value = ((p_priv->rts_state) ? TIOCM_RTS : 0) | ((p_priv->dtr_state) ? TIOCM_DTR : 0) | ((p_priv->cts_state) ? TIOCM_CTS : 0) | ((p_priv->dsr_state) ? TIOCM_DSR : 0) | ((p_priv->dcd_state) ? TIOCM_CAR : 0) | ((p_priv->ri_state) ? TIOCM_RNG : 0); if (put_user(value, (unsigned int *) arg)) return -EFAULT; return 0; case TIOCMSET: if (get_user(value, (unsigned int *) arg)) return -EFAULT; p_priv->rts_state = ((value & TIOCM_RTS) ? 1 : 0); p_priv->dtr_state = ((value & TIOCM_DTR) ? 1 : 0); keyspan_send_setup(port, 0); return 0; case TIOCMBIS: case TIOCMBIC: if (get_user(value, (unsigned int *) arg)) return -EFAULT; set = (cmd == TIOCMBIS); if (value & TIOCM_RTS) p_priv->rts_state = set; if (value & TIOCM_DTR) p_priv->dtr_state = set; keyspan_send_setup(port, 0); return 0; } return -ENOIOCTLCMD;} /* Write function is generic for the three protocols used with only a minor change for usa49 required */static int keyspan_write(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count){ struct keyspan_port_private *p_priv; const keyspan_device_details *d_details; int flip; int left, todo; urb_t *this_urb; int err; p_priv = (struct keyspan_port_private *)(port->private); d_details = p_priv->device_details;#if 0 dbg(__FUNCTION__ " for port %d (%d chars [%x]), flip=%d\n", port->number, count, buf[0], p_priv->out_flip);#endif for (left = count; left > 0; left -= todo) { todo = left; if (todo > 63) todo = 63; flip = p_priv->out_flip; /* Check we have a valid urb/endpoint before we use it... */ if ((this_urb = p_priv->out_urbs[flip]) == 0) { /* no bulk out, so return 0 bytes written */ dbg(__FUNCTION__ " no output urb :(\n"); return count; } dbg(__FUNCTION__ " endpoint %d\n", usb_pipeendpoint(this_urb->pipe)); if (this_urb->status == -EINPROGRESS) { if (this_urb->transfer_flags & USB_ASYNC_UNLINK) break; if (jiffies - p_priv->tx_start_time[flip] < 10 * HZ) break; this_urb->transfer_flags |= USB_ASYNC_UNLINK; usb_unlink_urb(this_urb); break; } /* First byte in buffer is "last flag" - unused so for now so set to zero */ ((char *)this_urb->transfer_buffer)[0] = 0; if (from_user) { if (copy_from_user(this_urb->transfer_buffer + 1, buf, todo)) return -EFAULT; } else { memcpy (this_urb->transfer_buffer + 1, buf, todo); } buf += todo; /* send the data out the bulk port */ this_urb->transfer_buffer_length = todo + 1; this_urb->transfer_flags &= ~USB_ASYNC_UNLINK; this_urb->dev = port->serial->dev; if ((err = usb_submit_urb(this_urb)) != 0) { dbg("usb_submit_urb(write bulk) failed (%d)\n", err); } p_priv->tx_start_time[flip] = jiffies; /* Flip for next time if usa26 or usa28 interface (not used on usa49) */ p_priv->out_flip = (flip + 1) & d_details->outdat_endp_flip; } return count - left;}static void usa26_indat_callback(struct urb *urb){ int i, err; int endpoint; struct usb_serial_port *port; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; dbg ("%s\n", __FUNCTION__); endpoint = usb_pipeendpoint(urb->pipe); if (urb->status) { dbg(__FUNCTION__ "nonzero status: %x on endpoint %d.\n", urb->status, endpoint); return; } port = (struct usb_serial_port *) urb->context; tty = port->tty; if (urb->actual_length) { if (data[0] == 0) { /* no error on any byte */ for (i = 1; i < urb->actual_length ; ++i) { tty_insert_flip_char(tty, data[i], 0); } } else { /* some bytes had errors, every byte has status */ for (i = 0; i + 1 < urb->actual_length; i += 2) { int stat = data[i], flag = 0; if (stat & RXERROR_OVERRUN) flag |= TTY_OVERRUN; if (stat & RXERROR_FRAMING) flag |= TTY_FRAME; if (stat & RXERROR_PARITY) flag |= TTY_PARITY; /* XXX should handle break (0x10) */ tty_insert_flip_char(tty, data[i+1], flag); } } tty_flip_buffer_push(tty); } /* Resubmit urb so we continue receiving */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -