diff options
Diffstat (limited to 'drivers/usb/class')
| -rw-r--r-- | drivers/usb/class/cdc-acm.c | 81 | ||||
| -rw-r--r-- | drivers/usb/class/cdc-acm.h | 7 | 
2 files changed, 45 insertions, 43 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 1ded83b66af..d9b40811392 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -31,6 +31,7 @@   *	v0.23 - use softirq for rx processing, as needed by tty layer   *	v0.24 - change probe method to evaluate CDC union descriptor   *	v0.25 - downstream tasks paralelized to maximize throughput + *	v0.26 - multiple write urbs, writesize increased   */  /* @@ -72,7 +73,7 @@  /*   * Version Information   */ -#define DRIVER_VERSION "v0.25" +#define DRIVER_VERSION "v0.26"  #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"  #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" @@ -118,7 +119,7 @@ static int acm_wb_alloc(struct acm *acm)  	int i, wbn;  	struct acm_wb *wb; -	wbn = acm->write_current; +	wbn = 0;  	i = 0;  	for (;;) {  		wb = &acm->wb[wbn]; @@ -132,11 +133,6 @@ static int acm_wb_alloc(struct acm *acm)  	}  } -static void acm_wb_free(struct acm *acm, int wbn) -{ -	acm->wb[wbn].use = 0; -} -  static int acm_wb_is_avail(struct acm *acm)  {  	int i, n; @@ -156,26 +152,22 @@ static inline int acm_wb_is_used(struct acm *acm, int wbn)  /*   * Finish write.   */ -static void acm_write_done(struct acm *acm) +static void acm_write_done(struct acm *acm, struct acm_wb *wb)  {  	unsigned long flags; -	int wbn;  	spin_lock_irqsave(&acm->write_lock, flags);  	acm->write_ready = 1; -	wbn = acm->write_current; -	acm_wb_free(acm, wbn); -	acm->write_current = (wbn + 1) % ACM_NW; +	wb->use = 0;  	spin_unlock_irqrestore(&acm->write_lock, flags);  }  /*   * Poke write.   */ -static int acm_write_start(struct acm *acm) +static int acm_write_start(struct acm *acm, int wbn)  {  	unsigned long flags; -	int wbn;  	struct acm_wb *wb;  	int rc; @@ -190,24 +182,24 @@ static int acm_write_start(struct acm *acm)  		return 0;	/* A white lie */  	} -	wbn = acm->write_current;  	if (!acm_wb_is_used(acm, wbn)) {  		spin_unlock_irqrestore(&acm->write_lock, flags);  		return 0;  	}  	wb = &acm->wb[wbn]; -	acm->write_ready = 0; +	if(acm_wb_is_avail(acm) <= 1) +		acm->write_ready = 0;  	spin_unlock_irqrestore(&acm->write_lock, flags); -	acm->writeurb->transfer_buffer = wb->buf; -	acm->writeurb->transfer_dma = wb->dmah; -	acm->writeurb->transfer_buffer_length = wb->len; -	acm->writeurb->dev = acm->dev; +	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(acm->writeurb, GFP_ATOMIC)) < 0) { +	if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {  		dbg("usb_submit_urb(write bulk) failed: %d", rc); -		acm_write_done(acm); +		acm_write_done(acm, wb);  	}  	return rc;  } @@ -450,12 +442,13 @@ urbs:  /* data interface wrote those outgoing bytes */  static void acm_write_bulk(struct urb *urb)  { -	struct acm *acm = (struct acm *)urb->context; +	struct acm *acm; +	struct acm_wb *wb = (struct acm_wb *)urb->context;  	dbg("Entering acm_write_bulk with status %d", urb->status); -	acm_write_done(acm); -	acm_write_start(acm); +	acm = wb->instance; +	acm_write_done(acm, wb);  	if (ACM_READY(acm))  		schedule_work(&acm->work);  } @@ -557,7 +550,8 @@ static void acm_tty_unregister(struct acm *acm)  	usb_put_intf(acm->control);  	acm_table[acm->minor] = NULL;  	usb_free_urb(acm->ctrlurb); -	usb_free_urb(acm->writeurb); +	for (i = 0; i < ACM_NW; i++) +		usb_free_urb(acm->wb[i].urb);  	for (i = 0; i < nr; i++)  		usb_free_urb(acm->ru[i].urb);  	kfree(acm->country_codes); @@ -578,7 +572,8 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)  		if (acm->dev) {  			acm_set_control(acm, acm->ctrlout = 0);  			usb_kill_urb(acm->ctrlurb); -			usb_kill_urb(acm->writeurb); +			for (i = 0; i < ACM_NW; i++) +				usb_kill_urb(acm->wb[i].urb);  			for (i = 0; i < nr; i++)  				usb_kill_urb(acm->ru[i].urb);  			usb_autopm_put_interface(acm->control); @@ -606,7 +601,6 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c  	spin_lock_irqsave(&acm->write_lock, flags);  	if ((wbn = acm_wb_alloc(acm)) < 0) {  		spin_unlock_irqrestore(&acm->write_lock, flags); -		acm_write_start(acm);  		return 0;  	}  	wb = &acm->wb[wbn]; @@ -617,7 +611,7 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c  	wb->len = count;  	spin_unlock_irqrestore(&acm->write_lock, flags); -	if ((stat = acm_write_start(acm)) < 0) +	if ((stat = acm_write_start(acm, wbn)) < 0)  		return stat;  	return count;  } @@ -977,7 +971,7 @@ skip_normal_probe:  	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);  	readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2); -	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize); +	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;  	acm->control = control_interface;  	acm->data = data_interface;  	acm->minor = minor; @@ -1032,10 +1026,19 @@ skip_normal_probe:  			goto alloc_fail7;  		}  	} -	acm->writeurb = usb_alloc_urb(0, GFP_KERNEL); -	if (!acm->writeurb) { -		dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n"); -		goto alloc_fail7; +	for(i = 0; i < ACM_NW; i++) +	{ +		struct acm_wb *snd = &(acm->wb[i]); + +		if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) { +			dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)"); +			goto alloc_fail7; +		} + +		usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), +				NULL, acm->writesize, acm_write_bulk, snd); +		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; +		snd->instance = acm;  	}  	usb_set_intfdata (intf, acm); @@ -1071,10 +1074,6 @@ skip_countries:  	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  	acm->ctrlurb->transfer_dma = acm->ctrl_dma; -	usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), -			  NULL, acm->writesize, acm_write_bulk, acm); -	acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; -  	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);  	acm_set_control(acm, acm->ctrlout); @@ -1092,7 +1091,8 @@ skip_countries:  	return 0;  alloc_fail8: -	usb_free_urb(acm->writeurb); +	for (i = 0; i < ACM_NW; i++) +		usb_free_urb(acm->wb[i].urb);  alloc_fail7:  	for (i = 0; i < num_rx_buf; i++)  		usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); @@ -1116,7 +1116,8 @@ static void stop_data_traffic(struct acm *acm)  	tasklet_disable(&acm->urb_task);  	usb_kill_urb(acm->ctrlurb); -	usb_kill_urb(acm->writeurb); +	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->ru[i].urb); diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 8df6a57dcf9..046e064b033 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -59,7 +59,7 @@   * when processing onlcr, so we only need 2 buffers. These values must be   * powers of 2.   */ -#define ACM_NW  2 +#define ACM_NW  16  #define ACM_NR  16  struct acm_wb { @@ -67,6 +67,8 @@ struct acm_wb {  	dma_addr_t dmah;  	int len;  	int use; +	struct urb		*urb; +	struct acm		*instance;  };  struct acm_rb { @@ -88,7 +90,7 @@ struct acm {  	struct usb_interface *control;			/* control interface */  	struct usb_interface *data;			/* data interface */  	struct tty_struct *tty;				/* the corresponding tty */ -	struct urb *ctrlurb, *writeurb;			/* urbs */ +	struct urb *ctrlurb;			/* urbs */  	u8 *ctrl_buffer;				/* buffers of urbs */  	dma_addr_t ctrl_dma;				/* dma handles of buffers */  	u8 *country_codes;				/* country codes from device */ @@ -103,7 +105,6 @@ struct acm {  	struct list_head spare_read_urbs;  	struct list_head spare_read_bufs;  	struct list_head filled_read_bufs; -	int write_current;				/* current write buffer */  	int write_used;					/* number of non-empty write buffers */  	int write_ready;				/* write urb is not running */  	spinlock_t write_lock;  |