diff options
author | Oliver Neukum <oliver@neukum.org> | 2009-05-16 15:13:19 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-16 00:44:45 -0400 |
commit | a2bfb4a346d2c2e25f84b35c6044ff53296be1ee (patch) | |
tree | 494cea0b367252d248a73bd8fe2490d0539ac5f1 /drivers/usb/class/cdc-acm.c | |
parent | 0b10395ab86c11bef10e882a4323367e6735c9b2 (diff) |
USB: support for cdc-acm of single interface devices
This implement support in cdc-acm for acm devices another popular OS can handle
- adds support for autodetection of devices that use one interface
- autodetection of endpoints
- add a quirk for surpressing a setting that OS doesn't use
- autoassume that quirk for single interface devices
Signed-off-by: Oliver Neukum <oliver@neukum.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 68 |
1 files changed, 56 insertions, 12 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index ddeb69192537..778e023d1467 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c | |||
@@ -937,9 +937,9 @@ static int acm_probe(struct usb_interface *intf, | |||
937 | int buflen = intf->altsetting->extralen; | 937 | int buflen = intf->altsetting->extralen; |
938 | struct usb_interface *control_interface; | 938 | struct usb_interface *control_interface; |
939 | struct usb_interface *data_interface; | 939 | struct usb_interface *data_interface; |
940 | struct usb_endpoint_descriptor *epctrl; | 940 | struct usb_endpoint_descriptor *epctrl = NULL; |
941 | struct usb_endpoint_descriptor *epread; | 941 | struct usb_endpoint_descriptor *epread = NULL; |
942 | struct usb_endpoint_descriptor *epwrite; | 942 | struct usb_endpoint_descriptor *epwrite = NULL; |
943 | struct usb_device *usb_dev = interface_to_usbdev(intf); | 943 | struct usb_device *usb_dev = interface_to_usbdev(intf); |
944 | struct acm *acm; | 944 | struct acm *acm; |
945 | int minor; | 945 | int minor; |
@@ -952,6 +952,7 @@ static int acm_probe(struct usb_interface *intf, | |||
952 | unsigned long quirks; | 952 | unsigned long quirks; |
953 | int num_rx_buf; | 953 | int num_rx_buf; |
954 | int i; | 954 | int i; |
955 | int combined_interfaces = 0; | ||
955 | 956 | ||
956 | /* normal quirks */ | 957 | /* normal quirks */ |
957 | quirks = (unsigned long)id->driver_info; | 958 | quirks = (unsigned long)id->driver_info; |
@@ -1033,9 +1034,15 @@ next_desc: | |||
1033 | data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); | 1034 | data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); |
1034 | control_interface = intf; | 1035 | control_interface = intf; |
1035 | } else { | 1036 | } else { |
1036 | dev_dbg(&intf->dev, | 1037 | if (intf->cur_altsetting->desc.bNumEndpoints != 3) { |
1037 | "No union descriptor, giving up\n"); | 1038 | dev_dbg(&intf->dev,"No union descriptor, giving up\n"); |
1038 | return -ENODEV; | 1039 | return -ENODEV; |
1040 | } else { | ||
1041 | dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n"); | ||
1042 | combined_interfaces = 1; | ||
1043 | control_interface = data_interface = intf; | ||
1044 | goto look_for_collapsed_interface; | ||
1045 | } | ||
1039 | } | 1046 | } |
1040 | } else { | 1047 | } else { |
1041 | control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); | 1048 | control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); |
@@ -1049,6 +1056,36 @@ next_desc: | |||
1049 | if (data_interface_num != call_interface_num) | 1056 | if (data_interface_num != call_interface_num) |
1050 | dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n"); | 1057 | dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n"); |
1051 | 1058 | ||
1059 | if (control_interface == data_interface) { | ||
1060 | /* some broken devices designed for windows work this way */ | ||
1061 | dev_warn(&intf->dev,"Control and data interfaces are not separated!\n"); | ||
1062 | combined_interfaces = 1; | ||
1063 | /* a popular other OS doesn't use it */ | ||
1064 | quirks |= NO_CAP_LINE; | ||
1065 | if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) { | ||
1066 | dev_err(&intf->dev, "This needs exactly 3 endpoints\n"); | ||
1067 | return -EINVAL; | ||
1068 | } | ||
1069 | look_for_collapsed_interface: | ||
1070 | for (i = 0; i < 3; i++) { | ||
1071 | struct usb_endpoint_descriptor *ep; | ||
1072 | ep = &data_interface->cur_altsetting->endpoint[i].desc; | ||
1073 | |||
1074 | if (usb_endpoint_is_int_in(ep)) | ||
1075 | epctrl = ep; | ||
1076 | else if (usb_endpoint_is_bulk_out(ep)) | ||
1077 | epwrite = ep; | ||
1078 | else if (usb_endpoint_is_bulk_in(ep)) | ||
1079 | epread = ep; | ||
1080 | else | ||
1081 | return -EINVAL; | ||
1082 | } | ||
1083 | if (!epctrl || !epread || !epwrite) | ||
1084 | return -ENODEV; | ||
1085 | else | ||
1086 | goto made_compressed_probe; | ||
1087 | } | ||
1088 | |||
1052 | skip_normal_probe: | 1089 | skip_normal_probe: |
1053 | 1090 | ||
1054 | /*workaround for switched interfaces */ | 1091 | /*workaround for switched interfaces */ |
@@ -1068,10 +1105,11 @@ skip_normal_probe: | |||
1068 | } | 1105 | } |
1069 | 1106 | ||
1070 | /* Accept probe requests only for the control interface */ | 1107 | /* Accept probe requests only for the control interface */ |
1071 | if (intf != control_interface) | 1108 | if (!combined_interfaces && intf != control_interface) |
1072 | return -ENODEV; | 1109 | return -ENODEV; |
1073 | 1110 | ||
1074 | if (usb_interface_claimed(data_interface)) { /* valid in this context */ | 1111 | if (!combined_interfaces && usb_interface_claimed(data_interface)) { |
1112 | /* valid in this context */ | ||
1075 | dev_dbg(&intf->dev, "The data interface isn't available\n"); | 1113 | dev_dbg(&intf->dev, "The data interface isn't available\n"); |
1076 | return -EBUSY; | 1114 | return -EBUSY; |
1077 | } | 1115 | } |
@@ -1095,6 +1133,7 @@ skip_normal_probe: | |||
1095 | epread = epwrite; | 1133 | epread = epwrite; |
1096 | epwrite = t; | 1134 | epwrite = t; |
1097 | } | 1135 | } |
1136 | made_compressed_probe: | ||
1098 | dbg("interfaces are valid"); | 1137 | dbg("interfaces are valid"); |
1099 | for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); | 1138 | for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); |
1100 | 1139 | ||
@@ -1112,12 +1151,15 @@ skip_normal_probe: | |||
1112 | ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); | 1151 | ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); |
1113 | readsize = le16_to_cpu(epread->wMaxPacketSize) * | 1152 | readsize = le16_to_cpu(epread->wMaxPacketSize) * |
1114 | (quirks == SINGLE_RX_URB ? 1 : 2); | 1153 | (quirks == SINGLE_RX_URB ? 1 : 2); |
1154 | acm->combined_interfaces = combined_interfaces; | ||
1115 | acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; | 1155 | acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; |
1116 | acm->control = control_interface; | 1156 | acm->control = control_interface; |
1117 | acm->data = data_interface; | 1157 | acm->data = data_interface; |
1118 | acm->minor = minor; | 1158 | acm->minor = minor; |
1119 | acm->dev = usb_dev; | 1159 | acm->dev = usb_dev; |
1120 | acm->ctrl_caps = ac_management_function; | 1160 | acm->ctrl_caps = ac_management_function; |
1161 | if (quirks & NO_CAP_LINE) | ||
1162 | acm->ctrl_caps &= ~USB_CDC_CAP_LINE; | ||
1121 | acm->ctrlsize = ctrlsize; | 1163 | acm->ctrlsize = ctrlsize; |
1122 | acm->readsize = readsize; | 1164 | acm->readsize = readsize; |
1123 | acm->rx_buflimit = num_rx_buf; | 1165 | acm->rx_buflimit = num_rx_buf; |
@@ -1223,9 +1265,10 @@ skip_normal_probe: | |||
1223 | 1265 | ||
1224 | skip_countries: | 1266 | skip_countries: |
1225 | usb_fill_int_urb(acm->ctrlurb, usb_dev, | 1267 | usb_fill_int_urb(acm->ctrlurb, usb_dev, |
1226 | usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), | 1268 | usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), |
1227 | acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, | 1269 | acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, |
1228 | epctrl->bInterval); | 1270 | /* works around buggy devices */ |
1271 | epctrl->bInterval ? epctrl->bInterval : 0xff); | ||
1229 | acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | 1272 | acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
1230 | acm->ctrlurb->transfer_dma = acm->ctrl_dma; | 1273 | acm->ctrlurb->transfer_dma = acm->ctrl_dma; |
1231 | 1274 | ||
@@ -1312,7 +1355,8 @@ static void acm_disconnect(struct usb_interface *intf) | |||
1312 | acm->ctrl_dma); | 1355 | acm->ctrl_dma); |
1313 | acm_read_buffers_free(acm); | 1356 | acm_read_buffers_free(acm); |
1314 | 1357 | ||
1315 | usb_driver_release_interface(&acm_driver, intf == acm->control ? | 1358 | if (!acm->combined_interfaces) |
1359 | usb_driver_release_interface(&acm_driver, intf == acm->control ? | ||
1316 | acm->data : acm->control); | 1360 | acm->data : acm->control); |
1317 | 1361 | ||
1318 | if (acm->port.count == 0) { | 1362 | if (acm->port.count == 0) { |