patch-2.4.23 linux-2.4.23/drivers/usb/serial/keyspan.c
Next file: linux-2.4.23/drivers/usb/serial/keyspan.h
Previous file: linux-2.4.23/drivers/usb/serial/ipaq.c
Back to the patch index
Back to the overall index
- Lines: 631
- Date:
2003-11-28 10:26:20.000000000 -0800
- Orig file:
linux-2.4.22/drivers/usb/serial/keyspan.c
- Orig date:
2003-06-13 07:51:37.000000000 -0700
diff -urN linux-2.4.22/drivers/usb/serial/keyspan.c linux-2.4.23/drivers/usb/serial/keyspan.c
@@ -28,6 +28,9 @@
Change History
+ 2003sep04 LPM (Keyspan) add support for new single port product USA19HS.
+ Improve setup message handling for all devices.
+
2003Apr16 LPM (Keyspan) fix delayed control message resend for multi-port
'Open' case. Fix 'mpr' entries in keyspan.h (previously broken)
@@ -174,6 +177,7 @@
int baud;
int old_baud;
unsigned int cflag;
+ unsigned int old_cflag;
enum {flow_none, flow_cts, flow_xon} flow_control;
int rts_state; /* Handshaking pins (outputs) */
int dtr_state;
@@ -189,11 +193,12 @@
/* 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. */
+ make use of one of four message formats which are referred
+ to as USA-26, USA-28 and USA-49, USA-90 by Keyspan and within this driver. */
#include "keyspan_usa26msg.h"
#include "keyspan_usa28msg.h"
#include "keyspan_usa49msg.h"
+#include "keyspan_usa90msg.h"
/* Functions used by new usb-serial code. */
@@ -327,8 +332,8 @@
return -ENOIOCTLCMD;
}
- /* Write function is generic for the three protocols used
- with only a minor change for usa49 required */
+ /* Write function is similar for the four protocols used
+ with only a minor change for usa90 (usa19hs) required */
static int keyspan_write(struct usb_serial_port *port, int from_user,
const unsigned char *buf, int count)
{
@@ -337,18 +342,27 @@
int flip;
int left, todo;
struct urb *this_urb;
- int err;
+ int err, maxDataLen, dataOffset;
p_priv = (struct keyspan_port_private *)(port->private);
d_details = p_priv->device_details;
+ if (d_details->msg_format == msg_usa90) {
+ maxDataLen = 64;
+ dataOffset = 0;
+ }
+ else {
+ maxDataLen = 63;
+ dataOffset = 1;
+ }
+
dbg("%s - for port %d (%d chars [%x]), flip=%d",
__FUNCTION__, port->number, count, buf[0], p_priv->out_flip);
for (left = count; left > 0; left -= todo) {
todo = left;
- if (todo > 63)
- todo = 63;
+ if (todo > maxDataLen)
+ todo = maxDataLen;
flip = p_priv->out_flip;
@@ -371,20 +385,20 @@
break;
}
- /* First byte in buffer is "last flag" - unused so
+ /* First byte in buffer is "last flag" (except for usa19hx) - 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))
+ if (copy_from_user(this_urb->transfer_buffer + dataOffset, buf, todo))
return -EFAULT;
} else {
- memcpy (this_urb->transfer_buffer + 1, buf, todo);
+ memcpy (this_urb->transfer_buffer + dataOffset, buf, todo);
}
buf += todo;
/* send the data out the bulk port */
- this_urb->transfer_buffer_length = todo + 1;
+ this_urb->transfer_buffer_length = todo + dataOffset;
this_urb->transfer_flags &= ~USB_ASYNC_UNLINK;
this_urb->dev = port->serial->dev;
@@ -424,9 +438,12 @@
if (urb->actual_length) {
/* 0x80 bit is error flag */
if ((data[0] & 0x80) == 0) {
- /* no error on any byte */
+ /* no errors on individual bytes, only possible overrun err*/
+ if (data[0] & RXERROR_OVERRUN)
+ err = TTY_OVERRUN;
+ else err = 0;
for (i = 1; i < urb->actual_length ; ++i) {
- tty_insert_flip_char(tty, data[i], 0);
+ tty_insert_flip_char(tty, data[i], err);
}
} else {
/* some bytes had errors, every byte has status */
@@ -455,7 +472,7 @@
return;
}
- /* Outdat handling is common for usa26, usa28 and usa49 messages */
+ /* Outdat handling is common for all devices */
static void usa2x_outdat_callback(struct urb *urb)
{
struct usb_serial_port *port;
@@ -561,7 +578,7 @@
}
-static void usa28_indat_callback(struct urb *urb)
+static void usa28_indat_callback(struct urb *urb)
{
int i, err;
struct usb_serial_port *port;
@@ -848,28 +865,171 @@
}
+static void usa90_indat_callback(struct urb *urb)
+{
+ int i, err;
+ int endpoint;
+ struct usb_serial_port *port;
+ struct keyspan_port_private *p_priv;
+ struct tty_struct *tty;
+ unsigned char *data = urb->transfer_buffer;
+
+ dbg ("%s", __FUNCTION__);
+
+ endpoint = usb_pipeendpoint(urb->pipe);
+
+
+ if (urb->status) {
+ dbg("%s - nonzero status: %x on endpoint %d.",
+ __FUNCTION__, urb->status, endpoint);
+ return;
+ }
+
+ port = (struct usb_serial_port *) urb->context;
+ p_priv = (struct keyspan_port_private *)(port->private);
+
+ tty = port->tty;
+ if (urb->actual_length) {
+
+ /* if current mode is DMA, looks like usa28 format
+ otherwise looks like usa26 data format */
+
+ if (p_priv->baud > 57600) {
+ for (i = 0; i < urb->actual_length ; ++i)
+ tty_insert_flip_char(tty, data[i], 0);
+ }
+ else {
+
+ /* 0x80 bit is error flag */
+ if ((data[0] & 0x80) == 0) {
+ /* no errors on individual bytes, only possible overrun err*/
+ if (data[0] & RXERROR_OVERRUN)
+ err = TTY_OVERRUN;
+ else err = 0;
+ for (i = 1; i < urb->actual_length ; ++i)
+ tty_insert_flip_char(tty, data[i], err);
+
+ }
+ else {
+ /* some bytes had errors, every byte has status */
+ dbg("%s - RX error!!!!", __FUNCTION__);
+ 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 (port->open_count)
+ if ((err = usb_submit_urb(urb)) != 0) {
+ dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
+ }
+ return;
+}
+
+
+static void usa90_instat_callback(struct urb *urb)
+{
+ unsigned char *data = urb->transfer_buffer;
+ struct keyspan_usa90_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("%s - nonzero status: %x", __FUNCTION__, urb->status);
+ return;
+ }
+ if (urb->actual_length < 14) {
+ dbg("%s - %d byte report??", __FUNCTION__, urb->actual_length);
+ goto exit;
+ }
+
+ msg = (struct keyspan_usa90_portStatusMessage *)data;
+
+ /* Now do something useful with the data */
+
+ port = &serial->port[0];
+ 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); */
+ }
+
+ /* Resubmit urb so we continue receiving */
+ urb->dev = serial->dev;
+ if ((err = usb_submit_urb(urb)) != 0) {
+ dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
+ }
+exit:
+ ;
+}
+
+static void usa90_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 ("%s - sending setup", __FUNCTION__);
+ keyspan_usa90_send_setup(port->serial, port, p_priv->resend_cont - 1);
+ }
+}
+
static int keyspan_write_room (struct usb_serial_port *port)
{
struct keyspan_port_private *p_priv;
const struct keyspan_device_details *d_details;
- int flip;
+ int flip,dataLen;
struct urb *this_urb;
dbg("%s", __FUNCTION__);
p_priv = (struct keyspan_port_private *)(port->private);
d_details = p_priv->device_details;
+ if (d_details->msg_format == msg_usa90)
+ dataLen = 64;
+ else dataLen = 63;
+
flip = p_priv->out_flip;
/* Check both endpoints to see if any are available. */
if ((this_urb = p_priv->out_urbs[flip]) != 0) {
if (this_urb->status != -EINPROGRESS)
- return (63);
+ return (dataLen);
flip = (flip + 1) & d_details->outdat_endp_flip;
if ((this_urb = p_priv->out_urbs[flip]) != 0)
if (this_urb->status != -EINPROGRESS)
- return (63);
+ return (dataLen);
}
return (0);
}
@@ -888,20 +1048,25 @@
struct usb_serial *serial = port->serial;
const struct keyspan_device_details *d_details;
int i, err;
+ int baud_rate, device_port;
struct urb *urb;
+ unsigned int cflag;
s_priv = (struct keyspan_serial_private *)(serial->private);
p_priv = (struct keyspan_port_private *)(port->private);
- d_details = s_priv->device_details;
+ d_details = p_priv->device_details;
dbg("%s - port%d.", __FUNCTION__, port->number);
- p_priv = (struct keyspan_port_private *)(port->private);
-
/* Set some sane defaults */
p_priv->rts_state = 1;
p_priv->dtr_state = 1;
+ p_priv->baud = 9600;
+ /* force baud and lcr to be set on open */
+ p_priv->old_baud = 0;
+ p_priv->old_cflag = 0;
+
p_priv->out_flip = 0;
p_priv->in_flip = 0;
@@ -910,7 +1075,10 @@
if ((urb = p_priv->in_urbs[i]) == NULL)
continue;
urb->dev = serial->dev;
- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0);
+
+ /* make sure endpoint data toggle is synchronized with the device */
+
+ usb_clear_halt(urb->dev, urb->pipe);
if ((err = usb_submit_urb(urb)) != 0) {
dbg("%s - submit urb %d failed (%d)", __FUNCTION__, i, err);
@@ -925,12 +1093,29 @@
/* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0); */
}
- // if the device is a USA49x, determine whether it is an W or WLC model
- // and set the baud clock accordingly
+ /* get the terminal config for the setup message now so we don't
+ * need to send 2 of them */
+
+ cflag = port->tty->termios->c_cflag;
+ device_port = port->number - port->serial->minor;
+
+ /* 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, leave as default */
+ if (baud_rate >= 0
+ && d_details->calculate_baud_rate(baud_rate, d_details->baudclk,
+ NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) {
+ 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, 1);
//mdelay(100);
- keyspan_set_termios(port, NULL);
+ //keyspan_set_termios(port, NULL);
return (0);
}
@@ -965,7 +1150,7 @@
keyspan_send_setup(port, 2);
/* pilot-xfer seems to work best with this delay */
mdelay(100);
- keyspan_set_termios(port, NULL);
+ // keyspan_set_termios(port, NULL);
}
/*while (p_priv->outcont_urb->status == -EINPROGRESS) {
@@ -1160,6 +1345,14 @@
.outdat_callback = usa2x_outdat_callback,
.inack_callback = usa49_inack_callback,
.outcont_callback = usa49_outcont_callback,
+ }, {
+ /* msg_usa90 callbacks */
+ .instat_callback = usa90_instat_callback,
+ .glocont_callback = usa28_glocont_callback,
+ .indat_callback = usa90_indat_callback,
+ .outdat_callback = usa2x_outdat_callback,
+ .inack_callback = usa28_inack_callback,
+ .outcont_callback = usa90_outcont_callback,
}
};
@@ -1283,6 +1476,41 @@
return (KEYSPAN_BAUD_RATE_OK);
}
+/* usa19hs function doesn't require prescaler */
+static int keyspan_usa19hs_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
+ u8 *rate_low, u8 *prescaler, int portnum)
+{
+ u32 b16, /* baud rate times 16 (actual rate used internally) */
+ div; /* divisor */
+
+ dbg ("%s - %d.", __FUNCTION__, baud_rate);
+
+ /* prevent divide by zero... */
+ if( (b16 = (baud_rate * 16L)) == 0)
+ return (KEYSPAN_INVALID_BAUD_RATE);
+
+
+
+ /* calculate the divisor */
+ if( (div = (baudclk / b16)) == 0)
+ return (KEYSPAN_INVALID_BAUD_RATE);
+
+ if(div > 0xffff)
+ return (KEYSPAN_INVALID_BAUD_RATE);
+
+ /* return the counter values if non-null */
+ if (rate_low)
+ *rate_low = (u8) (div & 0xff);
+
+ if (rate_hi)
+ *rate_hi = (u8) ((div >> 8) & 0xff);
+
+ if (rate_low && rate_hi)
+ dbg ("%s - %d %02x %02x.", __FUNCTION__, 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, int portnum)
{
@@ -1435,6 +1663,7 @@
p_priv->resend_cont = reset_port + 1;
if (this_urb->status == -EINPROGRESS) {
/* dbg ("%s - already writing", __FUNCTION__); */
+ mdelay(5);
return(-1);
}
@@ -1585,6 +1814,7 @@
p_priv->resend_cont = reset_port + 1;
if (this_urb->status == -EINPROGRESS) {
dbg ("%s already writing", __FUNCTION__);
+ mdelay(5);
return(-1);
}
@@ -1717,6 +1947,7 @@
p_priv->resend_cont = reset_port + 1;
if (this_urb->status == -EINPROGRESS) {
/* dbg ("%s - already writing", __FUNCTION__); */
+ mdelay(5);
return(-1);
}
@@ -1845,6 +2076,144 @@
return (0);
}
+static int keyspan_usa90_send_setup(struct usb_serial *serial,
+ struct usb_serial_port *port,
+ int reset_port)
+{
+ struct keyspan_usa90_portControlMessage msg;
+ struct keyspan_serial_private *s_priv;
+ struct keyspan_port_private *p_priv;
+ const struct keyspan_device_details *d_details;
+ struct urb *this_urb;
+ int err;
+ u8 prescaler;
+
+ dbg ("%s", __FUNCTION__);
+
+ 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("%s - oops no urb.", __FUNCTION__);
+ return -1;
+ }
+
+ /* Save reset port val for resend.
+ Don't overwrite resend for open/close condition. */
+ if ((reset_port + 1) > p_priv->resend_cont)
+ p_priv->resend_cont = reset_port + 1;
+ if (this_urb->status == -EINPROGRESS) {
+ dbg ("%s already writing", __FUNCTION__);
+ mdelay(5);
+ return(-1);
+ }
+
+ memset(&msg, 0, sizeof (struct keyspan_usa90_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 = 0x01;
+ if (d_details->calculate_baud_rate
+ (p_priv->baud, d_details->baudclk, &msg.baudHi,
+ &msg.baudLo, &prescaler, 0) == KEYSPAN_INVALID_BAUD_RATE ) {
+ dbg("%s - Invalid baud rate %d requested, using 9600.", __FUNCTION__,
+ p_priv->baud);
+ p_priv->baud = 9600;
+ d_details->calculate_baud_rate (p_priv->baud, d_details->baudclk,
+ &msg.baudHi, &msg.baudLo, &prescaler, 0);
+ }
+ msg.setRxMode = 1;
+ msg.setTxMode = 1;
+ }
+
+ /* modes must always be correctly specified */
+ if (p_priv->baud > 57600)
+ {
+ msg.rxMode = RXMODE_DMA;
+ msg.txMode = TXMODE_DMA;
+ }
+ else
+ {
+ msg.rxMode = RXMODE_BYHAND;
+ msg.txMode = TXMODE_BYHAND;
+ }
+
+ msg.lcr = (p_priv->cflag & CSTOPB)? STOPBITS_678_2: STOPBITS_5678_1;
+ switch (p_priv->cflag & CSIZE) {
+ case CS5:
+ msg.lcr |= USA_DATABITS_5;
+ break;
+ case CS6:
+ msg.lcr |= USA_DATABITS_6;
+ break;
+ case CS7:
+ msg.lcr |= USA_DATABITS_7;
+ break;
+ case CS8:
+ msg.lcr |= USA_DATABITS_8;
+ break;
+ }
+ if (p_priv->cflag & PARENB) {
+ /* note USA_PARITY_NONE == 0 */
+ msg.lcr |= (p_priv->cflag & PARODD)?
+ USA_PARITY_ODD: USA_PARITY_EVEN;
+ }
+ if (p_priv->old_cflag != p_priv->cflag) {
+ p_priv->old_cflag = p_priv->cflag;
+ msg.setLcr = 0x01;
+ }
+
+ if (p_priv->flow_control == flow_cts)
+ msg.txFlowControl = TXFLOW_CTS;
+ msg.setTxFlowControl = 0x01;
+ msg.setRxFlowControl = 0x01;
+
+ msg.rxForwardingLength = 16;
+ msg.rxForwardingTimeout = 16;
+ msg.txAckSetting = 0;
+ msg.xonChar = 17;
+ msg.xoffChar = 19;
+
+ /* Opening port */
+ if (reset_port == 1) {
+ msg.portEnabled = 1;
+ msg.rxFlush = 1;
+ msg.txBreak = (p_priv->break_on);
+ }
+ /* Closing port */
+ else if (reset_port == 2) {
+ msg.portEnabled = 0;
+ }
+ /* Sending intermediate configs */
+ else {
+ if (port->open_count)
+ msg.portEnabled = 1;
+ msg.txBreak = (p_priv->break_on);
+ }
+
+ /* Do handshaking outputs */
+ msg.setRts = 0x01;
+ msg.rts = p_priv->rts_state;
+
+ msg.setDtr = 0x01;
+ msg.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("%s - usb_submit_urb(setup) failed (%d)", __FUNCTION__, err);
+ }
+ return (0);
+}
+
static void keyspan_send_setup(struct usb_serial_port *port, int reset_port)
{
struct usb_serial *serial = port->serial;
@@ -1866,9 +2235,13 @@
case msg_usa49:
keyspan_usa49_send_setup(serial, port, reset_port);
break;
+ case msg_usa90:
+ keyspan_usa90_send_setup(serial, port, reset_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)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)