linux - bluetooth - hid - demo

linux HID驱动分析
http://blog.csdn.net/walkingman321/article/details/7213710

HID 总线
HID的总线在hid-core.c的hid-init中初始化:
bus_register(&hid_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,*
    

};
一般来说,HID驱动很少定义自己的probe函数,所以HID设备的匹配基本都是由总线probe和match函数完成。
HID的匹配
hid_bus_match用于检查设备和驱动的VID、PID是否匹配,代码如下:
*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);*
    
  •   // 匹配hdev和hdrv的vendorID和productID*
    

if (!hid_match_device(hdev, hdrv))

  •          return 0;*
    
  •   // 如果是generic-开头的驱动,那么只要不在黑名单中即可匹配*
    
  •   if (!strncmp(hdrv->name, "generic-", 8))*
    
  •          return !hid_match_id(hdev, hid_blacklist);*
    
  •   return 1;*
    

匹配了PID、VID之后,就进入到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 (!hdev->driver) {*
    
  •          // 再匹配一次,这里似乎与前面的hid_bus_match有些重复*
    

id = hid_match_device(hdev, hdrv);

  •          if (id == NULL)*
    
  •                 return -ENODEV;*
    
  •          hdev->driver = hdrv;*
    
  •          if (hdrv->probe) { // 若驱动定义了自己的probe函数则调用该probe,但一般HID驱动不会定义*
    
  •                 ret = hdrv->probe(hdev, id);*
    
  •          } else { // 默认的probe过程*
    
  •                 ret = hid_parse(hdev);*
    
  •                 if (!ret)*
    
  •                        ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);*
    
  •          }*
    
  •          if (ret)*
    
  •                 hdev->driver = NULL;*
    
  •   }*
    
  •   return ret;*
    

hid_parse函数的作用是解析HID描述符,具体实现由hid_device->ll_driver->parse函数完成。关于HID描述符的文档可在www.usb.org下载。
*static inline int __must_check hid_parse(struct hid_device hdev)

  •   ret = hdev->ll_driver->parse(hdev);*
    

由于HID描述符的解析是通用操作,所以HID框架中实现了一个解析函数hid_parse_report。一般来说,hdev->ll_driver->parse函数中只要调用hid_parse_report即可。
hid_parse_report比较复杂,其功能是解析HID描述符,然后把解析出的结果放在hid_device->report_enum[type]-> report_list中。每个解析出的HID结构由一个hid_report描述。report_enum中的type可以是HID_INPUT_REPORT、HID_OUTPUT_REPORT或者HID_FEATURE_REPORT。
parse之后,probe函数又会调用hid_hw_start启动HID设备:

  •   hid_hw_start(hdev, HID_CONNECT_DEFAULT);*
    

注意这里的HID_CONNECT_DEFAULT被定义为:
*#define HID_CONNECT_DEFAULT (HID_CONNECT_HIDINPUT|HID_CONNECT_HIDRAW| *

  •          HID_CONNECT_HIDDEV|HID_CONNECT_FF)*
    

在hid_hw_start中,首先会调用hdev->ll_driver->start启动设备,然后是hid_connect将设备与HID框架关联起来。
hdev->ll_driver->start函数由hid的具体设备提供,由该设备所属的总线提供,用于底层的初始化,这里暂不讨论。
hid_connect会将hid_dev与具体驱动关联起来。
*int hid_connect(struct hid_device hdev, unsigned int connect_mask)

  •   if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE)  // 一般不会到这里*
    
  •          connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV);*
    
  •   if (hdev->bus != BUS_USB) // 如果不是USB总线,那么去掉HID_CONNECT_HIDDEV标记*
    
  •          connect_mask &= ~HID_CONNECT_HIDDEV;*
    
  •   if (hid_hiddev(hdev))  // 匹配某些特定vendorID和productID*
    
  •          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 ((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;*
    

由此可见,hid_connect共支持3种设备,首先是input设备,调用hidinput_connect登记;其次是hid_dev设备,调用hdev->hiddev_connect登记;最后是raw设备,调用hidraw_connect登记。
HID input
HID中最常用的是input设备,使用hidinput_connect登记到系统。hidinput_connect的主要作用是对hiddev中的每一个report,都建立一个input_dev设备,并登记到input框架中。
*int hidinput_connect(struct hid_device hid, unsigned int force)

  •   // 对每一个report,建立一个input设备*
    

for (k = HID_INPUT_REPORT; k <= max_report_type; k++)

  •          list_for_each_entry(report, &hid->report_enum[k].report_list, list) {*
    
  •                 if (!hidinput) {*
    
  •                        hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL);*
    
  •                        input_dev = input_allocate_device();*
    
  •                        。。。*
    
  •                        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);*
    
  •          }*
    

HID dev
HID dev设备目前仅在USB总线中用到,其用于登记的hiddev_connect函数指针目前仅有一个实例hiddev_connect,在usbhid_probe函数中被赋值。
hid->hiddev_connect = hiddev_connect;
HID raw dev
hidraw.c中定义了一个class hidraw,并创建设备设备驱动
alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR, HIDRAW_MAX_DEVICES, "hidraw");
cdev_init(&hidraw_cdev, &hidraw_ops);
hidraw_ops中定义了一个基本的字符设备驱动
static const struct file_operations hidraw_ops = {

  •   .owner =        THIS_MODULE,*
    
  •   .read =         hidraw_read,*
    
  •   .write =        hidraw_write,*
    
  •   .poll =         hidraw_poll,*
    
  •   .open =         hidraw_open,*
    
  •   .release =      hidraw_release,*
    
  •   .unlocked_ioctl = hidraw_ioctl,*
    

};
由于是raw设备,所以这个驱动中不会解析任何数据,只是简单的将应用层数据传给下层设备,以及将设备产生的数据传给应用层。具体实现可查看代码。

hid-core.c
http://www.cs.fsu.edu/~baker/devices/lxr/http/source/linux/drivers/hid/hid-core.c#L495
hid-sersor-gyro-3d.c
http://lxr.free-electrons.com/source/drivers/iio/gyro/hid-sensor-gyro-3d.c#L264

android 驱动学习
http://blog.csdn.net/vv0_0vv/article/category/1067351

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容