mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-05 07:57:45 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
140
Documentation/hid/hid-sensor.txt
Normal file
140
Documentation/hid/hid-sensor.txt
Normal file
|
@ -0,0 +1,140 @@
|
|||
|
||||
HID Sensors Framework
|
||||
======================
|
||||
HID sensor framework provides necessary interfaces to implement sensor drivers,
|
||||
which are connected to a sensor hub. The sensor hub is a HID device and it provides
|
||||
a report descriptor conforming to HID 1.12 sensor usage tables.
|
||||
|
||||
Description from the HID 1.12 "HID Sensor Usages" specification:
|
||||
"Standardization of HID usages for sensors would allow (but not require) sensor
|
||||
hardware vendors to provide a consistent Plug And Play interface at the USB boundary,
|
||||
thereby enabling some operating systems to incorporate common device drivers that
|
||||
could be reused between vendors, alleviating any need for the vendors to provide
|
||||
the drivers themselves."
|
||||
|
||||
This specification describes many usage IDs, which describe the type of sensor
|
||||
and also the individual data fields. Each sensor can have variable number of
|
||||
data fields. The length and order is specified in the report descriptor. For
|
||||
example a part of report descriptor can look like:
|
||||
|
||||
INPUT(1)[INPUT]
|
||||
..
|
||||
Field(2)
|
||||
Physical(0020.0073)
|
||||
Usage(1)
|
||||
0020.045f
|
||||
Logical Minimum(-32767)
|
||||
Logical Maximum(32767)
|
||||
Report Size(8)
|
||||
Report Count(1)
|
||||
Report Offset(16)
|
||||
Flags(Variable Absolute)
|
||||
..
|
||||
..
|
||||
|
||||
The report is indicating "sensor page (0x20)" contains an accelerometer-3D (0x73).
|
||||
This accelerometer-3D has some fields. Here for example field 2 is motion intensity
|
||||
(0x045f) with a logical minimum value of -32767 and logical maximum of 32767. The
|
||||
order of fields and length of each field is important as the input event raw
|
||||
data will use this format.
|
||||
|
||||
|
||||
Implementation
|
||||
=================
|
||||
|
||||
This specification defines many different types of sensors with different sets of
|
||||
data fields. It is difficult to have a common input event to user space applications,
|
||||
for different sensors. For example an accelerometer can send X,Y and Z data, whereas
|
||||
an ambient light sensor can send illumination data.
|
||||
So the implementation has two parts:
|
||||
- Core hid driver
|
||||
- Individual sensor processing part (sensor drivers)
|
||||
|
||||
Core driver
|
||||
-----------
|
||||
The core driver registers (hid-sensor-hub) registers as a HID driver. It parses
|
||||
report descriptors and identifies all the sensors present. It adds an MFD device
|
||||
with name HID-SENSOR-xxxx (where xxxx is usage id from the specification).
|
||||
For example
|
||||
HID-SENSOR-200073 is registered for an Accelerometer 3D driver.
|
||||
So if any driver with this name is inserted, then the probe routine for that
|
||||
function will be called. So an accelerometer processing driver can register
|
||||
with this name and will be probed if there is an accelerometer-3D detected.
|
||||
|
||||
The core driver provides a set of APIs which can be used by the processing
|
||||
drivers to register and get events for that usage id. Also it provides parsing
|
||||
functions, which get and set each input/feature/output report.
|
||||
|
||||
Individual sensor processing part (sensor drivers)
|
||||
-----------
|
||||
The processing driver will use an interface provided by the core driver to parse
|
||||
the report and get the indexes of the fields and also can get events. This driver
|
||||
can use IIO interface to use the standard ABI defined for a type of sensor.
|
||||
|
||||
|
||||
Core driver Interface
|
||||
=====================
|
||||
|
||||
Callback structure:
|
||||
Each processing driver can use this structure to set some callbacks.
|
||||
int (*suspend)(..): Callback when HID suspend is received
|
||||
int (*resume)(..): Callback when HID resume is received
|
||||
int (*capture_sample)(..): Capture a sample for one of its data fields
|
||||
int (*send_event)(..): One complete event is received which can have
|
||||
multiple data fields.
|
||||
|
||||
Registration functions:
|
||||
int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id,
|
||||
struct hid_sensor_hub_callbacks *usage_callback):
|
||||
|
||||
Registers callbacks for an usage id. The callback functions are not allowed
|
||||
to sleep.
|
||||
|
||||
|
||||
int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id):
|
||||
|
||||
Removes callbacks for an usage id.
|
||||
|
||||
|
||||
Parsing function:
|
||||
int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
|
||||
u8 type,
|
||||
u32 usage_id, u32 attr_usage_id,
|
||||
struct hid_sensor_hub_attribute_info *info);
|
||||
|
||||
A processing driver can look for some field of interest and check if it exists
|
||||
in a report descriptor. If it exists it will store necessary information
|
||||
so that fields can be set or get individually.
|
||||
These indexes avoid searching every time and getting field index to get or set.
|
||||
|
||||
|
||||
Set Feature report
|
||||
int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
|
||||
u32 field_index, s32 value);
|
||||
|
||||
This interface is used to set a value for a field in feature report. For example
|
||||
if there is a field report_interval, which is parsed by a call to
|
||||
sensor_hub_input_get_attribute_info before, then it can directly set that individual
|
||||
field.
|
||||
|
||||
|
||||
int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
|
||||
u32 field_index, s32 *value);
|
||||
|
||||
This interface is used to get a value for a field in input report. For example
|
||||
if there is a field report_interval, which is parsed by a call to
|
||||
sensor_hub_input_get_attribute_info before, then it can directly get that individual
|
||||
field value.
|
||||
|
||||
|
||||
int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id,
|
||||
u32 attr_usage_id, u32 report_id);
|
||||
|
||||
This is used to get a particular field value through input reports. For example
|
||||
accelerometer wants to poll X axis value, then it can call this function with
|
||||
the usage id of X axis. HID sensors can provide events, so this is not necessary
|
||||
to poll for any field. If there is some new sample, the core driver will call
|
||||
registered callback function to process the sample.
|
317
Documentation/hid/hid-transport.txt
Normal file
317
Documentation/hid/hid-transport.txt
Normal file
|
@ -0,0 +1,317 @@
|
|||
HID I/O Transport Drivers
|
||||
===========================
|
||||
|
||||
The HID subsystem is independent of the underlying transport driver. Initially,
|
||||
only USB was supported, but other specifications adopted the HID design and
|
||||
provided new transport drivers. The kernel includes at least support for USB,
|
||||
Bluetooth, I2C and user-space I/O drivers.
|
||||
|
||||
1) HID Bus
|
||||
==========
|
||||
|
||||
The HID subsystem is designed as a bus. Any I/O subsystem may provide HID
|
||||
devices and register them with the HID bus. HID core then loads generic device
|
||||
drivers on top of it. The transport drivers are responsible of raw data
|
||||
transport and device setup/management. HID core is responsible of
|
||||
report-parsing, report interpretation and the user-space API. Device specifics
|
||||
and quirks are handled by all layers depending on the quirk.
|
||||
|
||||
+-----------+ +-----------+ +-----------+ +-----------+
|
||||
| Device #1 | | Device #i | | Device #j | | Device #k |
|
||||
+-----------+ +-----------+ +-----------+ +-----------+
|
||||
\\ // \\ //
|
||||
+------------+ +------------+
|
||||
| I/O Driver | | I/O Driver |
|
||||
+------------+ +------------+
|
||||
|| ||
|
||||
+------------------+ +------------------+
|
||||
| Transport Driver | | Transport Driver |
|
||||
+------------------+ +------------------+
|
||||
\___ ___/
|
||||
\ /
|
||||
+----------------+
|
||||
| HID Core |
|
||||
+----------------+
|
||||
/ | | \
|
||||
/ | | \
|
||||
____________/ | | \_________________
|
||||
/ | | \
|
||||
/ | | \
|
||||
+----------------+ +-----------+ +------------------+ +------------------+
|
||||
| Generic Driver | | MT Driver | | Custom Driver #1 | | Custom Driver #2 |
|
||||
+----------------+ +-----------+ +------------------+ +------------------+
|
||||
|
||||
Example Drivers:
|
||||
I/O: USB, I2C, Bluetooth-l2cap
|
||||
Transport: USB-HID, I2C-HID, BT-HIDP
|
||||
|
||||
Everything below "HID Core" is simplified in this graph as it is only of
|
||||
interest to HID device drivers. Transport drivers do not need to know the
|
||||
specifics.
|
||||
|
||||
1.1) Device Setup
|
||||
-----------------
|
||||
|
||||
I/O drivers normally provide hotplug detection or device enumeration APIs to the
|
||||
transport drivers. Transport drivers use this to find any suitable HID device.
|
||||
They allocate HID device objects and register them with HID core. Transport
|
||||
drivers are not required to register themselves with HID core. HID core is never
|
||||
aware of which transport drivers are available and is not interested in it. It
|
||||
is only interested in devices.
|
||||
|
||||
Transport drivers attach a constant "struct hid_ll_driver" object with each
|
||||
device. Once a device is registered with HID core, the callbacks provided via
|
||||
this struct are used by HID core to communicate with the device.
|
||||
|
||||
Transport drivers are responsible of detecting device failures and unplugging.
|
||||
HID core will operate a device as long as it is registered regardless of any
|
||||
device failures. Once transport drivers detect unplug or failure events, they
|
||||
must unregister the device from HID core and HID core will stop using the
|
||||
provided callbacks.
|
||||
|
||||
1.2) Transport Driver Requirements
|
||||
----------------------------------
|
||||
|
||||
The terms "asynchronous" and "synchronous" in this document describe the
|
||||
transmission behavior regarding acknowledgements. An asynchronous channel must
|
||||
not perform any synchronous operations like waiting for acknowledgements or
|
||||
verifications. Generally, HID calls operating on asynchronous channels must be
|
||||
running in atomic-context just fine.
|
||||
On the other hand, synchronous channels can be implemented by the transport
|
||||
driver in whatever way they like. They might just be the same as asynchronous
|
||||
channels, but they can also provide acknowledgement reports, automatic
|
||||
retransmission on failure, etc. in a blocking manner. If such functionality is
|
||||
required on asynchronous channels, a transport-driver must implement that via
|
||||
its own worker threads.
|
||||
|
||||
HID core requires transport drivers to follow a given design. A Transport
|
||||
driver must provide two bi-directional I/O channels to each HID device. These
|
||||
channels must not necessarily be bi-directional in the hardware itself. A
|
||||
transport driver might just provide 4 uni-directional channels. Or it might
|
||||
multiplex all four on a single physical channel. However, in this document we
|
||||
will describe them as two bi-directional channels as they have several
|
||||
properties in common.
|
||||
|
||||
- Interrupt Channel (intr): The intr channel is used for asynchronous data
|
||||
reports. No management commands or data acknowledgements are sent on this
|
||||
channel. Any unrequested incoming or outgoing data report must be sent on
|
||||
this channel and is never acknowledged by the remote side. Devices usually
|
||||
send their input events on this channel. Outgoing events are normally
|
||||
not send via intr, except if high throughput is required.
|
||||
- Control Channel (ctrl): The ctrl channel is used for synchronous requests and
|
||||
device management. Unrequested data input events must not be sent on this
|
||||
channel and are normally ignored. Instead, devices only send management
|
||||
events or answers to host requests on this channel.
|
||||
The control-channel is used for direct blocking queries to the device
|
||||
independent of any events on the intr-channel.
|
||||
Outgoing reports are usually sent on the ctrl channel via synchronous
|
||||
SET_REPORT requests.
|
||||
|
||||
Communication between devices and HID core is mostly done via HID reports. A
|
||||
report can be of one of three types:
|
||||
|
||||
- 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) Initialization
|
||||
-------------------
|
||||
|
||||
Transport drivers normally use the following procedure to register a new device
|
||||
with 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_<...>;
|
||||
|
||||
Once hid_add_device() is entered, HID core might use the callbacks provided in
|
||||
"custom_ll_driver". Note that fields like "country" can be ignored by underlying
|
||||
transport-drivers if not supported.
|
||||
|
||||
To unregister a device, use:
|
||||
|
||||
hid_destroy_device(hid);
|
||||
|
||||
Once hid_destroy_device() returns, HID core will no longer make use of any
|
||||
driver callbacks.
|
||||
|
||||
2.2) hid_ll_driver operations
|
||||
-----------------------------
|
||||
|
||||
The available HID callbacks are:
|
||||
- int (*start) (struct hid_device *hdev)
|
||||
Called from HID device drivers once they want to use the device. Transport
|
||||
drivers can choose to setup their device in this callback. However, normally
|
||||
devices are already set up before transport drivers register them to HID core
|
||||
so this is mostly only used by USB-HID.
|
||||
|
||||
- void (*stop) (struct hid_device *hdev)
|
||||
Called from HID device drivers once they are done with a device. Transport
|
||||
drivers can free any buffers and deinitialize the device. But note that
|
||||
->start() might be called again if another HID device driver is loaded on the
|
||||
device.
|
||||
Transport drivers are free to ignore it and deinitialize devices after they
|
||||
destroyed them via hid_destroy_device().
|
||||
|
||||
- int (*open) (struct hid_device *hdev)
|
||||
Called from HID device drivers once they are interested in data reports.
|
||||
Usually, while user-space didn't open any input API/etc., device drivers are
|
||||
not interested in device data and transport drivers can put devices asleep.
|
||||
However, once ->open() is called, transport drivers must be ready for I/O.
|
||||
->open() calls are nested for each client that opens the HID device.
|
||||
|
||||
- void (*close) (struct hid_device *hdev)
|
||||
Called from HID device drivers after ->open() was called but they are no
|
||||
longer interested in device reports. (Usually if user-space closed any input
|
||||
devices of the driver).
|
||||
Transport drivers can put devices asleep and terminate any I/O of all
|
||||
->open() calls have been followed by a ->close() call. However, ->start() may
|
||||
be called again if the device driver is interested in input reports again.
|
||||
|
||||
- int (*parse) (struct hid_device *hdev)
|
||||
Called once during device setup after ->start() has been called. Transport
|
||||
drivers must read the HID report-descriptor from the device and tell HID core
|
||||
about it via hid_parse_report().
|
||||
|
||||
- int (*power) (struct hid_device *hdev, int level)
|
||||
Called by HID core to give PM hints to transport drivers. Usually this is
|
||||
analogical to the ->open() and ->close() hints and redundant.
|
||||
|
||||
- void (*request) (struct hid_device *hdev, struct hid_report *report,
|
||||
int reqtype)
|
||||
Send an HID request on the ctrl channel. "report" contains the report that
|
||||
should be sent and "reqtype" the request type. Request-type can be
|
||||
HID_REQ_SET_REPORT or HID_REQ_GET_REPORT.
|
||||
This callback is optional. If not provided, HID core will assemble a raw
|
||||
report following the HID specs and send it via the ->raw_request() callback.
|
||||
The transport driver is free to implement this asynchronously.
|
||||
|
||||
- int (*wait) (struct hid_device *hdev)
|
||||
Used by HID core before calling ->request() again. A transport driver can use
|
||||
it to wait for any pending requests to complete if only one request is
|
||||
allowed at a time.
|
||||
|
||||
- int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
|
||||
__u8 *buf, size_t count, unsigned char rtype,
|
||||
int reqtype)
|
||||
Same as ->request() but provides the report as raw buffer. This request shall
|
||||
be synchronous. A transport driver must not use ->wait() to complete such
|
||||
requests. This request is mandatory and hid core will reject the device if
|
||||
it is missing.
|
||||
|
||||
- int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len)
|
||||
Send raw output report via intr channel. Used by some HID device drivers
|
||||
which require high throughput for outgoing requests on the intr channel. This
|
||||
must not cause SET_REPORT calls! This must be implemented as asynchronous
|
||||
output report on the intr channel!
|
||||
|
||||
- int (*idle) (struct hid_device *hdev, int report, int idle, int reqtype)
|
||||
Perform SET/GET_IDLE request. Only used by USB-HID, do not implement!
|
||||
|
||||
2.3) Data Path
|
||||
--------------
|
||||
|
||||
Transport drivers are responsible of reading data from I/O devices. They must
|
||||
handle any I/O-related state-tracking themselves. HID core does not implement
|
||||
protocol handshakes or other management commands which can be required by the
|
||||
given HID transport specification.
|
||||
|
||||
Every raw data packet read from a device must be fed into HID core via
|
||||
hid_input_report(). You must specify the channel-type (intr or ctrl) and report
|
||||
type (input/output/feature). Under normal conditions, only input reports are
|
||||
provided via this API.
|
||||
|
||||
Responses to GET_REPORT requests via ->request() must also be provided via this
|
||||
API. Responses to ->raw_request() are synchronous and must be intercepted by the
|
||||
transport driver and not passed to hid_input_report().
|
||||
Acknowledgements to SET_REPORT requests are not of interest to HID core.
|
||||
|
||||
----------------------------------------------------
|
||||
Written 2013, David Herrmann <dh.herrmann@gmail.com>
|
205
Documentation/hid/hiddev.txt
Normal file
205
Documentation/hid/hiddev.txt
Normal file
|
@ -0,0 +1,205 @@
|
|||
Care and feeding of your Human Interface Devices
|
||||
|
||||
INTRODUCTION
|
||||
|
||||
In addition to the normal input type HID devices, USB also uses the
|
||||
human interface device protocols for things that are not really human
|
||||
interfaces, but have similar sorts of communication needs. The two big
|
||||
examples for this are power devices (especially uninterruptable power
|
||||
supplies) and monitor control on higher end monitors.
|
||||
|
||||
To support these disparate requirements, the Linux USB system provides
|
||||
HID events to two separate interfaces:
|
||||
* the input subsystem, which converts HID events into normal input
|
||||
device interfaces (such as keyboard, mouse and joystick) and a
|
||||
normalised event interface - see Documentation/input/input.txt
|
||||
* the hiddev interface, which provides fairly raw HID events
|
||||
|
||||
The data flow for a HID event produced by a device is something like
|
||||
the following :
|
||||
|
||||
usb.c ---> hid-core.c ----> hid-input.c ----> [keyboard/mouse/joystick/event]
|
||||
|
|
||||
|
|
||||
--> hiddev.c ----> POWER / MONITOR CONTROL
|
||||
|
||||
In addition, other subsystems (apart from USB) can potentially feed
|
||||
events into the input subsystem, but these have no effect on the hid
|
||||
device interface.
|
||||
|
||||
USING THE HID DEVICE INTERFACE
|
||||
|
||||
The hiddev interface is a char interface using the normal USB major,
|
||||
with the minor numbers starting at 96 and finishing at 111. Therefore,
|
||||
you need the following commands:
|
||||
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
|
||||
|
||||
So you point your hiddev compliant user-space program at the correct
|
||||
interface for your device, and it all just works.
|
||||
|
||||
Assuming that you have a hiddev compliant user-space program, of
|
||||
course. If you need to write one, read on.
|
||||
|
||||
|
||||
THE HIDDEV API
|
||||
This description should be read in conjunction with the HID
|
||||
specification, freely available from http://www.usb.org, and
|
||||
conveniently linked of http://www.linux-usb.org.
|
||||
|
||||
The hiddev API uses a read() interface, and a set of ioctl() calls.
|
||||
|
||||
HID devices exchange data with the host computer using data
|
||||
bundles called "reports". Each report is divided into "fields",
|
||||
each of which can have one or more "usages". In the hid-core,
|
||||
each one of these usages has a single signed 32 bit value.
|
||||
|
||||
read():
|
||||
This is the event interface. When the HID device's state changes,
|
||||
it performs an interrupt transfer containing a report which contains
|
||||
the changed value. The hid-core.c module parses the report, and
|
||||
returns to hiddev.c the individual usages that have changed within
|
||||
the report. In its basic mode, the hiddev will make these individual
|
||||
usage changes available to the reader using a struct hiddev_event:
|
||||
|
||||
struct hiddev_event {
|
||||
unsigned hid;
|
||||
signed int value;
|
||||
};
|
||||
|
||||
containing the HID usage identifier for the status that changed, and
|
||||
the value that it was changed to. Note that the structure is defined
|
||||
within <linux/hiddev.h>, along with some other useful #defines and
|
||||
structures. The HID usage identifier is a composite of the HID usage
|
||||
page shifted to the 16 high order bits ORed with the usage code. The
|
||||
behavior of the read() function can be modified using the HIDIOCSFLAG
|
||||
ioctl() described below.
|
||||
|
||||
|
||||
ioctl():
|
||||
This is the control interface. There are a number of controls:
|
||||
|
||||
HIDIOCGVERSION - int (read)
|
||||
Gets the version code out of the hiddev driver.
|
||||
|
||||
HIDIOCAPPLICATION - (none)
|
||||
This ioctl call returns the HID application usage associated with the
|
||||
hid device. The third argument to ioctl() specifies which application
|
||||
index to get. This is useful when the device has more than one
|
||||
application collection. If the index is invalid (greater or equal to
|
||||
the number of application collections this device has) the ioctl
|
||||
returns -1. You can find out beforehand how many application
|
||||
collections the device has from the num_applications field from the
|
||||
hiddev_devinfo structure.
|
||||
|
||||
HIDIOCGCOLLECTIONINFO - struct hiddev_collection_info (read/write)
|
||||
This returns a superset of the information above, providing not only
|
||||
application collections, but all the collections the device has. It
|
||||
also returns the level the collection lives in the hierarchy.
|
||||
The user passes in a hiddev_collection_info struct with the index
|
||||
field set to the index that should be returned. The ioctl fills in
|
||||
the other fields. If the index is larger than the last collection
|
||||
index, the ioctl returns -1 and sets errno to -EINVAL.
|
||||
|
||||
HIDIOCGDEVINFO - struct hiddev_devinfo (read)
|
||||
Gets a hiddev_devinfo structure which describes the device.
|
||||
|
||||
HIDIOCGSTRING - struct hiddev_string_descriptor (read/write)
|
||||
Gets a string descriptor from the device. The caller must fill in the
|
||||
"index" field to indicate which descriptor should be returned.
|
||||
|
||||
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)
|
||||
Instructs the kernel to get a feature or input report from the device,
|
||||
in order to selectively update the usage structures (in contrast to
|
||||
INITREPORT).
|
||||
|
||||
HIDIOCSREPORT - struct hiddev_report_info (write)
|
||||
Instructs the kernel to send a report to the device. This report can
|
||||
be filled in by the user through HIDIOCSUSAGE calls (below) to fill in
|
||||
individual usage values in the report before sending the report in full
|
||||
to the device.
|
||||
|
||||
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.
|
119
Documentation/hid/hidraw.txt
Normal file
119
Documentation/hid/hidraw.txt
Normal file
|
@ -0,0 +1,119 @@
|
|||
HIDRAW - Raw Access to USB and Bluetooth Human Interface Devices
|
||||
==================================================================
|
||||
|
||||
The hidraw driver provides a raw interface to USB and Bluetooth Human
|
||||
Interface Devices (HIDs). It differs from hiddev in that reports sent and
|
||||
received are not parsed by the HID parser, but are sent to and received from
|
||||
the device unmodified.
|
||||
|
||||
Hidraw should be used if the userspace application knows exactly how to
|
||||
communicate with the hardware device, and is able to construct the HID
|
||||
reports manually. This is often the case when making userspace drivers for
|
||||
custom HID devices.
|
||||
|
||||
Hidraw is also useful for communicating with non-conformant HID devices
|
||||
which send and receive data in a way that is inconsistent with their report
|
||||
descriptors. Because hiddev parses reports which are sent and received
|
||||
through it, checking them against the device's report descriptor, such
|
||||
communication with these non-conformant devices is impossible using hiddev.
|
||||
Hidraw is the only alternative, short of writing a custom kernel driver, for
|
||||
these non-conformant devices.
|
||||
|
||||
A benefit of hidraw is that its use by userspace applications is independent
|
||||
of the underlying hardware type. Currently, Hidraw is implemented for USB
|
||||
and Bluetooth. In the future, as new hardware bus types are developed which
|
||||
use the HID specification, hidraw will be expanded to add support for these
|
||||
new bus types.
|
||||
|
||||
Hidraw uses a dynamic major number, meaning that udev should be relied on to
|
||||
create hidraw device nodes. Udev will typically create the device nodes
|
||||
directly under /dev (eg: /dev/hidraw0). As this location is distribution-
|
||||
and udev rule-dependent, applications should use libudev to locate hidraw
|
||||
devices attached to the system. There is a tutorial on libudev with a
|
||||
working example at:
|
||||
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
|
187
Documentation/hid/uhid.txt
Normal file
187
Documentation/hid/uhid.txt
Normal file
|
@ -0,0 +1,187 @@
|
|||
UHID - User-space I/O driver support for HID subsystem
|
||||
========================================================
|
||||
|
||||
UHID allows user-space to implement HID transport drivers. Please see
|
||||
hid-transport.txt for an introduction into HID transport drivers. This document
|
||||
relies heavily on the definitions declared there.
|
||||
|
||||
With UHID, a user-space transport driver can create kernel hid-devices for each
|
||||
device connected to the user-space controlled bus. The UHID API defines the I/O
|
||||
events provided from the kernel to user-space and vice versa.
|
||||
|
||||
There is an example user-space application in ./samples/uhid/uhid-example.c
|
||||
|
||||
The UHID API
|
||||
------------
|
||||
|
||||
UHID is accessed through a character misc-device. The minor-number is allocated
|
||||
dynamically so you need to rely on udev (or similar) to create the device node.
|
||||
This is /dev/uhid by default.
|
||||
|
||||
If a new device is detected by your HID I/O Driver and you want to register this
|
||||
device with the HID subsystem, then you need to open /dev/uhid once for each
|
||||
device you want to register. All further communication is done by read()'ing or
|
||||
write()'ing "struct uhid_event" objects. Non-blocking operations are supported
|
||||
by setting O_NONBLOCK.
|
||||
|
||||
struct uhid_event {
|
||||
__u32 type;
|
||||
union {
|
||||
struct uhid_create2_req create2;
|
||||
struct uhid_output_req output;
|
||||
struct uhid_input2_req input2;
|
||||
...
|
||||
} u;
|
||||
};
|
||||
|
||||
The "type" field contains the ID of the event. Depending on the ID different
|
||||
payloads are sent. You must not split a single event across multiple read()'s or
|
||||
multiple write()'s. A single event must always be sent as a whole. Furthermore,
|
||||
only a single event can be sent per read() or write(). Pending data is ignored.
|
||||
If you want to handle multiple events in a single syscall, then use vectored
|
||||
I/O with readv()/writev().
|
||||
The "type" field defines the payload. For each type, there is a
|
||||
payload-structure available in the union "u" (except for empty payloads). This
|
||||
payload contains management and/or device data.
|
||||
|
||||
The first thing you should do is sending an UHID_CREATE2 event. This will
|
||||
register the device. UHID will respond with an UHID_START event. You can now
|
||||
start sending data to and reading data from UHID. However, unless UHID sends the
|
||||
UHID_OPEN event, the internally attached HID Device Driver has no user attached.
|
||||
That is, you might put your device asleep unless you receive the UHID_OPEN
|
||||
event. If you receive the UHID_OPEN event, you should start I/O. If the last
|
||||
user closes the HID device, you will receive an UHID_CLOSE event. This may be
|
||||
followed by an UHID_OPEN event again and so on. There is no need to perform
|
||||
reference-counting in user-space. That is, you will never receive multiple
|
||||
UHID_OPEN events without an UHID_CLOSE event. The HID subsystem performs
|
||||
ref-counting for you.
|
||||
You may decide to ignore UHID_OPEN/UHID_CLOSE, though. I/O is allowed even
|
||||
though the device may have no users.
|
||||
|
||||
If you want to send data on the interrupt channel to the HID subsystem, you send
|
||||
an HID_INPUT2 event with your raw data payload. If the kernel wants to send data
|
||||
on the interrupt channel to the device, you will read an UHID_OUTPUT event.
|
||||
Data requests on the control channel are currently limited to GET_REPORT and
|
||||
SET_REPORT (no other data reports on the control channel are defined so far).
|
||||
Those requests are always synchronous. That means, the kernel sends
|
||||
UHID_GET_REPORT and UHID_SET_REPORT events and requires you to forward them to
|
||||
the device on the control channel. Once the device responds, you must forward
|
||||
the response via UHID_GET_REPORT_REPLY and UHID_SET_REPORT_REPLY to the kernel.
|
||||
The kernel blocks internal driver-execution during such round-trips (times out
|
||||
after a hard-coded period).
|
||||
|
||||
If your device disconnects, you should send an UHID_DESTROY event. This will
|
||||
unregister the device. You can now send UHID_CREATE2 again to register a new
|
||||
device.
|
||||
If you close() the fd, the device is automatically unregistered and destroyed
|
||||
internally.
|
||||
|
||||
write()
|
||||
-------
|
||||
write() allows you to modify the state of the device and feed input data into
|
||||
the kernel. The kernel will parse the event immediately and if the event ID is
|
||||
not supported, it will return -EOPNOTSUPP. If the payload is invalid, then
|
||||
-EINVAL is returned, otherwise, the amount of data that was read is returned and
|
||||
the request was handled successfully. O_NONBLOCK does not affect write() as
|
||||
writes are always handled immediately in a non-blocking fashion. Future requests
|
||||
might make use of O_NONBLOCK, though.
|
||||
|
||||
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() will return a queued output report. No reaction is required to any of
|
||||
them but you should handle them according to your needs.
|
||||
|
||||
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>
|
Loading…
Add table
Add a link
Reference in a new issue