diff options
Diffstat (limited to 'drivers/usb/serial/omninet.c')
-rw-r--r-- | drivers/usb/serial/omninet.c | 163 |
1 files changed, 107 insertions, 56 deletions
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 7a68a0072..1d1bd088c 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -10,6 +10,12 @@ * * Please report both successes and troubles to the author at omninet@kroah.com * + * (08/28/2000) gkh + * Added locks for SMP safeness. + * Fixed MOD_INC and MOD_DEC logic and the ability to open a port more + * than once. + * Fixed potential race in omninet_write_bulk_callback + * * (07/19/2000) gkh * Added module_init and module_exit functions to handle the fact that this * driver is a loadable module now. @@ -54,6 +60,7 @@ static void omninet_read_bulk_callback (struct urb *urb); static void omninet_write_bulk_callback (struct urb *urb); static int omninet_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); static int omninet_write_room (struct usb_serial_port *port); +static void omninet_shutdown (struct usb_serial *serial); /* All of the device info needed for the omni.net */ static __u16 zyxel_vendor_id = ZYXEL_VENDOR_ID; @@ -76,6 +83,7 @@ struct usb_serial_device_type zyxel_omninet_device = { write_room: omninet_write_room, read_bulk_callback: omninet_read_bulk_callback, write_bulk_callback: omninet_write_bulk_callback, + shutdown: omninet_shutdown, }; @@ -117,52 +125,87 @@ struct omninet_data static int omninet_open (struct usb_serial_port *port, struct file *filp) { - struct usb_serial *serial = port->serial; - struct usb_serial_port *wport = &serial->port[1]; - struct omninet_data *od; + struct usb_serial *serial; + struct usb_serial_port *wport; + struct omninet_data *od; + unsigned long flags; - dbg("omninet_open port %d", port->number); + if (port_paranoia_check (port, __FUNCTION__)) + return -ENODEV; - if (port->active) { - dbg ("device already open"); - return -EINVAL; - } - port->active = 1; + dbg(__FUNCTION__ " - port %d", port->number); - od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL ); + serial = get_usb_serial (port, __FUNCTION__); + if (!serial) + return -ENODEV; - if( !od ) - { - err("omninet_open: kmalloc(%d) failed.", sizeof(struct omninet_data)); - return -ENOMEM; - } + spin_lock_irqsave (&port->port_lock, flags); + + MOD_INC_USE_COUNT; + ++port->open_count; + + if (!port->active) { + port->active = 1; + + od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL ); + if( !od ) { + err(__FUNCTION__"- kmalloc(%d) failed.", sizeof(struct omninet_data)); + --port->open_count; + port->active = 0; + spin_unlock_irqrestore (&port->port_lock, flags); + MOD_DEC_USE_COUNT; + return -ENOMEM; + } - port->private = od; + port->private = od; + wport = &serial->port[1]; + wport->tty = port->tty; - /* Start reading from the device */ - if (usb_submit_urb(port->read_urb)) - dbg("usb_submit_urb(read bulk, %p) failed", port->read_urb); + /* Start reading from the device */ + if (usb_submit_urb(port->read_urb)) + dbg(__FUNCTION__" - read bulk (%p) failed", port->read_urb); + } - wport->tty = port->tty; + spin_unlock_irqrestore (&port->port_lock, flags); return (0); } static void omninet_close (struct usb_serial_port *port, struct file * filp) { - struct usb_serial *serial = port->serial; - struct usb_serial_port *wport = &serial->port[1]; - struct omninet_data *od = (struct omninet_data *) port->private; + struct usb_serial *serial; + struct usb_serial_port *wport; + struct omninet_data *od; + unsigned long flags; + + if (port_paranoia_check (port, __FUNCTION__)) + return; + + dbg(__FUNCTION__ " - port %d", port->number); - port->active = 0; + serial = get_usb_serial (port, __FUNCTION__); + if (!serial) + return; + + spin_lock_irqsave (&port->port_lock, flags); + --port->open_count; + MOD_DEC_USE_COUNT; - dbg("zyxel_close port %d", port->number); + if (port->open_count <= 0) { + od = (struct omninet_data *)port->private; + wport = &serial->port[1]; - usb_unlink_urb (wport->write_urb); - usb_unlink_urb (port->read_urb); + usb_unlink_urb (wport->write_urb); + usb_unlink_urb (port->read_urb); - if(od) kfree(od); + port->active = 0; + port->open_count = 0; + if (od) + kfree(od); + } + + spin_unlock_irqrestore (&port->port_lock, flags); } @@ -173,7 +216,7 @@ static void omninet_close (struct usb_serial_port *port, struct file * filp) static void omninet_read_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - struct usb_serial *serial = port->serial; + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); unsigned char *data = urb->transfer_buffer; struct omninet_header *header = (struct omninet_header *) &data[0]; @@ -182,22 +225,18 @@ static void omninet_read_bulk_callback (struct urb *urb) // dbg("omninet_read_bulk_callback"); - if (port_paranoia_check (port, "omninet_read_bulk_callback")) { - return; - } - - if (serial_paranoia_check (serial, "omninet_read_bulk_callback")) { + if (!serial) { + dbg(__FUNCTION__ " - bad serial pointer, exiting"); return; } if (urb->status) { - dbg("nonzero read bulk status received: %d", urb->status); + dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status); return; } #ifdef DEBUG - if(header->oh_xxx != 0x30) - { + if(header->oh_xxx != 0x30) { if (urb->actual_length) { printk (KERN_DEBUG __FILE__ ": omninet_read %d: ", header->oh_len); for (i = 0; i < (header->oh_len + OMNINET_HEADERLEN); i++) { @@ -208,8 +247,7 @@ static void omninet_read_bulk_callback (struct urb *urb) } #endif - if (urb->actual_length && header->oh_len) - { + if (urb->actual_length && header->oh_len) { for (i = 0; i < header->oh_len; i++) { tty_insert_flip_char(port->tty, data[OMNINET_DATAOFFSET + i], 0); } @@ -218,7 +256,7 @@ static void omninet_read_bulk_callback (struct urb *urb) /* Continue trying to always read */ if (usb_submit_urb(urb)) - dbg("failed resubmitting read urb"); + dbg(__FUNCTION__" - failed resubmitting read urb"); return; } @@ -230,6 +268,8 @@ static int omninet_write (struct usb_serial_port *port, int from_user, const uns struct omninet_data *od = (struct omninet_data *) port->private; struct omninet_header *header = (struct omninet_header *) wport->write_urb->transfer_buffer; + + unsigned long flags; /* #ifdef DEBUG int i; @@ -239,7 +279,7 @@ static int omninet_write (struct usb_serial_port *port, int from_user, const uns // dbg("omninet_write port %d", port->number); if (count == 0) { - dbg("write request of 0 bytes"); + dbg(__FUNCTION__" - write request of 0 bytes"); return (0); } /* @@ -255,10 +295,12 @@ static int omninet_write (struct usb_serial_port *port, int from_user, const uns #endif */ if (wport->write_urb->status == -EINPROGRESS) { - dbg ("already writing"); + dbg (__FUNCTION__" - already writing"); return (0); } + spin_lock_irqsave (&port->port_lock, flags); + count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count; if (from_user) { @@ -277,11 +319,15 @@ static int omninet_write (struct usb_serial_port *port, int from_user, const uns /* send the data out the bulk port, always 64 bytes */ wport->write_urb->transfer_buffer_length = 64; - if (usb_submit_urb(wport->write_urb)) - dbg("usb_submit_urb(write bulk) failed"); + if (usb_submit_urb(wport->write_urb)) { + dbg(__FUNCTION__" - usb_submit_urb(write bulk) failed"); + spin_unlock_irqrestore (&port->port_lock, flags); + return 0; + } // dbg("omninet_write returns %d", count); + spin_unlock_irqrestore (&port->port_lock, flags); return (count); } @@ -306,31 +352,26 @@ static void omninet_write_bulk_callback (struct urb *urb) /* struct omninet_header *header = (struct omninet_header *) urb->transfer_buffer; */ struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct usb_serial *serial; - struct tty_struct *tty; // dbg("omninet_write_bulk_callback, port %0x\n", port); - if (port_paranoia_check (port, "omninet_write_bulk_callback")) { + if (port_paranoia_check (port, __FUNCTION__)) { return; } serial = port->serial; - if (serial_paranoia_check (serial, "omninet_write_bulk_callback")) { + if (serial_paranoia_check (serial, __FUNCTION__)) { return; } if (urb->status) { - dbg("nonzero write bulk status received: %d", urb->status); + dbg(__FUNCTION__" - nonzero write bulk status received: %d", urb->status); return; } - tty = port->tty; - - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - - wake_up_interruptible(&tty->write_wait); + queue_task(&port->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); // dbg("omninet_write_bulk_callback, tty %0x\n", tty); @@ -338,14 +379,24 @@ static void omninet_write_bulk_callback (struct urb *urb) } -int omninet_init (void) +static void omninet_shutdown (struct usb_serial *serial) +{ + dbg (__FUNCTION__); + + while (serial->port[0].open_count > 0) { + omninet_close (&serial->port[0], NULL); + } +} + + +static int __init omninet_init (void) { usb_serial_register (&zyxel_omninet_device); return 0; } -void omninet_exit (void) +static void __exit omninet_exit (void) { usb_serial_deregister (&zyxel_omninet_device); } |