patch-2.2.19 linux/drivers/usb/serial/keyspan.c
Next file: linux/drivers/usb/serial/keyspan.h
Previous file: linux/drivers/usb/serial/ftdi_sio.h
Back to the patch index
Back to the overall index
- Lines: 1972
- Date:
Sun Mar 25 11:37:37 2001
- Orig file:
v2.2.18/drivers/usb/serial/keyspan.c
- Orig date:
Sun Mar 25 11:28:32 2001
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.18/drivers/usb/serial/keyspan.c linux/drivers/usb/serial/keyspan.c
@@ -19,21 +19,33 @@
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 Linuxcare for supporting staff in their work on
open source projects.
- 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.
-
+ Change History
+ 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>
@@ -42,59 +54,136 @@
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
+#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
-#include <linux/tty.h>
#include <linux/module.h>
#include <linux/spinlock.h>
-#ifdef CONFIG_USB_SERIAL_DEBUG
+#define DEBUG
+/* #ifdef CONFIG_USB_SERIAL_DEBUG */
#define DEBUG
-#else
- #undef DEBUG
-#endif
+/* #endif */
#include <linux/usb.h>
#include "usb-serial.h"
#include "keyspan.h"
+#define INSTAT_BUFLEN 32
+#define GLOCONT_BUFLEN 64
+
/* Per device and per port private data */
struct keyspan_serial_private {
- struct urb *in_urbs[8];
- struct urb *out_urbs[8];
- char out_buffer[64];
- char in_buffer[64];
+ /* 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 output endpoint to use */
+ /* Keep track of which input & output endpoints to use */
+ int in_flip;
int out_flip;
- /* Settings for the port */
+ /* 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;
- enum {parity_none, parity_odd, parity_even} parity;
+ unsigned int cflag;
enum {flow_none, flow_cts, flow_xon} flow_control;
- int rts_state;
+ int rts_state; /* Handshaking pins (outputs) */
int dtr_state;
+ int cts_state; /* Handshaking pins (inputs) */
+ int dsr_state;
+ int dcd_state;
+ int ri_state;
+ unsigned long tx_start_time[2];
+ int resend_cont; /* need to resend control packet */
};
-
- /* FIXME this will break if multiple physical interfaces used */
-static wait_queue_head_t out_wait;
- /* Include Keyspan message headers (not both yet, need some tweaks
- to get clean build) */
-/*#include "keyspan_usa26msg.h"*/
+/* 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. */
-#undef dbg
-#define dbg printk
+/* If you don't get debugging output, uncomment the following
+ two lines to enable cheat. */
+#if 0
+ #undef dbg
+ #define dbg printk
+#endif
+
+static void keyspan_send_setup(struct usb_serial_port *port);
+
+/* Functions used by new usb-serial code. */
+int 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_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_usa49w_device);
+ return 0;
+}
+
+void 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_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_usa49w_device);
+}
+
+module_init(keyspan_init);
+module_exit(keyspan_exit);
- /* Functions - mostly stubs for now */
static void keyspan_rx_throttle (struct usb_serial_port *port)
{
dbg("keyspan_rx_throttle port %d", port->number);
@@ -116,180 +205,597 @@
static void keyspan_set_termios (struct usb_serial_port *port,
struct termios *old_termios)
{
- dbg("keyspan_set_termios");
+ int baud_rate;
+ struct keyspan_port_private *p_priv;
+ const keyspan_device_details *d_details;
+ unsigned int cflag;
+
+ /* dbg(__FUNCTION__ "."); */
+
+ 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);
}
static int keyspan_ioctl(struct usb_serial_port *port, struct file *file,
unsigned int cmd, unsigned long arg)
{
- unsigned int value;
+ unsigned int value, set;
+ struct keyspan_port_private *p_priv;
- dbg("keyspan_ioctl_info");
+ p_priv = (struct keyspan_port_private *)(port->private);
switch (cmd) {
case TIOCMGET:
- value = TIOCM_DTR | TIOCM_RNG;
- if (copy_to_user((unsigned int *)arg, &value, sizeof(int))) {
+ 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;
- }
- else {
- return 0;
- }
-
- default:
- return -ENOIOCTLCMD;
+ 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);
+ 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);
+ 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)
+ const unsigned char *buf, int count)
{
- struct usb_serial *serial = port->serial;
- struct keyspan_serial_private *s_priv;
struct keyspan_port_private *p_priv;
- int current_urb;
- int i;
+ const keyspan_device_details *d_details;
+ int flip;
+ int left, todo;
+ urb_t *this_urb;
+ int err;
- s_priv = (struct keyspan_serial_private *)(serial->private);
p_priv = (struct keyspan_port_private *)(port->private);
-
- if (p_priv->out_flip == 0) {
- current_urb = 0;
- p_priv->out_flip = 1;
+ d_details = p_priv->device_details;
+
+#if 0
+ dbg(__FUNCTION__ " for port %d (%d chars [%x]), flip=%d",
+ 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 :(");
+ return count;
+ }
+
+ 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) {
+ copy_from_user(this_urb->transfer_buffer + 1, buf, todo);
+ } 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)", 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;
}
- else {
- current_urb = 1;
- p_priv->out_flip = 0;
+
+ 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 (__FUNCTION__); */
+
+ endpoint = usb_pipeendpoint(urb->pipe);
+
+ if (urb->status) {
+ dbg(__FUNCTION__ "nonzero status: %x on endpoint %d.",
+ 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 */
+ urb->dev = port->serial->dev;
+ if ((err = usb_submit_urb(urb)) != 0) {
+ dbg(__FUNCTION__ "resubmit read urb failed. (%d)", err);
+ }
+ return;
+}
+
+ /* Outdat handling is common for usa26, usa28 and usa49 messages */
+static void usa2x_outdat_callback(struct urb *urb)
+{
+ struct usb_serial_port *port;
+ struct keyspan_port_private *p_priv;
+
+ port = (struct usb_serial_port *) urb->context;
+ p_priv = (struct keyspan_port_private *)(port->private);
+ /* dbg (__FUNCTION__ " urb %d", urb == p_priv->out_urbs[1]); */
+
+ if (port->active) {
+ queue_task(&port->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
+}
+
+static void usa26_inack_callback(struct urb *urb)
+{
+ dbg (__FUNCTION__);
+
+}
+
+static void usa26_outcont_callback(struct urb *urb)
+{
+ struct usb_serial_port *port;
+ struct keyspan_port_private *p_priv;
+
+ port = (struct usb_serial_port *) urb->context;
+ p_priv = (struct keyspan_port_private *)(port->private);
+
+ if (p_priv->resend_cont) {
+ /* dbg (__FUNCTION__ " sending setup"); */
+ keyspan_usa26_send_setup(port->serial, port);
}
+}
- dbg("keyspan_write called for port %d (%d) chars {", port->number, count);
- for (i = 0; i < count ; i++) {
- dbg("%02x ", buf[i]);
+static void usa26_instat_callback(struct urb *urb)
+{
+ unsigned char *data = urb->transfer_buffer;
+ keyspan_usa26_portStatusMessage *msg;
+ struct usb_serial *serial;
+ struct usb_serial_port *port;
+ struct keyspan_port_private *p_priv;
+ int old_dcd_state, err;
+
+ serial = (struct usb_serial *) urb->context;
+
+ if (urb->status) {
+ dbg(__FUNCTION__ " nonzero status: %x", urb->status);
+ return;
+ }
+ if (urb->actual_length != 9) {
+ dbg(__FUNCTION__ " %d byte report??", urb->actual_length);
+ goto exit;
}
- dbg("}\n");
- if (count == 0) {
- dbg("write request of 0 bytes");
- return (0);
+ msg = (keyspan_usa26_portStatusMessage *)data;
+
+#if 0
+ dbg(__FUNCTION__ " port status: port %d cts %d dcd %d dsr %d ri %d toff %d txoff %d rxen %d cr %d",
+ msg->port, msg->hskia_cts, msg->gpia_dcd, msg->dsr, msg->ri, msg->_txOff,
+ msg->_txXoff, msg->rxEnabled, msg->controlResponse);
+#endif
+
+ /* Now do something useful with the data */
+
+
+ /* Check port number from message and retrieve private data */
+ if (msg->port >= serial->num_ports) {
+ dbg ("Unexpected port number %d", msg->port);
+ goto exit;
}
+ port = &serial->port[msg->port];
+ p_priv = (struct keyspan_port_private *)(port->private);
+
+ /* Update handshaking pin state information */
+ old_dcd_state = p_priv->dcd_state;
+ p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0);
+ p_priv->dsr_state = ((msg->dsr) ? 1 : 0);
+ p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0);
+ p_priv->ri_state = ((msg->ri) ? 1 : 0);
+
+ if (port->tty && !C_CLOCAL(port->tty)
+ && old_dcd_state != p_priv->dcd_state) {
+ if (old_dcd_state)
+ tty_hangup(port->tty);
+ /* else */
+ /* wake_up_interruptible(&p_priv->open_wait); */
+ }
+
+exit:
+ /* Resubmit urb so we continue receiving */
+ urb->dev = serial->dev;
+ if ((err = usb_submit_urb(urb)) != 0) {
+ dbg(__FUNCTION__ "resubmit read urb failed. (%d)", err);
+ }
+}
+
+static void usa26_glocont_callback(struct urb *urb)
+{
+ dbg (__FUNCTION__);
+
+}
+
+
+static void usa28_indat_callback(struct urb *urb)
+{
+ int i, err;
+ struct usb_serial_port *port;
+ struct tty_struct *tty;
+ unsigned char *data;
+ struct keyspan_port_private *p_priv;
+
+ /* dbg (__FUNCTION__); */
+
+ port = (struct usb_serial_port *) urb->context;
+ p_priv = (struct keyspan_port_private *)(port->private);
+ data = urb->transfer_buffer;
+
+ if (urb != p_priv->in_urbs[p_priv->in_flip])
+ return;
- /* only send data if we have a bulk out endpoint */
- if (s_priv->out_urbs[current_urb]) {
- while (s_priv->out_urbs[current_urb]->status == -EINPROGRESS) {
- dbg (__FUNCTION__ " INPROGRES\n");
- interruptible_sleep_on(&out_wait);
- if (signal_pending(current)) {
- dbg (__FUNCTION__ " signal\n");
- return (-ERESTARTSYS);
+ do {
+ if (urb->status) {
+ dbg(__FUNCTION__ "nonzero status: %x on endpoint
+%d.",
+ urb->status, usb_pipeendpoint(urb->pipe));
+ return;
+ }
+
+ port = (struct usb_serial_port *) urb->context;
+ p_priv = (struct keyspan_port_private *)(port->private);
+ data = urb->transfer_buffer;
+
+ tty = port->tty;
+ if (urb->actual_length) {
+ for (i = 0; i < urb->actual_length ; ++i) {
+ tty_insert_flip_char(tty, data[i], 0);
}
+ tty_flip_buffer_push(tty);
}
- /*if (s_priv->out_urbs[current_urb]->status == -EINPROGRESS) {
- dbg ("already writing");
- return (-EAGAIN);
- }*/
- /* First byte in buffer is "last flag" - unused so
- for now so set to zero */
- memset(s_priv->out_urbs[current_urb]->transfer_buffer, 0, 1);
- if (from_user) {
- copy_from_user(s_priv->out_urbs[current_urb]->transfer_buffer + 1, buf, count);
+ /* Resubmit urb so we continue receiving */
+ urb->dev = port->serial->dev;
+ if ((err = usb_submit_urb(urb)) != 0) {
+ dbg(__FUNCTION__ "resubmit read urb failed. (%d)",
+err);
}
- else {
- memcpy (s_priv->out_urbs[current_urb]->transfer_buffer + 1, buf, count);
- }
+ p_priv->in_flip ^= 1;
- /* send the data out the bulk port */
- s_priv->out_urbs[current_urb]->transfer_buffer_length = count + 1;
+ urb = p_priv->in_urbs[p_priv->in_flip];
+ } while (urb->status != -EINPROGRESS);
+}
- if (usb_submit_urb(s_priv->out_urbs[current_urb])) {
- dbg("usb_submit_urb(write bulk) failed");
- }
+static void usa28_inack_callback(struct urb *urb)
+{
+ dbg (__FUNCTION__);
+}
+
+static void usa28_outcont_callback(struct urb *urb)
+{
+ struct usb_serial_port *port;
+ struct keyspan_port_private *p_priv;
- return (count);
+ port = (struct usb_serial_port *) urb->context;
+ p_priv = (struct keyspan_port_private *)(port->private);
+
+ if (p_priv->resend_cont) {
+ dbg (__FUNCTION__ " sending setup");
+ keyspan_usa28_send_setup(port->serial, port);
}
+}
+
+static void usa28_instat_callback(struct urb *urb)
+{
+ int err;
+ unsigned char *data = urb->transfer_buffer;
+ keyspan_usa28_portStatusMessage *msg;
+ struct usb_serial *serial;
+ struct usb_serial_port *port;
+ struct keyspan_port_private *p_priv;
+ int old_dcd_state;
+
+ serial = (struct usb_serial *) urb->context;
+
+ if (urb->status) {
+ dbg(__FUNCTION__ " nonzero status: %x", urb->status);
+ return;
+ }
+
+ if (urb->actual_length != sizeof(struct keyspan_usa28_portStatusMessage)) {
+ dbg(__FUNCTION__ " bad length %d", urb->actual_length);
+ goto exit;
+ }
+
+ /*dbg(__FUNCTION__ " %x %x %x %x %x %x %x %x %x %x %x %x",
+ data[0], data[1], data[2], data[3], data[4], data[5],
+ data[6], data[7], data[8], data[9], data[10], data[11]);*/
- /* no bulk out, so return 0 bytes written */
- return (0);
+ /* Now do something useful with the data */
+ msg = (keyspan_usa28_portStatusMessage *)data;
+
+
+ /* Check port number from message and retrieve private data */
+ if (msg->port >= serial->num_ports) {
+ dbg ("Unexpected port number %d", msg->port);
+ goto exit;
+ }
+ port = &serial->port[msg->port];
+ p_priv = (struct keyspan_port_private *)(port->private);
+
+ /* Update handshaking pin state information */
+ old_dcd_state = p_priv->dcd_state;
+ p_priv->cts_state = ((msg->cts) ? 1 : 0);
+ p_priv->dsr_state = ((msg->dsr) ? 1 : 0);
+ p_priv->dcd_state = ((msg->dcd) ? 1 : 0);
+ p_priv->ri_state = ((msg->ri) ? 1 : 0);
+
+ if (port->tty && !C_CLOCAL(port->tty)
+ && old_dcd_state != p_priv->dcd_state) {
+ if (old_dcd_state)
+ tty_hangup(port->tty);
+ /* else */
+ /* wake_up_interruptible(&p_priv->open_wait); */
+ }
+
+exit:
+ /* Resubmit urb so we continue receiving */
+ urb->dev = serial->dev;
+ if ((err = usb_submit_urb(urb)) != 0) {
+ dbg(__FUNCTION__ "resubmit read urb failed. (%d)", err);
+ }
}
+static void usa28_glocont_callback(struct urb *urb)
+{
+ dbg (__FUNCTION__);
+}
-static void keyspan_write_bulk_callback (struct urb *urb)
+
+static void usa49_glocont_callback(struct urb *urb)
{
- int endpoint;
-
- endpoint = usb_pipeendpoint(urb->pipe);
+ struct usb_serial *serial;
+ struct usb_serial_port *port;
+ struct keyspan_port_private *p_priv;
+ int i;
+
+ /* dbg (__FUNCTION__); */
+
+ serial = (struct usb_serial *) urb->context;
+ for (i = 0; i < serial->num_ports; ++i) {
+ port = &serial->port[i];
+ p_priv = (struct keyspan_port_private *)(port->private);
- dbg("keyspan_write_bulk_callback for endpoint %d\n", endpoint);
+ if (p_priv->resend_cont) {
+ /* dbg (__FUNCTION__ " sending setup"); */
+ keyspan_usa49_send_setup(serial, port);
+ break;
+ }
+ }
+}
- /* Only do wakeup if this callback is from one of the data
- endpoints. */
- if (endpoint == 2 || endpoint == 3) {
- wake_up_interruptible(&out_wait);
+ /* This is actually called glostat in the Keyspan
+ doco */
+static void usa49_instat_callback(struct urb *urb)
+{
+ int err;
+ unsigned char *data = urb->transfer_buffer;
+ keyspan_usa49_portStatusMessage *msg;
+ struct usb_serial *serial;
+ struct usb_serial_port *port;
+ struct keyspan_port_private *p_priv;
+ int old_dcd_state;
+
+ /* dbg (__FUNCTION__); */
+
+ serial = (struct usb_serial *) urb->context;
+
+ if (urb->status) {
+ dbg(__FUNCTION__ " nonzero status: %x", urb->status);
+ return;
+ }
+
+ if (urb->actual_length != sizeof(struct keyspan_usa49_portStatusMessage)) {
+ dbg(__FUNCTION__ " bad length %d", urb->actual_length);
+ goto exit;
+ }
+
+ /*dbg(__FUNCTION__ " %x %x %x %x %x %x %x %x %x %x %x",
+ data[0], data[1], data[2], data[3], data[4], data[5],
+ data[6], data[7], data[8], data[9], data[10]);*/
+
+ /* Now do something useful with the data */
+ msg = (keyspan_usa49_portStatusMessage *)data;
+
+ /* Check port number from message and retrieve private data */
+ if (msg->portNumber >= serial->num_ports) {
+ dbg ("Unexpected port number %d", msg->portNumber);
+ goto exit;
+ }
+ port = &serial->port[msg->portNumber];
+ p_priv = (struct keyspan_port_private *)(port->private);
+
+ /* Update handshaking pin state information */
+ old_dcd_state = p_priv->dcd_state;
+ p_priv->cts_state = ((msg->cts) ? 1 : 0);
+ p_priv->dsr_state = ((msg->dsr) ? 1 : 0);
+ p_priv->dcd_state = ((msg->dcd) ? 1 : 0);
+ p_priv->ri_state = ((msg->ri) ? 1 : 0);
+
+ if (port->tty && !C_CLOCAL(port->tty)
+ && old_dcd_state != p_priv->dcd_state) {
+ if (old_dcd_state)
+ tty_hangup(port->tty);
+ /* else */
+ /* wake_up_interruptible(&p_priv->open_wait); */
}
+exit:
+ /* Resubmit urb so we continue receiving */
+ urb->dev = serial->dev;
+
+ if ((err = usb_submit_urb(urb)) != 0) {
+ dbg(__FUNCTION__ "resubmit read urb failed. (%d)", err);
+ }
}
+static void usa49_inack_callback(struct urb *urb)
+{
+ dbg (__FUNCTION__);
+}
-static void keyspan_read_bulk_callback (struct urb *urb)
+static void usa49_indat_callback(struct urb *urb)
{
- int i;
+ int i, err;
int endpoint;
- struct usb_serial *serial;
struct usb_serial_port *port;
struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
- endpoint = usb_pipeendpoint(urb->pipe);
+ /* dbg (__FUNCTION__); */
+ endpoint = usb_pipeendpoint(urb->pipe);
if (urb->status) {
- dbg(__FUNCTION__ "nonzero status: %x on endpoint %d.\n",
+ dbg(__FUNCTION__ "nonzero status: %x on endpoint %d.",
urb->status, endpoint);
return;
}
- switch (endpoint) {
-
- /* If this is one of the data endpoints, stuff it's
- contents into the tty flip_buffer. */
- case 1:
- case 2: serial = (struct usb_serial *) urb->context;
- port = &serial->port[0];
- tty = port->tty;
- if (urb->actual_length) {
- for (i = 0; i < urb->actual_length ; ++i) {
- tty_insert_flip_char(tty, data[i], 0);
- }
- tty_flip_buffer_push(tty);
+ 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);
}
- break;
-
- /* INACK endpoint */
- case 3: dbg(__FUNCTION__ " callback for INACK endpoint\n");
- break;
-
- /* INSTAT endpoint */
- case 4: dbg(__FUNCTION__ " callback for INSTAT endpoint\n");
- break;
-
- default:
- dbg(__FUNCTION__ " callback for unknown endpoint!\n");
- break;
+ } 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 */
- if (usb_submit_urb(urb)) {
- dbg(__FUNCTION__ "resubmit read urb failed.\n");
+ urb->dev = port->serial->dev;
+ if ((err = usb_submit_urb(urb)) != 0) {
+ dbg(__FUNCTION__ "resubmit read urb failed. (%d)", err);
}
- return;
-
}
+/* not used, usa-49 doesn't have per-port control endpoints */
+static void usa49_outcont_callback(struct urb *urb)
+{
+ dbg (__FUNCTION__);
+}
+
+
+
static int keyspan_write_room (struct usb_serial_port *port)
{
-// dbg("keyspan_write_room called\n");
+// dbg("keyspan_write_room called");
return (32);
}
@@ -306,62 +812,92 @@
struct keyspan_port_private *p_priv;
struct keyspan_serial_private *s_priv;
struct usb_serial *serial = port->serial;
- int i;
+ const keyspan_device_details *d_details;
+ int i, already_active, err;
+ unsigned long flags;
+ urb_t *urb;
s_priv = (struct keyspan_serial_private *)(serial->private);
p_priv = (struct keyspan_port_private *)(port->private);
+ d_details = s_priv->device_details;
- dbg("keyspan_open called.\n");
-
- if (port->active) {
- dbg(__FUNCTION__ "port->active already true!\n");
- return (-EINVAL);
- }
+ /* dbg("keyspan_open called."); */
+ MOD_INC_USE_COUNT;
- p_priv = (struct keyspan_port_private *)(port->private);
-
- p_priv->out_flip = 0;
+ spin_lock_irqsave (&port->port_lock, flags);
+ ++port->open_count;
+ already_active = port->active;
port->active = 1;
+ spin_unlock_irqrestore (&port->port_lock, flags);
- /* Start reading from port */
- for (i = 0; i < 4; i++) {
- if (s_priv->in_urbs[i]) {
- if (usb_submit_urb(s_priv->in_urbs[i])) {
- dbg(__FUNCTION__ " submit in urb %d failed", i);
- }
- }
+ if (already_active)
+ return 0;
+
+ p_priv = (struct keyspan_port_private *)(port->private);
+ /* Set some sane defaults */
+ p_priv->rts_state = 1;
+ p_priv->dtr_state = 1;
+
+ /* Start reading from endpoints */
+ for (i = 0; i < 2; i++) {
+ if ((urb = p_priv->in_urbs[i]) == NULL)
+ continue;
+ urb->dev = serial->dev;
+ if ((err = usb_submit_urb(urb)) != 0) {
+ dbg(__FUNCTION__ " submit urb %d failed (%d)", i, err);
+ }
}
-
- keyspan_usa19_send_setup(serial, port);
+
+ keyspan_set_termios(port, NULL);
return (0);
}
+static inline void stop_urb(urb_t *urb)
+{
+ if (urb && urb->status == -EINPROGRESS) {
+ urb->transfer_flags &= ~USB_ASYNC_UNLINK;
+ usb_unlink_urb(urb);
+ }
+}
static void keyspan_close(struct usb_serial_port *port, struct file *filp)
{
int i;
struct usb_serial *serial = port->serial; /* FIXME should so sanity check */
struct keyspan_serial_private *s_priv;
+ struct keyspan_port_private *p_priv;
+ unsigned long flags;
+ /* dbg("keyspan_close called"); */
s_priv = (struct keyspan_serial_private *)(serial->private);
-
- /* Stop reading/writing urbs */
- for (i = 0; i < 4; i++) {
- if (s_priv->in_urbs[i]) {
- usb_unlink_urb(s_priv->in_urbs[i]);
- }
+ p_priv = (struct keyspan_port_private *)(port->private);
- }
- for (i = 0; i < 3; i++) {
- if (s_priv->out_urbs[i]) {
- usb_unlink_urb(s_priv->out_urbs[i]);
- }
+ spin_lock_irqsave (&port->port_lock, flags);
+ if (--port->open_count <= 0) {
+ if (port->active) {
+ /* Stop reading/writing urbs */
+ stop_urb(p_priv->inack_urb);
+ stop_urb(p_priv->outcont_urb);
+ for (i = 0; i < 2; i++) {
+ stop_urb(p_priv->in_urbs[i]);
+ stop_urb(p_priv->out_urbs[i]);
+ }
+ /* Now done in shutdown
+ if (atomic_dec_return(&s_priv->active_count) <= 0) {
+ stop_urb(s_priv->instat_urb);
+ stop_urb(s_priv->glocont_urb);
+ } */
+ }
+ port->active = 0;
+ port->open_count = 0;
+ port->tty = 0;
}
- port->active = 0;
- dbg("keyspan_close called\n");
+ spin_unlock_irqrestore (&port->port_lock, flags);
+
+ MOD_DEC_USE_COUNT;
}
@@ -372,47 +908,59 @@
const struct ezusb_hex_record *record;
char *fw_name;
- dbg("Keyspan startup version %04x product %04x\n", serial->dev->descriptor.bcdDevice,
- serial->dev->descriptor.idProduct);
+ dbg("Keyspan startup version %04x product %04x",
+ serial->dev->descriptor.bcdDevice,
+ serial->dev->descriptor.idProduct);
if ((serial->dev->descriptor.bcdDevice & 0x8000) != 0x8000) {
- dbg("Firmware already loaded. Quitting.\n");
+ dbg("Firmware already loaded. Quitting.");
return(1);
}
/* Select firmware image on the basis of idProduct */
switch (serial->dev->descriptor.idProduct) {
- case 0x0101: record = &keyspan_usa28_firmware[0];
- fw_name = "USA28";
- break;
-
- case 0x0102: record = &keyspan_usa28x_firmware[0];
- fw_name = "USA28X";
- break;
-
- case 0x0103: record = &keyspan_usa19_firmware[0];
- fw_name = "USA19";
- break;
+ case 0x0101:
+ record = &keyspan_usa28_firmware[0];
+ fw_name = "USA28";
+ break;
+
+ case 0x0102:
+ record = &keyspan_usa28x_firmware[0];
+ fw_name = "USA28X";
+ break;
+
+ case 0x0103:
+ record = &keyspan_usa19_firmware[0];
+ fw_name = "USA19";
+ break;
- case 0x0105: record = &keyspan_usa18x_firmware[0];
- fw_name = "USA18X";
- break;
+ case 0x0105:
+ record = &keyspan_usa18x_firmware[0];
+ fw_name = "USA18X";
+ break;
- case 0x0106: record = &keyspan_usa19w_firmware[0];
- fw_name = "USA19W";
- break;
+ case 0x0106:
+ record = &keyspan_usa19w_firmware[0];
+ fw_name = "USA19W";
+ break;
- default: record = NULL;
- fw_name = "Unknown";
- break;
+ case 0x0109:
+ record = &keyspan_usa49w_firmware[0];
+ fw_name = "USA49W";
+ break;
+
+ default:
+ record = NULL;
+ fw_name = "Unknown";
+ break;
}
if (record == NULL) {
- err("Required keyspan firmware image (%s) unavailable.\n", fw_name);
+ err("Required keyspan firmware image (%s) unavailable.", fw_name);
return(1);
}
- dbg("Uploading Keyspan %s firmware.\n", fw_name);
+ dbg("Uploading Keyspan %s firmware.", fw_name);
/* download the firmware image */
response = ezusb_set_reset(serial, 1);
@@ -434,72 +982,165 @@
moment and the new device will bind to the real driver */
response = ezusb_set_reset(serial, 0);
- /* we don't want this device to have a driver assigned to it. */
+ /* we don't want this device to have a driver assigned to it. */
return (1);
}
- /* USA-19 uses three output endpoints and four input
- endpoints. First two output endpoints are for
- data (used in an alternating fashion), the third is
- output control. First two input endpoints are for
- data (again alternating), the third is the ACK
- endpoint, the fourth is input status. */
-static void keyspan_usa19_setup_urbs(struct usb_serial *serial)
+/* Helper functions used by keyspan_setup_urbs */
+static urb_t *keyspan_setup_urb(struct usb_serial *serial, int endpoint,
+ int dir, void *ctx, char *buf, int len,
+ void (*callback)(urb_t *))
+{
+ urb_t *urb;
+
+ if (endpoint == -1)
+ return NULL; /* endpoint not needed */
+
+ /* dbg (__FUNCTION__ " alloc for endpoint %d.", endpoint); */
+ urb = usb_alloc_urb(0); /* No ISO */
+ if (urb == NULL) {
+ dbg (__FUNCTION__ " alloc for endpoint %d failed.", endpoint);
+ return NULL;
+ }
+
+ /* Fill URB using supplied data. */
+ FILL_BULK_URB(urb, serial->dev,
+ usb_sndbulkpipe(serial->dev, endpoint) | dir,
+ buf, len, callback, ctx);
+
+ return urb;
+}
+
+struct callbacks {
+ void (*instat_callback)(urb_t *);
+ void (*glocont_callback)(urb_t *);
+ void (*indat_callback)(urb_t *);
+ void (*outdat_callback)(urb_t *);
+ void (*inack_callback)(urb_t *);
+ void (*outcont_callback)(urb_t *);
+} keyspan_callbacks[] = {
+ {
+ /* msg_usa26 callbacks */
+ instat_callback: usa26_instat_callback,
+ glocont_callback: usa26_glocont_callback,
+ indat_callback: usa26_indat_callback,
+ outdat_callback: usa2x_outdat_callback,
+ inack_callback: usa26_inack_callback,
+ outcont_callback: usa26_outcont_callback,
+ }, {
+ /* msg_usa28 callbacks */
+ instat_callback: usa28_instat_callback,
+ glocont_callback: usa28_glocont_callback,
+ indat_callback: usa28_indat_callback,
+ outdat_callback: usa2x_outdat_callback,
+ inack_callback: usa28_inack_callback,
+ outcont_callback: usa28_outcont_callback,
+ }, {
+ /* msg_usa49 callbacks */
+ instat_callback: usa49_instat_callback,
+ glocont_callback: usa49_glocont_callback,
+ indat_callback: usa49_indat_callback,
+ outdat_callback: usa2x_outdat_callback,
+ inack_callback: usa49_inack_callback,
+ outcont_callback: usa49_outcont_callback,
+ }
+};
+
+ /* Generic setup urbs function that uses
+ data in device_details */
+static void keyspan_setup_urbs(struct usb_serial *serial)
{
+ int i, j;
struct keyspan_serial_private *s_priv;
- int i;
+ const keyspan_device_details *d_details;
+ struct usb_serial_port *port;
+ struct keyspan_port_private *p_priv;
+ struct callbacks *cback;
+ int endp;
+
+ /* dbg (__FUNCTION__); */
s_priv = (struct keyspan_serial_private *)(serial->private);
+ d_details = s_priv->device_details;
- /* Output urbs first */
- dbg(__FUNCTION__ "Allocating output urbs.\n");
- for (i = 0; i < 3; i++) {
-
- s_priv->out_urbs[i] = usb_alloc_urb (0); /* No ISO */
- if (!s_priv->out_urbs[i]) {
- dbg (__FUNCTION__ "Alloc for %d out urb failed.\n", i);
- return;
- }
+ /* Setup values for the various callback routines */
+ cback = &keyspan_callbacks[d_details->msg_format];
- FILL_BULK_URB(s_priv->out_urbs[i], serial->dev,
- usb_sndbulkpipe(serial->dev, i + 1),
- &s_priv->out_buffer[i], sizeof(s_priv->out_buffer[i]),
- keyspan_write_bulk_callback,
- serial);
- }
+ /* Allocate and set up urbs for each one that is in use,
+ starting with instat endpoints */
+ s_priv->instat_urb = keyspan_setup_urb
+ (serial, d_details->instat_endpoint, USB_DIR_IN,
+ serial, s_priv->instat_buf, INSTAT_BUFLEN,
+ cback->instat_callback);
+
+ s_priv->glocont_urb = keyspan_setup_urb
+ (serial, d_details->glocont_endpoint, USB_DIR_OUT,
+ serial, s_priv->glocont_buf, GLOCONT_BUFLEN,
+ cback->glocont_callback);
- /* Now input urbs */
- dbg(__FUNCTION__ "Allocating input urbs.\n");
- for (i = 0; i < 4; i++) {
-
- s_priv->in_urbs[i] = usb_alloc_urb (0); /* No ISO */
- if (!s_priv->in_urbs[i]) {
- dbg (__FUNCTION__ "Alloc for %d in urb failed.\n", i);
- return;
+ /* Setup endpoints for each port specific thing */
+ for (i = 0; i < d_details->num_ports; i ++) {
+ port = &serial->port[i];
+ p_priv = (struct keyspan_port_private *)(port->private);
+
+ /* Do indat endpoints first, once for each flip */
+ endp = d_details->indat_endpoints[i];
+ for (j = 0; j <= d_details->indat_endp_flip; ++j, ++endp) {
+ p_priv->in_urbs[j] = keyspan_setup_urb
+ (serial, endp, USB_DIR_IN, port,
+ p_priv->in_buffer[j], 64,
+ cback->indat_callback);
}
+ for (; j < 2; ++j)
+ p_priv->in_urbs[j] = NULL;
+
+ /* outdat endpoints also have flip */
+ endp = d_details->outdat_endpoints[i];
+ for (j = 0; j <= d_details->outdat_endp_flip; ++j, ++endp) {
+ p_priv->out_urbs[j] = keyspan_setup_urb
+ (serial, endp, USB_DIR_OUT, port,
+ p_priv->out_buffer[j], 64,
+ cback->outdat_callback);
+ }
+ for (; j < 2; ++j)
+ p_priv->out_urbs[j] = NULL;
+
+ /* inack endpoint */
+ p_priv->inack_urb = keyspan_setup_urb
+ (serial, d_details->inack_endpoints[i], USB_DIR_IN,
+ port, p_priv->inack_buffer, 1, cback->inack_callback);
+
+ /* outcont endpoint */
+ p_priv->outcont_urb = keyspan_setup_urb
+ (serial, d_details->outcont_endpoints[i], USB_DIR_OUT,
+ port, p_priv->outcont_buffer, 64,
+ cback->outcont_callback);
+ }
- FILL_BULK_URB(s_priv->in_urbs[i], serial->dev,
- usb_rcvbulkpipe(serial->dev, i + 0x81),
- &s_priv->in_buffer[i], sizeof(s_priv->in_buffer[i]),
- keyspan_read_bulk_callback,
- serial);
- }
-
}
-static int keyspan_usa19_calc_baud(u32 baud_rate, u8 *rate_hi, u8 *rate_low)
+/* usa19 function doesn't require prescaler */
+static int keyspan_usa19_calc_baud(u32 baud_rate, u32 baudclk,
+ u8 *rate_hi, u8 *rate_low, u8 *prescaler)
{
- u32 b16, /* baud rate times 16 (actual rate used internally) */
+ u32 b16, /* baud rate times 16 (actual rate used internally) */
div, /* divisor */
cnt; /* inverse of divisor (programmed into 8051) */
+
/* prevent divide by zero... */
if( (b16 = (baud_rate * 16L)) == 0) {
return (KEYSPAN_INVALID_BAUD_RATE);
}
+ /* Any "standard" rate over 57k6 is marginal on the USA-19
+ as we run out of divisor resolution. */
+ if (baud_rate > 57600) {
+ return (KEYSPAN_INVALID_BAUD_RATE);
+ }
+
/* calculate the divisor and the counter (its inverse) */
- if( (div = (USA19_BAUDCLK / b16)) == 0) {
+ if( (div = (baudclk / b16)) == 0) {
return (KEYSPAN_INVALID_BAUD_RATE);
}
else {
@@ -510,216 +1151,520 @@
return (KEYSPAN_INVALID_BAUD_RATE);
}
- /* return the counter values */
- *rate_low = (u8) (cnt & 0xff);
- *rate_hi = (u8) ((cnt >> 8) & 0xff);
+ /* return the counter values if non-null */
+ if (rate_low) {
+ *rate_low = (u8) (cnt & 0xff);
+ }
+ if (rate_hi) {
+ *rate_hi = (u8) ((cnt >> 8) & 0xff);
+ }
+ if (rate_low && rate_hi) {
+ dbg (__FUNCTION__ " %d %02x %02x.", baud_rate, *rate_hi, *rate_low);
+ }
- dbg(__FUNCTION__ " Baud rate of %d is %02x %02x.\n", baud_rate, *rate_hi, *rate_low);
+ return (KEYSPAN_BAUD_RATE_OK);
+}
+
+static int keyspan_usa19w_calc_baud(u32 baud_rate, u32 baudclk,
+ u8 *rate_hi, u8 *rate_low, u8 *prescaler)
+{
+ u32 b16, /* baud rate times 16 (actual rate used internally) */
+ clk, /* clock with 13/8 prescaler */
+ div, /* divisor using 13/8 prescaler */
+ res, /* resulting baud rate using 13/8 prescaler */
+ diff, /* error using 13/8 prescaler */
+ smallest_diff;
+ u8 best_prescaler;
+ int i;
+
+ /* dbg (__FUNCTION__ " %d.", baud_rate); */
+
+ /* prevent divide by zero */
+ if( (b16 = baud_rate * 16L) == 0) {
+ return (KEYSPAN_INVALID_BAUD_RATE);
+ }
+ /* Calculate prescaler by trying them all and looking
+ for best fit */
+
+ /* start with largest possible difference */
+ smallest_diff = 0xffffffff;
+
+ /* 0 is an invalid prescaler, used as a flag */
+ best_prescaler = 0;
+
+ for(i = 8; i <= 0xff; ++i)
+ {
+ clk = (baudclk * 8) / (u32) i;
+
+ if( (div = clk / b16) == 0) {
+ continue;
+ }
+
+ res = clk / div;
+ diff= (res > b16) ? (res-b16) : (b16-res);
+
+ if(diff < smallest_diff)
+ {
+ best_prescaler = i;
+ smallest_diff = diff;
+ }
+ }
+
+ if(best_prescaler == 0) {
+ return (KEYSPAN_INVALID_BAUD_RATE);
+ }
+
+ clk = (baudclk * 8) / (u32) best_prescaler;
+ div = clk / b16;
+
+ /* return the divisor and prescaler if non-null */
+ if (rate_low) {
+ *rate_low = (u8) (div & 0xff);
+ }
+ if (rate_hi) {
+ *rate_hi = (u8) ((div >> 8) & 0xff);
+ }
+ if (prescaler) {
+ *prescaler = best_prescaler;
+ /* dbg(__FUNCTION__ " %d %d", *prescaler, div); */
+ }
return (KEYSPAN_BAUD_RATE_OK);
}
-static int keyspan_usa19_send_setup(struct usb_serial *serial, struct usb_serial_port *port)
+static int keyspan_usa26_send_setup(struct usb_serial *serial,
+ struct usb_serial_port *port)
{
- struct portControlMessage msg;
- struct keyspan_serial_private *s_priv;
- struct keyspan_port_private *p_priv;
+ struct keyspan_usa26_portControlMessage msg;
+ struct keyspan_serial_private *s_priv;
+ struct keyspan_port_private *p_priv;
+ const keyspan_device_details *d_details;
+ int outcont_urb;
+ urb_t *this_urb;
+ int err;
+
+ /* dbg (__FUNCTION__); */
s_priv = (struct keyspan_serial_private *)(serial->private);
p_priv = (struct keyspan_port_private *)(port->private);
+ d_details = s_priv->device_details;
+
+ outcont_urb = d_details->outcont_endpoints[port->number];
+ this_urb = p_priv->outcont_urb;
+
+ /* Make sure we have an urb then send the message */
+ if (this_urb == NULL) {
+ dbg(__FUNCTION__ " oops no urb.");
+ return -1;
+ }
+
+ p_priv->resend_cont = 1;
+ if (this_urb->status == -EINPROGRESS) {
+ /* dbg (__FUNCTION__ " already writing"); */
+ return(-1);
+ }
+
+ memset(&msg, 0, sizeof (struct keyspan_usa26_portControlMessage));
+
+ /* Only set baud rate if it's changed */
+ if (p_priv->old_baud != p_priv->baud) {
+ p_priv->old_baud = p_priv->baud;
+ msg.setClocking = 0xff;
+ if (d_details->calculate_baud_rate
+ (p_priv->baud, d_details->baudclk, &msg.baudHi,
+ &msg.baudLo, &msg.prescaler) == KEYSPAN_INVALID_BAUD_RATE ) {
+ dbg(__FUNCTION__ "Invalid baud rate %d requested, using 9600.",
+ p_priv->baud);
+ msg.baudLo = 0;
+ msg.baudHi = 125; /* Values for 9600 baud */
+ msg.prescaler = 10;
+ }
+ msg.setPrescaler = 0xff;
+ }
+
+ msg.lcr = USA_DATABITS_8 | STOPBITS_5678_1;
+ if (p_priv->cflag & PARENB) {
+ /* note USA_PARITY_NONE == 0 */
+ msg.lcr |= (p_priv->cflag & PARODD)?
+ USA_PARITY_ODD: USA_PARITY_EVEN;
+ }
+ msg.setLcr = 0xff;
- //memset(msg, 0, sizeof (struct portControlMessage));
+ msg.ctsFlowControl = (p_priv->flow_control == flow_cts);
+ msg.xonFlowControl = 0;
+ msg.setFlowControl = 0xff;
+
+ msg.forwardingLength = 1;
+ msg.xonChar = 17;
+ msg.xoffChar = 19;
+
+ msg._txOn = 1;
+ msg._txOff = 0;
+ msg.txFlush = 0;
+ msg.txBreak = 0;
+ msg.rxOn = 1;
+ msg.rxOff = 0;
+ msg.rxFlush = 0;
+ msg.rxForward = 0;
+ /*msg.returnStatus = 1;
+ msg.resetDataToggle = 0xff;*/
+
+ /* Do handshaking outputs */
+ msg.setTxTriState_setRts = 0xff;
+ msg.txTriState_rts = p_priv->rts_state;
+
+ msg.setHskoa_setDtr = 0xff;
+ msg.hskoa_dtr = p_priv->dtr_state;
+ p_priv->resend_cont = 0;
+ memcpy (this_urb->transfer_buffer, &msg, sizeof(msg));
+
+ /* send the data out the device on control endpoint */
+ this_urb->transfer_buffer_length = sizeof(msg);
+
+ this_urb->dev = serial->dev;
+ if ((err = usb_submit_urb(this_urb)) != 0) {
+ dbg(__FUNCTION__ " usb_submit_urb(setup) failed (%d)", err);
+ }
+#if 0
+ else {
+ dbg(__FUNCTION__ " usb_submit_urb(%d) OK %d bytes (end %d)",
+ outcont_urb, this_urb->transfer_buffer_length,
+ usb_pipeendpoint(this_urb->pipe));
+ }
+#endif
+
+ return (0);
+}
+
+static int keyspan_usa28_send_setup(struct usb_serial *serial,
+ struct usb_serial_port *port)
+{
+ struct keyspan_usa28_portControlMessage msg;
+ struct keyspan_serial_private *s_priv;
+ struct keyspan_port_private *p_priv;
+ const keyspan_device_details *d_details;
+ urb_t *this_urb;
+ int err;
+
+ s_priv = (struct keyspan_serial_private *)(serial->private);
+ p_priv = (struct keyspan_port_private *)(port->private);
+ d_details = s_priv->device_details;
+
+ /* only do something if we have a bulk out endpoint */
+ if ((this_urb = p_priv->outcont_urb) == NULL) {
+ dbg(__FUNCTION__ " oops no urb.");
+ return -1;
+ }
+
+ p_priv->resend_cont = 1;
+ if (this_urb->status == -EINPROGRESS) {
+ dbg (__FUNCTION__ " already writing");
+ return(-1);
+ }
+
+ memset(&msg, 0, sizeof (struct keyspan_usa28_portControlMessage));
+
msg.setBaudRate = 1;
- if (keyspan_usa19_calc_baud(9600, &msg.baudHi, &msg.baudLo) ==
- KEYSPAN_INVALID_BAUD_RATE ) {
- dbg(__FUNCTION__ "Invalid baud rate requested %d.\n", 9600);
+ if (keyspan_usa19_calc_baud(p_priv->baud, d_details->baudclk,
+ &msg.baudHi, &msg.baudLo, NULL) == KEYSPAN_INVALID_BAUD_RATE ) {
+ dbg(__FUNCTION__ "Invalid baud rate requested %d.", 9600);
msg.baudLo = 0xff;
msg.baudHi = 0xb2; /* Values for 9600 baud */
}
- /* If parity is enabled, we must calculate it ourselves. */
- if (p_priv->parity) {
- msg.parity = 1;
+ /* If parity is enabled, we must calculate it ourselves. */
+ msg.parity = 0; /* XXX for now */
+
+ msg.ctsFlowControl = (p_priv->flow_control == flow_cts);
+ msg.xonFlowControl = 0;
+
+ /* Do handshaking outputs, DTR is inverted relative to RTS */
+ msg.rts = p_priv->rts_state;
+ msg.dtr = p_priv->dtr_state;
+
+ msg.forwardingLength = 1;
+ msg.forwardMs = 10;
+ msg.breakThreshold = 45;
+ msg.xonChar = 17;
+ msg.xoffChar = 19;
+
+ msg._txOn = 1;
+ msg._txOff = 0;
+ msg.txFlush = 0;
+ msg.txForceXoff = 0;
+ msg.txBreak = 0;
+ msg.rxOn = 1;
+ msg.rxOff = 0;
+ msg.rxFlush = 0;
+ msg.rxForward = 0;
+ /*msg.returnStatus = 1;
+ msg.resetDataToggle = 0xff;*/
+
+ p_priv->resend_cont = 0;
+ memcpy (this_urb->transfer_buffer, &msg, sizeof(msg));
+
+ /* send the data out the device on control endpoint */
+ this_urb->transfer_buffer_length = sizeof(msg);
+
+ this_urb->dev = serial->dev;
+ if ((err = usb_submit_urb(this_urb)) != 0) {
+ dbg(__FUNCTION__ " usb_submit_urb(setup) failed");
}
+#if 0
else {
- msg.parity = 0;
+ dbg(__FUNCTION__ " usb_submit_urb(setup) OK %d bytes",
+ this_urb->transfer_buffer_length);
+ }
+#endif
+
+ return (0);
+}
+
+static int keyspan_usa49_send_setup(struct usb_serial *serial,
+ struct usb_serial_port *port)
+{
+ struct keyspan_usa49_portControlMessage msg;
+ struct keyspan_serial_private *s_priv;
+ struct keyspan_port_private *p_priv;
+ const keyspan_device_details *d_details;
+ int glocont_urb;
+ urb_t *this_urb;
+ int err;
+
+ /* dbg (__FUNCTION__); */
+
+ s_priv = (struct keyspan_serial_private *)(serial->private);
+ p_priv = (struct keyspan_port_private *)(port->private);
+ d_details = s_priv->device_details;
+
+ glocont_urb = d_details->glocont_endpoint;
+ this_urb = s_priv->glocont_urb;
+
+ /* dbg(__FUNCTION__ " port %d\n", port->number); */
+
+ /* Make sure we have an urb then send the message */
+ if (this_urb == NULL) {
+ dbg(__FUNCTION__ " oops no urb for port %d.", port->number);
+ return -1;
+ }
+
+ p_priv->resend_cont = 1;
+ if (this_urb->status == -EINPROGRESS) {
+ /* dbg (__FUNCTION__ " already writing"); */
+ return(-1);
+ }
+
+ memset(&msg, 0, sizeof (struct keyspan_usa49_portControlMessage));
+
+ msg.portNumber = port->number;
+
+ /* Only set baud rate if it's changed */
+ if (p_priv->old_baud != p_priv->baud) {
+ p_priv->old_baud = p_priv->baud;
+ msg.setClocking = 0xff;
+ if (d_details->calculate_baud_rate
+ (p_priv->baud, d_details->baudclk, &msg.baudHi,
+ &msg.baudLo, &msg.prescaler) == KEYSPAN_INVALID_BAUD_RATE ) {
+ dbg(__FUNCTION__ "Invalid baud rate %d requested, using 9600.",
+ p_priv->baud);
+ msg.baudLo = 0;
+ msg.baudHi = 125; /* Values for 9600 baud */
+ msg.prescaler = 10;
+ }
+ //msg.setPrescaler = 0xff;
}
- msg.ctsFlowControl = 0;
+ msg.lcr = USA_DATABITS_8 | STOPBITS_5678_1;
+ if (p_priv->cflag & PARENB) {
+ /* note USA_PARITY_NONE == 0 */
+ msg.lcr |= (p_priv->cflag & PARODD)?
+ USA_PARITY_ODD: USA_PARITY_EVEN;
+ }
+ msg.setLcr = 0xff;
+
+ msg.ctsFlowControl = (p_priv->flow_control == flow_cts);
msg.xonFlowControl = 0;
- msg.rts = 1;
- msg.dtr = 0;
+ msg.setFlowControl = 0xff;
msg.forwardingLength = 1;
- msg.forwardMs = 10;
- msg.breakThreshold = 45;
msg.xonChar = 17;
msg.xoffChar = 19;
msg._txOn = 1;
msg._txOff = 0;
msg.txFlush = 0;
- msg.txForceXoff = 0;
msg.txBreak = 0;
msg.rxOn = 1;
msg.rxOff = 0;
msg.rxFlush = 0;
msg.rxForward = 0;
- msg.returnStatus = 1;
- msg.resetDataToggle = 1;
+ msg.enablePort = 0xff;
+ msg.disablePort = 0;
+
+ /* Do handshaking outputs */
+ msg.setRts = 0xff;
+ msg.rts = p_priv->rts_state;
+ msg.setDtr = 0xff;
+ msg.dtr = p_priv->dtr_state;
- /* only do something if we have a bulk out endpoint */
- if (s_priv->out_urbs[2]) {
- if (s_priv->out_urbs[2]->status == -EINPROGRESS) {
- dbg (__FUNCTION__ " already writing");
- return(-1);
- }
- memcpy (s_priv->out_urbs[2]->transfer_buffer, &msg, sizeof(msg));
+ p_priv->resend_cont = 0;
+ memcpy (this_urb->transfer_buffer, &msg, sizeof(msg));
- /* send the data out the device on control endpoint */
- s_priv->out_urbs[2]->transfer_buffer_length = sizeof(msg);
+ /* send the data out the device on control endpoint */
+ this_urb->transfer_buffer_length = sizeof(msg);
- if (usb_submit_urb(s_priv->out_urbs[2])) {
- dbg(__FUNCTION__ " usb_submit_urb(setup) failed\n");
- }
- else {
- dbg(__FUNCTION__ " usb_submit_urb(setup) OK %d bytes\n", s_priv->out_urbs[2]->transfer_buffer_length);
- }
-
+ this_urb->dev = serial->dev;
+ if ((err = usb_submit_urb(this_urb)) != 0) {
+ dbg(__FUNCTION__ " usb_submit_urb(setup) failed (%d)", err);
}
+#if 0
+ else {
+ dbg(__FUNCTION__ " usb_submit_urb(%d) OK %d bytes (end %d)",
+ outcont_urb, this_urb->transfer_buffer_length,
+ usb_pipeendpoint(this_urb->pipe));
+ }
+#endif
+
return (0);
}
+static void keyspan_send_setup(struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
+ struct keyspan_serial_private *s_priv;
+ const keyspan_device_details *d_details;
+
+ s_priv = (struct keyspan_serial_private *)(serial->private);
+ d_details = s_priv->device_details;
- /* Gets called by the "real" driver (ie once firmware is loaded
- and renumeration has taken place. */
+ switch (d_details->msg_format) {
+ case msg_usa26:
+ keyspan_usa26_send_setup(serial, port);
+ break;
+ case msg_usa28:
+ keyspan_usa28_send_setup(serial, port);
+ break;
+ case msg_usa49:
+ keyspan_usa49_send_setup(serial, port);
+ break;
+ }
+}
+
+/* Gets called by the "real" driver (ie once firmware is loaded
+ and renumeration has taken place. */
static int keyspan_startup (struct usb_serial *serial)
{
- int i;
- struct usb_serial_port *port;
+ int i, err;
+ struct usb_serial_port *port;
+ struct keyspan_serial_private *s_priv;
+ struct keyspan_port_private *p_priv;
+ const keyspan_device_details *d_details;
+
+ /* dbg("keyspan_startup called."); */
- dbg("keyspan_startup called.\n");
+ for (i = 0; (d_details = keyspan_devices[i]) != NULL; ++i)
+ if (d_details->product_id == serial->dev->descriptor.idProduct)
+ break;
+ if (d_details == NULL) {
+ printk(KERN_ERR __FUNCTION__ ": unknown product id %x\n",
+ serial->dev->descriptor.idProduct);
+ return 1;
+ }
- /* Setup private data for serial driver */
- serial->private = kmalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL);
+ /* Setup private data for serial driver */
+ serial->private = kmalloc(sizeof(struct keyspan_serial_private),
+ GFP_KERNEL);
if (!serial->private) {
- dbg(__FUNCTION__ "kmalloc for keyspan_serial_private failed!.\n");
+ dbg(__FUNCTION__ "kmalloc for keyspan_serial_private failed.");
return (1);
}
memset(serial->private, 0, sizeof(struct keyspan_serial_private));
+
+ s_priv = (struct keyspan_serial_private *)(serial->private);
+ s_priv->device_details = d_details;
- init_waitqueue_head(&out_wait);
-
- /* Now setup per port private data */
+ /* Now setup per port private data */
for (i = 0; i < serial->num_ports; i++) {
port = &serial->port[i];
- port->private = kmalloc(sizeof(struct keyspan_port_private), GFP_KERNEL);
+ port->private = kmalloc(sizeof(struct keyspan_port_private),
+ GFP_KERNEL);
if (!port->private) {
- dbg(__FUNCTION__ "kmalloc for keyspan_port_private (%d) failed!.\n", i);
+ dbg(__FUNCTION__ "kmalloc for keyspan_port_private (%d) failed!.", i);
return (1);
}
memset(port->private, 0, sizeof(struct keyspan_port_private));
+ p_priv = (struct keyspan_port_private *)(port->private);
+ p_priv->device_details = d_details;
}
-
+ keyspan_setup_urbs(serial);
- switch (serial->dev->descriptor.idProduct) {
- case 0x0107: keyspan_usa19_setup_urbs(serial);
- //keyspan_send_usa19_setup(serial);
- break;
-
- default: break;
+ s_priv->instat_urb->dev = serial->dev;
+ if ((err = usb_submit_urb(s_priv->instat_urb)) != 0) {
+ dbg(__FUNCTION__ " submit instat urb failed %d", err);
}
-
return (0);
}
static void keyspan_shutdown (struct usb_serial *serial)
{
- int i;
+ int i, j;
struct usb_serial_port *port;
struct keyspan_serial_private *s_priv;
+ struct keyspan_port_private *p_priv;
- dbg("keyspan_shutdown called freeing ");
+ /* dbg("keyspan_shutdown called"); */
s_priv = (struct keyspan_serial_private *)(serial->private);
- /* Stop reading/writing urbs */
- for (i = 0; i < 4; i++) {
- if (s_priv->in_urbs[i]) {
- usb_unlink_urb(s_priv->in_urbs[i]);
+ /* Stop reading/writing urbs */
+ stop_urb(s_priv->instat_urb);
+ stop_urb(s_priv->glocont_urb);
+ for (i = 0; i < serial->num_ports; ++i) {
+ port = &serial->port[i];
+ p_priv = (struct keyspan_port_private *)(port->private);
+ stop_urb(p_priv->inack_urb);
+ stop_urb(p_priv->outcont_urb);
+ for (j = 0; j < 2; j++) {
+ stop_urb(p_priv->in_urbs[j]);
+ stop_urb(p_priv->out_urbs[j]);
}
-
}
- for (i = 0; i < 3; i++) {
- if (s_priv->out_urbs[i]) {
- usb_unlink_urb(s_priv->out_urbs[i]);
- }
- }
- /* Now free them */
- for (i = 0; i < 7; i ++) {
- if (s_priv->in_urbs[i] != NULL) {
- dbg("in%d ", i);
- usb_free_urb(s_priv->in_urbs[i]);
- }
-
- if (s_priv->out_urbs[i] != NULL) {
- dbg("out%d ", i);
- usb_free_urb(s_priv->out_urbs[i]);
+ /* Now free them */
+ if (s_priv->instat_urb)
+ usb_free_urb(s_priv->instat_urb);
+ if (s_priv->glocont_urb)
+ usb_free_urb(s_priv->glocont_urb);
+ for (i = 0; i < serial->num_ports; ++i) {
+ port = &serial->port[i];
+ p_priv = (struct keyspan_port_private *)(port->private);
+ if (p_priv->inack_urb)
+ usb_free_urb(p_priv->inack_urb);
+ if (p_priv->outcont_urb)
+ usb_free_urb(p_priv->outcont_urb);
+ for (j = 0; j < 2; j++) {
+ if (p_priv->in_urbs[j])
+ usb_free_urb(p_priv->in_urbs[j]);
+ if (p_priv->out_urbs[j])
+ usb_free_urb(p_priv->out_urbs[j]);
}
}
- dbg("urbs.\n");
- dbg("Freeing serial->private.\n");
+ /* dbg("Freeing serial->private."); */
kfree(serial->private);
- dbg("Freeing port->private.\n");
- /* Now free per port private data */
+ /* dbg("Freeing port->private."); */
+ /* Now free per port private data */
for (i = 0; i < serial->num_ports; i++) {
port = &serial->port[i];
+ while (port->open_count > 0) {
+ --port->open_count;
+ MOD_DEC_USE_COUNT;
+ }
kfree(port->private);
}
-
}
-
-
-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_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);
- 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_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);
-}
-
-
-module_init(keyspan_init);
-module_exit(keyspan_exit);
-
-MODULE_AUTHOR("Hugh Blemings <hugh@linuxcare.com>");
-MODULE_DESCRIPTION("Keyspan USB to Serial Converter driver");
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)