Re: [CDC-ACM] Linux USB "Reduced" CDC-ACM driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Am Freitag 24 April 2009 03:32:46 schrieb Xiaofan Chen:

> So I guess there are some USB composite device with
> this kind of non-compliant CDC interface since Windows does not
> support IAD well last time (XP SP3 and Vista SP1 works). But if it is
> not a composite device, this is the first time I know of a non-compliant
> CDC-ACM device trying to use Windows' built-in usbser.sys. If it
> has its own driver, that is another story.

This patch should make Linux support such devices without the
need for quirks. Please test.

	Regards
		Oliver
commit 401f3356bef24f90df863a7321509eeb16533738
Author: Oliver Neukum <oneukum@linux-d698.(none)>
Date:   Sun Apr 26 17:25:47 2009 +0200

    let cdc-acm handle devices that uses only one interface

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 0a69c09..85956e1 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -891,9 +891,9 @@ static int acm_probe (struct usb_interface *intf,
 	int buflen = intf->altsetting->extralen;
 	struct usb_interface *control_interface;
 	struct usb_interface *data_interface;
-	struct usb_endpoint_descriptor *epctrl;
-	struct usb_endpoint_descriptor *epread;
-	struct usb_endpoint_descriptor *epwrite;
+	struct usb_endpoint_descriptor *epctrl = NULL;
+	struct usb_endpoint_descriptor *epread = NULL;
+	struct usb_endpoint_descriptor *epwrite = NULL;
 	struct usb_device *usb_dev = interface_to_usbdev(intf);
 	struct acm *acm;
 	int minor;
@@ -906,6 +906,7 @@ static int acm_probe (struct usb_interface *intf,
 	unsigned long quirks;
 	int num_rx_buf;
 	int i;
+	int combined_interfaces = 0;
 
 	/* normal quirks */
 	quirks = (unsigned long)id->driver_info;
@@ -1004,6 +1005,30 @@ next_desc:
 	if (data_interface_num != call_interface_num)
 		dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n");
 
+	if (control_interface == data_interface) {
+		/* some broken devices designed for windows work this way */
+		dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
+		combined_interfaces = 1;
+		if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
+			dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
+			return -EINVAL;
+		}
+		for (i = 0; i < 3; i++) {
+			struct usb_endpoint_descriptor *ep;
+			ep = &data_interface->cur_altsetting->endpoint[i].desc;
+
+			if (usb_endpoint_is_int_in(ep))
+				epctrl = ep;
+			else if (usb_endpoint_is_bulk_out(ep))
+				epwrite = ep;
+			else if (usb_endpoint_is_bulk_in(ep))
+				epread = ep;
+			else
+				return -EINVAL;
+		}
+		goto made_compressed_probe;
+	}
+
 skip_normal_probe:
 
 	/*workaround for switched interfaces */
@@ -1048,6 +1073,7 @@ skip_normal_probe:
 		epread = epwrite;
 		epwrite = t;
 	}
+made_compressed_probe:
 	dbg("interfaces are valid");
 	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
 
@@ -1063,6 +1089,7 @@ skip_normal_probe:
 
 	ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
 	readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
+	acm->combined_interfaces = combined_interfaces;
 	acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
 	acm->control = control_interface;
 	acm->data = data_interface;
@@ -1165,7 +1192,9 @@ skip_normal_probe:
 
 skip_countries:
 	usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
-			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
+			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
+			 /* works around buggy devices */
+			 epctrl->bInterval ? epctrl->bInterval : 0xff);
 	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
 
@@ -1250,7 +1279,8 @@ static void acm_disconnect(struct usb_interface *intf)
 	usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
 	acm_read_buffers_free(acm);
 
-	usb_driver_release_interface(&acm_driver, intf == acm->control ?
+	if (!acm->combined_interfaces)
+		usb_driver_release_interface(&acm_driver, intf == acm->control ?
 					acm->data : acm->control);
 
 	if (!acm->used) {
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 1f95e7a..023ebe9 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -126,6 +126,7 @@ struct acm {
 	unsigned char clocal;				/* termios CLOCAL */
 	unsigned int ctrl_caps;				/* control capabilities from the class specific header */
 	unsigned int susp_count;			/* number of suspended interfaces */
+	int combined_interfaces:1;			/* control and data collapsed */
 	struct acm_wb *delayed_wb;			/* write queued for a device about to be woken */
 };
 

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux