mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	USB: additional power savings for cdc-acm devices that support remote wakeup
this patch saves power for cdc-acm devices that support remote wakeup while the device is connected. - request needs_remote_wakeup when needed - delayed write while a device is autoresumed - the device is marked busy when appropriate Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
		
							parent
							
								
									188d636027
								
							
						
					
					
						commit
						11ea859d64
					
				
					 2 changed files with 126 additions and 21 deletions
				
			
		| 
						 | 
					@ -159,12 +159,34 @@ static void acm_write_done(struct acm *acm, struct acm_wb *wb)
 | 
				
			||||||
	spin_lock_irqsave(&acm->write_lock, flags);
 | 
						spin_lock_irqsave(&acm->write_lock, flags);
 | 
				
			||||||
	acm->write_ready = 1;
 | 
						acm->write_ready = 1;
 | 
				
			||||||
	wb->use = 0;
 | 
						wb->use = 0;
 | 
				
			||||||
 | 
						acm->transmitting--;
 | 
				
			||||||
	spin_unlock_irqrestore(&acm->write_lock, flags);
 | 
						spin_unlock_irqrestore(&acm->write_lock, flags);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Poke write.
 | 
					 * Poke write.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * the caller is responsible for locking
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						acm->transmitting++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wb->urb->transfer_buffer = wb->buf;
 | 
				
			||||||
 | 
						wb->urb->transfer_dma = wb->dmah;
 | 
				
			||||||
 | 
						wb->urb->transfer_buffer_length = wb->len;
 | 
				
			||||||
 | 
						wb->urb->dev = acm->dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {
 | 
				
			||||||
 | 
							dbg("usb_submit_urb(write bulk) failed: %d", rc);
 | 
				
			||||||
 | 
							acm_write_done(acm, wb);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int acm_write_start(struct acm *acm, int wbn)
 | 
					static int acm_write_start(struct acm *acm, int wbn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
| 
						 | 
					@ -182,26 +204,31 @@ static int acm_write_start(struct acm *acm, int wbn)
 | 
				
			||||||
		return 0;	/* A white lie */
 | 
							return 0;	/* A white lie */
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wb = &acm->wb[wbn];
 | 
				
			||||||
 | 
						if(acm_wb_is_avail(acm) <= 1)
 | 
				
			||||||
 | 
							acm->write_ready = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbg("%s susp_count: %d", __func__, acm->susp_count);
 | 
				
			||||||
 | 
						if (acm->susp_count) {
 | 
				
			||||||
 | 
							acm->old_ready = acm->write_ready;
 | 
				
			||||||
 | 
							acm->delayed_wb = wb;
 | 
				
			||||||
 | 
							acm->write_ready = 0;
 | 
				
			||||||
 | 
							schedule_work(&acm->waker);
 | 
				
			||||||
 | 
							spin_unlock_irqrestore(&acm->write_lock, flags);
 | 
				
			||||||
 | 
							return 0;	/* A white lie */
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						usb_mark_last_busy(acm->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!acm_wb_is_used(acm, wbn)) {
 | 
						if (!acm_wb_is_used(acm, wbn)) {
 | 
				
			||||||
		spin_unlock_irqrestore(&acm->write_lock, flags);
 | 
							spin_unlock_irqrestore(&acm->write_lock, flags);
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	wb = &acm->wb[wbn];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if(acm_wb_is_avail(acm) <= 1)
 | 
						rc = acm_start_wb(acm, wb);
 | 
				
			||||||
		acm->write_ready = 0;
 | 
					 | 
				
			||||||
	spin_unlock_irqrestore(&acm->write_lock, flags);
 | 
						spin_unlock_irqrestore(&acm->write_lock, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wb->urb->transfer_buffer = wb->buf;
 | 
					 | 
				
			||||||
	wb->urb->transfer_dma = wb->dmah;
 | 
					 | 
				
			||||||
	wb->urb->transfer_buffer_length = wb->len;
 | 
					 | 
				
			||||||
	wb->urb->dev = acm->dev;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {
 | 
					 | 
				
			||||||
		dbg("usb_submit_urb(write bulk) failed: %d", rc);
 | 
					 | 
				
			||||||
		acm_write_done(acm, wb);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * attributes exported through sysfs
 | 
					 * attributes exported through sysfs
 | 
				
			||||||
| 
						 | 
					@ -304,6 +331,7 @@ static void acm_ctrl_irq(struct urb *urb)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
exit:
 | 
					exit:
 | 
				
			||||||
 | 
						usb_mark_last_busy(acm->dev);
 | 
				
			||||||
	retval = usb_submit_urb (urb, GFP_ATOMIC);
 | 
						retval = usb_submit_urb (urb, GFP_ATOMIC);
 | 
				
			||||||
	if (retval)
 | 
						if (retval)
 | 
				
			||||||
		err ("%s - usb_submit_urb failed with result %d",
 | 
							err ("%s - usb_submit_urb failed with result %d",
 | 
				
			||||||
| 
						 | 
					@ -320,8 +348,11 @@ static void acm_read_bulk(struct urb *urb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dbg("Entering acm_read_bulk with status %d", status);
 | 
						dbg("Entering acm_read_bulk with status %d", status);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!ACM_READY(acm))
 | 
						if (!ACM_READY(acm)) {
 | 
				
			||||||
 | 
							dev_dbg(&acm->data->dev, "Aborting, acm not ready");
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						usb_mark_last_busy(acm->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (status)
 | 
						if (status)
 | 
				
			||||||
		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
 | 
							dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
 | 
				
			||||||
| 
						 | 
					@ -331,6 +362,7 @@ static void acm_read_bulk(struct urb *urb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (likely(status == 0)) {
 | 
						if (likely(status == 0)) {
 | 
				
			||||||
		spin_lock(&acm->read_lock);
 | 
							spin_lock(&acm->read_lock);
 | 
				
			||||||
 | 
							acm->processing++;
 | 
				
			||||||
		list_add_tail(&rcv->list, &acm->spare_read_urbs);
 | 
							list_add_tail(&rcv->list, &acm->spare_read_urbs);
 | 
				
			||||||
		list_add_tail(&buf->list, &acm->filled_read_bufs);
 | 
							list_add_tail(&buf->list, &acm->filled_read_bufs);
 | 
				
			||||||
		spin_unlock(&acm->read_lock);
 | 
							spin_unlock(&acm->read_lock);
 | 
				
			||||||
| 
						 | 
					@ -343,7 +375,8 @@ static void acm_read_bulk(struct urb *urb)
 | 
				
			||||||
		/* nevertheless the tasklet must be kicked unconditionally
 | 
							/* nevertheless the tasklet must be kicked unconditionally
 | 
				
			||||||
		so the queue cannot dry up */
 | 
							so the queue cannot dry up */
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tasklet_schedule(&acm->urb_task);
 | 
						if (likely(!acm->susp_count))
 | 
				
			||||||
 | 
							tasklet_schedule(&acm->urb_task);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void acm_rx_tasklet(unsigned long _acm)
 | 
					static void acm_rx_tasklet(unsigned long _acm)
 | 
				
			||||||
| 
						 | 
					@ -354,16 +387,23 @@ static void acm_rx_tasklet(unsigned long _acm)
 | 
				
			||||||
	struct acm_ru *rcv;
 | 
						struct acm_ru *rcv;
 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
	unsigned char throttled;
 | 
						unsigned char throttled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dbg("Entering acm_rx_tasklet");
 | 
						dbg("Entering acm_rx_tasklet");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!ACM_READY(acm))
 | 
						if (!ACM_READY(acm))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							dbg("acm_rx_tasklet: ACM not ready");
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock_irqsave(&acm->throttle_lock, flags);
 | 
						spin_lock_irqsave(&acm->throttle_lock, flags);
 | 
				
			||||||
	throttled = acm->throttle;
 | 
						throttled = acm->throttle;
 | 
				
			||||||
	spin_unlock_irqrestore(&acm->throttle_lock, flags);
 | 
						spin_unlock_irqrestore(&acm->throttle_lock, flags);
 | 
				
			||||||
	if (throttled)
 | 
						if (throttled)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							dbg("acm_rx_tasklet: throttled");
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
next_buffer:
 | 
					next_buffer:
 | 
				
			||||||
	spin_lock_irqsave(&acm->read_lock, flags);
 | 
						spin_lock_irqsave(&acm->read_lock, flags);
 | 
				
			||||||
| 
						 | 
					@ -403,6 +443,7 @@ static void acm_rx_tasklet(unsigned long _acm)
 | 
				
			||||||
	while (!list_empty(&acm->spare_read_bufs)) {
 | 
						while (!list_empty(&acm->spare_read_bufs)) {
 | 
				
			||||||
		spin_lock_irqsave(&acm->read_lock, flags);
 | 
							spin_lock_irqsave(&acm->read_lock, flags);
 | 
				
			||||||
		if (list_empty(&acm->spare_read_urbs)) {
 | 
							if (list_empty(&acm->spare_read_urbs)) {
 | 
				
			||||||
 | 
								acm->processing = 0;
 | 
				
			||||||
			spin_unlock_irqrestore(&acm->read_lock, flags);
 | 
								spin_unlock_irqrestore(&acm->read_lock, flags);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -425,18 +466,23 @@ static void acm_rx_tasklet(unsigned long _acm)
 | 
				
			||||||
		rcv->urb->transfer_dma = buf->dma;
 | 
							rcv->urb->transfer_dma = buf->dma;
 | 
				
			||||||
		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 | 
							rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* This shouldn't kill the driver as unsuccessful URBs are returned to the
 | 
							/* This shouldn't kill the driver as unsuccessful URBs are returned to the
 | 
				
			||||||
		   free-urbs-pool and resubmited ASAP */
 | 
							   free-urbs-pool and resubmited ASAP */
 | 
				
			||||||
		if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
 | 
							spin_lock_irqsave(&acm->read_lock, flags);
 | 
				
			||||||
 | 
							if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
 | 
				
			||||||
			list_add(&buf->list, &acm->spare_read_bufs);
 | 
								list_add(&buf->list, &acm->spare_read_bufs);
 | 
				
			||||||
			spin_lock_irqsave(&acm->read_lock, flags);
 | 
					 | 
				
			||||||
			list_add(&rcv->list, &acm->spare_read_urbs);
 | 
								list_add(&rcv->list, &acm->spare_read_urbs);
 | 
				
			||||||
 | 
								acm->processing = 0;
 | 
				
			||||||
			spin_unlock_irqrestore(&acm->read_lock, flags);
 | 
								spin_unlock_irqrestore(&acm->read_lock, flags);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								spin_unlock_irqrestore(&acm->read_lock, flags);
 | 
				
			||||||
 | 
								dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						spin_lock_irqsave(&acm->read_lock, flags);
 | 
				
			||||||
 | 
						acm->processing = 0;
 | 
				
			||||||
 | 
						spin_unlock_irqrestore(&acm->read_lock, flags);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* data interface wrote those outgoing bytes */
 | 
					/* data interface wrote those outgoing bytes */
 | 
				
			||||||
| 
						 | 
					@ -463,6 +509,27 @@ static void acm_softint(struct work_struct *work)
 | 
				
			||||||
	tty_wakeup(acm->tty);
 | 
						tty_wakeup(acm->tty);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void acm_waker(struct work_struct *waker)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct acm *acm = container_of(waker, struct acm, waker);
 | 
				
			||||||
 | 
						long flags;
 | 
				
			||||||
 | 
						int rv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rv = usb_autopm_get_interface(acm->control);
 | 
				
			||||||
 | 
						if (rv < 0) {
 | 
				
			||||||
 | 
							err("Autopm failure in %s", __func__);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (acm->delayed_wb) {
 | 
				
			||||||
 | 
							acm_start_wb(acm, acm->delayed_wb);
 | 
				
			||||||
 | 
							acm->delayed_wb = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						spin_lock_irqsave(&acm->write_lock, flags);
 | 
				
			||||||
 | 
						acm->write_ready = acm->old_ready;
 | 
				
			||||||
 | 
						spin_unlock_irqrestore(&acm->write_lock, flags);
 | 
				
			||||||
 | 
						usb_autopm_put_interface(acm->control);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * TTY handlers
 | 
					 * TTY handlers
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -492,6 +559,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (usb_autopm_get_interface(acm->control) < 0)
 | 
						if (usb_autopm_get_interface(acm->control) < 0)
 | 
				
			||||||
		goto early_bail;
 | 
							goto early_bail;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							acm->control->needs_remote_wakeup = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&acm->mutex);
 | 
						mutex_lock(&acm->mutex);
 | 
				
			||||||
	if (acm->used++) {
 | 
						if (acm->used++) {
 | 
				
			||||||
| 
						 | 
					@ -509,6 +578,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 | 
				
			||||||
	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
 | 
						if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
 | 
				
			||||||
	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
 | 
						    (acm->ctrl_caps & USB_CDC_CAP_LINE))
 | 
				
			||||||
		goto full_bailout;
 | 
							goto full_bailout;
 | 
				
			||||||
 | 
						usb_autopm_put_interface(acm->control);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	INIT_LIST_HEAD(&acm->spare_read_urbs);
 | 
						INIT_LIST_HEAD(&acm->spare_read_urbs);
 | 
				
			||||||
	INIT_LIST_HEAD(&acm->spare_read_bufs);
 | 
						INIT_LIST_HEAD(&acm->spare_read_bufs);
 | 
				
			||||||
| 
						 | 
					@ -570,12 +640,14 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
 | 
				
			||||||
	mutex_lock(&open_mutex);
 | 
						mutex_lock(&open_mutex);
 | 
				
			||||||
	if (!--acm->used) {
 | 
						if (!--acm->used) {
 | 
				
			||||||
		if (acm->dev) {
 | 
							if (acm->dev) {
 | 
				
			||||||
 | 
								usb_autopm_get_interface(acm->control);
 | 
				
			||||||
			acm_set_control(acm, acm->ctrlout = 0);
 | 
								acm_set_control(acm, acm->ctrlout = 0);
 | 
				
			||||||
			usb_kill_urb(acm->ctrlurb);
 | 
								usb_kill_urb(acm->ctrlurb);
 | 
				
			||||||
			for (i = 0; i < ACM_NW; i++)
 | 
								for (i = 0; i < ACM_NW; i++)
 | 
				
			||||||
				usb_kill_urb(acm->wb[i].urb);
 | 
									usb_kill_urb(acm->wb[i].urb);
 | 
				
			||||||
			for (i = 0; i < nr; i++)
 | 
								for (i = 0; i < nr; i++)
 | 
				
			||||||
				usb_kill_urb(acm->ru[i].urb);
 | 
									usb_kill_urb(acm->ru[i].urb);
 | 
				
			||||||
 | 
								acm->control->needs_remote_wakeup = 0;
 | 
				
			||||||
			usb_autopm_put_interface(acm->control);
 | 
								usb_autopm_put_interface(acm->control);
 | 
				
			||||||
		} else
 | 
							} else
 | 
				
			||||||
			acm_tty_unregister(acm);
 | 
								acm_tty_unregister(acm);
 | 
				
			||||||
| 
						 | 
					@ -987,6 +1059,7 @@ static int acm_probe (struct usb_interface *intf,
 | 
				
			||||||
	acm->urb_task.func = acm_rx_tasklet;
 | 
						acm->urb_task.func = acm_rx_tasklet;
 | 
				
			||||||
	acm->urb_task.data = (unsigned long) acm;
 | 
						acm->urb_task.data = (unsigned long) acm;
 | 
				
			||||||
	INIT_WORK(&acm->work, acm_softint);
 | 
						INIT_WORK(&acm->work, acm_softint);
 | 
				
			||||||
 | 
						INIT_WORK(&acm->waker, acm_waker);
 | 
				
			||||||
	spin_lock_init(&acm->throttle_lock);
 | 
						spin_lock_init(&acm->throttle_lock);
 | 
				
			||||||
	spin_lock_init(&acm->write_lock);
 | 
						spin_lock_init(&acm->write_lock);
 | 
				
			||||||
	spin_lock_init(&acm->read_lock);
 | 
						spin_lock_init(&acm->read_lock);
 | 
				
			||||||
| 
						 | 
					@ -1116,6 +1189,7 @@ static int acm_probe (struct usb_interface *intf,
 | 
				
			||||||
static void stop_data_traffic(struct acm *acm)
 | 
					static void stop_data_traffic(struct acm *acm)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i;
 | 
						int i;
 | 
				
			||||||
 | 
						dbg("Entering stop_data_traffic");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tasklet_disable(&acm->urb_task);
 | 
						tasklet_disable(&acm->urb_task);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1128,6 +1202,7 @@ static void stop_data_traffic(struct acm *acm)
 | 
				
			||||||
	tasklet_enable(&acm->urb_task);
 | 
						tasklet_enable(&acm->urb_task);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cancel_work_sync(&acm->work);
 | 
						cancel_work_sync(&acm->work);
 | 
				
			||||||
 | 
						cancel_work_sync(&acm->waker);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void acm_disconnect(struct usb_interface *intf)
 | 
					static void acm_disconnect(struct usb_interface *intf)
 | 
				
			||||||
| 
						 | 
					@ -1181,8 +1256,27 @@ static void acm_disconnect(struct usb_interface *intf)
 | 
				
			||||||
static int acm_suspend(struct usb_interface *intf, pm_message_t message)
 | 
					static int acm_suspend(struct usb_interface *intf, pm_message_t message)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct acm *acm = usb_get_intfdata(intf);
 | 
						struct acm *acm = usb_get_intfdata(intf);
 | 
				
			||||||
 | 
						int cnt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (acm->susp_count++)
 | 
						if (acm->dev->auto_pm) {
 | 
				
			||||||
 | 
							int b;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							spin_lock_irq(&acm->read_lock);
 | 
				
			||||||
 | 
							spin_lock(&acm->write_lock);
 | 
				
			||||||
 | 
							b = acm->processing + acm->transmitting;
 | 
				
			||||||
 | 
							spin_unlock(&acm->write_lock);
 | 
				
			||||||
 | 
							spin_unlock_irq(&acm->read_lock);
 | 
				
			||||||
 | 
							if (b)
 | 
				
			||||||
 | 
								return -EBUSY;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock_irq(&acm->read_lock);
 | 
				
			||||||
 | 
						spin_lock(&acm->write_lock);
 | 
				
			||||||
 | 
						cnt = acm->susp_count++;
 | 
				
			||||||
 | 
						spin_unlock(&acm->write_lock);
 | 
				
			||||||
 | 
						spin_unlock_irq(&acm->read_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cnt)
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	we treat opened interfaces differently,
 | 
						we treat opened interfaces differently,
 | 
				
			||||||
| 
						 | 
					@ -1201,15 +1295,21 @@ static int acm_resume(struct usb_interface *intf)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct acm *acm = usb_get_intfdata(intf);
 | 
						struct acm *acm = usb_get_intfdata(intf);
 | 
				
			||||||
	int rv = 0;
 | 
						int rv = 0;
 | 
				
			||||||
 | 
						int cnt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (--acm->susp_count)
 | 
						spin_lock_irq(&acm->read_lock);
 | 
				
			||||||
 | 
						acm->susp_count -= 1;
 | 
				
			||||||
 | 
						cnt = acm->susp_count;
 | 
				
			||||||
 | 
						spin_unlock_irq(&acm->read_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cnt)
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&acm->mutex);
 | 
						mutex_lock(&acm->mutex);
 | 
				
			||||||
	if (acm->used) {
 | 
						if (acm->used) {
 | 
				
			||||||
		rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
 | 
							rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
 | 
				
			||||||
		if (rv < 0)
 | 
							if (rv < 0)
 | 
				
			||||||
		goto err_out;
 | 
								goto err_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tasklet_schedule(&acm->urb_task);
 | 
							tasklet_schedule(&acm->urb_task);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,10 +107,14 @@ struct acm {
 | 
				
			||||||
	struct list_head filled_read_bufs;
 | 
						struct list_head filled_read_bufs;
 | 
				
			||||||
	int write_used;					/* number of non-empty write buffers */
 | 
						int write_used;					/* number of non-empty write buffers */
 | 
				
			||||||
	int write_ready;				/* write urb is not running */
 | 
						int write_ready;				/* write urb is not running */
 | 
				
			||||||
 | 
						int old_ready;
 | 
				
			||||||
 | 
						int processing;
 | 
				
			||||||
 | 
						int transmitting;
 | 
				
			||||||
	spinlock_t write_lock;
 | 
						spinlock_t write_lock;
 | 
				
			||||||
	struct mutex mutex;
 | 
						struct mutex mutex;
 | 
				
			||||||
	struct usb_cdc_line_coding line;		/* bits, stop, parity */
 | 
						struct usb_cdc_line_coding line;		/* bits, stop, parity */
 | 
				
			||||||
	struct work_struct work;			/* work queue entry for line discipline waking up */
 | 
						struct work_struct work;			/* work queue entry for line discipline waking up */
 | 
				
			||||||
 | 
						struct work_struct waker;
 | 
				
			||||||
	struct tasklet_struct urb_task;                 /* rx processing */
 | 
						struct tasklet_struct urb_task;                 /* rx processing */
 | 
				
			||||||
	spinlock_t throttle_lock;			/* synchronize throtteling and read callback */
 | 
						spinlock_t throttle_lock;			/* synchronize throtteling and read callback */
 | 
				
			||||||
	unsigned int ctrlin;				/* input control lines (DCD, DSR, RI, break, overruns) */
 | 
						unsigned int ctrlin;				/* input control lines (DCD, DSR, RI, break, overruns) */
 | 
				
			||||||
| 
						 | 
					@ -123,6 +127,7 @@ struct acm {
 | 
				
			||||||
	unsigned char clocal;				/* termios CLOCAL */
 | 
						unsigned char clocal;				/* termios CLOCAL */
 | 
				
			||||||
	unsigned int ctrl_caps;				/* control capabilities from the class specific header */
 | 
						unsigned int ctrl_caps;				/* control capabilities from the class specific header */
 | 
				
			||||||
	unsigned int susp_count;			/* number of suspended interfaces */
 | 
						unsigned int susp_count;			/* number of suspended interfaces */
 | 
				
			||||||
 | 
						struct acm_wb *delayed_wb;			/* write queued for a device about to be woken */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define CDC_DATA_INTERFACE_TYPE	0x0a
 | 
					#define CDC_DATA_INTERFACE_TYPE	0x0a
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue