From: Greg KH <greg@kroah.com>
To: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: linux-usb-devel@lists.sourceforge.net
Subject: [PATCH 07 of 15] USB Serial Edgeport driver updated

This patch updates the io_edgeport driver.


diff -Nru a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
--- a/drivers/usb/serial/io_edgeport.c	Thu Jan  3 21:41:53 2002
+++ b/drivers/usb/serial/io_edgeport.c	Thu Jan  3 21:41:53 2002
@@ -25,6 +25,17 @@
  *
  * Version history:
  * 
+ * 2.2 2001_11_14 greg kroah-hartman
+ *	- fixed bug in edge_close that kept the port from being used more
+ *	  than once.
+ *	- fixed memory leak on device removal.
+ *	- fixed potential double free of memory when command urb submitting
+ *	  failed.
+ *	- other small cleanups when the device is removed
+ *	
+ * 2.1 2001_07_09 greg kroah-hartman
+ *	- added support for TIOCMBIS and TIOCMBIC.
+ *
  *     (04/08/2001) gb
  *	- Identify version on module load.
  *
@@ -260,7 +271,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v2.0.0"
+#define DRIVER_VERSION "v2.2"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com> and David Iacovelli"
 #define DRIVER_DESC "Edgeport USB Serial Driver"
 
@@ -307,11 +318,6 @@
 };
 
 
-/* the info for all of the devices that this driver supports */
-int EdgeportDevices[] = EDGEPORT_DEVICE_IDS;
-#define NUM_EDGEPORT_DEVICES (sizeof(EdgeportDevices) / sizeof(int))
-
-
 /* Transmit Fifo 
  * This Transmit queue is an extension of the edgeport Rx buffer. 
  * The maximum amount of data buffered in both the edgeport 
@@ -430,7 +436,7 @@
 
 /* function prototypes for all URB callbacks */
 static void edge_interrupt_callback	(struct urb *urb);
-static void edge_bulk_in_callback		(struct urb *urb);
+static void edge_bulk_in_callback	(struct urb *urb);
 static void edge_bulk_out_data_callback	(struct urb *urb);
 static void edge_bulk_out_cmd_callback	(struct urb *urb);
 
@@ -475,7 +481,6 @@
 
 static void unicode_to_ascii		(char *string, short *unicode, int unicode_size);
 
-static int  get_string_desc		(struct usb_device *dev, int Id, struct usb_string_descriptor **pRetDesc);
 
 
 
@@ -485,17 +490,15 @@
 // ************************************************************************
 // ************************************************************************
 
-// These functions should be in firmware.c
-
 /************************************************************************
  *									*
- *	update_edgeport_E2PROM()	Compare current versions of		*
+ * update_edgeport_E2PROM()	Compare current versions of		*
  *				Boot ROM and Manufacture 		*
  *				Descriptors with versions		*
  *				embedded in this driver			*
  *									*
  ************************************************************************/
-void update_edgeport_E2PROM (struct edgeport_serial *edge_serial)
+static void update_edgeport_E2PROM (struct edgeport_serial *edge_serial)
 {
 	__u32 BootCurVer;
 	__u32 BootNewVer;
@@ -611,6 +614,7 @@
 }
 
 
+#if 0
 /************************************************************************
  *
  *  Get string descriptor from device
@@ -641,14 +645,8 @@
 	*pRetDesc = pStringDesc;
 	return 0;
 }
+#endif
 
-
-
-
-/************************************************************************
- *																		*
- *																		*
- ************************************************************************/
 static void get_product_info(struct edgeport_serial *edge_serial)
 {
 	struct edgeport_product_info *product_info = &edge_serial->product_info;
@@ -930,7 +928,7 @@
 	dbg(__FUNCTION__" - FREE URB %p (outstanding %d)", urb, CmdUrbs);
 
 
-	/* if this urb had a transfer buffer already (old transfer) free it */
+	/* clean up the transfer buffer */
 	if (urb->transfer_buffer != NULL) {
 		kfree(urb->transfer_buffer);
 	}
@@ -944,7 +942,7 @@
 	}
 
 	if (status) {
-		dbg(__FUNCTION__" - nonzero write bulk status received: %d", urb->status);
+		dbg(__FUNCTION__" - nonzero write bulk status received: %d", status);
 		return;
 	}
 
@@ -954,7 +952,6 @@
 	/* tell the tty driver that something has changed */
 	wake_up_interruptible(&tty->write_wait);
 
-
 	/* we have completed the command */
 	edge_port->commandPending = FALSE;
 	wake_up_interruptible(&edge_port->wait_command);
@@ -984,6 +981,9 @@
 	
 	dbg(__FUNCTION__ " - port %d", port->number);
 
+	if (edge_port == NULL)
+		return -ENODEV;
+
 	++port->open_count;
 	MOD_INC_USE_COUNT;
 	
@@ -999,6 +999,12 @@
 		   as the structures were not set up at that time.) */
 		serial = port->serial;
 		edge_serial = (struct edgeport_serial *)serial->private;
+		if (edge_serial == NULL) {
+			port->active = 0;
+			port->open_count = 0;
+			MOD_DEC_USE_COUNT;
+			return -ENODEV;
+		}
 		if (edge_serial->interrupt_in_buffer == NULL) {
 			struct usb_serial_port *port0 = &serial->port[0];
 			
@@ -1012,25 +1018,22 @@
 			edge_serial->bulk_out_endpoint = port0->bulk_out_endpointAddress;
 		
 			/* set up our interrupt urb */
-			/* Like to use FILL_INT_URB, but we don't know wMaxPacketSize or bInterval, something to change for 2.5... */
-			edge_serial->interrupt_read_urb->complete = edge_interrupt_callback;
-			edge_serial->interrupt_read_urb->context = edge_serial;
-			/* FILL_INT_URB(edge_serial->interrupt_read_urb, serial->dev, 
-				     usb_rcvintpipe (serial->dev, edge_serial->interrupt_in_endpoint),
-				     edge_serial->interrupt_in_buffer, edge_serial->interrupt_in_endpoint.wMaxPacketSize,
-				     edge_interrupt_callback, edge_serial, edge_serial->interrupt_in_endpoint.bInterval);
-			*/
+			FILL_INT_URB(edge_serial->interrupt_read_urb,
+				     serial->dev,
+				     usb_rcvintpipe(serial->dev,
+					            port0->interrupt_in_endpointAddress),
+				     port0->interrupt_in_buffer,
+				     edge_serial->interrupt_read_urb->transfer_buffer_length,
+				     edge_interrupt_callback, edge_serial,
+				     edge_serial->interrupt_read_urb->interval);
 			
 			/* set up our bulk in urb */
-			/* Like to use FILL_BULK_URB, but we don't know wMaxPacketSize or bInterval, something to change for 2.5... */
-			edge_serial->read_urb->complete = edge_bulk_in_callback;
-			edge_serial->read_urb->context = edge_serial;
-			/* FILL_BULK_URB(edge_serial->read_urb, serial->dev, 
-				      usb_rcvbulkpipe (serial->dev, edge_serial->bulk_in_endpoint),
-				      edge_serial->bulk_in_buffer, edge_serial->bulk_in_endpoint->wMaxPacketSize,
+			FILL_BULK_URB(edge_serial->read_urb, serial->dev,
+				      usb_rcvbulkpipe(serial->dev, port0->bulk_in_endpointAddress),
+				      port0->bulk_in_buffer,
+				      edge_serial->read_urb->transfer_buffer_length,
 				      edge_bulk_in_callback, edge_serial);
-			*/
-		
+
 			/* start interrupt read for this edgeport
 			 * this interrupt will continue as long as the edgeport is connected */
 			response = usb_submit_urb (edge_serial->interrupt_read_urb);
@@ -1234,58 +1237,62 @@
 	
 	edge_serial = (struct edgeport_serial *)serial->private;
 	edge_port = (struct edgeport_port *)port->private;
-
+	if ((edge_serial == NULL) || (edge_port == NULL))
+		return;
+	
 	--port->open_count;
 
 	if (port->open_count <= 0) {
-		// block until tx is empty
-		block_until_tx_empty(edge_port);
-	
-		edge_port->closePending = TRUE;
-	
-		/* flush and chase */
-		edge_port->chaseResponsePending = TRUE;
-	
-		dbg(__FUNCTION__" - Sending IOSP_CMD_CHASE_PORT");
-		status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
-		if (status == 0) {
-			// block until chase finished
-			block_until_chase_response(edge_port);
-		} else {
-			edge_port->chaseResponsePending = FALSE;
+		if (serial->dev) {
+			// block until tx is empty
+			block_until_tx_empty(edge_port);
+
+			edge_port->closePending = TRUE;
+
+			/* flush and chase */
+			edge_port->chaseResponsePending = TRUE;
+
+			dbg(__FUNCTION__" - Sending IOSP_CMD_CHASE_PORT");
+			status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
+			if (status == 0) {
+				// block until chase finished
+				block_until_chase_response(edge_port);
+			} else {
+				edge_port->chaseResponsePending = FALSE;
+			}
+
+			/* close the port */
+			dbg(__FUNCTION__" - Sending IOSP_CMD_CLOSE_PORT");
+			send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0);
+
+			//port->close = TRUE;
+			edge_port->closePending = FALSE;
+			edge_port->open = FALSE;
+			edge_port->openPending = FALSE;
+
+			if (edge_port->write_urb) {
+				usb_unlink_urb (edge_port->write_urb);
+			}
 		}
 	
-		/* close the port */
-		dbg(__FUNCTION__" - Sending IOSP_CMD_CLOSE_PORT");
-		send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0);
-	
-		//port->close = TRUE;
-		edge_port->closePending = FALSE;
-		edge_port->open = FALSE;
-		edge_port->openPending = FALSE;
-	
 		if (edge_port->write_urb) {
 			/* if this urb had a transfer buffer already (old transfer) free it */
 			if (edge_port->write_urb->transfer_buffer != NULL) {
 				kfree(edge_port->write_urb->transfer_buffer);
 			}
-	
-			usb_unlink_urb (edge_port->write_urb);
 			usb_free_urb   (edge_port->write_urb);
 		}
-	
 		if (edge_port->txfifo.fifo) {
 			kfree(edge_port->txfifo.fifo);
 		}
+		port->active = 0;
+		port->open_count = 0;
 	}
 
 	MOD_DEC_USE_COUNT;
 	dbg(__FUNCTION__" exited");
 }   
 
-
-
-
 /*****************************************************************************
  * SerialWrite
  *	this function is called by the tty driver when data should be written to
@@ -1304,6 +1311,9 @@
 
 	dbg(__FUNCTION__ " - port %d", port->number);
 
+	if (edge_port == NULL)
+		return -ENODEV;
+
 	// get a pointer to the Tx fifo
 	fifo = &edge_port->txfifo;
 
@@ -1331,7 +1341,8 @@
 
 	/* now copy our data */
 	if (from_user) {
-		copy_from_user(&fifo->fifo[fifo->head], data, firsthalf);
+		if (copy_from_user(&fifo->fifo[fifo->head], data, firsthalf))
+			return -EFAULT;
 	} else {
 		memcpy(&fifo->fifo[fifo->head], data, firsthalf);
 	}  
@@ -1350,7 +1361,8 @@
 	if (secondhalf) {
 		dbg (__FUNCTION__" - copy rest of data %d", secondhalf);
 		if (from_user) {
-			copy_from_user(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf);
+			if (copy_from_user(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf))
+				return -EFAULT;
 		} else {
 			memcpy(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf);
 		}
@@ -1391,7 +1403,6 @@
 	struct urb	*urb;
 	unsigned char	*buffer;
 	int		status;
-	unsigned long	flags;
 	int		count;
 	int		bytesleft;
 	int		firsthalf;
@@ -1399,13 +1410,9 @@
 
 	dbg(__FUNCTION__"(%d)", edge_port->port->number);
 
-	/* find our next free urb */	// ICK!!! FIXME!!!
-	save_flags(flags); cli();
-
 	if (edge_port->write_in_progress ||
 	    !edge_port->open             ||
 	    (fifo->count == 0)) {
-		restore_flags(flags);
 		dbg(__FUNCTION__"(%d) EXIT - fifo %d, PendingWrite = %d", edge_port->port->number, fifo->count, edge_port->write_in_progress);
 		return;
 	}
@@ -1418,14 +1425,12 @@
 	//	it's better to wait for more credits so we can do a larger
 	//	write.
 	if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits)) {
-		restore_flags(flags);
 		dbg(__FUNCTION__"(%d) Not enough credit - fifo %d TxCredit %d", edge_port->port->number, fifo->count, edge_port->txCredits );
 		return;
 	}
 
 	// lock this write
 	edge_port->write_in_progress = TRUE;
-	restore_flags(flags);
 
 	// get a pointer to the write_urb
 	urb = edge_port->write_urb;
@@ -1506,9 +1511,10 @@
 
 	dbg(__FUNCTION__);
 
-	if (edge_port->closePending == TRUE) {
+	if (edge_port == NULL)
+		return -ENODEV;
+	if (edge_port->closePending == TRUE)
 		return -ENODEV;
-	}
 
 	dbg(__FUNCTION__" - port %d", port->number);
 
@@ -1541,6 +1547,11 @@
 
 	dbg(__FUNCTION__);
 
+	if (edge_port == NULL)
+		return -ENODEV;
+	if (edge_port->closePending == TRUE)
+		return -ENODEV;
+
 	if (!edge_port->open) {
 		dbg (__FUNCTION__" - port not opened");
 		return -EINVAL;
@@ -1568,6 +1579,9 @@
 
 	dbg(__FUNCTION__" - port %d", port->number);
 
+	if (edge_port == NULL)
+		return;
+
 	if (!edge_port->open) {
 		dbg (__FUNCTION__" - port not opened");
 		return;
@@ -1610,6 +1624,9 @@
 
 	dbg(__FUNCTION__" - port %d", port->number);
 
+	if (edge_port == NULL)
+		return;
+
 	if (!edge_port->open) {
 		dbg (__FUNCTION__" - port not opened");
 		return;
@@ -1667,6 +1684,9 @@
 
 	dbg(__FUNCTION__" - port %d", port->number);
 
+	if (edge_port == NULL)
+		return;
+
 	if (!edge_port->open) {
 		dbg (__FUNCTION__" - port not opened");
 		return;
@@ -1718,7 +1738,7 @@
 	return -ENOIOCTLCMD;
 }
 
-static int set_modem_info(struct edgeport_port *edge_port, unsigned int *value)
+static int set_modem_info(struct edgeport_port *edge_port, unsigned int cmd, unsigned int *value)
 {
 	unsigned int mcr = edge_port->shadowMCR;
 	unsigned int arg;
@@ -1726,11 +1746,29 @@
 	if (copy_from_user(&arg, value, sizeof(int)))
 		return -EFAULT;
 
-	// turn off the RTS and DTR
-	mcr &=  ~(MCR_RTS | MCR_DTR);
+	switch (cmd) {
+		case TIOCMBIS:
+			if (arg & TIOCM_RTS)
+				mcr |= MCR_RTS;
+			if (arg & TIOCM_DTR)
+				mcr |= MCR_RTS;
+			break;
 
-	mcr |= ((arg & TIOCM_RTS) ? MCR_RTS : 0);
-	mcr |= ((arg & TIOCM_DTR) ? MCR_DTR : 0);
+		case TIOCMBIC:
+			if (arg & TIOCM_RTS)
+				mcr &= ~MCR_RTS;
+			if (arg & TIOCM_DTR)
+				mcr &= ~MCR_RTS;
+			break;
+
+		case TIOCMSET:
+			/* turn off the RTS and DTR and LOOPBACK 
+			 * and then only turn on what was asked to */
+			mcr &=  ~(MCR_RTS | MCR_DTR | MCR_LOOPBACK);
+			mcr |= ((arg & TIOCM_RTS) ? MCR_RTS : 0);
+			mcr |= ((arg & TIOCM_DTR) ? MCR_DTR : 0);
+			break;
+	}
 
 	edge_port->shadowMCR = mcr;
 
@@ -1802,7 +1840,6 @@
 	struct async_icount cnow;
 	struct async_icount cprev;
 	struct serial_icounter_struct icount;
-	unsigned long flags;
 
 
 	dbg(__FUNCTION__" - port %d, cmd = 0x%x", port->number, cmd);
@@ -1814,22 +1851,16 @@
 			return get_number_bytes_avail(edge_port, (unsigned int *) arg);
 			break;
 
-//		case TCGETS:		
-//			dbg(__FUNCTION__" (%d) TCGETS",  port->number);
-//			break;
-
-//		case TCSETS:		
-//			dbg(__FUNCTION__" (%d) TCSETS",  port->number);
-//			break;
-
 		case TIOCSERGETLSR:
 			dbg(__FUNCTION__" (%d) TIOCSERGETLSR",  port->number);
 			return get_lsr_info(edge_port, (unsigned int *) arg);
 			return 0;
 
-		case TIOCMSET:  
-			dbg(__FUNCTION__" (%d) TIOCMSET",  port->number);
-			return set_modem_info(edge_port, (unsigned int *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			dbg(__FUNCTION__" (%d) TIOCMSET/TIOCMBIC/TIOCMSET",  port->number);
+			return set_modem_info(edge_port, cmd, (unsigned int *) arg);
 
 		case TIOCMGET:  
 			dbg(__FUNCTION__" (%d) TIOCMGET",  port->number);
@@ -1845,17 +1876,13 @@
 
 		case TIOCMIWAIT:
 			dbg(__FUNCTION__" (%d) TIOCMIWAIT",  port->number);
-			save_flags(flags); cli();
 			cprev = edge_port->icount;
-			restore_flags(flags);
 			while (1) {
 				interruptible_sleep_on(&edge_port->delta_msr_wait);
 				/* see if a signal did it */
 				if (signal_pending(current))
 					return -ERESTARTSYS;
-				save_flags(flags); cli();
-				cnow = edge_port->icount; /* atomic copy */
-				restore_flags(flags);
+				cnow = edge_port->icount;
 				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
 				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
 					return -EIO; /* no change => error */
@@ -1871,9 +1898,7 @@
 			break;
 
 		case TIOCGICOUNT:
-			save_flags(flags); cli();
 			cnow = edge_port->icount;
-			restore_flags(flags);
 			icount.cts = cnow.cts;
 			icount.dsr = cnow.dsr;
 			icount.rng = cnow.rng;
@@ -2444,15 +2469,12 @@
 
 	/* Allocate our next urb */
 	urb = usb_alloc_urb (0);
+	if (!urb)
+		return -ENOMEM;
 
 	CmdUrbs++;
-
 	dbg(__FUNCTION__" - ALLOCATE URB %p (outstanding %d)", urb, CmdUrbs);
 
-	if (!urb) {
-		return -ENOMEM;
-	}
-
 	FILL_BULK_URB (urb, edge_serial->serial->dev, 
 		       usb_sndbulkpipe(edge_serial->serial->dev, edge_serial->bulk_out_endpoint),
 		       buffer, length, edge_bulk_out_cmd_callback, edge_port);
@@ -2461,17 +2483,11 @@
 	urb->transfer_flags |= USB_QUEUE_BULK;
 
 	edge_port->commandPending = TRUE;
-	urb->dev = edge_serial->serial->dev;
 	status = usb_submit_urb(urb);
 
 	if (status) {
 		/* something went wrong */
 		dbg(__FUNCTION__" - usb_submit_urb(write bulk) failed");
-
-		/* if this urb had a transfer buffer already (old transfer) free it */
-		if (urb->transfer_buffer != NULL) {
-			kfree(urb->transfer_buffer);
-		}
 		usb_unlink_urb (urb);
 		usb_free_urb   (urb);
 		return status;
@@ -2535,6 +2551,10 @@
 	MAKE_CMD_WRITE_REG( &currCmd, &cmdLen, number, LCR, edge_port->shadowLCR);
 
 	status = write_cmd_usb(edge_port, cmdBuffer, cmdLen );
+	if (status) {
+		/* something bad happened, let's free up the memory */
+		kfree (cmdBuffer);
+	}
 
 	return status;
 }
@@ -2610,6 +2630,10 @@
 	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, edge_port->port->number, regNum, regValue);
 
 	status = write_cmd_usb(edge_port, cmdBuffer, cmdLen);
+	if (status) {
+		/* something bad happened, let's free up the memory */
+		kfree (cmdBuffer);
+	}
 
 	return status;
 }
@@ -2694,10 +2718,8 @@
 		unsigned char stop_char  = STOP_CHAR(tty);
 		unsigned char start_char = START_CHAR(tty);
 
-		{
-			send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XON_CHAR, start_char);
-			send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char);
-		}
+		send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XON_CHAR, start_char);
+		send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char);
 
 		/* if we are implementing INBOUND XON/XOFF */
 		if (I_IXOFF(tty)) {
@@ -2949,10 +2971,11 @@
 	get_product_info(edge_serial);
 
 	/* set the number of ports from the manufacturing description */
-	// FIXME  should we override this???
-	//serial->num_ports = serial->product_info.NumPorts;
+	/* serial->num_ports = serial->product_info.NumPorts; */
 	if (edge_serial->product_info.NumPorts != serial->num_ports) {
-		warn(__FUNCTION__ " - Device Reported %d serial ports vs core thinking we have %d ports, email greg@kroah.com this info.", edge_serial->product_info.NumPorts, serial->num_ports);
+		warn(__FUNCTION__ " - Device Reported %d serial ports vs core "
+		     "thinking we have %d ports, email greg@kroah.com this info.",
+		     edge_serial->product_info.NumPorts, serial->num_ports);
 	}
 
 	dbg(__FUNCTION__ " - time 1 %ld", jiffies);
@@ -2992,10 +3015,9 @@
 
 
 /****************************************************************************
- * usb_edgeport_disconnect
+ * edge_shutdown
  *	This function is called whenever the device is removed from the usb bus.
  ****************************************************************************/
-//static void usb_edgeport_disconnect (struct usb_device *dev, void *ptr)
 static void edge_shutdown (struct usb_serial *serial)
 {
 	int i;
@@ -3007,11 +3029,11 @@
 		while (serial->port[i].open_count > 0) {
 			edge_close (&serial->port[i], NULL);
 		}
+		kfree (serial->port[i].private);
+		serial->port[i].private = NULL;
 	}
-
-	/* free up any memory that we allocated */
-	// FIXME
-	
+	kfree (serial->private);
+	serial->private = NULL;
 }
 
 
@@ -3037,7 +3059,7 @@
 	usb_serial_register (&edgeport_16dual_device);
 	usb_serial_register (&edgeport_compat_id_device);
 	usb_serial_register (&edgeport_8i_device);
-	info(DRIVER_VERSION ":" DRIVER_DESC);
+	info(DRIVER_DESC " " DRIVER_VERSION);
 	return 0;
 }
 

