Linux Hid设备调研
一.HID I/O传输驱动程序
                     ===========================

HID子系统独立于底层传输驱动程序,开始仅支持USB,但其它规格采用HID设计提供了新的传输驱动,内核至少支持USB、蓝牙、I2C和用户空间I/O驱动程序。
1) HID 总线
==========
HID子系统作为一个总线设计,任何I/O子系统都可以提供HID设备,并将其与HID总线进行注册。HID core然后加载通用设备驱动在它上面。传输驱动负责原始数据传输和设备设置/管理,HID core负责report解析、report解释和用户空间AIP。设备特征和quirks由所有层根据quicks处理。

传输驱动实例:
  I/O: USB, I2C, Bluetooth-l2cap
  Transport: USB-HID, I2C-HID, BT-HIDP

1.1) 设备设置
-----------------

I / O驱动程序通常为传输驱动程序提供热插拔检测或设备枚举API。运输驱动使用I/O驱动来找到任何合适的HID设备,传输驱动分配HID设备对象并使用HID core注册。传输驱动程序不需要注册HID core,HID core从来不知道哪些传输程序可用,并且不感兴趣,它只对设备感兴趣。

传输驱动在每个设备上附加一个常量“struct hid_ll_driver”对象,一旦设备注册到HID core,HID core就会使用该结构提供的回调与设备进行通信。

传输驱动负责检测设备故障和是否插入,只要注册了,无论任何的故障,HID core都会对其操作;一旦传输驱动程序检测到拔出或故障,他们必须HID core注销,HID core将停止使用提供的回调。

1.2) 传输驱动要求
----------------------------------
本文档中的术语“异步”和“同步”描述了关于确认的传输行为。异步通道不得执行任何同步操作,如等待确认或验证。 通常,在异步通道上运行的HID调用必须在原子上下文中正常运行。
另一方面,同步信道可以由传输驱动器以任何他们喜欢的方式实现。 它们可能与异步通道相同,但也可以以阻塞方式提供确认报告,故障自动重传等。 如果异步通道需要这样的功能,传输驱动程序必须通过自己的工作线程实现。

HID core要求传输驱动遵循给定的设计,传输驱动程序必须为每个HID设备提供两个双向I/O通道,这些通道在硬件本身不一定是双向的,传输驱动可能只提供4个单向通道,或者它可以在一个物理通道上复用所有四个,然而,在本文中,我们将它们描述为两个双向通道,因为它们具有多个属性。

- Interrupt Channel (intr):
   Intr通道用于一步的reports数据,在此通道上不发送管理命令或数据确认,任何未经请求的传入或传出report数据都必须在此通道上发送,并且远程方不能被确认。 设备通常会在此通道上发送其输入事件,传出的事件通常不会通过intr发送,除非需要高吞吐量。
 - Control Channel (ctrl):
   ctrl通道用于同步请求和设备管理,未经请求的数据输入事件不得在此通道上发送,通常被忽略。相反,设备仅仅在此通道上发送管理事件或回复主机的请求。
控制通道以直接阻塞的方式访问设备,而不依赖与intr通道的任何事件,传出报告通常通过同步SET_REPORT请求在ctrl通道上发送。

设备和HID core之间的通信主要是通过HID report完成的。report可以是三种类型之一:

- INPUT Report: Input reports provide data from device to host. This
   data may include button events, axis events, battery status or more. This
   data is generated by the device and sent to the host with or without
   requiring explicit requests. Devices can choose to send data continuously or
   only on change.
 - OUTPUT Report: Output reports change device states. They are sent from host
   to device and may include LED requests, rumble requests or more. Output
   reports are never sent from device to host, but a host can retrieve their
   current state.
   Hosts may choose to send output reports either continuously or only on
   change.
 - FEATURE Report: Feature reports are used for specific static device features
   and never reported spontaneously. A host can read and/or write them to access
   data like battery-state or device-settings.
   Feature reports are never sent without requests. A host must explicitly set
   or retrieve a feature report. This also means, feature reports are never sent
   on the intr channel as this channel is asynchronous.

INPUT and OUTPUT reports can be sent as pure data reports on the intr channel.
For INPUT reports this is the usual operational mode. But for OUTPUT reports,
this is rarely done as OUTPUT reports are normally quite scarce. But devices are
free to make excessive use of asynchronous OUTPUT reports (for instance, custom
HID audio speakers make great use of it).

Plain reports must not be sent on the ctrl channel, though. Instead, the ctrl
channel provides synchronous GET/SET_REPORT requests. Plain reports are only
allowed on the intr channel and are the only means of data there.

- GET_REPORT: A GET_REPORT request has a report ID as payload and is sent
   from host to device. The device must answer with a data report for the
   requested report ID on the ctrl channel as a synchronous acknowledgement.
   Only one GET_REPORT request can be pending for each device. This restriction
   is enforced by HID core as several transport drivers don't allow multiple
   simultaneous GET_REPORT requests.
   Note that data reports which are sent as answer to a GET_REPORT request are
   not handled as generic device events. That is, if a device does not operate
   in continuous data reporting mode, an answer to GET_REPORT does not replace
   the raw data report on the intr channel on state change.
   GET_REPORT is only used by custom HID device drivers to query device state.
   Normally, HID core caches any device state so this request is not necessary
   on devices that follow the HID specs except during device initialization to
   retrieve the current state.
   GET_REPORT requests can be sent for any of the 3 report types and shall
   return the current report state of the device. However, OUTPUT reports as
   payload may be blocked by the underlying transport driver if the
   specification does not allow them.
 - SET_REPORT: A SET_REPORT request has a report ID plus data as payload. It is
   sent from host to device and a device must update it's current report state
   according to the given data. Any of the 3 report types can be used. However,
   INPUT reports as payload might be blocked by the underlying transport driver
   if the specification does not allow them.
   A device must answer with a synchronous acknowledgement. However, HID core
   does not require transport drivers to forward this acknowledgement to HID
   core.
   Same as for GET_REPORT, only one SET_REPORT can be pending at a time. This
   restriction is enforced by HID core as some transport drivers do not support
   multiple synchronous SET_REPORT requests.

Other ctrl-channel requests are supported by USB-HID but are not available
(or deprecated) in most other transport level specifications:

- GET/SET_IDLE: Only used by USB-HID and I2C-HID.
 - GET/SET_PROTOCOL: Not used by HID core.
 - RESET: Used by I2C-HID, not hooked up in HID core.
 - SET_POWER: Used by I2C-HID, not hooked up in HID core.

2) HID API
==========
2.1) 初始化
-------------------

传输驱动程序通常使用以下步骤向HID core注册新设备:
 struct hid_device *hid;
 int ret;

hid = hid_allocate_device();
 if (IS_ERR(hid)) {
  ret = PTR_ERR(hid);
  goto err_<...>;
 }

strlcpy(hid->name, <device-name-src>, 127);
 strlcpy(hid->phys, <device-phys-src>, 63);
 strlcpy(hid->uniq, <device-uniq-src>, 63);

hid->ll_driver = &custom_ll_driver;
 hid->bus = <device-bus>;
 hid->vendor = <device-vendor>;
 hid->product = <device-product>;
 hid->version = <device-version>;
 hid->country = <device-country>;
 hid->dev.parent = <pointer-to-parent-device>;
 hid->driver_data = <transport-driver-data-field>;

ret = hid_add_device(hid);
 if (ret)
  goto err_<...>;

一旦hid_add_device()被调用,HID core可以使用custom_ll_driver中提供的回调。

注销设备:

hid_destroy_device(hid);

一旦hid_destory_device()返回,HID core将不可以再使用任何驱动回调。

2.2) hid_ll_driver operations
-----------------------------

有效的HID回调:
 - int (*start) (struct hid_device *hdev)
    HID驱动程序想要使用设备,就会调用。传输驱动程序可以选择在此回调中设置其设备。 然而,一般来说,设备在传输驱动程序将其注册到HID核心之前已经建立,因此这主要仅用于USB HID。

- void (*stop) (struct hid_device *hdev)
    一旦使用完一个设备,就从HID设备驱动程序调用,传输驱动程序可以释放任何缓冲区并将设备取消初始化。但是,如果在设备上加载了另一个HID设备驱动程序,则可能再次调用->start().
   
 - int (*open) (struct hid_device *hdev)
如果对数据reports感兴趣,就从HID设备驱动程序调用。
通常,当用户空间没有打开任何输入API / etc时,设备驱动程序是
 对设备数据和传输驱动程序不感兴趣,可以将设备置于睡眠状态。
但是,一旦 - > open()被调用,传输驱动程序必须准备好进行I / O。
->open()是为每一个要打开HID设备的客户端嵌套调用。
 - void (*close) (struct hid_device *hdev)
    在->open()之后在HID设备驱动调用,它们将不再对设备reports感兴趣,(通常是用户空间关闭驱动程序的任何输入设备)。传输驱动可以将设备置于睡眠状态并终止所有I/O.

- int (*parse) (struct hid_device *hdev)
   在->start()之后调用,传输驱动必须从设备读取HID report描述符,并通过  hid_parse_report()告知HID core。

- int (*power) (struct hid_device *hdev, int level)
    HID core调用给PM提示。
 
 - void (*request) (struct hid_device *hdev, struct hid_report *report,
                    int reqtype)
在ctrl通道发送一个HID请求,“report”包含应发送的报告和“reqtype”请求类型, 请求类型可以是HID_REQ_SET_REPORT或HID_REQ_GET_REPORT.这个回调是可选的。 如果没有提供,HID core将按照HID规范组装原始报告,并通过 - > raw_request()回调发送它。

- int (*wait) (struct hid_device *hdev)
    在再次调用->request()之前有HID core调用,如果一次只允许一个请求,传输驱动程序可以使用它来等待任何待处理的请求完成。

- int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
                       __u8 *buf, size_t count, unsigned char rtype,
                       int reqtype)
    与 - > request()相同,但以报告为原始缓冲区。该请求应同步。传输驱动程序不能使用 - > wait()来完成这些请求。 此请求是强制性的,并且如果缺少,HID core将拒绝该设备。

- int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len)
    通过intr通道发送原始输出报告。由一些需要高吞吐量的HID设备驱动程序用于内部通道的传出请求。 这不能导致SET_REPORT调用! 这必须在内部通道上实现为异步输出报告!

- int (*idle) (struct hid_device *hdev, int report, int idle, int reqtype)
   执行SET / GET_IDLE请求。

2.3) Data Path
--------------

传输驱动负责从I/O设备读取数据。它们必须自己处理与I/O有关的状态跟踪。HID core不实现协议握手或其它管理命令,这些命令由给定的HID传输规范来执行。
从设备读取的每个原始数据包都必须通过HID core进行hid_input_report().必须指定通道类型(intr或ctrl)并报告类型(输入/输出/功能)。在正常情况下,只有输入报告通过此API提供。

还必须通过API提供对GET_REPORT请求的响应->request()。对->raw_request()的响应是同步的,必须被传输驱动程序拦截,而不会传递给hid_input_report()。对SET_REPORT请求的确认不是HID core所关心的。

----------------------------------------------------
David Herrmann <dh.herrmann@gmail.com>写于2013年

二.UHID - HID子系统用户空间I/O驱动支持
       ========================================================

UHID允许用户空间实现HID传输驱动程序,HID传输驱动程序的定义可以查看第一部分。

使用UHID,一个用户空间传输驱动可以为每一个连接到用户空间控制总线的设备创建内核hid设备。UHID API定义从内核提供给用户空间的I / O事件。

在 ./samples/uhid/uhid-example.c有一个用户空间应用的例子。

UHID API
------------

通过字符混杂设备访问UHID,minor-number被动态的分配,因此需要依靠udev(或类似的)来创建设备节点,默认情况下是/dev/uhid。

如果HID I/O驱动程序检测到新设备,并且你想要注册到HID子系统,那么你需要为注册的每个设备打开一次/dev/uhid。所有以后的通信是通过read()或write()“struct uhid_event”对象实现,设置O_NONBLOCK支持非阻塞操作。

struct uhid_event {
        __u32 type;
        union {
                struct uhid_create2_req create2;
                struct uhid_output_req output;
                struct uhid_input2_req input2;
                ...
        } u;
};

“type”字段包含事件的ID,依靠该ID区分不同有效载荷的发送,不能在多个read()或多个write()中分割单个事件,一个事件必须始终作为一个整体发送。此外,每个read()或write()只能发送一个事件。待处理数据被忽略。如果要在单个系统调用中处理多个事件,可以使用readv()/write()向量I/O.
“tupe”字段定义了有效载荷,对于每个type,在union“u”中都有一个有效负载结构(空载荷除外)。该有效载荷包含管理和/或设备数据。

应该做的第一件事是发送一个UHID_CREATE2事件,这会注册设备,UHID会响应一个UHID_START事件,就可以开始从UHID发送数据和读数据。但是,除非UHID发送UHID_OPEN事件,否则内部连接的HID设备驱动程序没有附加用户。也就是说,除非收到UHID_OPEN事件,否则可能会将设备置于睡眠状态。如果收到UHID_OPEN事件,则应启动I / O。 如果最后一个用户关闭HID设备,将收到一个UHID_CLOSE事件。此后可再次发生UHID_OPEN事件,依此类推。在用户空间中不需要执行引用计数。也就是说,如果没有UHID_CLOSE事件,将永远不会收到多个UHID_OPEN事件。HID子系统执行重新计数。

如果要将中断通道上的数据发送到HID子系统,可以发送一个HID_INPUT2事件和原始数据有效载荷。如果内核想要将中断通道上的数据发送到设备,则会读取一个UHID_OUTPUT事件。
到目前为止控制通道上的数据请求目前仅限于GET_REPORT和SET_REPORT(没有定义控制通道上的其他数据报告)。
这些请求总是同步的,这意味着,内核发送UHID_GET_REPORT和UHID_SET_REPORT事件,并要求你将它们转发到控制通道的设备上。设备响应后,必须转发通过UHID_GET_REPORT_REPLY和UHID_SET_REPORT_REPLY到内核的响应。

内核在这种调用期间阻止内部驱动程序执行(在固定的周期后超时)。

如果设备断开连接,则应发送一个UHID_DESTROY事件。 这将取消注册设备。之后可以再次发送UHID_CREATE2来注册新设备。
如果close() fd,设备将自动被注销并在内部销毁。

write()
-------
Write()允许你修改设备状态,并将数据输入内核,内核会立即解析事件,如果事件ID不受支持,它将返回-EOPNOTSUPP。如果有效载荷无效,则返回-EINVAL,否则返回读取的数据量,请求被成功处理。 O_NONBLOCK不会影响write(),因为写入总是以非阻塞方式立即处理。 未来的请求可能会使用O_NONBLOCK。

UHID_CREATE2:
  This creates the internal HID device. No I/O is possible until you send this
  event to the kernel. The payload is of type struct uhid_create2_req and
  contains information about your device. You can start I/O now.

UHID_DESTROY:
  This destroys the internal HID device. No further I/O will be accepted. There
  may still be pending messages that you can receive with read() but no further
  UHID_INPUT events can be sent to the kernel.
  You can create a new device by sending UHID_CREATE2 again. There is no need to
  reopen the character device.

UHID_INPUT2:
  You must send UHID_CREATE2 before sending input to the kernel! This event
  contains a data-payload. This is the raw data that you read from your device
  on the interrupt channel. The kernel will parse the HID reports.

UHID_GET_REPORT_REPLY:
  If you receive a UHID_GET_REPORT request you must answer with this request.
  You  must copy the "id" field from the request into the answer. Set the "err"
  field to 0 if no error occurred or to EIO if an I/O error occurred.
  If "err" is 0 then you should fill the buffer of the answer with the results
  of the GET_REPORT request and set "size" correspondingly.

UHID_SET_REPORT_REPLY:
  This is the SET_REPORT equivalent of UHID_GET_REPORT_REPLY. Unlike GET_REPORT,
  SET_REPORT never returns a data buffer, therefore, it's sufficient to set the
  "id" and "err" fields correctly.

read()
------
read()返回一个请求的输出报告,

UHID_START:
  This is sent when the HID device is started. Consider this as an answer to
  UHID_CREATE2. This is always the first event that is sent. Note that this
  event might not be available immediately after write(UHID_CREATE2) returns.
  Device drivers might required delayed setups.
  This event contains a payload of type uhid_start_req. The "dev_flags" field
  describes special behaviors of a device. The following flags are defined:
      UHID_DEV_NUMBERED_FEATURE_REPORTS:
      UHID_DEV_NUMBERED_OUTPUT_REPORTS:
      UHID_DEV_NUMBERED_INPUT_REPORTS:
          Each of these flags defines whether a given report-type uses numbered
          reports. If numbered reports are used for a type, all messages from
          the kernel already have the report-number as prefix. Otherwise, no
          prefix is added by the kernel.
          For messages sent by user-space to the kernel, you must adjust the
          prefixes according to these flags.

UHID_STOP:
  This is sent when the HID device is stopped. Consider this as an answer to
  UHID_DESTROY.
  If you didn't destroy your device via UHID_DESTROY, but the kernel sends an
  UHID_STOP event, this should usually be ignored. It means that the kernel
  reloaded/changed the device driver loaded on your HID device (or some other
  maintenance actions happened).
  You can usually ignored any UHID_STOP events safely.

UHID_OPEN:
  This is sent when the HID device is opened. That is, the data that the HID
  device provides is read by some other process. You may ignore this event but
  it is useful for power-management. As long as you haven't received this event
  there is actually no other process that reads your data so there is no need to
  send UHID_INPUT2 events to the kernel.

UHID_CLOSE:
  This is sent when there are no more processes which read the HID data. It is
  the counterpart of UHID_OPEN and you may as well ignore this event.

UHID_OUTPUT:
  This is sent if the HID device driver wants to send raw data to the I/O
  device on the interrupt channel. You should read the payload and forward it to
  the device. The payload is of type "struct uhid_data_req".
  This may be received even though you haven't received UHID_OPEN, yet.

UHID_GET_REPORT:
  This event is sent if the kernel driver wants to perform a GET_REPORT request
  on the control channeld as described in the HID specs. The report-type and
  report-number are available in the payload.
  The kernel serializes GET_REPORT requests so there will never be two in
  parallel. However, if you fail to respond with a UHID_GET_REPORT_REPLY, the
  request might silently time out.
  Once you read a GET_REPORT request, you shall forward it to the hid device and
  remember the "id" field in the payload. Once your hid device responds to the
  GET_REPORT (or if it fails), you must send a UHID_GET_REPORT_REPLY to the
  kernel with the exact same "id" as in the request. If the request already
  timed out, the kernel will ignore the response silently. The "id" field is
  never re-used, so conflicts cannot happen.

UHID_SET_REPORT:
  This is the SET_REPORT equivalent of UHID_GET_REPORT. On receipt, you shall
  send a SET_REPORT request to your hid device. Once it replies, you must tell
  the kernel about it via UHID_SET_REPORT_REPLY.
  The same restrictions as for UHID_GET_REPORT apply.

----------------------------------------------------
Written 2012, David Herrmann <dh.herrmann@gmail.com>
结合内核源码分析原理:在内核drivers/hid/下有一个uhid.c
/*
 * User-space I/O driver support for HID subsystem
 * Copyright (c) 2012 David Herrmann
 */
uhid在内核中本身是一个misc杂项设备驱动(对一些字符设备的封装),它对应的设备文件是/dev/uhid,用户空间就是访问的它,然后内核中uhid misc驱动再与hid子系统交互。其实就是可以间接的在用户空间实现了hid传输驱动的职能。

三.内核中驱动实现重要的宏:
标准实现:
static int __init sony_init(void)
{
         dbg_hid("Sony:%s\n", __func__);
 
         return hid_register_driver(&sony_driver);
}

static void __exit sony_exit(void)
{
         dbg_hid("Sony:%s\n", __func__);

hid_unregister_driver(&sony_driver);
         ida_destroy(&sony_device_id_allocator);
}
 module_init(sony_init);
 module_exit(sony_exit);

简洁实现:
只有一条 module_xxx_driver(starct hid_driver)
宏:
模块头文件
#define module_hid_driver(__hid_driver) \
         module_driver(__hid_driver, hid_register_driver, \    //hid_register_driver为总线上函数                                                                                                   
                        hid_unregister_driver)
device总头文件
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \                                                                                                                                                                                
         return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
         __unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

四.Care and feeding of your Human Interface Devices
介绍
除了正常的输入类型的HID设备之外,USB还将HID协议用于不是真正的人机交互但具有相似种类的通信需求的东西。两个很大的例子是电源设备(尤其是不间断电源)和监控高端显示器的控制。
The two big examples for this are power devices (especially uninterruptable power
supplies) and monitor control on higher end monitors.

为了支持这些不同的要求,Linux USB系统向两个独立的接口提供HID事件:

*input子系统将HID事件转换为普通输入设备接口(如键盘,鼠标和操纵杆)和规范化的事件接口 - 请参阅Documentation / input / input.txt

* hiddev接口,提供相当原始的HID事件

由设备产生的HID事件的数据流是类似的
下列 :
 usb.c ---> hid-core.c  ----> hid-input.c ----> [keyboard/mouse/joystick/event]
                         |
                         |
                          --> hiddev.c ----> POWER / MONITOR CONTROL

此外,其他子系统(除了USB)可能会将事件提供给输入子系统,但这些对HID设备接口没有任何影响。

使用HID设备接口

Hiddev接口是一个使用普通usb主设备号以及从设备号96到111的char类型接口,因此需要一下命令:
mknod /dev/usb/hiddev0 c 180 96
mknod /dev/usb/hiddev1 c 180 97
mknod /dev/usb/hiddev2 c 180 98
mknod /dev/usb/hiddev3 c 180 99
mknod /dev/usb/hiddev4 c 180 100
mknod /dev/usb/hiddev5 c 180 101
mknod /dev/usb/hiddev6 c 180 102
mknod /dev/usb/hiddev7 c 180 103
mknod /dev/usb/hiddev8 c 180 104
mknod /dev/usb/hiddev9 c 180 105
mknod /dev/usb/hiddev10 c 180 106
mknod /dev/usb/hiddev11 c 180 107
mknod /dev/usb/hiddev12 c 180 108
mknod /dev/usb/hiddev13 c 180 109
mknod /dev/usb/hiddev14 c 180 110
mknod /dev/usb/hiddev15 c 180 111

真样就可以将hiddev兼容的用户空间程序指向设备的正确接口,并且它们都可以正常工作。
前提是假设有一个hiddev兼容的用户空间程序,当然,如果需要写一个,请继续阅读。
THE HIDDEV API
该描述应与HID规范一起阅读,可从http://www.usb.org  http://www.linux-usb.org获得。

hiddev API使用一个read()接口和一组ioctl()调用。

HID设备使用称为“report”的数据包与主机交换数据,每个报告用“fields”分开,每一个都可以有一个或多个“usages”。 在HID core中,这些“usages”中每一个都具有单个带符号的32位值。

read():
这是一个事件接口,当HID设备的状态改变时,它执行一个中断传输,这个中断传输包含一个带有变化了的值得report,hid-core.c模块解析报告,并在report中将单个的含有usages的变化返回到hiddev.c。在其基本模式下,hiddev将使用struct hiddev_event向调用者提供这些单个的usage改变值。
       struct hiddev_event {
           unsigned hid;
           signed int value;
       };
结构体包含更改状态的HID使用情况标识符以及更改的值,需要注意,结构在<linux / hiddev.h>中定义,以及一些其他有用的#define和结构。可以使用下面描述的HIDIOCSFLAG ioctl()来修改read()函数的行为。

ioctl():
该控制接口,有下面一些实现:

HIDIOCGVERSION - int (read)
从hiddev驱动程序中获取版本信息。
HIDIOCAPPLICATION - (none)
该ioctl调用返回与hid设备关联的HID应用usage,ioctl()的第三个参数指定要获取的应用程序索引。当设备有多个应用程序集合时,这是非常有用的。如果索引无效(大于或等于此设备的应用程序集合数),则ioctl将返回-1。可以从hiddev_devinfo结构的num_applications字段中预先了解设备有多少应用程序集合。

HIDIOCGCOLLECTIONINFO - struct hiddev_collection_info (read/write)
这返回上述信息的超集,不仅提供应用程序集合,还提供设备所有的集合。 它还返回集合在层次结构中的层次。 用户通过索引hiddev_collection_info结构体
字段设置为应返回的索引。 ioctl填充其他字段。 如果索引大于上一个集合索引,则ioctl返回-1并将errno设置为-EINVAL。

HIDIOCGDEVINFO - struct hiddev_devinfo (read)
获取hiddev_devinfo结构体数据;

HIDIOCGSTRING - struct hiddev_string_descriptor (read/write)
从设备获取字符串描述符,必须填写“index”字段来指示应该返回哪个描述符。

HIDIOCINITREPORT - (none)
Instructs the kernel to retrieve all input and feature report values
from the device. At this point, all the usage structures will contain
current values for the device, and will maintain it as the device
changes.  Note that the use of this ioctl is unnecessary in general,
since later kernels automatically initialize the reports from the
device at attach time.

HIDIOCGNAME - string (variable length)
Gets the device name

HIDIOCGREPORT - struct hiddev_report_info (write)
指示内核从设备获取功能或输入report,以便有选择地更新usage结构(与INITREPORT相反)。

HIDIOCSREPORT - struct hiddev_report_info (write)
指示内核向设备发送report,该report可以由用户通过HIDIOCSUSAGE调用后填写,以便在将report全部发送到设备之前填写report中的各个usage值。

HIDIOCGREPORTINFO - struct hiddev_report_info (read/write)
Fills in a hiddev_report_info structure for the user. The report is
looked up by type (input, output or feature) and id, so these fields
must be filled in by the user. The ID can be absolute -- the actual
report id as reported by the device -- or relative --
HID_REPORT_ID_FIRST for the first report, and (HID_REPORT_ID_NEXT |
report_id) for the next report after report_id. Without a-priori
information about report ids, the right way to use this ioctl is to
use the relative IDs above to enumerate the valid IDs. The ioctl
returns non-zero when there is no more next ID. The real report ID is
filled into the returned hiddev_report_info structure.

HIDIOCGFIELDINFO - struct hiddev_field_info (read/write)
Returns the field information associated with a report in a
hiddev_field_info structure. The user must fill in report_id and
report_type in this structure, as above. The field_index should also
be filled in, which should be a number from 0 and maxfield-1, as
returned from a previous HIDIOCGREPORTINFO call.

HIDIOCGUCODE - struct hiddev_usage_ref (read/write)
Returns the usage_code in a hiddev_usage_ref structure, given that
given its report type, report id, field index, and index within the
field have already been filled into the structure.

HIDIOCGUSAGE - struct hiddev_usage_ref (read/write)
Returns the value of a usage in a hiddev_usage_ref structure. The
usage to be retrieved can be specified as above, or the user can
choose to fill in the report_type field and specify the report_id as
HID_REPORT_ID_UNKNOWN. In this case, the hiddev_usage_ref will be
filled in with the report and field information associated with this
usage if it is found.

HIDIOCSUSAGE - struct hiddev_usage_ref (write)
Sets the value of a usage in an output report.  The user fills in
the hiddev_usage_ref structure as above, but additionally fills in
the value field.

HIDIOGCOLLECTIONINDEX - struct hiddev_usage_ref (write)
Returns the collection index associated with this usage.  This
indicates where in the collection hierarchy this usage sits.

HIDIOCGFLAG - int (read)
HIDIOCSFLAG - int (write)
These operations respectively inspect and replace the mode flags
that influence the read() call above.  The flags are as follows:

HIDDEV_FLAG_UREF - read() calls will now return
        struct hiddev_usage_ref instead of struct hiddev_event.
        This is a larger structure, but in situations where the
        device has more than one usage in its reports with the
        same usage code, this mode serves to resolve such
        ambiguity.

HIDDEV_FLAG_REPORT - This flag can only be used in conjunction
        with HIDDEV_FLAG_UREF.  With this flag set, when the device
        sends a report, a struct hiddev_usage_ref will be returned
        to read() filled in with the report_type and report_id, but
        with field_index set to FIELD_INDEX_NONE.  This serves as
        additional notification when the device has sent a report.

五.HIDRAW - 原始的访问USB和BT hid设备
     ==================================================================
介绍
hidraw驱动程序为USB和蓝牙人机接口设备(HID)提供了一个原始接口。 它与hiddev不同之处在于发送和接收的报告不会被HID解析器解析,而是发送到设备并从设备接收未修改。

如果用户空间应用程序知道如何与硬件设备通信,并且能够手动构建HID report,则应使用Hidraw。 当为自定义HID设备创建用户空间驱动程序时,通常是这种情况。

Hidraw还可用于与non-conformant的HID设备进行通信,HID设备以与其报告描述符不一致的方式发送和接收数据。因为hiddev解析通过它发送和接收的报告,检查它们对设备的报告描述符,这种与这些不一致的设备的通信是不可能使用hiddev。除了为non-conformant设备编写自定义内核驱动程序,Hidraw是唯一的替代方案。

hidraw的一个好处是用户空间应用程序的使用与底层硬件类型无关。 目前,Hidraw已经实现了USB和蓝牙。 将来,随着开发使用HID规范的新硬件总线类型,hidraw将被扩展以增加对这些新总线类型的支持。

Hidraw使用一个动态的主设备号,这意味着应该依赖udev来创建hidraw设备节点。 Udev通常会直接在/ dev下创建设备节点(例如:/ dev / hidraw0)。 由于这个位置是分布式的并且依赖于udev 规则,应用程序应该使用libudev来定位连接到系统的hidraw设备。 有一个关于libudev的教程,其工作示例如下:
http://www.signal11.us/oss/udev/

The HIDRAW API
---------------

read()
-------
read() will read a queued report received from the HID device. On USB
devices, the reports read using read() are the reports sent from the device
on the INTERRUPT IN endpoint.  By default, read() will block until there is
a report available to be read.  read() can be made non-blocking, by passing
the O_NONBLOCK flag to open(), or by setting the O_NONBLOCK flag using
fcntl().

On a device which uses numbered reports, the first byte of the returned data
will be the report number; the report data follows, beginning in the second
byte.  For devices which do not use numbered reports, the report data
will begin at the first byte.

write()
--------
The write() function will write a report to the device. For USB devices, if
the device has an INTERRUPT OUT endpoint, the report will be sent on that
endpoint. If it does not, the report will be sent over the control endpoint,
using a SET_REPORT transfer.

The first byte of the buffer passed to write() should be set to the report
number.  If the device does not use numbered reports, the first byte should
be set to 0. The report data itself should begin at the second byte.

ioctl()
--------
Hidraw supports the following ioctls:

HIDIOCGRDESCSIZE: Get Report Descriptor Size
This ioctl will get the size of the device's report descriptor.

HIDIOCGRDESC: Get Report Descriptor
This ioctl returns the device's report descriptor using a
hidraw_report_descriptor struct.  Make sure to set the size field of the
hidraw_report_descriptor struct to the size returned from HIDIOCGRDESCSIZE.

HIDIOCGRAWINFO: Get Raw Info
This ioctl will return a hidraw_devinfo struct containing the bus type, the
vendor ID (VID), and product ID (PID) of the device. The bus type can be one
of:
 BUS_USB
 BUS_HIL
 BUS_BLUETOOTH
 BUS_VIRTUAL
which are defined in linux/input.h.

HIDIOCGRAWNAME(len): Get Raw Name
This ioctl returns a string containing the vendor and product strings of
the device.  The returned string is Unicode, UTF-8 encoded.

HIDIOCGRAWPHYS(len): Get Physical Address
This ioctl returns a string representing the physical address of the device.
For USB devices, the string contains the physical path to the device (the
USB controller, hubs, ports, etc).  For Bluetooth devices, the string
contains the hardware (MAC) address of the device.

HIDIOCSFEATURE(len): Send a Feature Report
This ioctl will send a feature report to the device.  Per the HID
specification, feature reports are always sent using the control endpoint.
Set the first byte of the supplied buffer to the report number.  For devices
which do not use numbered reports, set the first byte to 0. The report data
begins in the second byte. Make sure to set len accordingly, to one more
than the length of the report (to account for the report number).

HIDIOCGFEATURE(len): Get a Feature Report
This ioctl will request a feature report from the device using the control
endpoint.  The first byte of the supplied buffer should be set to the report
number of the requested report.  For devices which do not use numbered
reports, set the first byte to 0.  The report will be returned starting at
the first byte of the buffer (ie: the report number is not returned).

Example
---------
In samples/, find hid-example.c, which shows examples of read(), write(),
and all the ioctls for hidraw.  The code may be used by anyone for any
purpose, and can serve as a starting point for developing applications using
hidraw.

Document by:
 Alan Ott <alan@signal11.us>, Signal 11 Software

六.HID core
介绍
HID core本身以module的形式存在,在初始化函数hid_init中核心实现是调用了
ret = bus_register(&hid_bus_type);
... ...
ret = hidraw_init(); //与hidraw相关
这里与驱动的实现明显的区别。

Hid驱动初始化时调用的注册函数
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;
}

Usbhid传输驱动
usbhid也以module的形式存在,在它的初始化函数hid_init中,核心执行了下列操作
 retval = usbhid_quirks_init(quirks_param);
    ... ...
 retval = usb_register(&hid_driver);
先看usb_register函数,usbhid core将自己注册到了usb总线,函数实现在usb core,首先指定bus type为usb_bus_type,然后调用了driver_register注册。

上面结构图提到他与hid core是有关系的,查看usbhid注册自己的结构体:
static struct usb_driver hid_driver = {
 .name =  "usbhid",
 .probe = usbhid_probe,
 .disconnect = usbhid_disconnect,
#ifdef CONFIG_PM
 .suspend = hid_suspend,
 .resume = hid_resume,
 .reset_resume = hid_reset_resume,
#endif
 .pre_reset = hid_pre_reset,
 .post_reset = hid_post_reset,
 .id_table = hid_usb_ids,
 .supports_autosuspend = 1,
};
其中有一个函数指针usbhid_probe,如果该函数被调用(一般是usb core),在该函数里调用了ret = hid_add_device(hid);此时就传入了一些usbhid实现的回调给hid core,通过这些回调他们交互。
具体实现:
hid->ll_driver = &usb_hid_driver;
//包括的操作,上面传输驱动部分介绍了
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,
 .request = usbhid_request,
 .wait = usbhid_wait_io,
 .raw_request = usbhid_raw_request,
 .output_report = usbhid_output_report,
 .idle = usbhid_idle,
};

Linux HID分析相关推荐

  1. Linux内核分析——可执行程序的装载

    链接的过程 首先运行C预处理器cpp,将C的源程序(a.c)翻译成ASCII码的中间文件(a.i) 接着C编译器ccl,将a.i翻译成ASCII汇编语言文件a.s 接着运行汇编器as,将a.s翻译成可 ...

  2. Linux性能分析命令工具汇总

    转自:http://rdc.hundsun.com/portal/article/731.html?ref=myread 出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望,因此整理了这篇文章. ...

  3. linux文本分析利器awk

    转 快速理解linux文本分析利器awk 原文链接 杜亦舒 性能与架构 awk是什么 如果工作中需要操作linux比较多,那么awk是非常值得学习的 awk是一个极其强大的文本分析工具,把文件逐行的读 ...

  4. 【转】一文掌握 Linux 性能分析之网络篇(续)

    [转]一文掌握 Linux 性能分析之网络篇(续) 在上篇网络篇中,我们已经介绍了几个 Linux 网络方向的性能分析工具,本文再补充几个.总结下来,余下的工具包括但不限于以下几个: sar:统计信息 ...

  5. Linux 性能分析的前 60 秒

    Linux 性能分析的前 60 秒 为了解决性能问题,你登入了一台 Linux 服务器,在最开始的一分钟内需要查看什么? 在 Netflix 我们有一个庞大的 EC2 Linux 集群,还有非常多的性 ...

  6. Linux性能分析和调整的基本原则

    Linux性能分析和调整的基本原则 优化linux系统需要考虑多方面的因素,因为各个因素之间相互关联,因此遇到性能问题以及性能的调节需要综合考虑,基本要素考虑与分析:   1,那些措施能确实×××能? ...

  7. 自学linux指令分析-cat

    自学linux指令分析-cat 1·命令格式 cat [OPTION]... [FILE]... cat [参数][文件名] 2·命令参数 -n                    --number ...

  8. LINUX内核分析第二周学习总结——操作系统是如何工作的

    LINUX内核分析第二周学习总结--操作系统是如何工作的 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course ...

  9. 《Linux内核分析》实验一

    陈智威,<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 课堂学习笔记: 作业截图: 汇编代码堆栈分析: ...

最新文章

  1. 不得不提的团队协作工具
  2. Springboot 启动问题
  3. 题目1493:公约数
  4. 如何创造char二叉树C语言,递归创建二叉树c语言实现+详细解释
  5. Oracle SQL语句大全(三)
  6. 异步加载metadata
  7. 2017 JavaScript 调查报告概述
  8. 半监督学习(semi-supervised learning)
  9. 通过外挂插件向预训练语言模型中融入知识
  10. 连接mysql出现Access denied for user ‘root’@’localhost’ (using password:YES)问题解决办法...
  11. centos自定义服务并加入开机启动
  12. fae专业领域的发展前景_未来这7个专业会“很吃香”,对毕业生的需求量较大,值得报考...
  13. OpenCV-Python教程(5、初级滤波内容)
  14. 使用163邮箱+Python3.6 发送邮件/批量发送邮件
  15. XJTUSE专业课与实验指南(已经开源)
  16. 为什么会用到浅拷贝和深拷贝
  17. 椭圆曲线上的群和构造方法
  18. 数据分析师未来发展前景如何?
  19. seq to seq
  20. 挖矿病毒 解决思路 xmr

热门文章

  1. 修改google搜索引擎非hk方法
  2. 用代码向网站提交数据
  3. Springmvc-简单入门
  4. css单位vw vh,css3自适应布局单位vw,vh详解
  5. JavaScript中replace
  6. 基于Python多元线性回归、机器学习、深度学习在近红外光谱分析中的实践应用培训班
  7. 各阶段产品经理突破自身瓶颈总结(建议收藏)
  8. python表达式3 5_运行以下Python中表达式后,X的值为是x=3==3,5A.3B.5C.(True,5)D.(False,5)...
  9. 系统分析师(一)软考简介
  10. java二维数组的赋值_java二维数组三种初始化方法(实例)