From bab04c3adbb55aeb5e8db60522f14ce0bb0d4179 Mon Sep 17 00:00:00 2001 From: Denis Joseph Barrow Date: Tue, 25 Nov 2008 00:26:12 -0800 Subject: hso: Add new usb device id's. Signed-off-by: Denis Joseph Barrow Signed-off-by: David S. Miller --- drivers/net/usb/hso.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/net/usb/hso.c') diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 8e90891f0e4..cee1d2a280b 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -417,6 +417,11 @@ static const struct usb_device_id hso_ids[] = { {USB_DEVICE(0x0af0, 0x7401)}, /* GI 0401 */ {USB_DEVICE(0x0af0, 0x7501)}, /* GTM 382 */ {USB_DEVICE(0x0af0, 0x7601)}, /* GE40x */ + {USB_DEVICE(0x0af0, 0x7701)}, + {USB_DEVICE(0x0af0, 0x7801)}, + {USB_DEVICE(0x0af0, 0x7901)}, + {USB_DEVICE(0x0af0, 0x7361)}, + {icon321_port_device(0x0af0, 0xd051)}, {} }; MODULE_DEVICE_TABLE(usb, hso_ids); -- cgit v1.2.3-70-g09d2 From 4a3e818181e1baf970e9232ca8b747e233176b87 Mon Sep 17 00:00:00 2001 From: Denis Joseph Barrow Date: Tue, 25 Nov 2008 00:27:50 -0800 Subject: hso: Fix crashes on close. Moved serial_open_count in hso_serial_open to prevent crashes owing to the serial structure being made NULL when hso_serial_close is called even though hso_serial_open returned -ENODEV, Alan Cox pointed out this happens, also put in sanity check in hso_serial_close to check for a valid serial structure which should prevent the most reproducable crash in the driver when the hso device is disconnected while in use. Signed-off-by: Denis Joseph Barrow Signed-off-by: David S. Miller --- drivers/net/usb/hso.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/net/usb/hso.c') diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index cee1d2a280b..d5857321979 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -1235,6 +1235,11 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) } mutex_lock(&serial->parent->mutex); + /* check for port already opened, if not set the termios */ + /* The serial->open count needs to be here as hso_serial_close + * will be called even if hso_serial_open returns -ENODEV. + */ + serial->open_count++; result = usb_autopm_get_interface(serial->parent->interface); if (result < 0) goto err_out; @@ -1246,8 +1251,6 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) tty->driver_data = serial; serial->tty = tty; - /* check for port already opened, if not set the termios */ - serial->open_count++; if (serial->open_count == 1) { tty->low_latency = 1; serial->rx_state = RX_IDLE; @@ -1285,6 +1288,10 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) u8 usb_gone; D1("Closing serial port"); + if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { + D1("invalid serial structure bailing out.\n"); + return; + } mutex_lock(&serial->parent->mutex); usb_gone = serial->parent->usb_gone; -- cgit v1.2.3-70-g09d2 From 89930b7b5e3e9bfe9c6ec5e19920451c8f5d9088 Mon Sep 17 00:00:00 2001 From: Denis Joseph Barrow Date: Tue, 25 Nov 2008 00:30:48 -0800 Subject: hso: Fix URB submission -EINVAL. Added check for IFF_UP in hso_resume, this should eliminate -EINVAL (-22) errors caused from urb's being submitted twice, once by hso_resume & once in hso_net_open, if suspend/resume USB power saving mode is enabled Signed-off-by: Denis Joseph Barrow Signed-off-by: David S. Miller --- drivers/net/usb/hso.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'drivers/net/usb/hso.c') diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index d5857321979..2c172435495 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2762,18 +2762,21 @@ static int hso_resume(struct usb_interface *iface) if (network_table[i] && (network_table[i]->interface == iface)) { hso_net = dev2net(network_table[i]); - /* First transmit any lingering data, then restart the - * device. */ - if (hso_net->skb_tx_buf) { - dev_dbg(&iface->dev, - "Transmitting lingering data\n"); - hso_net_start_xmit(hso_net->skb_tx_buf, - hso_net->net); - hso_net->skb_tx_buf = NULL; + if (hso_net->flags & IFF_UP) { + /* First transmit any lingering data, + then restart the device. */ + if (hso_net->skb_tx_buf) { + dev_dbg(&iface->dev, + "Transmitting" + " lingering data\n"); + hso_net_start_xmit(hso_net->skb_tx_buf, + hso_net->net); + hso_net->skb_tx_buf = NULL; + } + result = hso_start_net_device(network_table[i]); + if (result) + goto out; } - result = hso_start_net_device(network_table[i]); - if (result) - goto out; } } -- cgit v1.2.3-70-g09d2 From 52429eb216385fdc6969c0112ba8b46cffefaaef Mon Sep 17 00:00:00 2001 From: Denis Joseph Barrow Date: Tue, 25 Nov 2008 00:33:13 -0800 Subject: hso: Fix free of mutexes still in use. A new structure hso_mutex_table had to be declared statically & used as as hso_device mutex_lock(&serial->parent->mutex) etc is freed in hso_serial_open & hso_serial_close by kref_put while the mutex is still in use. This is a substantial change but should make the driver much stabler. Signed-off-by: Denis Joseph Barrow Signed-off-by: David S. Miller --- drivers/net/usb/hso.c | 110 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 87 insertions(+), 23 deletions(-) (limited to 'drivers/net/usb/hso.c') diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 2c172435495..cc37a2e6717 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -230,6 +230,11 @@ struct hso_serial { struct work_struct retry_unthrottle_workqueue; }; +struct hso_mutex_t { + struct mutex mutex; + u8 allocated; +}; + struct hso_device { union { struct hso_serial *dev_serial; @@ -248,7 +253,7 @@ struct hso_device { struct device *dev; struct kref ref; - struct mutex mutex; + struct hso_mutex_t *mutex; }; /* Type of interface */ @@ -364,6 +369,13 @@ static struct hso_device *network_table[HSO_MAX_NET_DEVICES]; static spinlock_t serial_table_lock; static struct ktermios *hso_serial_termios[HSO_SERIAL_TTY_MINORS]; static struct ktermios *hso_serial_termios_locked[HSO_SERIAL_TTY_MINORS]; +/* hso_mutex_table has to be declared statically as hso_device + * is freed in hso_serial_open & hso_serial_close while + * the mutex is still in use. + */ +#define HSO_NUM_MUTEXES (HSO_SERIAL_TTY_MINORS+HSO_MAX_NET_DEVICES) +static struct hso_mutex_t hso_mutex_table[HSO_NUM_MUTEXES]; +static spinlock_t hso_mutex_lock; static const s32 default_port_spec[] = { HSO_INTF_MUX | HSO_PORT_NETWORK, @@ -604,6 +616,34 @@ static void set_serial_by_index(unsigned index, struct hso_serial *serial) spin_unlock_irqrestore(&serial_table_lock, flags); } + +static struct hso_mutex_t *hso_get_free_mutex(void) +{ + int index; + struct hso_mutex_t *curr_hso_mutex; + + spin_lock(&hso_mutex_lock); + for (index = 0; index < HSO_NUM_MUTEXES; index++) { + curr_hso_mutex = &hso_mutex_table[index]; + if (!curr_hso_mutex->allocated) { + curr_hso_mutex->allocated = 1; + spin_unlock(&hso_mutex_lock); + return curr_hso_mutex; + } + } + printk(KERN_ERR "BUG %s: no free hso_mutexs devices in table\n" + , __func__); + spin_unlock(&hso_mutex_lock); + return NULL; +} + +static void hso_free_mutex(struct hso_mutex_t *mutex) +{ + spin_lock(&hso_mutex_lock); + mutex->allocated = 0; + spin_unlock(&hso_mutex_lock); +} + /* log a meaningful explanation of an USB status */ static void log_usb_status(int status, const char *function) { @@ -1225,7 +1265,9 @@ void hso_unthrottle_workfunc(struct work_struct *work) static int hso_serial_open(struct tty_struct *tty, struct file *filp) { struct hso_serial *serial = get_serial_by_index(tty->index); - int result; + int result1 = 0, result2 = 0; + struct mutex *hso_mutex = NULL; + int refcnt = 1; /* sanity check */ if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { @@ -1234,14 +1276,15 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) return -ENODEV; } - mutex_lock(&serial->parent->mutex); + hso_mutex = &serial->parent->mutex->mutex; + mutex_lock(hso_mutex); /* check for port already opened, if not set the termios */ /* The serial->open count needs to be here as hso_serial_close * will be called even if hso_serial_open returns -ENODEV. */ serial->open_count++; - result = usb_autopm_get_interface(serial->parent->interface); - if (result < 0) + result1 = usb_autopm_get_interface(serial->parent->interface); + if (result1 < 0) goto err_out; D1("Opening %d", serial->minor); @@ -1261,11 +1304,10 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) (unsigned long)serial); INIT_WORK(&serial->retry_unthrottle_workqueue, hso_unthrottle_workfunc); - result = hso_start_serial_device(serial->parent, GFP_KERNEL); - if (result) { + result2 = hso_start_serial_device(serial->parent, GFP_KERNEL); + if (result2) { hso_stop_serial_device(serial->parent); serial->open_count--; - kref_put(&serial->parent->ref, hso_serial_ref_free); } } else { D1("Port was already open"); @@ -1274,11 +1316,16 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) usb_autopm_put_interface(serial->parent->interface); /* done */ - if (result) + if (result1) hso_serial_tiocmset(tty, NULL, TIOCM_RTS | TIOCM_DTR, 0); err_out: - mutex_unlock(&serial->parent->mutex); - return result; + if (result2) + refcnt = kref_put(&serial->parent->ref, hso_serial_ref_free); + mutex_unlock(hso_mutex); + if (refcnt == 0) + hso_free_mutex(container_of(hso_mutex, + struct hso_mutex_t, mutex)); + return result1 == 0 ? result2 : result1; } /* close the requested serial port */ @@ -1286,6 +1333,8 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) { struct hso_serial *serial = tty->driver_data; u8 usb_gone; + struct mutex *hso_mutex; + int refcnt; D1("Closing serial port"); if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { @@ -1293,8 +1342,9 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) return; } - mutex_lock(&serial->parent->mutex); usb_gone = serial->parent->usb_gone; + hso_mutex = &serial->parent->mutex->mutex; + mutex_lock(hso_mutex); if (!usb_gone) usb_autopm_get_interface(serial->parent->interface); @@ -1302,7 +1352,6 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) /* reset the rts and dtr */ /* do the actual close */ serial->open_count--; - kref_put(&serial->parent->ref, hso_serial_ref_free); if (serial->open_count <= 0) { serial->open_count = 0; if (serial->tty) { @@ -1317,8 +1366,11 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) if (!usb_gone) usb_autopm_put_interface(serial->parent->interface); - - mutex_unlock(&serial->parent->mutex); + refcnt = kref_put(&serial->parent->ref, hso_serial_ref_free); + mutex_unlock(hso_mutex); + if (refcnt == 0) + hso_free_mutex(container_of(hso_mutex, + struct hso_mutex_t, mutex)); } /* close the requested serial port */ @@ -2084,8 +2136,12 @@ static struct hso_device *hso_create_device(struct usb_interface *intf, hso_dev->usb = interface_to_usbdev(intf); hso_dev->interface = intf; kref_init(&hso_dev->ref); - mutex_init(&hso_dev->mutex); - + hso_dev->mutex = hso_get_free_mutex(); + if (!hso_dev->mutex) { + kfree(hso_dev); + return NULL; + } + mutex_init(&hso_dev->mutex->mutex); INIT_WORK(&hso_dev->async_get_intf, async_get_intf); INIT_WORK(&hso_dev->async_put_intf, async_put_intf); @@ -2131,7 +2187,7 @@ static void hso_free_net_device(struct hso_device *hso_dev) unregister_netdev(hso_net->net); free_netdev(hso_net->net); } - + hso_free_mutex(hso_dev->mutex); hso_free_device(hso_dev); } @@ -2180,14 +2236,14 @@ static int hso_radio_toggle(void *data, enum rfkill_state state) int enabled = (state == RFKILL_STATE_ON); int rv; - mutex_lock(&hso_dev->mutex); + mutex_lock(&hso_dev->mutex->mutex); if (hso_dev->usb_gone) rv = 0; else rv = usb_control_msg(hso_dev->usb, usb_rcvctrlpipe(hso_dev->usb, 0), enabled ? 0x82 : 0x81, 0x40, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - mutex_unlock(&hso_dev->mutex); + mutex_unlock(&hso_dev->mutex->mutex); return rv; } @@ -2795,6 +2851,8 @@ static void hso_free_interface(struct usb_interface *interface) { struct hso_serial *hso_dev; int i; + struct mutex *hso_mutex = NULL; + int refcnt = 1; for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { if (serial_table[i] @@ -2802,10 +2860,12 @@ static void hso_free_interface(struct usb_interface *interface) hso_dev = dev2ser(serial_table[i]); if (hso_dev->tty) tty_hangup(hso_dev->tty); - mutex_lock(&hso_dev->parent->mutex); + hso_mutex = &hso_dev->parent->mutex->mutex; + mutex_lock(hso_mutex); hso_dev->parent->usb_gone = 1; - mutex_unlock(&hso_dev->parent->mutex); - kref_put(&serial_table[i]->ref, hso_serial_ref_free); + refcnt = kref_put(&serial_table[i]->ref, + hso_serial_ref_free); + mutex_unlock(hso_mutex); } } @@ -2824,6 +2884,9 @@ static void hso_free_interface(struct usb_interface *interface) hso_free_net_device(network_table[i]); } } + if (refcnt == 0) + hso_free_mutex(container_of(hso_mutex, + struct hso_mutex_t, mutex)); } /* Helper functions */ @@ -2922,6 +2985,7 @@ static int __init hso_init(void) /* Initialise the serial table semaphore and table */ spin_lock_init(&serial_table_lock); + spin_lock_init(&hso_mutex_lock); for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) serial_table[i] = NULL; -- cgit v1.2.3-70-g09d2 From 7ea3a9ad9bf360f746a7ad6fa72511a5c359490d Mon Sep 17 00:00:00 2001 From: Denis Joseph Barrow Date: Tue, 25 Nov 2008 00:35:26 -0800 Subject: hso: Add TIOCM ioctl handling. Makes TIOCM ioctls for Data Carrier Detect & related functions work like /drivers/serial/serial-core.c potentially needed for pppd & similar user programs. Signed-off-by: Denis Joseph Barrow Signed-off-by: David S. Miller --- drivers/net/usb/hso.c | 329 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 317 insertions(+), 12 deletions(-) (limited to 'drivers/net/usb/hso.c') diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index cc37a2e6717..fd723dc79c5 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -39,8 +39,11 @@ * port is opened, as this have a huge impact on the network port * throughput. * - * Interface 2: Standard modem interface - circuit switched interface, should - * not be used. + * Interface 2: Standard modem interface - circuit switched interface, this + * can be used to make a standard ppp connection however it + * should not be used in conjunction with the IP network interface + * enabled for USB performance reasons i.e. if using this set + * ideally disable_net=1. * *****************************************************************************/ @@ -63,6 +66,8 @@ #include #include #include +#include +#include #define DRIVER_VERSION "1.2" @@ -182,6 +187,41 @@ enum rx_ctrl_state{ RX_PENDING }; +#define BM_REQUEST_TYPE (0xa1) +#define B_NOTIFICATION (0x20) +#define W_VALUE (0x0) +#define W_INDEX (0x2) +#define W_LENGTH (0x2) + +#define B_OVERRUN (0x1<<6) +#define B_PARITY (0x1<<5) +#define B_FRAMING (0x1<<4) +#define B_RING_SIGNAL (0x1<<3) +#define B_BREAK (0x1<<2) +#define B_TX_CARRIER (0x1<<1) +#define B_RX_CARRIER (0x1<<0) + +struct hso_serial_state_notification { + u8 bmRequestType; + u8 bNotification; + u16 wValue; + u16 wIndex; + u16 wLength; + u16 UART_state_bitmap; +} __attribute__((packed)); + +struct hso_tiocmget { + struct mutex mutex; + wait_queue_head_t waitq; + int intr_completed; + struct usb_endpoint_descriptor *endp; + struct urb *urb; + struct hso_serial_state_notification serial_state_notification; + u16 prev_UART_state_bitmap; + struct uart_icount icount; +}; + + struct hso_serial { struct hso_device *parent; int magic; @@ -219,6 +259,7 @@ struct hso_serial { spinlock_t serial_lock; int (*write_data) (struct hso_serial *serial); + struct hso_tiocmget *tiocmget; /* Hacks required to get flow control * working on the serial receive buffers * so as not to drop characters on the floor. @@ -310,7 +351,7 @@ static void async_get_intf(struct work_struct *data); static void async_put_intf(struct work_struct *data); static int hso_put_activity(struct hso_device *hso_dev); static int hso_get_activity(struct hso_device *hso_dev); - +static void tiocmget_intr_callback(struct urb *urb); /*****************************************************************************/ /* Helping functions */ /*****************************************************************************/ @@ -1460,25 +1501,217 @@ static int hso_serial_chars_in_buffer(struct tty_struct *tty) return chars; } +int tiocmget_submit_urb(struct hso_serial *serial, + struct hso_tiocmget *tiocmget, + struct usb_device *usb) +{ + int result; + + if (serial->parent->usb_gone) + return -ENODEV; + usb_fill_int_urb(tiocmget->urb, usb, + usb_rcvintpipe(usb, + tiocmget->endp-> + bEndpointAddress & 0x7F), + &tiocmget->serial_state_notification, + sizeof(struct hso_serial_state_notification), + tiocmget_intr_callback, serial, + tiocmget->endp->bInterval); + result = usb_submit_urb(tiocmget->urb, GFP_ATOMIC); + if (result) { + dev_warn(&usb->dev, "%s usb_submit_urb failed %d\n", __func__, + result); + } + return result; + +} + +static void tiocmget_intr_callback(struct urb *urb) +{ + struct hso_serial *serial = urb->context; + struct hso_tiocmget *tiocmget; + int status = urb->status; + u16 UART_state_bitmap, prev_UART_state_bitmap; + struct uart_icount *icount; + struct hso_serial_state_notification *serial_state_notification; + struct usb_device *usb; + + /* Sanity checks */ + if (!serial) + return; + if (status) { + log_usb_status(status, __func__); + return; + } + tiocmget = serial->tiocmget; + if (!tiocmget) + return; + usb = serial->parent->usb; + serial_state_notification = &tiocmget->serial_state_notification; + if (serial_state_notification->bmRequestType != BM_REQUEST_TYPE || + serial_state_notification->bNotification != B_NOTIFICATION || + le16_to_cpu(serial_state_notification->wValue) != W_VALUE || + le16_to_cpu(serial_state_notification->wIndex) != W_INDEX || + le16_to_cpu(serial_state_notification->wLength) != W_LENGTH) { + dev_warn(&usb->dev, + "hso received invalid serial state notification\n"); + DUMP(serial_state_notification, + sizeof(hso_serial_state_notifation)) + } else { + + UART_state_bitmap = le16_to_cpu(serial_state_notification-> + UART_state_bitmap); + prev_UART_state_bitmap = tiocmget->prev_UART_state_bitmap; + icount = &tiocmget->icount; + spin_lock(&serial->serial_lock); + if ((UART_state_bitmap & B_OVERRUN) != + (prev_UART_state_bitmap & B_OVERRUN)) + icount->parity++; + if ((UART_state_bitmap & B_PARITY) != + (prev_UART_state_bitmap & B_PARITY)) + icount->parity++; + if ((UART_state_bitmap & B_FRAMING) != + (prev_UART_state_bitmap & B_FRAMING)) + icount->frame++; + if ((UART_state_bitmap & B_RING_SIGNAL) && + !(prev_UART_state_bitmap & B_RING_SIGNAL)) + icount->rng++; + if ((UART_state_bitmap & B_BREAK) != + (prev_UART_state_bitmap & B_BREAK)) + icount->brk++; + if ((UART_state_bitmap & B_TX_CARRIER) != + (prev_UART_state_bitmap & B_TX_CARRIER)) + icount->dsr++; + if ((UART_state_bitmap & B_RX_CARRIER) != + (prev_UART_state_bitmap & B_RX_CARRIER)) + icount->dcd++; + tiocmget->prev_UART_state_bitmap = UART_state_bitmap; + spin_unlock(&serial->serial_lock); + tiocmget->intr_completed = 1; + wake_up_interruptible(&tiocmget->waitq); + } + memset(serial_state_notification, 0, + sizeof(struct hso_serial_state_notification)); + tiocmget_submit_urb(serial, + tiocmget, + serial->parent->usb); +} + +/* + * next few functions largely stolen from drivers/serial/serial_core.c + */ +/* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ +static int +hso_wait_modem_status(struct hso_serial *serial, unsigned long arg) +{ + DECLARE_WAITQUEUE(wait, current); + struct uart_icount cprev, cnow; + struct hso_tiocmget *tiocmget; + int ret; + + tiocmget = serial->tiocmget; + if (!tiocmget) + return -ENOENT; + /* + * note the counters on entry + */ + spin_lock_irq(&serial->serial_lock); + memcpy(&cprev, &tiocmget->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&serial->serial_lock); + add_wait_queue(&tiocmget->waitq, &wait); + for (;;) { + spin_lock_irq(&serial->serial_lock); + memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&serial->serial_lock); + set_current_state(TASK_INTERRUPTIBLE); + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd))) { + ret = 0; + break; + } + schedule(); + /* see if a signal did it */ + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + cprev = cnow; + } + current->state = TASK_RUNNING; + remove_wait_queue(&tiocmget->waitq, &wait); + + return ret; +} + +/* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +static int hso_get_count(struct hso_serial *serial, + struct serial_icounter_struct __user *icnt) +{ + struct serial_icounter_struct icount; + struct uart_icount cnow; + struct hso_tiocmget *tiocmget = serial->tiocmget; + + if (!tiocmget) + return -ENOENT; + spin_lock_irq(&serial->serial_lock); + memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&serial->serial_lock); + + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; +} + static int hso_serial_tiocmget(struct tty_struct *tty, struct file *file) { - unsigned int value; + int retval; struct hso_serial *serial = get_serial_by_tty(tty); - unsigned long flags; + struct hso_tiocmget *tiocmget; + u16 UART_state_bitmap; /* sanity check */ if (!serial) { D1("no tty structures"); return -EINVAL; } - - spin_lock_irqsave(&serial->serial_lock, flags); - value = ((serial->rts_state) ? TIOCM_RTS : 0) | + spin_lock_irq(&serial->serial_lock); + retval = ((serial->rts_state) ? TIOCM_RTS : 0) | ((serial->dtr_state) ? TIOCM_DTR : 0); - spin_unlock_irqrestore(&serial->serial_lock, flags); + tiocmget = serial->tiocmget; + if (tiocmget) { - return value; + UART_state_bitmap = le16_to_cpu( + tiocmget->prev_UART_state_bitmap); + if (UART_state_bitmap & B_RING_SIGNAL) + retval |= TIOCM_RNG; + if (UART_state_bitmap & B_RX_CARRIER) + retval |= TIOCM_CD; + if (UART_state_bitmap & B_TX_CARRIER) + retval |= TIOCM_DSR; + } + spin_unlock_irq(&serial->serial_lock); + return retval; } static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file, @@ -1520,6 +1753,32 @@ static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file, USB_CTRL_SET_TIMEOUT); } +static int hso_serial_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct hso_serial *serial = get_serial_by_tty(tty); + void __user *uarg = (void __user *)arg; + int ret = 0; + D4("IOCTL cmd: %d, arg: %ld", cmd, arg); + + if (!serial) + return -ENODEV; + switch (cmd) { + case TIOCMIWAIT: + ret = hso_wait_modem_status(serial, arg); + break; + + case TIOCGICOUNT: + ret = hso_get_count(serial, uarg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + return ret; +} + + /* starts a transmit */ static void hso_kick_transmit(struct hso_serial *serial) { @@ -1982,7 +2241,10 @@ static int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags) serial->shared_int->use_count++; mutex_unlock(&serial->shared_int->shared_int_lock); } - + if (serial->tiocmget) + tiocmget_submit_urb(serial, + serial->tiocmget, + serial->parent->usb); return result; } @@ -1990,6 +2252,7 @@ static int hso_stop_serial_device(struct hso_device *hso_dev) { int i; struct hso_serial *serial = dev2ser(hso_dev); + struct hso_tiocmget *tiocmget; if (!serial) return -ENODEV; @@ -2018,6 +2281,11 @@ static int hso_stop_serial_device(struct hso_device *hso_dev) } mutex_unlock(&serial->shared_int->shared_int_lock); } + tiocmget = serial->tiocmget; + if (tiocmget) { + wake_up_interruptible(&tiocmget->waitq); + usb_kill_urb(tiocmget->urb); + } return 0; } @@ -2368,6 +2636,20 @@ exit: return NULL; } +static void hso_free_tiomget(struct hso_serial *serial) +{ + struct hso_tiocmget *tiocmget = serial->tiocmget; + if (tiocmget) { + kfree(tiocmget); + if (tiocmget->urb) { + usb_free_urb(tiocmget->urb); + tiocmget->urb = NULL; + } + serial->tiocmget = NULL; + + } +} + /* Frees an AT channel ( goes for both mux and non-mux ) */ static void hso_free_serial_device(struct hso_device *hso_dev) { @@ -2386,6 +2668,7 @@ static void hso_free_serial_device(struct hso_device *hso_dev) else mutex_unlock(&serial->shared_int->shared_int_lock); } + hso_free_tiomget(serial); kfree(serial); hso_free_device(hso_dev); } @@ -2397,6 +2680,7 @@ static struct hso_device *hso_create_bulk_serial_device( struct hso_device *hso_dev; struct hso_serial *serial; int num_urbs; + struct hso_tiocmget *tiocmget; hso_dev = hso_create_device(interface, port); if (!hso_dev) @@ -2409,8 +2693,27 @@ static struct hso_device *hso_create_bulk_serial_device( serial->parent = hso_dev; hso_dev->port_data.dev_serial = serial; - if (port & HSO_PORT_MODEM) + if (port & HSO_PORT_MODEM) { num_urbs = 2; + serial->tiocmget = kzalloc(sizeof(struct hso_tiocmget), + GFP_KERNEL); + /* it isn't going to break our heart if serial->tiocmget + * allocation fails don't bother checking this. + */ + if (serial->tiocmget) { + tiocmget = serial->tiocmget; + tiocmget->urb = usb_alloc_urb(0, GFP_KERNEL); + if (tiocmget->urb) { + mutex_init(&tiocmget->mutex); + init_waitqueue_head(&tiocmget->waitq); + tiocmget->endp = hso_get_ep( + interface, + USB_ENDPOINT_XFER_INT, + USB_DIR_IN); + } else + hso_free_tiomget(serial); + } + } else num_urbs = 1; @@ -2446,6 +2749,7 @@ static struct hso_device *hso_create_bulk_serial_device( exit2: hso_serial_common_free(serial); exit: + hso_free_tiomget(serial); kfree(serial); hso_free_device(hso_dev); return NULL; @@ -2958,6 +3262,7 @@ static const struct tty_operations hso_serial_ops = { .close = hso_serial_close, .write = hso_serial_write, .write_room = hso_serial_write_room, + .ioctl = hso_serial_ioctl, .set_termios = hso_serial_set_termios, .chars_in_buffer = hso_serial_chars_in_buffer, .tiocmget = hso_serial_tiocmget, -- cgit v1.2.3-70-g09d2 From 9c8f92aed16dbd1924910f3305f5992a4f29fe2a Mon Sep 17 00:00:00 2001 From: Denis Joseph Barrow Date: Tue, 25 Nov 2008 00:36:10 -0800 Subject: hso: Hook up ->reset_resume Made usb_drivers reset_resume function point to hso_resume this fixes problems a usb reset is done when the network interface is left idle for a few minutes. Possibly reset_resume should initialise hardware more but this works in the common case. Signed-off-by: Denis Joseph Barrow Signed-off-by: David S. Miller --- drivers/net/usb/hso.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/usb/hso.c') diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index fd723dc79c5..7fde27eef44 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -3277,6 +3277,7 @@ static struct usb_driver hso_driver = { .id_table = hso_ids, .suspend = hso_suspend, .resume = hso_resume, + .reset_resume = hso_resume, .supports_autosuspend = 1, }; -- cgit v1.2.3-70-g09d2 From cd90ee1799136bd74158b734cf71b72609244a91 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 25 Nov 2008 03:52:17 -0800 Subject: Revert "hso: Add TIOCM ioctl handling." This reverts commit 7ea3a9ad9bf360f746a7ad6fa72511a5c359490d. On request from Alan Cox. Signed-off-by: David S. Miller --- drivers/net/usb/hso.c | 329 ++------------------------------------------------ 1 file changed, 12 insertions(+), 317 deletions(-) (limited to 'drivers/net/usb/hso.c') diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 7fde27eef44..af62f58ffcb 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -39,11 +39,8 @@ * port is opened, as this have a huge impact on the network port * throughput. * - * Interface 2: Standard modem interface - circuit switched interface, this - * can be used to make a standard ppp connection however it - * should not be used in conjunction with the IP network interface - * enabled for USB performance reasons i.e. if using this set - * ideally disable_net=1. + * Interface 2: Standard modem interface - circuit switched interface, should + * not be used. * *****************************************************************************/ @@ -66,8 +63,6 @@ #include #include #include -#include -#include #define DRIVER_VERSION "1.2" @@ -187,41 +182,6 @@ enum rx_ctrl_state{ RX_PENDING }; -#define BM_REQUEST_TYPE (0xa1) -#define B_NOTIFICATION (0x20) -#define W_VALUE (0x0) -#define W_INDEX (0x2) -#define W_LENGTH (0x2) - -#define B_OVERRUN (0x1<<6) -#define B_PARITY (0x1<<5) -#define B_FRAMING (0x1<<4) -#define B_RING_SIGNAL (0x1<<3) -#define B_BREAK (0x1<<2) -#define B_TX_CARRIER (0x1<<1) -#define B_RX_CARRIER (0x1<<0) - -struct hso_serial_state_notification { - u8 bmRequestType; - u8 bNotification; - u16 wValue; - u16 wIndex; - u16 wLength; - u16 UART_state_bitmap; -} __attribute__((packed)); - -struct hso_tiocmget { - struct mutex mutex; - wait_queue_head_t waitq; - int intr_completed; - struct usb_endpoint_descriptor *endp; - struct urb *urb; - struct hso_serial_state_notification serial_state_notification; - u16 prev_UART_state_bitmap; - struct uart_icount icount; -}; - - struct hso_serial { struct hso_device *parent; int magic; @@ -259,7 +219,6 @@ struct hso_serial { spinlock_t serial_lock; int (*write_data) (struct hso_serial *serial); - struct hso_tiocmget *tiocmget; /* Hacks required to get flow control * working on the serial receive buffers * so as not to drop characters on the floor. @@ -351,7 +310,7 @@ static void async_get_intf(struct work_struct *data); static void async_put_intf(struct work_struct *data); static int hso_put_activity(struct hso_device *hso_dev); static int hso_get_activity(struct hso_device *hso_dev); -static void tiocmget_intr_callback(struct urb *urb); + /*****************************************************************************/ /* Helping functions */ /*****************************************************************************/ @@ -1501,217 +1460,25 @@ static int hso_serial_chars_in_buffer(struct tty_struct *tty) return chars; } -int tiocmget_submit_urb(struct hso_serial *serial, - struct hso_tiocmget *tiocmget, - struct usb_device *usb) -{ - int result; - - if (serial->parent->usb_gone) - return -ENODEV; - usb_fill_int_urb(tiocmget->urb, usb, - usb_rcvintpipe(usb, - tiocmget->endp-> - bEndpointAddress & 0x7F), - &tiocmget->serial_state_notification, - sizeof(struct hso_serial_state_notification), - tiocmget_intr_callback, serial, - tiocmget->endp->bInterval); - result = usb_submit_urb(tiocmget->urb, GFP_ATOMIC); - if (result) { - dev_warn(&usb->dev, "%s usb_submit_urb failed %d\n", __func__, - result); - } - return result; - -} - -static void tiocmget_intr_callback(struct urb *urb) -{ - struct hso_serial *serial = urb->context; - struct hso_tiocmget *tiocmget; - int status = urb->status; - u16 UART_state_bitmap, prev_UART_state_bitmap; - struct uart_icount *icount; - struct hso_serial_state_notification *serial_state_notification; - struct usb_device *usb; - - /* Sanity checks */ - if (!serial) - return; - if (status) { - log_usb_status(status, __func__); - return; - } - tiocmget = serial->tiocmget; - if (!tiocmget) - return; - usb = serial->parent->usb; - serial_state_notification = &tiocmget->serial_state_notification; - if (serial_state_notification->bmRequestType != BM_REQUEST_TYPE || - serial_state_notification->bNotification != B_NOTIFICATION || - le16_to_cpu(serial_state_notification->wValue) != W_VALUE || - le16_to_cpu(serial_state_notification->wIndex) != W_INDEX || - le16_to_cpu(serial_state_notification->wLength) != W_LENGTH) { - dev_warn(&usb->dev, - "hso received invalid serial state notification\n"); - DUMP(serial_state_notification, - sizeof(hso_serial_state_notifation)) - } else { - - UART_state_bitmap = le16_to_cpu(serial_state_notification-> - UART_state_bitmap); - prev_UART_state_bitmap = tiocmget->prev_UART_state_bitmap; - icount = &tiocmget->icount; - spin_lock(&serial->serial_lock); - if ((UART_state_bitmap & B_OVERRUN) != - (prev_UART_state_bitmap & B_OVERRUN)) - icount->parity++; - if ((UART_state_bitmap & B_PARITY) != - (prev_UART_state_bitmap & B_PARITY)) - icount->parity++; - if ((UART_state_bitmap & B_FRAMING) != - (prev_UART_state_bitmap & B_FRAMING)) - icount->frame++; - if ((UART_state_bitmap & B_RING_SIGNAL) && - !(prev_UART_state_bitmap & B_RING_SIGNAL)) - icount->rng++; - if ((UART_state_bitmap & B_BREAK) != - (prev_UART_state_bitmap & B_BREAK)) - icount->brk++; - if ((UART_state_bitmap & B_TX_CARRIER) != - (prev_UART_state_bitmap & B_TX_CARRIER)) - icount->dsr++; - if ((UART_state_bitmap & B_RX_CARRIER) != - (prev_UART_state_bitmap & B_RX_CARRIER)) - icount->dcd++; - tiocmget->prev_UART_state_bitmap = UART_state_bitmap; - spin_unlock(&serial->serial_lock); - tiocmget->intr_completed = 1; - wake_up_interruptible(&tiocmget->waitq); - } - memset(serial_state_notification, 0, - sizeof(struct hso_serial_state_notification)); - tiocmget_submit_urb(serial, - tiocmget, - serial->parent->usb); -} - -/* - * next few functions largely stolen from drivers/serial/serial_core.c - */ -/* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ -static int -hso_wait_modem_status(struct hso_serial *serial, unsigned long arg) -{ - DECLARE_WAITQUEUE(wait, current); - struct uart_icount cprev, cnow; - struct hso_tiocmget *tiocmget; - int ret; - - tiocmget = serial->tiocmget; - if (!tiocmget) - return -ENOENT; - /* - * note the counters on entry - */ - spin_lock_irq(&serial->serial_lock); - memcpy(&cprev, &tiocmget->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&serial->serial_lock); - add_wait_queue(&tiocmget->waitq, &wait); - for (;;) { - spin_lock_irq(&serial->serial_lock); - memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&serial->serial_lock); - set_current_state(TASK_INTERRUPTIBLE); - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd))) { - ret = 0; - break; - } - schedule(); - /* see if a signal did it */ - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } - cprev = cnow; - } - current->state = TASK_RUNNING; - remove_wait_queue(&tiocmget->waitq, &wait); - - return ret; -} - -/* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ -static int hso_get_count(struct hso_serial *serial, - struct serial_icounter_struct __user *icnt) -{ - struct serial_icounter_struct icount; - struct uart_icount cnow; - struct hso_tiocmget *tiocmget = serial->tiocmget; - - if (!tiocmget) - return -ENOENT; - spin_lock_irq(&serial->serial_lock); - memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&serial->serial_lock); - - icount.cts = cnow.cts; - icount.dsr = cnow.dsr; - icount.rng = cnow.rng; - icount.dcd = cnow.dcd; - icount.rx = cnow.rx; - icount.tx = cnow.tx; - icount.frame = cnow.frame; - icount.overrun = cnow.overrun; - icount.parity = cnow.parity; - icount.brk = cnow.brk; - icount.buf_overrun = cnow.buf_overrun; - - return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; -} - static int hso_serial_tiocmget(struct tty_struct *tty, struct file *file) { - int retval; + unsigned int value; struct hso_serial *serial = get_serial_by_tty(tty); - struct hso_tiocmget *tiocmget; - u16 UART_state_bitmap; + unsigned long flags; /* sanity check */ if (!serial) { D1("no tty structures"); return -EINVAL; } - spin_lock_irq(&serial->serial_lock); - retval = ((serial->rts_state) ? TIOCM_RTS : 0) | + + spin_lock_irqsave(&serial->serial_lock, flags); + value = ((serial->rts_state) ? TIOCM_RTS : 0) | ((serial->dtr_state) ? TIOCM_DTR : 0); - tiocmget = serial->tiocmget; - if (tiocmget) { + spin_unlock_irqrestore(&serial->serial_lock, flags); - UART_state_bitmap = le16_to_cpu( - tiocmget->prev_UART_state_bitmap); - if (UART_state_bitmap & B_RING_SIGNAL) - retval |= TIOCM_RNG; - if (UART_state_bitmap & B_RX_CARRIER) - retval |= TIOCM_CD; - if (UART_state_bitmap & B_TX_CARRIER) - retval |= TIOCM_DSR; - } - spin_unlock_irq(&serial->serial_lock); - return retval; + return value; } static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file, @@ -1753,32 +1520,6 @@ static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file, USB_CTRL_SET_TIMEOUT); } -static int hso_serial_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct hso_serial *serial = get_serial_by_tty(tty); - void __user *uarg = (void __user *)arg; - int ret = 0; - D4("IOCTL cmd: %d, arg: %ld", cmd, arg); - - if (!serial) - return -ENODEV; - switch (cmd) { - case TIOCMIWAIT: - ret = hso_wait_modem_status(serial, arg); - break; - - case TIOCGICOUNT: - ret = hso_get_count(serial, uarg); - break; - default: - ret = -ENOIOCTLCMD; - break; - } - return ret; -} - - /* starts a transmit */ static void hso_kick_transmit(struct hso_serial *serial) { @@ -2241,10 +1982,7 @@ static int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags) serial->shared_int->use_count++; mutex_unlock(&serial->shared_int->shared_int_lock); } - if (serial->tiocmget) - tiocmget_submit_urb(serial, - serial->tiocmget, - serial->parent->usb); + return result; } @@ -2252,7 +1990,6 @@ static int hso_stop_serial_device(struct hso_device *hso_dev) { int i; struct hso_serial *serial = dev2ser(hso_dev); - struct hso_tiocmget *tiocmget; if (!serial) return -ENODEV; @@ -2281,11 +2018,6 @@ static int hso_stop_serial_device(struct hso_device *hso_dev) } mutex_unlock(&serial->shared_int->shared_int_lock); } - tiocmget = serial->tiocmget; - if (tiocmget) { - wake_up_interruptible(&tiocmget->waitq); - usb_kill_urb(tiocmget->urb); - } return 0; } @@ -2636,20 +2368,6 @@ exit: return NULL; } -static void hso_free_tiomget(struct hso_serial *serial) -{ - struct hso_tiocmget *tiocmget = serial->tiocmget; - if (tiocmget) { - kfree(tiocmget); - if (tiocmget->urb) { - usb_free_urb(tiocmget->urb); - tiocmget->urb = NULL; - } - serial->tiocmget = NULL; - - } -} - /* Frees an AT channel ( goes for both mux and non-mux ) */ static void hso_free_serial_device(struct hso_device *hso_dev) { @@ -2668,7 +2386,6 @@ static void hso_free_serial_device(struct hso_device *hso_dev) else mutex_unlock(&serial->shared_int->shared_int_lock); } - hso_free_tiomget(serial); kfree(serial); hso_free_device(hso_dev); } @@ -2680,7 +2397,6 @@ static struct hso_device *hso_create_bulk_serial_device( struct hso_device *hso_dev; struct hso_serial *serial; int num_urbs; - struct hso_tiocmget *tiocmget; hso_dev = hso_create_device(interface, port); if (!hso_dev) @@ -2693,27 +2409,8 @@ static struct hso_device *hso_create_bulk_serial_device( serial->parent = hso_dev; hso_dev->port_data.dev_serial = serial; - if (port & HSO_PORT_MODEM) { + if (port & HSO_PORT_MODEM) num_urbs = 2; - serial->tiocmget = kzalloc(sizeof(struct hso_tiocmget), - GFP_KERNEL); - /* it isn't going to break our heart if serial->tiocmget - * allocation fails don't bother checking this. - */ - if (serial->tiocmget) { - tiocmget = serial->tiocmget; - tiocmget->urb = usb_alloc_urb(0, GFP_KERNEL); - if (tiocmget->urb) { - mutex_init(&tiocmget->mutex); - init_waitqueue_head(&tiocmget->waitq); - tiocmget->endp = hso_get_ep( - interface, - USB_ENDPOINT_XFER_INT, - USB_DIR_IN); - } else - hso_free_tiomget(serial); - } - } else num_urbs = 1; @@ -2749,7 +2446,6 @@ static struct hso_device *hso_create_bulk_serial_device( exit2: hso_serial_common_free(serial); exit: - hso_free_tiomget(serial); kfree(serial); hso_free_device(hso_dev); return NULL; @@ -3262,7 +2958,6 @@ static const struct tty_operations hso_serial_ops = { .close = hso_serial_close, .write = hso_serial_write, .write_room = hso_serial_write_room, - .ioctl = hso_serial_ioctl, .set_termios = hso_serial_set_termios, .chars_in_buffer = hso_serial_chars_in_buffer, .tiocmget = hso_serial_tiocmget, -- cgit v1.2.3-70-g09d2 From ab153d84d9609b4e6f53632a6f14b882e866cb47 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 25 Nov 2008 03:52:46 -0800 Subject: Revert "hso: Fix free of mutexes still in use." This reverts commit 52429eb216385fdc6969c0112ba8b46cffefaaef. On request from Alan Cox. Signed-off-by: David S. Miller --- drivers/net/usb/hso.c | 110 +++++++++++--------------------------------------- 1 file changed, 23 insertions(+), 87 deletions(-) (limited to 'drivers/net/usb/hso.c') diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index af62f58ffcb..39df44f99d6 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -230,11 +230,6 @@ struct hso_serial { struct work_struct retry_unthrottle_workqueue; }; -struct hso_mutex_t { - struct mutex mutex; - u8 allocated; -}; - struct hso_device { union { struct hso_serial *dev_serial; @@ -253,7 +248,7 @@ struct hso_device { struct device *dev; struct kref ref; - struct hso_mutex_t *mutex; + struct mutex mutex; }; /* Type of interface */ @@ -369,13 +364,6 @@ static struct hso_device *network_table[HSO_MAX_NET_DEVICES]; static spinlock_t serial_table_lock; static struct ktermios *hso_serial_termios[HSO_SERIAL_TTY_MINORS]; static struct ktermios *hso_serial_termios_locked[HSO_SERIAL_TTY_MINORS]; -/* hso_mutex_table has to be declared statically as hso_device - * is freed in hso_serial_open & hso_serial_close while - * the mutex is still in use. - */ -#define HSO_NUM_MUTEXES (HSO_SERIAL_TTY_MINORS+HSO_MAX_NET_DEVICES) -static struct hso_mutex_t hso_mutex_table[HSO_NUM_MUTEXES]; -static spinlock_t hso_mutex_lock; static const s32 default_port_spec[] = { HSO_INTF_MUX | HSO_PORT_NETWORK, @@ -616,34 +604,6 @@ static void set_serial_by_index(unsigned index, struct hso_serial *serial) spin_unlock_irqrestore(&serial_table_lock, flags); } - -static struct hso_mutex_t *hso_get_free_mutex(void) -{ - int index; - struct hso_mutex_t *curr_hso_mutex; - - spin_lock(&hso_mutex_lock); - for (index = 0; index < HSO_NUM_MUTEXES; index++) { - curr_hso_mutex = &hso_mutex_table[index]; - if (!curr_hso_mutex->allocated) { - curr_hso_mutex->allocated = 1; - spin_unlock(&hso_mutex_lock); - return curr_hso_mutex; - } - } - printk(KERN_ERR "BUG %s: no free hso_mutexs devices in table\n" - , __func__); - spin_unlock(&hso_mutex_lock); - return NULL; -} - -static void hso_free_mutex(struct hso_mutex_t *mutex) -{ - spin_lock(&hso_mutex_lock); - mutex->allocated = 0; - spin_unlock(&hso_mutex_lock); -} - /* log a meaningful explanation of an USB status */ static void log_usb_status(int status, const char *function) { @@ -1265,9 +1225,7 @@ void hso_unthrottle_workfunc(struct work_struct *work) static int hso_serial_open(struct tty_struct *tty, struct file *filp) { struct hso_serial *serial = get_serial_by_index(tty->index); - int result1 = 0, result2 = 0; - struct mutex *hso_mutex = NULL; - int refcnt = 1; + int result; /* sanity check */ if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { @@ -1276,15 +1234,14 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) return -ENODEV; } - hso_mutex = &serial->parent->mutex->mutex; - mutex_lock(hso_mutex); + mutex_lock(&serial->parent->mutex); /* check for port already opened, if not set the termios */ /* The serial->open count needs to be here as hso_serial_close * will be called even if hso_serial_open returns -ENODEV. */ serial->open_count++; - result1 = usb_autopm_get_interface(serial->parent->interface); - if (result1 < 0) + result = usb_autopm_get_interface(serial->parent->interface); + if (result < 0) goto err_out; D1("Opening %d", serial->minor); @@ -1304,10 +1261,11 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) (unsigned long)serial); INIT_WORK(&serial->retry_unthrottle_workqueue, hso_unthrottle_workfunc); - result2 = hso_start_serial_device(serial->parent, GFP_KERNEL); - if (result2) { + result = hso_start_serial_device(serial->parent, GFP_KERNEL); + if (result) { hso_stop_serial_device(serial->parent); serial->open_count--; + kref_put(&serial->parent->ref, hso_serial_ref_free); } } else { D1("Port was already open"); @@ -1316,16 +1274,11 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) usb_autopm_put_interface(serial->parent->interface); /* done */ - if (result1) + if (result) hso_serial_tiocmset(tty, NULL, TIOCM_RTS | TIOCM_DTR, 0); err_out: - if (result2) - refcnt = kref_put(&serial->parent->ref, hso_serial_ref_free); - mutex_unlock(hso_mutex); - if (refcnt == 0) - hso_free_mutex(container_of(hso_mutex, - struct hso_mutex_t, mutex)); - return result1 == 0 ? result2 : result1; + mutex_unlock(&serial->parent->mutex); + return result; } /* close the requested serial port */ @@ -1333,8 +1286,6 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) { struct hso_serial *serial = tty->driver_data; u8 usb_gone; - struct mutex *hso_mutex; - int refcnt; D1("Closing serial port"); if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { @@ -1342,9 +1293,8 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) return; } + mutex_lock(&serial->parent->mutex); usb_gone = serial->parent->usb_gone; - hso_mutex = &serial->parent->mutex->mutex; - mutex_lock(hso_mutex); if (!usb_gone) usb_autopm_get_interface(serial->parent->interface); @@ -1352,6 +1302,7 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) /* reset the rts and dtr */ /* do the actual close */ serial->open_count--; + kref_put(&serial->parent->ref, hso_serial_ref_free); if (serial->open_count <= 0) { serial->open_count = 0; if (serial->tty) { @@ -1366,11 +1317,8 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) if (!usb_gone) usb_autopm_put_interface(serial->parent->interface); - refcnt = kref_put(&serial->parent->ref, hso_serial_ref_free); - mutex_unlock(hso_mutex); - if (refcnt == 0) - hso_free_mutex(container_of(hso_mutex, - struct hso_mutex_t, mutex)); + + mutex_unlock(&serial->parent->mutex); } /* close the requested serial port */ @@ -2136,12 +2084,8 @@ static struct hso_device *hso_create_device(struct usb_interface *intf, hso_dev->usb = interface_to_usbdev(intf); hso_dev->interface = intf; kref_init(&hso_dev->ref); - hso_dev->mutex = hso_get_free_mutex(); - if (!hso_dev->mutex) { - kfree(hso_dev); - return NULL; - } - mutex_init(&hso_dev->mutex->mutex); + mutex_init(&hso_dev->mutex); + INIT_WORK(&hso_dev->async_get_intf, async_get_intf); INIT_WORK(&hso_dev->async_put_intf, async_put_intf); @@ -2187,7 +2131,7 @@ static void hso_free_net_device(struct hso_device *hso_dev) unregister_netdev(hso_net->net); free_netdev(hso_net->net); } - hso_free_mutex(hso_dev->mutex); + hso_free_device(hso_dev); } @@ -2236,14 +2180,14 @@ static int hso_radio_toggle(void *data, enum rfkill_state state) int enabled = (state == RFKILL_STATE_ON); int rv; - mutex_lock(&hso_dev->mutex->mutex); + mutex_lock(&hso_dev->mutex); if (hso_dev->usb_gone) rv = 0; else rv = usb_control_msg(hso_dev->usb, usb_rcvctrlpipe(hso_dev->usb, 0), enabled ? 0x82 : 0x81, 0x40, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - mutex_unlock(&hso_dev->mutex->mutex); + mutex_unlock(&hso_dev->mutex); return rv; } @@ -2851,8 +2795,6 @@ static void hso_free_interface(struct usb_interface *interface) { struct hso_serial *hso_dev; int i; - struct mutex *hso_mutex = NULL; - int refcnt = 1; for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { if (serial_table[i] @@ -2860,12 +2802,10 @@ static void hso_free_interface(struct usb_interface *interface) hso_dev = dev2ser(serial_table[i]); if (hso_dev->tty) tty_hangup(hso_dev->tty); - hso_mutex = &hso_dev->parent->mutex->mutex; - mutex_lock(hso_mutex); + mutex_lock(&hso_dev->parent->mutex); hso_dev->parent->usb_gone = 1; - refcnt = kref_put(&serial_table[i]->ref, - hso_serial_ref_free); - mutex_unlock(hso_mutex); + mutex_unlock(&hso_dev->parent->mutex); + kref_put(&serial_table[i]->ref, hso_serial_ref_free); } } @@ -2884,9 +2824,6 @@ static void hso_free_interface(struct usb_interface *interface) hso_free_net_device(network_table[i]); } } - if (refcnt == 0) - hso_free_mutex(container_of(hso_mutex, - struct hso_mutex_t, mutex)); } /* Helper functions */ @@ -2986,7 +2923,6 @@ static int __init hso_init(void) /* Initialise the serial table semaphore and table */ spin_lock_init(&serial_table_lock); - spin_lock_init(&hso_mutex_lock); for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) serial_table[i] = NULL; -- cgit v1.2.3-70-g09d2 From 2f9889a20cd2854bc6305198255c617b0b4eb719 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 25 Nov 2008 03:53:09 -0800 Subject: Revert "hso: Fix crashes on close." This reverts commit 4a3e818181e1baf970e9232ca8b747e233176b87. On request from Alan Cox. Signed-off-by: David S. Miller --- drivers/net/usb/hso.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/net/usb/hso.c') diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 39df44f99d6..c5c1aeae94f 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -1235,11 +1235,6 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) } mutex_lock(&serial->parent->mutex); - /* check for port already opened, if not set the termios */ - /* The serial->open count needs to be here as hso_serial_close - * will be called even if hso_serial_open returns -ENODEV. - */ - serial->open_count++; result = usb_autopm_get_interface(serial->parent->interface); if (result < 0) goto err_out; @@ -1251,6 +1246,8 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) tty->driver_data = serial; serial->tty = tty; + /* check for port already opened, if not set the termios */ + serial->open_count++; if (serial->open_count == 1) { tty->low_latency = 1; serial->rx_state = RX_IDLE; @@ -1288,10 +1285,6 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) u8 usb_gone; D1("Closing serial port"); - if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { - D1("invalid serial structure bailing out.\n"); - return; - } mutex_lock(&serial->parent->mutex); usb_gone = serial->parent->usb_gone; -- cgit v1.2.3-70-g09d2 From 889bd9b6dbcd426b8698c4a779dd7dbf247f57b8 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 18 Dec 2008 03:57:35 +0000 Subject: net: startup race in hso driver The flag marking a device running must be set before the URBs for recption are submitted or they may complete too early and fail to resubmit. Signed-off-by: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/hso.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net/usb/hso.c') diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index c5c1aeae94f..198ce3cf378 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -663,10 +663,9 @@ static int hso_net_open(struct net_device *net) odev->rx_buf_missing = sizeof(struct iphdr); spin_unlock_irqrestore(&odev->net_lock, flags); - hso_start_net_device(odev->parent); - /* We are up and running. */ set_bit(HSO_NET_RUNNING, &odev->flags); + hso_start_net_device(odev->parent); /* Tell the kernel we are ready to start receiving from it */ netif_start_queue(net); -- cgit v1.2.3-70-g09d2