dde_linux: enable QEMU support for new usb_hid_drv

This commit is contained in:
Stefan Kalkowski 2018-09-04 14:46:36 +02:00 committed by Christian Helmuth
parent 1ec2f43713
commit 9221f7916a
8 changed files with 305 additions and 78 deletions

View File

@ -1 +1 @@
477b1b59ec591bd7ef01c853f15df4acabca6624
b88acdf93d8db71180a25937fba45ec6b39a2841

View File

@ -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 *);
};

View File

@ -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;
}

View File

@ -25,6 +25,73 @@
#include <lx_kit/backend_alloc.h>
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<Lx_driver>;
@ -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;
}

View File

@ -19,8 +19,6 @@
#include <lx_emul/extern_c_begin.h>
#define __KERNEL__ 1
#include <lx_emul/compiler.h>
#include <lx_emul/printf.h>
#include <lx_emul/types.h>
@ -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 <lx_emul/extern_c_end.h>
#endif /* _SRC__DRIVERS__USB_HID__LX_EMUL_H_ */

View File

@ -26,66 +26,7 @@
#include <linux/usb.h>
#include <lx_emul/extern_c_end.h>
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);
}
}

View File

@ -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

View File

@ -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