|
@@ -31,6 +31,7 @@
|
|
* v0.23 - use softirq for rx processing, as needed by tty layer
|
|
* v0.23 - use softirq for rx processing, as needed by tty layer
|
|
* v0.24 - change probe method to evaluate CDC union descriptor
|
|
* v0.24 - change probe method to evaluate CDC union descriptor
|
|
* v0.25 - downstream tasks paralelized to maximize throughput
|
|
* v0.25 - downstream tasks paralelized to maximize throughput
|
|
|
|
+ * v0.26 - multiple write urbs, writesize increased
|
|
*/
|
|
*/
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -72,7 +73,7 @@
|
|
/*
|
|
/*
|
|
* Version Information
|
|
* 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_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"
|
|
#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;
|
|
int i, wbn;
|
|
struct acm_wb *wb;
|
|
struct acm_wb *wb;
|
|
|
|
|
|
- wbn = acm->write_current;
|
|
|
|
|
|
+ wbn = 0;
|
|
i = 0;
|
|
i = 0;
|
|
for (;;) {
|
|
for (;;) {
|
|
wb = &acm->wb[wbn];
|
|
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)
|
|
static int acm_wb_is_avail(struct acm *acm)
|
|
{
|
|
{
|
|
int i, n;
|
|
int i, n;
|
|
@@ -156,26 +152,22 @@ static inline int acm_wb_is_used(struct acm *acm, int wbn)
|
|
/*
|
|
/*
|
|
* Finish write.
|
|
* 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;
|
|
unsigned long flags;
|
|
- int wbn;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&acm->write_lock, flags);
|
|
spin_lock_irqsave(&acm->write_lock, flags);
|
|
acm->write_ready = 1;
|
|
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);
|
|
spin_unlock_irqrestore(&acm->write_lock, flags);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
* Poke write.
|
|
* Poke write.
|
|
*/
|
|
*/
|
|
-static int acm_write_start(struct acm *acm)
|
|
|
|
|
|
+static int acm_write_start(struct acm *acm, int wbn)
|
|
{
|
|
{
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
- int wbn;
|
|
|
|
struct acm_wb *wb;
|
|
struct acm_wb *wb;
|
|
int rc;
|
|
int rc;
|
|
|
|
|
|
@@ -190,24 +182,24 @@ static int acm_write_start(struct acm *acm)
|
|
return 0; /* A white lie */
|
|
return 0; /* A white lie */
|
|
}
|
|
}
|
|
|
|
|
|
- wbn = acm->write_current;
|
|
|
|
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];
|
|
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);
|
|
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);
|
|
dbg("usb_submit_urb(write bulk) failed: %d", rc);
|
|
- acm_write_done(acm);
|
|
|
|
|
|
+ acm_write_done(acm, wb);
|
|
}
|
|
}
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
@@ -450,12 +442,13 @@ urbs:
|
|
/* data interface wrote those outgoing bytes */
|
|
/* data interface wrote those outgoing bytes */
|
|
static void acm_write_bulk(struct urb *urb)
|
|
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);
|
|
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))
|
|
if (ACM_READY(acm))
|
|
schedule_work(&acm->work);
|
|
schedule_work(&acm->work);
|
|
}
|
|
}
|
|
@@ -557,7 +550,8 @@ static void acm_tty_unregister(struct acm *acm)
|
|
usb_put_intf(acm->control);
|
|
usb_put_intf(acm->control);
|
|
acm_table[acm->minor] = NULL;
|
|
acm_table[acm->minor] = NULL;
|
|
usb_free_urb(acm->ctrlurb);
|
|
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++)
|
|
for (i = 0; i < nr; i++)
|
|
usb_free_urb(acm->ru[i].urb);
|
|
usb_free_urb(acm->ru[i].urb);
|
|
kfree(acm->country_codes);
|
|
kfree(acm->country_codes);
|
|
@@ -578,7 +572,8 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
|
|
if (acm->dev) {
|
|
if (acm->dev) {
|
|
acm_set_control(acm, acm->ctrlout = 0);
|
|
acm_set_control(acm, acm->ctrlout = 0);
|
|
usb_kill_urb(acm->ctrlurb);
|
|
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++)
|
|
for (i = 0; i < nr; i++)
|
|
usb_kill_urb(acm->ru[i].urb);
|
|
usb_kill_urb(acm->ru[i].urb);
|
|
usb_autopm_put_interface(acm->control);
|
|
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);
|
|
spin_lock_irqsave(&acm->write_lock, flags);
|
|
if ((wbn = acm_wb_alloc(acm)) < 0) {
|
|
if ((wbn = acm_wb_alloc(acm)) < 0) {
|
|
spin_unlock_irqrestore(&acm->write_lock, flags);
|
|
spin_unlock_irqrestore(&acm->write_lock, flags);
|
|
- acm_write_start(acm);
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
wb = &acm->wb[wbn];
|
|
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;
|
|
wb->len = count;
|
|
spin_unlock_irqrestore(&acm->write_lock, flags);
|
|
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 stat;
|
|
return count;
|
|
return count;
|
|
}
|
|
}
|
|
@@ -977,7 +971,7 @@ skip_normal_probe:
|
|
|
|
|
|
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
|
|
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
|
|
readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
|
|
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->control = control_interface;
|
|
acm->data = data_interface;
|
|
acm->data = data_interface;
|
|
acm->minor = minor;
|
|
acm->minor = minor;
|
|
@@ -1032,10 +1026,19 @@ skip_normal_probe:
|
|
goto alloc_fail7;
|
|
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);
|
|
usb_set_intfdata (intf, acm);
|
|
@@ -1071,10 +1074,6 @@ skip_countries:
|
|
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
|
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
|
acm->ctrlurb->transfer_dma = acm->ctrl_dma;
|
|
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);
|
|
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
|
|
|
|
|
|
acm_set_control(acm, acm->ctrlout);
|
|
acm_set_control(acm, acm->ctrlout);
|
|
@@ -1092,7 +1091,8 @@ skip_countries:
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
alloc_fail8:
|
|
alloc_fail8:
|
|
- usb_free_urb(acm->writeurb);
|
|
|
|
|
|
+ for (i = 0; i < ACM_NW; i++)
|
|
|
|
+ usb_free_urb(acm->wb[i].urb);
|
|
alloc_fail7:
|
|
alloc_fail7:
|
|
for (i = 0; i < num_rx_buf; i++)
|
|
for (i = 0; i < num_rx_buf; i++)
|
|
usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
|
|
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);
|
|
tasklet_disable(&acm->urb_task);
|
|
|
|
|
|
usb_kill_urb(acm->ctrlurb);
|
|
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++)
|
|
for (i = 0; i < acm->rx_buflimit; i++)
|
|
usb_kill_urb(acm->ru[i].urb);
|
|
usb_kill_urb(acm->ru[i].urb);
|
|
|
|
|