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

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)