From 9221f7916ae512d4a43556dd956b62f3414b728b Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Tue, 4 Sep 2018 14:46:36 +0200 Subject: [PATCH] dde_linux: enable QEMU support for new usb_hid_drv --- repos/dde_linux/ports/dde_linux.hash | 2 +- repos/dde_linux/src/drivers/usb_hid/driver.h | 3 - repos/dde_linux/src/drivers/usb_hid/dummies.c | 8 +- .../dde_linux/src/drivers/usb_hid/lx_emul.cc | 255 +++++++++++++++++- repos/dde_linux/src/drivers/usb_hid/lx_emul.h | 9 +- repos/dde_linux/src/drivers/usb_hid/main.cc | 96 +++---- repos/dde_linux/src/drivers/usb_hid/target.mk | 5 + repos/dde_linux/usb_hid.list | 5 + 8 files changed, 305 insertions(+), 78 deletions(-) diff --git a/repos/dde_linux/ports/dde_linux.hash b/repos/dde_linux/ports/dde_linux.hash index ff3ffc271..6e8fc7a76 100644 --- a/repos/dde_linux/ports/dde_linux.hash +++ b/repos/dde_linux/ports/dde_linux.hash @@ -1 +1 @@ -477b1b59ec591bd7ef01c853f15df4acabca6624 +b88acdf93d8db71180a25937fba45ec6b39a2841 diff --git a/repos/dde_linux/src/drivers/usb_hid/driver.h b/repos/dde_linux/src/drivers/usb_hid/driver.h index a05172236..62f6978ba 100644 --- a/repos/dde_linux/src/drivers/usb_hid/driver.h +++ b/repos/dde_linux/src/drivers/usb_hid/driver.h @@ -68,9 +68,6 @@ struct Driver static void urb_task_entry(void *); void register_device(); void unregister_device(); - void scan_interfaces(unsigned iface_idx); - void scan_altsettings(usb_interface * iface, - unsigned iface_idx, unsigned alt_idx); void probe_interface(usb_interface *, usb_device_id *); void remove_interface(usb_interface *); }; diff --git a/repos/dde_linux/src/drivers/usb_hid/dummies.c b/repos/dde_linux/src/drivers/usb_hid/dummies.c index f5f395328..852c98a26 100644 --- a/repos/dde_linux/src/drivers/usb_hid/dummies.c +++ b/repos/dde_linux/src/drivers/usb_hid/dummies.c @@ -334,7 +334,7 @@ void kref_get(struct kref *kref) void kref_init(struct kref *kref) { - TRACE_AND_STOP; + TRACE; } int kref_put(struct kref *kref, void (*release) (struct kref *kref)) @@ -660,3 +660,9 @@ void up(struct semaphore *sem) { TRACE; } + +bool usb_device_is_owned(struct usb_device *udev) +{ + TRACE; + return false; +} diff --git a/repos/dde_linux/src/drivers/usb_hid/lx_emul.cc b/repos/dde_linux/src/drivers/usb_hid/lx_emul.cc index 2cb7649c7..189d0e0a0 100644 --- a/repos/dde_linux/src/drivers/usb_hid/lx_emul.cc +++ b/repos/dde_linux/src/drivers/usb_hid/lx_emul.cc @@ -25,6 +25,73 @@ #include +extern "C" int usb_match_device(struct usb_device *dev, + const struct usb_device_id *id) +{ + if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + id->idVendor != le16_to_cpu(dev->descriptor.idVendor)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && + id->idProduct != le16_to_cpu(dev->descriptor.idProduct)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && + (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice))) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && + (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice))) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && + (id->bDeviceClass != dev->descriptor.bDeviceClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && + (id->bDeviceSubClass != dev->descriptor.bDeviceSubClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && + (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) + return 0; + + return 1; +} + + +extern "C" int usb_match_one_id_intf(struct usb_device *dev, + struct usb_host_interface *intf, + const struct usb_device_id *id) +{ + if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC && + !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS | + USB_DEVICE_ID_MATCH_INT_PROTOCOL | + USB_DEVICE_ID_MATCH_INT_NUMBER))) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && + (id->bInterfaceClass != intf->desc.bInterfaceClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && + (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && + (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) && + (id->bInterfaceNumber != intf->desc.bInterfaceNumber)) + return 0; + + return 1; +} + + struct Lx_driver { using Element = Genode::List_element; @@ -93,12 +160,6 @@ void Driver::Device::probe_interface(usb_interface * iface, usb_device_id * id) void Driver::Device::remove_interface(usb_interface * iface) { hid_driver->disconnect(iface); - for (unsigned i = 0; i < iface->num_altsetting; i++) { - if (iface->altsetting[i].extra) - kfree(iface->altsetting[i].extra); - kfree(iface->altsetting[i].endpoint); - kfree(iface->altsetting); - } kfree(iface); } @@ -308,3 +369,185 @@ void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp) { return kzalloc(size, gfp); } + + +int usb_get_descriptor(struct usb_device *dev, unsigned char type, + unsigned char index, void *buf, int size) +{ + int i; + int result; + + memset(buf, 0, size); + + for (i = 0; i < 3; ++i) { + /* retry on length 0 or error; some devices are flakey */ + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (type << 8) + index, 0, buf, size, + USB_CTRL_GET_TIMEOUT); + if (result <= 0 && result != -ETIMEDOUT) + continue; + if (result > 1 && ((u8 *)buf)[1] != type) { + result = -ENODATA; + continue; + } + break; + } + return result; +} + + + +static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev, + struct usb_host_config *config, + u8 inum) +{ + struct usb_interface_assoc_descriptor *retval = NULL; + struct usb_interface_assoc_descriptor *intf_assoc; + int first_intf; + int last_intf; + int i; + + for (i = 0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) { + intf_assoc = config->intf_assoc[i]; + if (intf_assoc->bInterfaceCount == 0) + continue; + + first_intf = intf_assoc->bFirstInterface; + last_intf = first_intf + (intf_assoc->bInterfaceCount - 1); + if (inum >= first_intf && inum <= last_intf) { + if (!retval) + retval = intf_assoc; + else + dev_err(&dev->dev, "Interface #%d referenced" + " by multiple IADs\n", inum); + } + } + + return retval; +} + + +struct usb_host_interface *usb_altnum_to_altsetting(const struct usb_interface *intf, + unsigned int altnum) +{ + for (unsigned i = 0; i < intf->num_altsetting; i++) { + if (intf->altsetting[i].desc.bAlternateSetting == altnum) + return &intf->altsetting[i]; + } + return NULL; +} + + +int usb_set_configuration(struct usb_device *dev, int configuration) +{ + int i, ret; + struct usb_host_config *cp = NULL; + struct usb_interface **new_interfaces = NULL; + int n, nintf; + + if (dev->authorized == 0 || configuration == -1) + configuration = 0; + else { + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + if (dev->config[i].desc.bConfigurationValue == + configuration) { + cp = &dev->config[i]; + break; + } + } + } + if ((!cp && configuration != 0)) + return -EINVAL; + + /* The USB spec says configuration 0 means unconfigured. + * But if a device includes a configuration numbered 0, + * we will accept it as a correctly configured state. + * Use -1 if you really want to unconfigure the device. + */ + if (cp && configuration == 0) + dev_warn(&dev->dev, "config 0 descriptor??\n"); + + /* Allocate memory for new interfaces before doing anything else, + * so that if we run out then nothing will have changed. */ + n = nintf = 0; + if (cp) { + nintf = cp->desc.bNumInterfaces; + new_interfaces = (struct usb_interface **) + kmalloc(nintf * sizeof(*new_interfaces), GFP_KERNEL); + if (!new_interfaces) + return -ENOMEM; + + for (; n < nintf; ++n) { + new_interfaces[n] = (struct usb_interface*) + kzalloc( sizeof(struct usb_interface), GFP_KERNEL); + if (!new_interfaces[n]) { + ret = -ENOMEM; + while (--n >= 0) + kfree(new_interfaces[n]); + kfree(new_interfaces); + return ret; + } + } + } + + /* + * Initialize the new interface structures and the + * hc/hcd/usbcore interface/endpoint state. + */ + for (i = 0; i < nintf; ++i) { + struct usb_interface_cache *intfc; + struct usb_interface *intf; + struct usb_host_interface *alt; + u8 ifnum; + + cp->interface[i] = intf = new_interfaces[i]; + intfc = cp->intf_cache[i]; + intf->altsetting = intfc->altsetting; + intf->num_altsetting = intfc->num_altsetting; + intf->authorized = 1; //FIXME + + alt = usb_altnum_to_altsetting(intf, 0); + + /* No altsetting 0? We'll assume the first altsetting. + * We could use a GetInterface call, but if a device is + * so non-compliant that it doesn't have altsetting 0 + * then I wouldn't trust its reply anyway. + */ + if (!alt) + alt = &intf->altsetting[0]; + + ifnum = alt->desc.bInterfaceNumber; + intf->intf_assoc = find_iad(dev, cp, ifnum); + intf->cur_altsetting = alt; + intf->dev.parent = &dev->dev; + intf->dev.driver = NULL; + intf->dev.bus = (bus_type*) 0xdeadbeef /*&usb_bus_type*/; + intf->minor = -1; + device_initialize(&intf->dev); + dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, + dev->devpath, configuration, ifnum); + } + kfree(new_interfaces); + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_CONFIGURATION, 0, configuration, 0, + NULL, 0, USB_CTRL_SET_TIMEOUT); + if (ret < 0 && cp) { + for (i = 0; i < nintf; ++i) { + put_device(&cp->interface[i]->dev); + cp->interface[i] = NULL; + } + cp = NULL; + } + + dev->actconfig = cp; + + if (!cp) { + dev->state = USB_STATE_ADDRESS; + return ret; + } + dev->state = USB_STATE_CONFIGURED; + + return 0; +} diff --git a/repos/dde_linux/src/drivers/usb_hid/lx_emul.h b/repos/dde_linux/src/drivers/usb_hid/lx_emul.h index d182461a5..368020338 100644 --- a/repos/dde_linux/src/drivers/usb_hid/lx_emul.h +++ b/repos/dde_linux/src/drivers/usb_hid/lx_emul.h @@ -19,8 +19,6 @@ #include -#define __KERNEL__ 1 - #include #include #include @@ -615,6 +613,13 @@ int module_wacom_driver_init(); struct input_handle; void genode_evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value); +struct usb_device; +extern int usb_get_configuration(struct usb_device *dev); + +struct usb_hcd { unsigned amd_resume_bug:1; }; + +bool usb_device_is_owned(struct usb_device *udev); + #include #endif /* _SRC__DRIVERS__USB_HID__LX_EMUL_H_ */ diff --git a/repos/dde_linux/src/drivers/usb_hid/main.cc b/repos/dde_linux/src/drivers/usb_hid/main.cc index 3927fcd4c..093e81ad2 100644 --- a/repos/dde_linux/src/drivers/usb_hid/main.cc +++ b/repos/dde_linux/src/drivers/usb_hid/main.cc @@ -26,66 +26,7 @@ #include #include -void Driver::Device::scan_altsettings(usb_interface * iface, - unsigned iface_idx, unsigned alt_idx) -{ - Usb::Interface_descriptor iface_desc; - usb.interface_descriptor(iface_idx, alt_idx, &iface_desc); - Genode::memcpy(&iface->altsetting[alt_idx].desc, &iface_desc, - sizeof(usb_interface_descriptor)); - if (iface_desc.active) - iface->cur_altsetting = &iface->altsetting[alt_idx]; - - if (iface_desc.iclass == USB_CLASS_HID) { - hid_descriptor * hd = (hid_descriptor*) - kzalloc(sizeof(hid_descriptor), GFP_KERNEL); - if (usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - USB_REQ_GET_DESCRIPTOR, - USB_RECIP_INTERFACE | USB_DIR_IN, - (HID_DT_HID << 8), iface_idx, (void*)hd, - sizeof(hid_descriptor), USB_CTRL_GET_TIMEOUT) < 0) { - Genode::warning("could not get HID descriptor"); - } else { - iface->altsetting[alt_idx].extra = (unsigned char*)hd; - iface->altsetting[alt_idx].extralen = sizeof(hid_descriptor); - } - } - - iface->altsetting[alt_idx].endpoint = (usb_host_endpoint*) - kzalloc(sizeof(usb_host_endpoint)*iface->altsetting[alt_idx].desc.bNumEndpoints, GFP_KERNEL); - - for (unsigned i = 0; i < iface->altsetting[alt_idx].desc.bNumEndpoints; i++) { - Usb::Endpoint_descriptor ep_desc; - usb.endpoint_descriptor(iface_idx, alt_idx, i, &ep_desc); - Genode::memcpy(&iface->altsetting[alt_idx].endpoint[i].desc, - &ep_desc, sizeof(usb_endpoint_descriptor)); - int epnum = usb_endpoint_num(&iface->altsetting[alt_idx].endpoint[i].desc); - if (usb_endpoint_dir_out(&iface->altsetting[alt_idx].endpoint[i].desc)) - udev->ep_out[epnum] = &iface->altsetting[alt_idx].endpoint[i]; - else - udev->ep_in[epnum] = &iface->altsetting[alt_idx].endpoint[i]; - } -} - - -void Driver::Device::scan_interfaces(unsigned iface_idx) -{ - struct usb_interface * iface = - (usb_interface*) kzalloc(sizeof(usb_interface), GFP_KERNEL); - iface->num_altsetting = usb.alt_settings(iface_idx); - iface->altsetting = (usb_host_interface*) - kzalloc(sizeof(usb_host_interface)*iface->num_altsetting, GFP_KERNEL); - iface->dev.parent = &udev->dev; - iface->dev.bus = (bus_type*) 0xdeadbeef; - - for (unsigned i = 0; i < iface->num_altsetting; i++) - scan_altsettings(iface, iface_idx, i); - - struct usb_device_id id; - probe_interface(iface, &id); - udev->config->interface[iface_idx] = iface; -}; - +extern "C" void usb_detect_interface_quirks(struct usb_device *udev); void Driver::Device::register_device() { @@ -103,13 +44,38 @@ void Driver::Device::register_device() udev->config = (usb_host_config*) kzalloc(sizeof(usb_host_config), GFP_KERNEL); udev->bus->bus_name = "usbbus"; udev->bus->controller = (device*) (&usb); + udev->bus_mA = 900; /* set to maximum USB3.0 */ - udev->descriptor.idVendor = dev_desc.vendor_id; - udev->descriptor.idProduct = dev_desc.product_id; - udev->descriptor.bcdDevice = dev_desc.device_release; + Genode::memcpy(&udev->descriptor, &dev_desc, sizeof(usb_device_descriptor)); + Genode::memcpy(&udev->config->desc, &config_desc, sizeof(usb_config_descriptor)); + udev->devnum = dev_desc.num; + udev->speed = (usb_device_speed) dev_desc.speed; + udev->authorized = 1; - for (unsigned i = 0; i < config_desc.num_interfaces; i++) - scan_interfaces(i); + int cfg = usb_get_configuration(udev); + if (cfg < 0) { + Genode::error("usb_get_configuration returned error ", cfg); + return; + } + usb_detect_interface_quirks(udev); + cfg = usb_choose_configuration(udev); + usb_set_configuration(udev, cfg); + + for (int i = 0; i < udev->config->desc.bNumInterfaces; i++) { + struct usb_interface * iface = udev->config->interface[i]; + struct usb_host_interface * alt = iface->cur_altsetting; + + for (int j = 0; j < alt->desc.bNumEndpoints; ++j) { + struct usb_host_endpoint * ep = &alt->endpoint[j]; + int epnum = usb_endpoint_num(&ep->desc); + int is_out = usb_endpoint_dir_out(&ep->desc); + if (is_out) udev->ep_out[epnum] = ep; + else udev->ep_in[epnum] = ep; + } + + struct usb_device_id id; + probe_interface(iface, &id); + } } diff --git a/repos/dde_linux/src/drivers/usb_hid/target.mk b/repos/dde_linux/src/drivers/usb_hid/target.mk index f79a202fb..581b978c7 100644 --- a/repos/dde_linux/src/drivers/usb_hid/target.mk +++ b/repos/dde_linux/src/drivers/usb_hid/target.mk @@ -7,6 +7,7 @@ LIBS := base usb_hid_include lx_kit_setjmp USB_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/drivers/usb_hid +INC_DIR += $(USB_CONTRIB_DIR)/drivers/usb/core INC_DIR += $(PRG_DIR) INC_DIR += $(REP_DIR)/src/include @@ -23,7 +24,11 @@ SRC_C += drivers/hid/usbhid/hid-core.c SRC_C += drivers/input/evdev.c SRC_C += drivers/input/input-mt.c SRC_C += drivers/input/input.c +SRC_C += drivers/usb/core/config.c +SRC_C += drivers/usb/core/generic.c +SRC_C += drivers/usb/core/quirks.c +CC_OPT += -D__KERNEL__ CC_C_OPT += -Wno-unused-but-set-variable -Wno-pointer-sign \ -Wno-incompatible-pointer-types -Wno-unused-variable \ -Wno-unused-function -Wno-uninitialized -Wno-maybe-uninitialized diff --git a/repos/dde_linux/usb_hid.list b/repos/dde_linux/usb_hid.list index cc05c00e6..9c8d42aeb 100644 --- a/repos/dde_linux/usb_hid.list +++ b/repos/dde_linux/usb_hid.list @@ -16,6 +16,10 @@ linux-x.x.x/drivers/input/evdev.c linux-x.x.x/drivers/input/input-compat.h linux-x.x.x/drivers/input/input-mt.c linux-x.x.x/drivers/input/input.c +linux-x.x.x/drivers/usb/core/config.c +linux-x.x.x/drivers/usb/core/generic.c +linux-x.x.x/drivers/usb/core/quirks.c +linux-x.x.x/drivers/usb/core/usb.h linux-x.x.x/include/asm-generic/atomic64.h linux-x.x.x/include/asm-generic/bitops/__ffs.h linux-x.x.x/include/asm-generic/bitops/__fls.h @@ -35,6 +39,7 @@ linux-x.x.x/include/linux/power_supply.h linux-x.x.x/include/linux/swab.h linux-x.x.x/include/linux/usb.h linux-x.x.x/include/linux/usb/ch9.h +linux-x.x.x/include/linux/usb/quirks.h linux-x.x.x/include/uapi/linux/byteorder/little_endian.h linux-x.x.x/include/uapi/linux/hid.h linux-x.x.x/include/uapi/linux/input.h