.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
1 | 2 | /* |
---|
2 | 3 | * Gadget Function Driver for Android USB accessories |
---|
3 | 4 | * |
---|
.. | .. |
---|
119 | 120 | /* delayed work for handling ACCESSORY_START */ |
---|
120 | 121 | struct delayed_work start_work; |
---|
121 | 122 | |
---|
| 123 | + /* work for handling ACCESSORY GET PROTOCOL */ |
---|
| 124 | + struct work_struct getprotocol_work; |
---|
| 125 | + |
---|
| 126 | + /* work for handling ACCESSORY SEND STRING */ |
---|
| 127 | + struct work_struct sendstring_work; |
---|
| 128 | + |
---|
122 | 129 | /* worker for registering and unregistering hid devices */ |
---|
123 | 130 | struct work_struct hid_work; |
---|
124 | 131 | |
---|
.. | .. |
---|
142 | 149 | .bInterfaceProtocol = 0, |
---|
143 | 150 | }; |
---|
144 | 151 | |
---|
| 152 | +static struct usb_endpoint_descriptor acc_superspeedplus_in_desc = { |
---|
| 153 | + .bLength = USB_DT_ENDPOINT_SIZE, |
---|
| 154 | + .bDescriptorType = USB_DT_ENDPOINT, |
---|
| 155 | + .bEndpointAddress = USB_DIR_IN, |
---|
| 156 | + .bmAttributes = USB_ENDPOINT_XFER_BULK, |
---|
| 157 | + .wMaxPacketSize = cpu_to_le16(1024), |
---|
| 158 | +}; |
---|
| 159 | + |
---|
| 160 | +static struct usb_endpoint_descriptor acc_superspeedplus_out_desc = { |
---|
| 161 | + .bLength = USB_DT_ENDPOINT_SIZE, |
---|
| 162 | + .bDescriptorType = USB_DT_ENDPOINT, |
---|
| 163 | + .bEndpointAddress = USB_DIR_OUT, |
---|
| 164 | + .bmAttributes = USB_ENDPOINT_XFER_BULK, |
---|
| 165 | + .wMaxPacketSize = cpu_to_le16(1024), |
---|
| 166 | +}; |
---|
| 167 | + |
---|
| 168 | +static struct usb_ss_ep_comp_descriptor acc_superspeedplus_comp_desc = { |
---|
| 169 | + .bLength = sizeof(acc_superspeedplus_comp_desc), |
---|
| 170 | + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, |
---|
| 171 | + |
---|
| 172 | + /* the following 2 values can be tweaked if necessary */ |
---|
| 173 | + /* .bMaxBurst = 0, */ |
---|
| 174 | + /* .bmAttributes = 0, */ |
---|
| 175 | +}; |
---|
| 176 | + |
---|
| 177 | +static struct usb_endpoint_descriptor acc_superspeed_in_desc = { |
---|
| 178 | + .bLength = USB_DT_ENDPOINT_SIZE, |
---|
| 179 | + .bDescriptorType = USB_DT_ENDPOINT, |
---|
| 180 | + .bEndpointAddress = USB_DIR_IN, |
---|
| 181 | + .bmAttributes = USB_ENDPOINT_XFER_BULK, |
---|
| 182 | + .wMaxPacketSize = cpu_to_le16(1024), |
---|
| 183 | +}; |
---|
| 184 | + |
---|
| 185 | +static struct usb_endpoint_descriptor acc_superspeed_out_desc = { |
---|
| 186 | + .bLength = USB_DT_ENDPOINT_SIZE, |
---|
| 187 | + .bDescriptorType = USB_DT_ENDPOINT, |
---|
| 188 | + .bEndpointAddress = USB_DIR_OUT, |
---|
| 189 | + .bmAttributes = USB_ENDPOINT_XFER_BULK, |
---|
| 190 | + .wMaxPacketSize = cpu_to_le16(1024), |
---|
| 191 | +}; |
---|
| 192 | + |
---|
| 193 | +static struct usb_ss_ep_comp_descriptor acc_superspeed_comp_desc = { |
---|
| 194 | + .bLength = sizeof(acc_superspeed_comp_desc), |
---|
| 195 | + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, |
---|
| 196 | + |
---|
| 197 | + /* the following 2 values can be tweaked if necessary */ |
---|
| 198 | + /* .bMaxBurst = 0, */ |
---|
| 199 | + /* .bmAttributes = 0, */ |
---|
| 200 | +}; |
---|
| 201 | + |
---|
145 | 202 | static struct usb_endpoint_descriptor acc_highspeed_in_desc = { |
---|
146 | 203 | .bLength = USB_DT_ENDPOINT_SIZE, |
---|
147 | 204 | .bDescriptorType = USB_DT_ENDPOINT, |
---|
148 | 205 | .bEndpointAddress = USB_DIR_IN, |
---|
149 | 206 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
---|
150 | | - .wMaxPacketSize = __constant_cpu_to_le16(512), |
---|
| 207 | + .wMaxPacketSize = cpu_to_le16(512), |
---|
151 | 208 | }; |
---|
152 | 209 | |
---|
153 | 210 | static struct usb_endpoint_descriptor acc_highspeed_out_desc = { |
---|
.. | .. |
---|
155 | 212 | .bDescriptorType = USB_DT_ENDPOINT, |
---|
156 | 213 | .bEndpointAddress = USB_DIR_OUT, |
---|
157 | 214 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
---|
158 | | - .wMaxPacketSize = __constant_cpu_to_le16(512), |
---|
| 215 | + .wMaxPacketSize = cpu_to_le16(512), |
---|
159 | 216 | }; |
---|
160 | 217 | |
---|
161 | 218 | static struct usb_endpoint_descriptor acc_fullspeed_in_desc = { |
---|
.. | .. |
---|
183 | 240 | (struct usb_descriptor_header *) &acc_interface_desc, |
---|
184 | 241 | (struct usb_descriptor_header *) &acc_highspeed_in_desc, |
---|
185 | 242 | (struct usb_descriptor_header *) &acc_highspeed_out_desc, |
---|
| 243 | + NULL, |
---|
| 244 | +}; |
---|
| 245 | + |
---|
| 246 | +static struct usb_descriptor_header *ss_acc_descs[] = { |
---|
| 247 | + (struct usb_descriptor_header *) &acc_interface_desc, |
---|
| 248 | + (struct usb_descriptor_header *) &acc_superspeed_in_desc, |
---|
| 249 | + (struct usb_descriptor_header *) &acc_superspeed_comp_desc, |
---|
| 250 | + (struct usb_descriptor_header *) &acc_superspeed_out_desc, |
---|
| 251 | + (struct usb_descriptor_header *) &acc_superspeed_comp_desc, |
---|
| 252 | + NULL, |
---|
| 253 | +}; |
---|
| 254 | + |
---|
| 255 | +static struct usb_descriptor_header *ssp_acc_descs[] = { |
---|
| 256 | + (struct usb_descriptor_header *) &acc_interface_desc, |
---|
| 257 | + (struct usb_descriptor_header *) &acc_superspeedplus_in_desc, |
---|
| 258 | + (struct usb_descriptor_header *) &acc_superspeedplus_comp_desc, |
---|
| 259 | + (struct usb_descriptor_header *) &acc_superspeedplus_out_desc, |
---|
| 260 | + (struct usb_descriptor_header *) &acc_superspeedplus_comp_desc, |
---|
186 | 261 | NULL, |
---|
187 | 262 | }; |
---|
188 | 263 | |
---|
.. | .. |
---|
229 | 304 | |
---|
230 | 305 | /* Cancel any async work */ |
---|
231 | 306 | cancel_delayed_work_sync(&dev->start_work); |
---|
| 307 | + cancel_work_sync(&dev->getprotocol_work); |
---|
| 308 | + cancel_work_sync(&dev->sendstring_work); |
---|
232 | 309 | cancel_work_sync(&dev->hid_work); |
---|
233 | 310 | |
---|
234 | 311 | ref->acc_dev = NULL; |
---|
.. | .. |
---|
721 | 798 | } |
---|
722 | 799 | |
---|
723 | 800 | while (count > 0) { |
---|
724 | | - if (!dev->online) { |
---|
| 801 | + /* get an idle tx request to use */ |
---|
| 802 | + req = 0; |
---|
| 803 | + ret = wait_event_interruptible(dev->write_wq, |
---|
| 804 | + ((req = req_get(dev, &dev->tx_idle)) || !dev->online)); |
---|
| 805 | + if (!dev->online || dev->disconnected) { |
---|
725 | 806 | pr_debug("acc_write dev->error\n"); |
---|
726 | 807 | r = -EIO; |
---|
727 | 808 | break; |
---|
728 | 809 | } |
---|
729 | 810 | |
---|
730 | | - /* get an idle tx request to use */ |
---|
731 | | - req = 0; |
---|
732 | | - ret = wait_event_interruptible(dev->write_wq, |
---|
733 | | - ((req = req_get(dev, &dev->tx_idle)) || !dev->online)); |
---|
734 | 811 | if (!req) { |
---|
735 | 812 | r = ret; |
---|
736 | 813 | break; |
---|
.. | .. |
---|
854 | 931 | .read = acc_read, |
---|
855 | 932 | .write = acc_write, |
---|
856 | 933 | .unlocked_ioctl = acc_ioctl, |
---|
857 | | -#ifdef CONFIG_COMPAT |
---|
858 | 934 | .compat_ioctl = acc_ioctl, |
---|
859 | | -#endif |
---|
860 | 935 | .open = acc_open, |
---|
861 | 936 | .release = acc_release, |
---|
862 | 937 | }; |
---|
.. | .. |
---|
926 | 1001 | value = 0; |
---|
927 | 1002 | cdev->req->complete = acc_complete_setup_noop; |
---|
928 | 1003 | } else if (b_request == ACCESSORY_SEND_STRING) { |
---|
| 1004 | + schedule_work(&dev->sendstring_work); |
---|
929 | 1005 | dev->string_index = w_index; |
---|
930 | 1006 | cdev->gadget->ep0->driver_data = dev; |
---|
931 | 1007 | cdev->req->complete = acc_complete_set_string; |
---|
.. | .. |
---|
972 | 1048 | } |
---|
973 | 1049 | } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { |
---|
974 | 1050 | if (b_request == ACCESSORY_GET_PROTOCOL) { |
---|
| 1051 | + schedule_work(&dev->getprotocol_work); |
---|
975 | 1052 | *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; |
---|
976 | 1053 | value = sizeof(u16); |
---|
977 | 1054 | cdev->req->complete = acc_complete_setup_noop; |
---|
.. | .. |
---|
1007 | 1084 | return value; |
---|
1008 | 1085 | } |
---|
1009 | 1086 | EXPORT_SYMBOL_GPL(acc_ctrlrequest); |
---|
| 1087 | + |
---|
| 1088 | +int acc_ctrlrequest_composite(struct usb_composite_dev *cdev, |
---|
| 1089 | + const struct usb_ctrlrequest *ctrl) |
---|
| 1090 | +{ |
---|
| 1091 | + u16 w_length = le16_to_cpu(ctrl->wLength); |
---|
| 1092 | + |
---|
| 1093 | + if (w_length > USB_COMP_EP0_BUFSIZ) { |
---|
| 1094 | + if (ctrl->bRequestType & USB_DIR_IN) { |
---|
| 1095 | + /* Cast away the const, we are going to overwrite on purpose. */ |
---|
| 1096 | + __le16 *temp = (__le16 *)&ctrl->wLength; |
---|
| 1097 | + |
---|
| 1098 | + *temp = cpu_to_le16(USB_COMP_EP0_BUFSIZ); |
---|
| 1099 | + w_length = USB_COMP_EP0_BUFSIZ; |
---|
| 1100 | + } else { |
---|
| 1101 | + return -EINVAL; |
---|
| 1102 | + } |
---|
| 1103 | + } |
---|
| 1104 | + return acc_ctrlrequest(cdev, ctrl); |
---|
| 1105 | +} |
---|
| 1106 | +EXPORT_SYMBOL_GPL(acc_ctrlrequest_composite); |
---|
1010 | 1107 | |
---|
1011 | 1108 | static int |
---|
1012 | 1109 | __acc_function_bind(struct usb_configuration *c, |
---|
.. | .. |
---|
1048 | 1145 | return ret; |
---|
1049 | 1146 | |
---|
1050 | 1147 | /* support high speed hardware */ |
---|
1051 | | - if (gadget_is_dualspeed(c->cdev->gadget)) { |
---|
1052 | | - acc_highspeed_in_desc.bEndpointAddress = |
---|
1053 | | - acc_fullspeed_in_desc.bEndpointAddress; |
---|
1054 | | - acc_highspeed_out_desc.bEndpointAddress = |
---|
1055 | | - acc_fullspeed_out_desc.bEndpointAddress; |
---|
1056 | | - } |
---|
| 1148 | + acc_highspeed_in_desc.bEndpointAddress = |
---|
| 1149 | + acc_fullspeed_in_desc.bEndpointAddress; |
---|
| 1150 | + acc_highspeed_out_desc.bEndpointAddress = |
---|
| 1151 | + acc_fullspeed_out_desc.bEndpointAddress; |
---|
| 1152 | + |
---|
| 1153 | + /* support super speed hardware */ |
---|
| 1154 | + acc_superspeed_in_desc.bEndpointAddress = |
---|
| 1155 | + acc_fullspeed_in_desc.bEndpointAddress; |
---|
| 1156 | + acc_superspeed_out_desc.bEndpointAddress = |
---|
| 1157 | + acc_fullspeed_out_desc.bEndpointAddress; |
---|
| 1158 | + |
---|
| 1159 | + /* support super speed plus hardware */ |
---|
| 1160 | + acc_superspeedplus_in_desc.bEndpointAddress = |
---|
| 1161 | + acc_fullspeed_in_desc.bEndpointAddress; |
---|
| 1162 | + acc_superspeedplus_out_desc.bEndpointAddress = |
---|
| 1163 | + acc_fullspeed_out_desc.bEndpointAddress; |
---|
1057 | 1164 | |
---|
1058 | 1165 | DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", |
---|
1059 | 1166 | gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", |
---|
.. | .. |
---|
1116 | 1223 | } |
---|
1117 | 1224 | |
---|
1118 | 1225 | acc_hid_unbind(dev); |
---|
| 1226 | +} |
---|
| 1227 | + |
---|
| 1228 | +static void acc_getprotocol_work(struct work_struct *data) |
---|
| 1229 | +{ |
---|
| 1230 | + char *envp[2] = { "ACCESSORY=GETPROTOCOL", NULL }; |
---|
| 1231 | + |
---|
| 1232 | + kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); |
---|
| 1233 | +} |
---|
| 1234 | + |
---|
| 1235 | +static void acc_sendstring_work(struct work_struct *data) |
---|
| 1236 | +{ |
---|
| 1237 | + char *envp[2] = { "ACCESSORY=SENDSTRING", NULL }; |
---|
| 1238 | + |
---|
| 1239 | + kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); |
---|
1119 | 1240 | } |
---|
1120 | 1241 | |
---|
1121 | 1242 | static void acc_start_work(struct work_struct *data) |
---|
.. | .. |
---|
1293 | 1414 | INIT_LIST_HEAD(&dev->dead_hid_list); |
---|
1294 | 1415 | INIT_DELAYED_WORK(&dev->start_work, acc_start_work); |
---|
1295 | 1416 | INIT_WORK(&dev->hid_work, acc_hid_work); |
---|
| 1417 | + INIT_WORK(&dev->getprotocol_work, acc_getprotocol_work); |
---|
| 1418 | + INIT_WORK(&dev->sendstring_work, acc_sendstring_work); |
---|
1296 | 1419 | |
---|
1297 | 1420 | dev->ref = ref; |
---|
1298 | 1421 | if (cmpxchg_relaxed(&ref->acc_dev, NULL, dev)) { |
---|
.. | .. |
---|
1436 | 1559 | dev->function.strings = acc_strings, |
---|
1437 | 1560 | dev->function.fs_descriptors = fs_acc_descs; |
---|
1438 | 1561 | dev->function.hs_descriptors = hs_acc_descs; |
---|
| 1562 | + dev->function.ss_descriptors = ss_acc_descs; |
---|
| 1563 | + dev->function.ssp_descriptors = ssp_acc_descs; |
---|
1439 | 1564 | dev->function.bind = acc_function_bind_configfs; |
---|
1440 | 1565 | dev->function.unbind = acc_function_unbind; |
---|
1441 | 1566 | dev->function.set_alt = acc_function_set_alt; |
---|