| .. | .. |
|---|
| 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; |
|---|