diff options
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
| -rw-r--r-- | drivers/usb/class/cdc-acm.c | 216 | 
1 files changed, 116 insertions, 100 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index fada988512a1..e35b1508d3eb 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -133,8 +133,8 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value,  		buf, len, 5000);  	dev_dbg(&acm->control->dev, -			"%s - rq 0x%02x, val %#x, len %#x, result %d\n", -			__func__, request, value, len, retval); +		"%s - rq 0x%02x, val %#x, len %#x, result %d\n", +		__func__, request, value, len, retval);  	usb_autopm_put_interface(acm->control); @@ -158,6 +158,17 @@ static inline int acm_set_control(struct acm *acm, int control)  #define acm_send_break(acm, ms) \  	acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) +static void acm_kill_urbs(struct acm *acm) +{ +	int i; + +	usb_kill_urb(acm->ctrlurb); +	for (i = 0; i < ACM_NW; i++) +		usb_kill_urb(acm->wb[i].urb); +	for (i = 0; i < acm->rx_buflimit; i++) +		usb_kill_urb(acm->read_urbs[i]); +} +  /*   * Write buffer management.   * All of these assume proper locks taken by the caller. @@ -291,13 +302,13 @@ static void acm_ctrl_irq(struct urb *urb)  	case -ESHUTDOWN:  		/* this urb is terminated, clean up */  		dev_dbg(&acm->control->dev, -				"%s - urb shutting down with status: %d\n", -				__func__, status); +			"%s - urb shutting down with status: %d\n", +			__func__, status);  		return;  	default:  		dev_dbg(&acm->control->dev, -				"%s - nonzero urb status received: %d\n", -				__func__, status); +			"%s - nonzero urb status received: %d\n", +			__func__, status);  		goto exit;  	} @@ -306,16 +317,16 @@ static void acm_ctrl_irq(struct urb *urb)  	data = (unsigned char *)(dr + 1);  	switch (dr->bNotificationType) {  	case USB_CDC_NOTIFY_NETWORK_CONNECTION: -		dev_dbg(&acm->control->dev, "%s - network connection: %d\n", -							__func__, dr->wValue); +		dev_dbg(&acm->control->dev, +			"%s - network connection: %d\n", __func__, dr->wValue);  		break;  	case USB_CDC_NOTIFY_SERIAL_STATE:  		newctrl = get_unaligned_le16(data);  		if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { -			dev_dbg(&acm->control->dev, "%s - calling hangup\n", -					__func__); +			dev_dbg(&acm->control->dev, +				"%s - calling hangup\n", __func__);  			tty_port_tty_hangup(&acm->port, false);  		} @@ -357,8 +368,8 @@ static void acm_ctrl_irq(struct urb *urb)  exit:  	retval = usb_submit_urb(urb, GFP_ATOMIC);  	if (retval && retval != -EPERM) -		dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n", -							__func__, retval); +		dev_err(&acm->control->dev, +			"%s - usb_submit_urb failed: %d\n", __func__, retval);  }  static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags) @@ -372,8 +383,8 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)  	if (res) {  		if (res != -EPERM) {  			dev_err(&acm->data->dev, -					"urb %d failed submission with %d\n", -					index, res); +				"urb %d failed submission with %d\n", +				index, res);  		}  		set_bit(index, &acm->read_urbs_free);  		return res; @@ -416,30 +427,43 @@ static void acm_read_bulk_callback(struct urb *urb)  	int status = urb->status;  	dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n", -					rb->index, urb->actual_length, -					status); +		rb->index, urb->actual_length, status); + +	set_bit(rb->index, &acm->read_urbs_free);  	if (!acm->dev) { -		set_bit(rb->index, &acm->read_urbs_free);  		dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);  		return;  	} -	if (status) { -		set_bit(rb->index, &acm->read_urbs_free); -		if ((status != -ENOENT) || (urb->actual_length == 0)) -			return; +	switch (status) { +	case 0: +		usb_mark_last_busy(acm->dev); +		acm_process_read_urb(acm, urb); +		break; +	case -EPIPE: +		set_bit(EVENT_RX_STALL, &acm->flags); +		schedule_work(&acm->work); +		return; +	case -ENOENT: +	case -ECONNRESET: +	case -ESHUTDOWN: +		dev_dbg(&acm->data->dev, +			"%s - urb shutting down with status: %d\n", +			__func__, status); +		return; +	default: +		dev_dbg(&acm->data->dev, +			"%s - nonzero urb status received: %d\n", +			__func__, status); +		break;  	} -	usb_mark_last_busy(acm->dev); - -	acm_process_read_urb(acm, urb);  	/*  	 * Unthrottle may run on another CPU which needs to see events  	 * in the same order. Submission has an implict barrier  	 */  	smp_mb__before_atomic(); -	set_bit(rb->index, &acm->read_urbs_free);  	/* throttle device if requested by tty */  	spin_lock_irqsave(&acm->read_lock, flags); @@ -469,14 +493,30 @@ static void acm_write_bulk(struct urb *urb)  	spin_lock_irqsave(&acm->write_lock, flags);  	acm_write_done(acm, wb);  	spin_unlock_irqrestore(&acm->write_lock, flags); +	set_bit(EVENT_TTY_WAKEUP, &acm->flags);  	schedule_work(&acm->work);  }  static void acm_softint(struct work_struct *work)  { +	int i;  	struct acm *acm = container_of(work, struct acm, work); -	tty_port_tty_wakeup(&acm->port); +	if (test_bit(EVENT_RX_STALL, &acm->flags)) { +		if (!(usb_autopm_get_interface(acm->data))) { +			for (i = 0; i < acm->rx_buflimit; i++) +				usb_kill_urb(acm->read_urbs[i]); +			usb_clear_halt(acm->dev, acm->in); +			acm_submit_read_urbs(acm, GFP_KERNEL); +			usb_autopm_put_interface(acm->data); +		} +		clear_bit(EVENT_RX_STALL, &acm->flags); +	} + +	if (test_bit(EVENT_TTY_WAKEUP, &acm->flags)) { +		tty_port_tty_wakeup(&acm->port); +		clear_bit(EVENT_TTY_WAKEUP, &acm->flags); +	}  }  /* @@ -608,7 +648,6 @@ static void acm_port_shutdown(struct tty_port *port)  	struct acm *acm = container_of(port, struct acm, port);  	struct urb *urb;  	struct acm_wb *wb; -	int i;  	/*  	 * Need to grab write_lock to prevent race with resume, but no need to @@ -630,11 +669,7 @@ static void acm_port_shutdown(struct tty_port *port)  		usb_autopm_put_interface_async(acm->control);  	} -	usb_kill_urb(acm->ctrlurb); -	for (i = 0; i < ACM_NW; i++) -		usb_kill_urb(acm->wb[i].urb); -	for (i = 0; i < acm->rx_buflimit; i++) -		usb_kill_urb(acm->read_urbs[i]); +	acm_kill_urbs(acm);  }  static void acm_tty_cleanup(struct tty_struct *tty) @@ -837,8 +872,8 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state)  	retval = acm_send_break(acm, state ? 0xffff : 0);  	if (retval < 0) -		dev_dbg(&acm->control->dev, "%s - send break failed\n", -								__func__); +		dev_dbg(&acm->control->dev, +			"%s - send break failed\n", __func__);  	return retval;  } @@ -877,9 +912,6 @@ static int get_serial_info(struct acm *acm, struct serial_struct __user *info)  {  	struct serial_struct tmp; -	if (!info) -		return -EINVAL; -  	memset(&tmp, 0, sizeof(tmp));  	tmp.flags = ASYNC_LOW_LATENCY;  	tmp.xmit_fifo_size = acm->writesize; @@ -969,25 +1001,20 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)  	return rv;  } -static int get_serial_usage(struct acm *acm, -			    struct serial_icounter_struct __user *count) +static int acm_tty_get_icount(struct tty_struct *tty, +					struct serial_icounter_struct *icount)  { -	struct serial_icounter_struct icount; -	int rv = 0; - -	memset(&icount, 0, sizeof(icount)); -	icount.dsr = acm->iocount.dsr; -	icount.rng = acm->iocount.rng; -	icount.dcd = acm->iocount.dcd; -	icount.frame = acm->iocount.frame; -	icount.overrun = acm->iocount.overrun; -	icount.parity = acm->iocount.parity; -	icount.brk = acm->iocount.brk; +	struct acm *acm = tty->driver_data; -	if (copy_to_user(count, &icount, sizeof(icount)) > 0) -		rv = -EFAULT; +	icount->dsr = acm->iocount.dsr; +	icount->rng = acm->iocount.rng; +	icount->dcd = acm->iocount.dcd; +	icount->frame = acm->iocount.frame; +	icount->overrun = acm->iocount.overrun; +	icount->parity = acm->iocount.parity; +	icount->brk = acm->iocount.brk; -	return rv; +	return 0;  }  static int acm_tty_ioctl(struct tty_struct *tty, @@ -1012,9 +1039,6 @@ static int acm_tty_ioctl(struct tty_struct *tty,  		rv = wait_serial_change(acm, arg);  		usb_autopm_put_interface(acm->control);  		break; -	case TIOCGICOUNT: -		rv = get_serial_usage(acm, (struct serial_icounter_struct __user *) arg); -		break;  	}  	return rv; @@ -1088,19 +1112,17 @@ static void acm_write_buffers_free(struct acm *acm)  {  	int i;  	struct acm_wb *wb; -	struct usb_device *usb_dev = interface_to_usbdev(acm->control);  	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) -		usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah); +		usb_free_coherent(acm->dev, acm->writesize, wb->buf, wb->dmah);  }  static void acm_read_buffers_free(struct acm *acm)  { -	struct usb_device *usb_dev = interface_to_usbdev(acm->control);  	int i;  	for (i = 0; i < acm->rx_buflimit; i++) -		usb_free_coherent(usb_dev, acm->readsize, +		usb_free_coherent(acm->dev, acm->readsize,  			  acm->read_buffers[i].base, acm->read_buffers[i].dma);  } @@ -1345,9 +1367,16 @@ made_compressed_probe:  	spin_lock_init(&acm->write_lock);  	spin_lock_init(&acm->read_lock);  	mutex_init(&acm->mutex); -	acm->is_int_ep = usb_endpoint_xfer_int(epread); -	if (acm->is_int_ep) +	if (usb_endpoint_xfer_int(epread)) {  		acm->bInterval = epread->bInterval; +		acm->in = usb_rcvintpipe(usb_dev, epread->bEndpointAddress); +	} else { +		acm->in = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); +	} +	if (usb_endpoint_xfer_int(epwrite)) +		acm->out = usb_sndintpipe(usb_dev, epwrite->bEndpointAddress); +	else +		acm->out = usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress);  	tty_port_init(&acm->port);  	acm->port.ops = &acm_port_ops;  	init_usb_anchor(&acm->delayed); @@ -1382,20 +1411,15 @@ made_compressed_probe:  		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  		urb->transfer_dma = rb->dma; -		if (acm->is_int_ep) { -			usb_fill_int_urb(urb, acm->dev, -					 usb_rcvintpipe(usb_dev, epread->bEndpointAddress), -					 rb->base, +		if (usb_endpoint_xfer_int(epread)) +			usb_fill_int_urb(urb, acm->dev, acm->in, rb->base,  					 acm->readsize,  					 acm_read_bulk_callback, rb,  					 acm->bInterval); -		} else { -			usb_fill_bulk_urb(urb, acm->dev, -					  usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress), -					  rb->base, +		else +			usb_fill_bulk_urb(urb, acm->dev, acm->in, rb->base,  					  acm->readsize,  					  acm_read_bulk_callback, rb); -		}  		acm->read_urbs[i] = urb;  		__set_bit(i, &acm->read_urbs_free); @@ -1408,12 +1432,10 @@ made_compressed_probe:  			goto alloc_fail7;  		if (usb_endpoint_xfer_int(epwrite)) -			usb_fill_int_urb(snd->urb, usb_dev, -				usb_sndintpipe(usb_dev, epwrite->bEndpointAddress), +			usb_fill_int_urb(snd->urb, usb_dev, acm->out,  				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);  		else -			usb_fill_bulk_urb(snd->urb, usb_dev, -				usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), +			usb_fill_bulk_urb(snd->urb, usb_dev, acm->out,  				NULL, acm->writesize, acm_write_bulk, snd);  		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  		if (quirks & SEND_ZERO_PACKET) @@ -1485,8 +1507,8 @@ skip_countries:  	}  	if (quirks & CLEAR_HALT_CONDITIONS) { -		usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress)); -		usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress)); +		usb_clear_halt(usb_dev, acm->in); +		usb_clear_halt(usb_dev, acm->out);  	}  	return 0; @@ -1520,25 +1542,10 @@ alloc_fail:  	return rv;  } -static void stop_data_traffic(struct acm *acm) -{ -	int i; - -	usb_kill_urb(acm->ctrlurb); -	for (i = 0; i < ACM_NW; i++) -		usb_kill_urb(acm->wb[i].urb); -	for (i = 0; i < acm->rx_buflimit; i++) -		usb_kill_urb(acm->read_urbs[i]); - -	cancel_work_sync(&acm->work); -} -  static void acm_disconnect(struct usb_interface *intf)  {  	struct acm *acm = usb_get_intfdata(intf); -	struct usb_device *usb_dev = interface_to_usbdev(intf);  	struct tty_struct *tty; -	int i;  	/* sibling interface is already cleaning up */  	if (!acm) @@ -1564,17 +1571,13 @@ static void acm_disconnect(struct usb_interface *intf)  		tty_kref_put(tty);  	} -	stop_data_traffic(acm); +	acm_kill_urbs(acm); +	cancel_work_sync(&acm->work);  	tty_unregister_device(acm_tty_driver, acm->minor); -	usb_free_urb(acm->ctrlurb); -	for (i = 0; i < ACM_NW; i++) -		usb_free_urb(acm->wb[i].urb); -	for (i = 0; i < acm->rx_buflimit; i++) -		usb_free_urb(acm->read_urbs[i]);  	acm_write_buffers_free(acm); -	usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); +	usb_free_coherent(acm->dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);  	acm_read_buffers_free(acm);  	if (!acm->combined_interfaces) @@ -1603,7 +1606,8 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)  	if (cnt)  		return 0; -	stop_data_traffic(acm); +	acm_kill_urbs(acm); +	cancel_work_sync(&acm->work);  	return 0;  } @@ -1657,6 +1661,15 @@ static int acm_reset_resume(struct usb_interface *intf)  #endif /* CONFIG_PM */ +static int acm_pre_reset(struct usb_interface *intf) +{ +	struct acm *acm = usb_get_intfdata(intf); + +	clear_bit(EVENT_RX_STALL, &acm->flags); + +	return 0; +} +  #define NOKIA_PCSUITE_ACM_INFO(x) \  		USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \  		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \ @@ -1719,6 +1732,7 @@ static const struct usb_device_id acm_ids[] = {  	{ USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */  	.driver_info = QUIRK_CONTROL_LINE_STATE, },  	{ USB_DEVICE(0x2184, 0x001c) },	/* GW Instek AFG-2225 */ +	{ USB_DEVICE(0x2184, 0x0036) },	/* GW Instek AFG-125 */  	{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */  	},  	/* Motorola H24 HSPA module: */ @@ -1898,6 +1912,7 @@ static struct usb_driver acm_driver = {  	.resume =	acm_resume,  	.reset_resume =	acm_reset_resume,  #endif +	.pre_reset =	acm_pre_reset,  	.id_table =	acm_ids,  #ifdef CONFIG_PM  	.supports_autosuspend = 1, @@ -1927,6 +1942,7 @@ static const struct tty_operations acm_ops = {  	.set_termios =		acm_tty_set_termios,  	.tiocmget =		acm_tty_tiocmget,  	.tiocmset =		acm_tty_tiocmset, +	.get_icount =		acm_tty_get_icount,  };  /*  |