From: Greg KH To: Alan Cox 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 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; }