hid驱动代码看了一下,顺便做了做笔记,不详细,见谅。
Hid驱动代码(kernel/drivers/hid/hid-multitouch.c)。
static int __init mt_init(void) { return hid_register_driver(&mt_driver); }
这里面主要是调用了一个hid_register_driver函数。看一下传进去的参数。
static struct hid_driver mt_driver = { .name = "hid-multitouch", .id_table = mt_devices, .probe = mt_probe, .remove = mt_remove, .input_mapping = mt_input_mapping, .input_mapped = mt_input_mapped, .feature_mapping = mt_feature_mapping, .usage_table = mt_grabbed_usages, .event = mt_event, #ifdef CONFIG_PM .reset_resume = mt_reset_resume, #endif };
上面都是一些基本的动作。重点看一下hid_register_driver函数。
hid_register_driver是定义在inlcude/linux/hid.h中的一个宏。
#define hid_register_driver(driver) \ __hid_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
__hid_register_driver定义在drivers/hid/hid-core.c中。
int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, const char *mod_name) { int ret; hdrv->driver.name = hdrv->name; hdrv->driver.bus = &hid_bus_type; hdrv->driver.owner = owner; hdrv->driver.mod_name = mod_name; INIT_LIST_HEAD(&hdrv->dyn_list); spin_lock_init(&hdrv->dyn_lock); ret = driver_register(&hdrv->driver); if (ret) return ret; ret = driver_create_file(&hdrv->driver, &driver_attr_new_id); if (ret) driver_unregister(&hdrv->driver); return ret; } EXPORT_SYMBOL_GPL(__hid_register_driver);
在上面的那段代码中首先填充了hid_driver结构体变量hdrv的一些成员。注意到有这样一行代码hdrv->driver.bus = &hid_bus_type;这里指定了hdrv的驱动总线类型。
static struct bus_type hid_bus_type = { .name = "hid", .match = hid_bus_match, .probe = hid_device_probe, .remove = hid_device_remove, .uevent = hid_uevent, };
这个hid_bus_type在之前已经注册到系统中了。
在初始化了链表和锁之后,利用driver_register将hdrv->driver注册到系统中。
driver_register是个很通用的函数,看过驱动模型的人大都会了解,这里就不详细说明了。
int driver_register(struct device_driver *drv) { int ret; struct device_driver *other; BUG_ON(!drv->bus->p); if ((drv->bus->probe && drv->probe) || (drv->bus->remove && drv->remove) || (drv->bus->shutdown && drv->shutdown)) printk(KERN_WARNING "Driver '%s' needs updating - please use " "bus_type methods\n", drv->name); other = driver_find(drv->name, drv->bus); if (other) { printk(KERN_ERR "Error: Driver '%s' is already registered, " "aborting...\n", drv->name); return -EBUSY; } ret = bus_add_driver(drv); if (ret) return ret; ret = driver_add_groups(drv, drv->groups); if (ret) bus_remove_driver(drv); return ret; }
首先利用driver_find函数从我们这个驱动所属的总线中找寻名字和我们的driver相同的驱动,如果能够找到,说明在这个总线下面已经有一个同样的驱动了,所以会返回出错标志。如果找不到这样一个驱动就会把我们现在的这个驱动添加到这个总线下面,添加过程中使用的是bus_add_driver函数。代码如下:
int bus_add_driver(struct device_driver *drv) { struct bus_type *bus; struct driver_private *priv; int error = 0; bus = bus_get(drv->bus); if (!bus) return -EINVAL; pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name); priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { error = -ENOMEM; goto out_put_bus; } klist_init(&priv->klist_devices, NULL, NULL); priv->driver = drv; drv->p = priv; priv->kobj.kset = bus->p->drivers_kset; error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name); if (error) goto out_unregister; if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_unregister; } klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); module_add_driver(drv->owner, drv); error = driver_create_file(drv, &driver_attr_uevent); if (error) { printk(KERN_ERR "%s: uevent attr (%s) failed\n", __func__, drv->name); } error = driver_add_attrs(bus, drv); if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", __func__, drv->name); } if (!drv->suppress_bind_attrs) { error = add_bind_files(drv); if (error) { /* Ditto */ printk(KERN_ERR "%s: add_bind_files(%s) failed\n", __func__, drv->name); } } kobject_uevent(&priv->kobj, KOBJ_ADD); return 0; out_unregister: kobject_put(&priv->kobj); kfree(drv->p); drv->p = NULL; out_put_bus: bus_put(bus); return error; }
着重看一下几行代码:
if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_unregister; }
在bus注册的过程中bus->p->drivers_autoprobe会被设置为1,所以这里的if里面的代码是可以执行的。
int driver_attach(struct device_driver *drv) { return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); }
在这里最终会去执行__driver_attach函数。
static int __driver_attach(struct device *dev, void *data) { struct device_driver *drv = data; /* * Lock device and try to bind to it. We drop the error * here and always return 0, because we need to keep trying * to bind to devices and some drivers will return an error * simply if it didn't support the device. * * driver_probe_device() will spit a warning if there * is an error. */ if (!driver_match_device(drv, dev)) return 0; if (dev->parent) /* Needed for USB */ device_lock(dev->parent); device_lock(dev); if (!dev->driver) driver_probe_device(drv, dev); device_unlock(dev); if (dev->parent) device_unlock(dev->parent); return 0; }
在这里面有几行代码是我们需要的
if (!driver_match_device(drv, dev)) return 0;
match_device?对,这里就是去执行一些match操作。
static inline int driver_match_device(struct device_driver *drv, struct device *dev) { return drv->bus->match ? drv->bus->match(dev, drv) : 1; }
看到没,这里其实调用的就是bus上的match函数。我们这里先做个记号,我们继续沿着代码往下走,回头再具体的去看那些match的东西。
回到__driver_attach函数中继续看下面的代码
if (!dev->driver) driver_probe_device(drv, dev);
从名字上可以判断出,这里是去probe device。
int driver_probe_device(struct device_driver *drv, struct device *dev) { int ret = 0; if (!device_is_registered(dev)) return -ENODEV; pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); pm_runtime_get_noresume(dev); pm_runtime_barrier(dev); ret = really_probe(dev, drv); pm_runtime_put_sync(dev); return ret; }
继续去调用really_probe函数。
static int really_probe(struct device *dev, struct device_driver *drv) { int ret = 0; atomic_inc(&probe_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", __func__, dev_name(dev)); goto probe_failed; } if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; } driver_bound(dev); ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); goto done; probe_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL; if (ret == -EPROBE_DEFER) { /* Driver requested deferred probing */ dev_info(dev, "Driver %s requests probe deferral\n", drv->name); driver_deferred_probe_add(dev); } else if (ret != -ENODEV && ret != -ENXIO) { /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d\n", drv->name, dev_name(dev), ret); } else { pr_debug("%s: probe of %s rejects match %d\n", drv->name, dev_name(dev), ret); } /* * Ignore errors returned by ->probe so that the next driver can try * its luck. */ ret = 0; done: atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret; }
看这里出现的这段代码
if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; }
如果bus的probe函数存在就调用bus的probe函数,否则就去调用driver的probe函数。这几个probe函数就是我们在前面看到的那个probe函数。
到现在为止我们知道,在add driver的过程中,我们首先要去调用bus的match函数,然后再去调用bus的probe函数。在这里我们了解到这些就可以了,想具体的再去详细看的话,可以找设备驱动模型的资料去了解。
好,回到hid-core.c文件中,我们先去看看bus的match函数。还记得那个bus_type结构体hid_bus_type吧?
static struct bus_type hid_bus_type = { .name = "hid", .match = hid_bus_match, .probe = hid_device_probe, .remove = hid_device_remove, .uevent = hid_uevent, };
这里面就是我们需要的match和probe函数。我们先看hid_bus_match函数。
static int hid_bus_match(struct device *dev, struct device_driver *drv) { struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); struct hid_device *hdev = container_of(dev, struct hid_device, dev); if ((hdev->quirks & HID_QUIRK_MULTITOUCH) && !strncmp(hdrv->name, "hid-multitouch", 14)) return 1; if (!hid_match_device(hdev, hdrv)) return 0; /* generic wants all that don't have specialized driver */ if (!strncmp(hdrv->name, "generic-", 8) && !hid_ignore_special_drivers) return !hid_match_id(hdev, hid_have_special_driver); return 1; }
首先利用container_of从系统中取出hid_driver结构体和hid_device结构体。
如果hdev的quirks变量里面包含有HID_QUIRK_MULTITOUCH变量并且hdrv的name为"hid-multitouch",那就直接返回一个1,下面的就不用匹配了。否则调用hid_match_device匹配函数,进行匹配。
static const struct hid_device_id *hid_match_device(struct hid_device *hdev, struct hid_driver *hdrv) { struct hid_dynid *dynid; spin_lock(&hdrv->dyn_lock); list_for_each_entry(dynid, &hdrv->dyn_list, list) { if (hid_match_one_id(hdev, &dynid->id)) { spin_unlock(&hdrv->dyn_lock); return &dynid->id; } } spin_unlock(&hdrv->dyn_lock); return hid_match_id(hdev, hdrv->id_table); }
在这里面主要是要匹配vendor id 和product id。涉及到两个函数一并贴出来。
static bool hid_match_one_id(struct hid_device *hdev, const struct hid_device_id *id) { return id->bus == hdev->bus && (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) && (id->product == HID_ANY_ID || id->product == hdev->product); } const struct hid_device_id *hid_match_id(struct hid_device *hdev, const struct hid_device_id *id) { for (; id->bus; id++) if (hid_match_one_id(hdev, id)) return id; return NULL; }
首先从hid_driver结构体的dyn_list中依次取出hid_device_id结构体变量id,将其总线类型,vendor id, product id依次与hid_device结构体变量hdev的对应值匹配,如果匹配成功就返回hid_device_id结构体。如果无法匹配的话,就调用hid_match_id函数将hdev与hdrv的id_table匹配。
匹配过程就是上面那样,下面调用hid_device_probe函数。
static int hid_device_probe(struct device *dev) { struct hid_driver *hdrv = container_of(dev->driver, struct hid_driver, driver); struct hid_device *hdev = container_of(dev, struct hid_device, dev); const struct hid_device_id *id; int ret = 0; if (down_interruptible(&hdev->driver_lock)) return -EINTR; if (!hdev->driver) { id = hid_match_device(hdev, hdrv); if (id == NULL) { if (!((hdev->quirks & HID_QUIRK_MULTITOUCH) && !strncmp(hdrv->name, "hid-multitouch", 14))) { ret = -ENODEV; goto unlock; } } hdev->driver = hdrv; if (hdrv->probe) { ret = hdrv->probe(hdev, id); } else { /* default probe */ ret = hid_parse(hdev); if (!ret) ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); } if (ret) hdev->driver = NULL; } unlock: up(&hdev->driver_lock); return ret; }
probe过程相当的简单,首先再次匹配一遍,将hdrv保存到hdev的driver里面。如果hdrv的probe函数存在就调用hdrv的probe函数,否则调用一个default的probe函数。当然在这里我们是调用我们自己的probe函数啦。
我们重新把hid_driver的结构体成员表贴一下。
static struct hid_driver mt_driver = { .name = "hid-multitouch", .id_table = mt_devices, .probe = mt_probe, .remove = mt_remove, .input_mapping = mt_input_mapping, .input_mapped = mt_input_mapped, .feature_mapping = mt_feature_mapping, .usage_table = mt_grabbed_usages, .event = mt_event, #ifdef CONFIG_PM .reset_resume = mt_reset_resume, #endif };
现在我们着重看mt_probe函数。
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret, i; struct mt_device *td; struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */ if (id) { for (i = 0; mt_classes[i].name ; i++) { if (id->driver_data == mt_classes[i].name) { mtclass = &(mt_classes[i]); break; } } } /* This allows the driver to correctly support devices * that emit events over several HID messages. */ hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; hdev->quirks &= ~HID_QUIRK_MULTITOUCH; td = kzalloc(sizeof(struct mt_device), GFP_KERNEL); if (!td) { dev_err(&hdev->dev, "cannot allocate multitouch data\n"); return -ENOMEM; } td->mtclass = *mtclass; td->inputmode = -1; td->maxcontact_report_id = -1; hid_set_drvdata(hdev, td); td->fields = kzalloc(sizeof(struct mt_fields), GFP_KERNEL); if (!td->fields) { dev_err(&hdev->dev, "cannot allocate multitouch fields data\n"); ret = -ENOMEM; goto fail; } ret = hid_parse(hdev); if (ret != 0) goto fail; ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) goto fail; mt_post_parse(td); if (!id && td->touches_by_report == 1) { /* the device has been sent by hid-generic */ mtclass = &td->mtclass; mtclass->quirks |= MT_QUIRK_ALWAYS_VALID; mtclass->quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; mtclass->quirks &= ~MT_QUIRK_VALID_IS_INRANGE; mtclass->quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; } td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot), GFP_KERNEL); if (!td->slots) { dev_err(&hdev->dev, "cannot allocate multitouch slots\n"); hid_hw_stop(hdev); ret = -ENOMEM; goto fail; } ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); mt_set_maxcontacts(hdev); mt_set_input_mode(hdev); kfree(td->fields); td->fields = NULL; return 0; fail: kfree(td->fields); kfree(td); return ret; }
代码中涉及到一个结构体mt_class
struct mt_class { __s32 name; /* MT_CLS */ __s32 quirks; __s32 sn_move; /* Signal/noise ratio for move events */ __s32 sn_width; /* Signal/noise ratio for width events */ __s32 sn_height; /* Signal/noise ratio for height events */ __s32 sn_pressure; /* Signal/noise ratio for pressure events */ __u8 maxcontacts; bool is_indirect; /* true for touchpads */ };
这个是multitouch class结构体。从mt_classes数组中取得一个mt_class结构体,方法是去匹配id->driver_data和每个数组的name成员。然后填充结构体mt_device *td,调用hid_parse函数。看一下这个函数。
/** * hid_parse - parse HW reports * * @hdev: hid device * * Call this from probe after you set up the device (if needed). Your * report_fixup will be called (if non-NULL) after reading raw report from * device before passing it to hid layer for real parsing. */ static inline int __must_check hid_parse(struct hid_device *hdev) { int ret; if (hdev->status & HID_STAT_PARSED) return 0; ret = hdev->ll_driver->parse(hdev); if (!ret) hdev->status |= HID_STAT_PARSED; return ret; }
hdev的status变量保存了hid的status flags。如果我们已经parse过一次,其status中将会保存了一个HID_STAT_PARSEDflag。也就不用再次parse了。如果没有那个状态就调用hdev->ll_driver->parse(hdev)函数。
到这里为止,我们终于要面对这个这个hdev参数了,由于这个multitouch是通过usb连接的,所以这里在drivers/hid/usbhid/里面我们能够找到这个hdev结构,同样我们可以在里面找到这样一行代码:
hid->ll_driver = &usb_hid_driver;
看一下usb_hid_deiver
static struct hid_ll_driver usb_hid_driver = { .parse = usbhid_parse, .start = usbhid_start, .stop = usbhid_stop, .open = usbhid_open, .close = usbhid_close, .power = usbhid_power, .hidinput_input_event = usb_hidinput_input_event, };
可以看到这里面有几个函数,有我们现在需要的usbhid_parse。这个函数太复杂了我们后面分析。
接下来是这行代码ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
static inline int __must_check hid_hw_start(struct hid_device *hdev, unsigned int connect_mask) { int ret = hdev->ll_driver->start(hdev); if (ret || !connect_mask) return ret; ret = hid_connect(hdev, connect_mask); if (ret) hdev->ll_driver->stop(hdev); return ret; }
同样,这里是调用的刚才这个usb_hid_driver结构体里面的函数start。具体涉及到usbhid目录下的文件都在后面补上。
下面调用hid_connect函数。
int hid_connect(struct hid_device *hdev, unsigned int connect_mask) { static const char *types[] = { "Device", "Pointer", "Mouse", "Device", "Joystick", "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller" }; const char *type, *bus; char buf[64]; unsigned int i; int len; int ret; if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE) connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV); if (hdev->quirks & HID_QUIRK_HIDINPUT_FORCE) connect_mask |= HID_CONNECT_HIDINPUT_FORCE; if (hdev->bus != BUS_USB) connect_mask &= ~HID_CONNECT_HIDDEV; if (hid_hiddev(hdev)) connect_mask |= HID_CONNECT_HIDDEV_FORCE; if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev, connect_mask & HID_CONNECT_HIDINPUT_FORCE)) hdev->claimed |= HID_CLAIMED_INPUT; if (hdev->quirks & HID_QUIRK_MULTITOUCH) { /* this device should be handled by hid-multitouch, skip it */ return -ENODEV; } if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect && !hdev->hiddev_connect(hdev, connect_mask & HID_CONNECT_HIDDEV_FORCE)) hdev->claimed |= HID_CLAIMED_HIDDEV; if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev)) hdev->claimed |= HID_CLAIMED_HIDRAW; if (!hdev->claimed) { hid_err(hdev, "claimed by neither input, hiddev nor hidraw\n"); return -ENODEV; } if ((hdev->claimed & HID_CLAIMED_INPUT) && (connect_mask & HID_CONNECT_FF) && hdev->ff_init) hdev->ff_init(hdev); len = 0; if (hdev->claimed & HID_CLAIMED_INPUT) len += sprintf(buf + len, "input"); if (hdev->claimed & HID_CLAIMED_HIDDEV) len += sprintf(buf + len, "%shiddev%d", len ? "," : "", hdev->minor); if (hdev->claimed & HID_CLAIMED_HIDRAW) len += sprintf(buf + len, "%shidraw%d", len ? "," : "", ((struct hidraw *)hdev->hidraw)->minor); type = "Device"; for (i = 0; i < hdev->maxcollection; i++) { struct hid_collection *col = &hdev->collection[i]; if (col->type == HID_COLLECTION_APPLICATION && (col->usage & HID_USAGE_PAGE) == HID_UP_GENDESK && (col->usage & 0xffff) < ARRAY_SIZE(types)) { type = types[col->usage & 0xffff]; break; } } switch (hdev->bus) { case BUS_USB: bus = "USB"; break; case BUS_BLUETOOTH: bus = "BLUETOOTH"; break; default: bus = "<UNKNOWN>"; } ret = device_create_bin_file(&hdev->dev, &dev_bin_attr_report_desc); if (ret) hid_warn(hdev, "can't create sysfs report descriptor attribute err: %d\n", ret); hid_info(hdev, "%s: %s HID v%x.%02x %s [%s] on %s\n", buf, bus, hdev->version >> 8, hdev->version & 0xff, type, hdev->name, hdev->phys); return 0; } EXPORT_SYMBOL_GPL(hid_connect);
这里首先根据hdev的quirks值去改变connect_mask值。然后判断connect_mask是否具有HID_CONNECT_HIDINPUT标志,如果具有那个标志就执行hidinput_connect函数。可以看出这个函数就是将hid子系统跟input子系统联系起来的函数。
/* * Register the input device; print a message. * Configure the input layer interface * Read all reports and initialize the absolute field values. */ int hidinput_connect(struct hid_device *hid, unsigned int force) { struct hid_report *report; struct hid_input *hidinput = NULL; struct input_dev *input_dev; int i, j, k; INIT_LIST_HEAD(&hid->inputs); if (!force) { for (i = 0; i < hid->maxcollection; i++) { struct hid_collection *col = &hid->collection[i]; if (col->type == HID_COLLECTION_APPLICATION || col->type == HID_COLLECTION_PHYSICAL) if (IS_INPUT_APPLICATION(col->usage)) break; } if (i == hid->maxcollection) return -1; } report_features(hid); for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { if (k == HID_OUTPUT_REPORT && hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) continue; list_for_each_entry(report, &hid->report_enum[k].report_list, list) { if (!report->maxfield) continue; if (!hidinput) { hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL); input_dev = input_allocate_device(); if (!hidinput || !input_dev) { kfree(hidinput); input_free_device(input_dev); hid_err(hid, "Out of memory during hid input probe\n"); goto out_unwind; } input_set_drvdata(input_dev, hid); input_dev->event = hid->ll_driver->hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; input_dev->setkeycode = hidinput_setkeycode; input_dev->getkeycode = hidinput_getkeycode; input_dev->name = hid->name; input_dev->phys = hid->phys; input_dev->uniq = hid->uniq; input_dev->id.bustype = hid->bus; input_dev->id.vendor = hid->vendor; input_dev->id.product = hid->product; input_dev->id.version = hid->version; input_dev->dev.parent = hid->dev.parent; hidinput->input = input_dev; list_add_tail(&hidinput->list, &hid->inputs); } for (i = 0; i < report->maxfield; i++) for (j = 0; j < report->field[i]->maxusage; j++) hidinput_configure_usage(hidinput, report->field[i], report->field[i]->usage + j); if (hid->quirks & HID_QUIRK_MULTI_INPUT) { /* This will leave hidinput NULL, so that it * allocates another one if we have more inputs on * the same interface. Some devices (e.g. Happ's * UGCI) cram a lot of unrelated inputs into the * same interface. */ hidinput->report = report; if (hid->driver->input_register && hid->driver->input_register(hid, hidinput)) goto out_cleanup; if (input_register_device(hidinput->input)) goto out_cleanup; hidinput = NULL; } } } if (hid->quirks & HID_QUIRK_MULTITOUCH) { /* generic hid does not know how to handle multitouch devices */ if (hidinput) goto out_cleanup; goto out_unwind; } if (hidinput && hid->driver->input_register && hid->driver->input_register(hid, hidinput)) goto out_cleanup; if (hidinput && input_register_device(hidinput->input)) goto out_cleanup; return 0; out_cleanup: list_del(&hidinput->list); input_free_device(hidinput->input); kfree(hidinput); out_unwind: /* unwind the ones we already registered */ hidinput_disconnect(hid); return -1; } EXPORT_SYMBOL_GPL(hidinput_connect);
注意到传给force的参数为connect_mask & HID_CONNECT_HIDINPUT_FORCE,也就是说如果connect_mask含有HID_CONNECT_HIDINPUT_FORCE标志的话,第一个if里面的代码就不需要执行了。report_features之后是一个大大的for循环。在这里面主要是建立input_dev结构体,并将该结构体添加到对应的链表中。这样就将hid子系统和input子系统联系起来了。
Usb设备代码
multitouch是通过usb连接到板子上的,我们在前面函数调用的时候都有提到过hdev,即hid_device结构体指针,那个这个东西是什么呢?在哪里定义的?我们现在就看看这部分代码。
这部分的代码位于drivers/hid/usbhid/目录下。首先看一下init函数,位于hid-core.c文件中。
static int __init hid_init(void) { int retval = -ENOMEM; retval = hid_register_driver(&hid_usb_driver); if (retval) goto hid_register_fail; retval = usbhid_quirks_init(quirks_param); if (retval) goto usbhid_quirks_init_fail; retval = usb_register(&hid_driver); if (retval) goto usb_register_fail; printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n"); return 0; usb_register_fail: usbhid_quirks_exit(); usbhid_quirks_init_fail: hid_unregister_driver(&hid_usb_driver); hid_register_fail: return retval; }
在hid_init函数中,主要是调用了三个函数,即hid_register_driver,usbhid_quirks_init,usb_register。
关于对这部分代码的解析可以参考一下文档
http://www.cnblogs.com/sdphome/archive/2011/09/29/2195799.html
在那篇文档中对hid写的很详细,大家可以去深入的看看,在这里我主要简单的介绍一下需要用到的函数,其他的就不一一的去解释了。
主要看我们在multitouch中调用到的那几个函数。
ret = hdev->ll_driver->parse(hdev);
在这里会调用到parse函数也就是usbhid_parse。
static int usbhid_parse(struct hid_device *hid) { struct usb_interface *intf = to_usb_interface(hid->dev.parent); struct usb_host_interface *interface = intf->cur_altsetting; struct usb_device *dev = interface_to_usbdev (intf); struct hid_descriptor *hdesc; u32 quirks = 0; unsigned int rsize = 0; char *rdesc; int ret, n; quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); if (quirks & HID_QUIRK_IGNORE) return -ENODEV; /* Many keyboards and mice don't like to be polled for reports, * so we will always set the HID_QUIRK_NOGET flag for them. */ if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) { if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD || interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) quirks |= HID_QUIRK_NOGET; } if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && (!interface->desc.bNumEndpoints || usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) { dbg_hid("class descriptor not present\n"); return -ENODEV; } hid->version = le16_to_cpu(hdesc->bcdHID); hid->country = hdesc->bCountryCode; for (n = 0; n < hdesc->bNumDescriptors; n++) if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT) rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { dbg_hid("weird size of report descriptor (%u)\n", rsize); return -EINVAL; } if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) { dbg_hid("couldn't allocate rdesc memory\n"); return -ENOMEM; } hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); ret = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize); if (ret < 0) { dbg_hid("reading report descriptor failed\n"); kfree(rdesc); goto err; } ret = hid_parse_report(hid, rdesc, rsize); kfree(rdesc); if (ret) { dbg_hid("parsing report descriptor failed\n"); goto err; } hid->quirks |= quirks; return 0; err: return ret; }
这段代码中首先通过vendor id和product id 去获得对应设备的quirks,然后根据描述符类型去选择一下操作,由于这里的hid驱动不仅仅是为multitouch服务的,所以这里的判断比较多。得到描述符信息,调用hid_parse_report去解析这些描述符。
继续看下一个函数
int ret =hdev->ll_driver->start(hdev);
这里调用的是usbhid_start函数。
static int usbhid_start(struct hid_device *hid) { struct usb_interface *intf = to_usb_interface(hid->dev.parent); struct usb_host_interface *interface = intf->cur_altsetting; struct usb_device *dev = interface_to_usbdev(intf); struct usbhid_device *usbhid = hid->driver_data; unsigned int n, insize = 0; int ret; clear_bit(HID_DISCONNECTED, &usbhid->iofl); usbhid->bufsize = HID_MIN_BUFFER_SIZE; hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize); hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize); hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize); if (usbhid->bufsize > HID_MAX_BUFFER_SIZE) usbhid->bufsize = HID_MAX_BUFFER_SIZE; hid_find_max_report(hid, HID_INPUT_REPORT, &insize); if (insize > HID_MAX_BUFFER_SIZE) insize = HID_MAX_BUFFER_SIZE; if (hid_alloc_buffers(dev, hid)) { ret = -ENOMEM; goto fail; } for (n = 0; n < interface->desc.bNumEndpoints; n++) { struct usb_endpoint_descriptor *endpoint; int pipe; int interval; endpoint = &interface->endpoint[n].desc; if (!usb_endpoint_xfer_int(endpoint)) continue; interval = endpoint->bInterval; /* Some vendors give fullspeed interval on highspeed devides */ if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL && dev->speed == USB_SPEED_HIGH) { interval = fls(endpoint->bInterval*8); printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n", hid->name, endpoint->bInterval, interval); } /* Change the polling interval of mice. */ if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) interval = hid_mousepoll_interval; ret = -ENOMEM; if (usb_endpoint_dir_in(endpoint)) { if (usbhid->urbin) continue; if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL))) goto fail; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize, hid_irq_in, hid, interval); usbhid->urbin->transfer_dma = usbhid->inbuf_dma; usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; } else { if (usbhid->urbout) continue; if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL))) goto fail; pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress); usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0, hid_irq_out, hid, interval); usbhid->urbout->transfer_dma = usbhid->outbuf_dma; usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; } } usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL); if (!usbhid->urbctrl) { ret = -ENOMEM; goto fail; } usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr, usbhid->ctrlbuf, 1, hid_ctrl, hid); usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma; usbhid->urbctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS)) usbhid_init_reports(hid); set_bit(HID_STARTED, &usbhid->iofl); /* Some keyboards don't work until their LEDs have been set. * Since BIOSes do set the LEDs, it must be safe for any device * that supports the keyboard boot protocol. * In addition, enable remote wakeup by default for all keyboard * devices supporting the boot protocol. */ if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT && interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD) { usbhid_set_leds(hid); device_set_wakeup_enable(&dev->dev, 1); } return 0; fail: usb_free_urb(usbhid->urbin); usb_free_urb(usbhid->urbout); usb_free_urb(usbhid->urbctrl); usbhid->urbin = NULL; usbhid->urbout = NULL; usbhid->urbctrl = NULL; hid_free_buffers(dev, hid); return ret; }
在这里主要是初始化了urb。
注意到有这么一行代码,这里使用这个函数提交USB_DIR_IN方向的reports.
usb_fill_control_urb(usbhid->urbctrl,dev, 0, (void *) usbhid->cr,
usbhid->ctrlbuf, 1, hid_ctrl, hid);
所以在提交完了之后会调用hid_ctrl函数。
static void hid_ctrl(struct urb *urb) { struct hid_device *hid = urb->context; struct usbhid_device *usbhid = hid->driver_data; int unplug = 0, status = urb->status; spin_lock(&usbhid->lock); switch (status) { case 0: /* success */ if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type, urb->transfer_buffer, urb->actual_length, 0); break; case -ESHUTDOWN: /* unplug */ unplug = 1; case -EILSEQ: /* protocol error or unplug */ case -EPROTO: /* protocol error or unplug */ case -ECONNRESET: /* unlink */ case -ENOENT: case -EPIPE: /* report not available */ break; default: /* error */ hid_warn(urb->dev, "ctrl urb status %d received\n", status); } if (unplug) usbhid->ctrltail = usbhid->ctrlhead; else usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); if (!ctrl_pump_restart(hid)) { /* Successfully submitted next urb in queue */ spin_unlock(&usbhid->lock); return; } clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); spin_unlock(&usbhid->lock); usb_autopm_put_interface_async(usbhid->intf); wake_up(&usbhid->wait); }
在这个函数里面对我们有用的是case 0的情况。
case 0: /* success */ if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type, urb->transfer_buffer, urb->actual_length, 0); break;
这里又去调用了hid_input_report函数。hid_input_report函数定义在drivers/hid/hid-core.c文件中,这样又再次回到了主目录下的hid-core中。在drivers/hid/hid-core.c函数中我们需要关注的是
hid_report_raw_event(hid, type, data, size, interrupt);
这里最终会用到下面几行代码
for (a = 0; a < report->maxfield; a++) hid_input_field(hid, report->field[a], cdata, interrupt); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_report_event(hid, report);
其中for循环用于上报,hidinput_report_event发送上报结束标志EV_SYN。