* 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
*/
/*
/*
* 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"
int i, wbn;
struct acm_wb *wb;
- wbn = acm->write_current;
+ wbn = 0;
i = 0;
for (;;) {
wb = &acm->wb[wbn];
}
}
-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;
/*
* 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;
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;
}
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, status);
+ dbg("%s - urb shutting down with status: %d", __func__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, status);
+ dbg("%s - nonzero urb status received: %d", __func__, status);
goto exit;
}
retval = usb_submit_urb (urb, GFP_ATOMIC);
if (retval)
err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, retval);
+ __func__, retval);
}
/* data interface returns incoming bytes, or we got unthrottled */
return;
if (status)
- dev_dbg(&acm->data->dev, "bulk rx status %d", status);
+ dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
buf = rcv->buffer;
buf->size = urb->actual_length;
/* 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);
}
else
rv = 0;
+ set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
tty->driver_data = acm;
acm->tty = tty;
otherwise it is scheduled, and with high data rates data can get lost. */
tty->low_latency = 1;
+ if (usb_autopm_get_interface(acm->control) < 0)
+ goto early_bail;
+
+ mutex_lock(&acm->mutex);
if (acm->used++) {
+ usb_autopm_put_interface(acm->control);
goto done;
}
+
acm->ctrlurb->dev = acm->dev;
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
dbg("usb_submit_urb(ctrl irq) failed");
done:
err_out:
+ mutex_unlock(&acm->mutex);
mutex_unlock(&open_mutex);
return rv;
full_bailout:
usb_kill_urb(acm->ctrlurb);
bail_out:
+ usb_autopm_put_interface(acm->control);
acm->used--;
+ mutex_unlock(&acm->mutex);
+early_bail:
mutex_unlock(&open_mutex);
return -EIO;
}
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);
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);
} else
acm_tty_unregister(acm);
}
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];
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;
}
{
struct usb_cdc_union_desc *union_header = NULL;
struct usb_cdc_country_functional_desc *cfd = NULL;
- char *buffer = intf->altsetting->extra;
+ unsigned char *buffer = intf->altsetting->extra;
int buflen = intf->altsetting->extralen;
struct usb_interface *control_interface;
struct usb_interface *data_interface;
/* normal probing*/
if (!buffer) {
- err("Wierd descriptor references\n");
+ err("Weird descriptor references\n");
return -EINVAL;
}
if (!buflen) {
if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
- dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint");
+ dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
buflen = intf->cur_altsetting->endpoint->extralen;
buffer = intf->cur_altsetting->endpoint->extra;
} else {
if ((call_management_function & 3) != 3)
err("This device cannot do calls on its own. It is no modem.");
break;
-
default:
- err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]);
+ /* there are LOTS more CDC descriptors that
+ * could legitimately be found here.
+ */
+ dev_dbg(&intf->dev, "Ignoring descriptor: "
+ "type %02x, length %d\n",
+ buffer[2], buffer[0]);
break;
}
next_desc:
if (!union_header) {
if (call_interface_num > 0) {
- dev_dbg(&intf->dev,"No union descriptor, using call management descriptor");
+ dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
control_interface = intf;
} else {
- dev_dbg(&intf->dev,"No union descriptor, giving up");
+ dev_dbg(&intf->dev,"No union descriptor, giving up\n");
return -ENODEV;
}
} else {
control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
if (!control_interface || !data_interface) {
- dev_dbg(&intf->dev,"no interfaces");
+ dev_dbg(&intf->dev,"no interfaces\n");
return -ENODEV;
}
}
if (data_interface_num != call_interface_num)
- dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.");
+ dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n");
skip_normal_probe:
if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
struct usb_interface *t;
- dev_dbg(&intf->dev,"Your device has switched interfaces.");
+ dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
t = control_interface;
control_interface = data_interface;
return -ENODEV;
if (usb_interface_claimed(data_interface)) { /* valid in this context */
- dev_dbg(&intf->dev,"The data interface isn't available");
+ dev_dbg(&intf->dev,"The data interface isn't available\n");
return -EBUSY;
}
if (!usb_endpoint_dir_in(epread)) {
/* descriptors are swapped */
struct usb_endpoint_descriptor *t;
- dev_dbg(&intf->dev,"The data interface has switched endpoints");
+ dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
t = epread;
epread = epwrite;
}
if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
- dev_dbg(&intf->dev, "out of memory (acm kzalloc)");
+ dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
goto alloc_fail;
}
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;
spin_lock_init(&acm->throttle_lock);
spin_lock_init(&acm->write_lock);
spin_lock_init(&acm->read_lock);
+ mutex_init(&acm->mutex);
acm->write_ready = 1;
acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
if (!buf) {
- dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)");
+ dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
goto alloc_fail2;
}
acm->ctrl_buffer = buf;
if (acm_write_buffers_alloc(acm) < 0) {
- dev_dbg(&intf->dev, "out of memory (write buffer alloc)");
+ dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
goto alloc_fail4;
}
acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->ctrlurb) {
- dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)");
+ dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
goto alloc_fail5;
}
for (i = 0; i < num_rx_buf; i++) {
struct acm_ru *rcv = &(acm->ru[i]);
if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
- dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)");
+ dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
goto alloc_fail7;
}
struct acm_rb *buf = &(acm->rb[i]);
if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
- dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)");
+ dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
goto alloc_fail7;
}
}
- acm->writeurb = usb_alloc_urb(0, GFP_KERNEL);
- if (!acm->writeurb) {
- dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)");
- 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);
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);
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);
return -ENOMEM;
}
+static void stop_data_traffic(struct acm *acm)
+{
+ int i;
+
+ tasklet_disable(&acm->urb_task);
+
+ 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->ru[i].urb);
+
+ INIT_LIST_HEAD(&acm->filled_read_bufs);
+ INIT_LIST_HEAD(&acm->spare_read_bufs);
+
+ tasklet_enable(&acm->urb_task);
+
+ cancel_work_sync(&acm->work);
+}
+
static void acm_disconnect(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata(intf);
usb_set_intfdata(acm->control, NULL);
usb_set_intfdata(acm->data, NULL);
- tasklet_disable(&acm->urb_task);
-
- usb_kill_urb(acm->ctrlurb);
- usb_kill_urb(acm->writeurb);
- for (i = 0; i < acm->rx_buflimit; i++)
- usb_kill_urb(acm->ru[i].urb);
-
- INIT_LIST_HEAD(&acm->filled_read_bufs);
- INIT_LIST_HEAD(&acm->spare_read_bufs);
-
- tasklet_enable(&acm->urb_task);
-
- flush_scheduled_work(); /* wait for acm_softint */
+ stop_data_traffic(acm);
acm_write_buffers_free(acm);
usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
tty_hangup(acm->tty);
}
+static int acm_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct acm *acm = usb_get_intfdata(intf);
+
+ if (acm->susp_count++)
+ return 0;
+ /*
+ we treat opened interfaces differently,
+ we must guard against open
+ */
+ mutex_lock(&acm->mutex);
+
+ if (acm->used)
+ stop_data_traffic(acm);
+
+ mutex_unlock(&acm->mutex);
+ return 0;
+}
+
+static int acm_resume(struct usb_interface *intf)
+{
+ struct acm *acm = usb_get_intfdata(intf);
+ int rv = 0;
+
+ if (--acm->susp_count)
+ return 0;
+
+ mutex_lock(&acm->mutex);
+ if (acm->used) {
+ rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
+ if (rv < 0)
+ goto err_out;
+
+ tasklet_schedule(&acm->urb_task);
+ }
+
+err_out:
+ mutex_unlock(&acm->mutex);
+ return rv;
+}
/*
* USB driver structure.
*/
.name = "cdc_acm",
.probe = acm_probe,
.disconnect = acm_disconnect,
+ .suspend = acm_suspend,
+ .resume = acm_resume,
.id_table = acm_ids,
+ .supports_autosuspend = 1,
};
/*