HID: Documentation for hidraw
authorAlan Ott <alan@signal11.us>
Sun, 20 Mar 2011 00:29:44 +0000 (20:29 -0400)
committerJiri Kosina <jkosina@suse.cz>
Tue, 22 Mar 2011 10:43:50 +0000 (11:43 +0100)
Documenation for the hidraw driver, with sample program.

Signed-off-by: Alan Ott <alan@signal11.us>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Documentation/hid/hidraw.txt [new file with mode: 0644]
samples/Kconfig
samples/Makefile
samples/hidraw/Makefile [new file with mode: 0644]
samples/hidraw/hid-example.c [new file with mode: 0644]

diff --git a/Documentation/hid/hidraw.txt b/Documentation/hid/hidraw.txt
new file mode 100644 (file)
index 0000000..029e6cb
--- /dev/null
@@ -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
index e03cf0e..52f4264 100644 (file)
@@ -61,4 +61,10 @@ config SAMPLE_KDB
          Build an example of how to dynamically add the hello
          command to the kdb shell.
 
+config SAMPLE_HIDRAW
+       tristate "Build simple hidraw example"
+       depends on HIDRAW
+       help
+         Build an example of how to use hidraw from userspace.
+
 endif # SAMPLES
index f26c095..6280817 100644 (file)
@@ -1,4 +1,4 @@
 # Makefile for Linux samples code
 
 obj-$(CONFIG_SAMPLES)  += kobject/ kprobes/ tracepoints/ trace_events/ \
-                          hw_breakpoint/ kfifo/ kdb/
+                          hw_breakpoint/ kfifo/ kdb/ hidraw/
diff --git a/samples/hidraw/Makefile b/samples/hidraw/Makefile
new file mode 100644 (file)
index 0000000..7811cb0
--- /dev/null
@@ -0,0 +1,8 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+# List of programs to build
+hostprogs-y := hid-example
+
+# Tell kbuild to always build the programs
+always := $(hostprogs-y)
diff --git a/samples/hidraw/hid-example.c b/samples/hidraw/hid-example.c
new file mode 100644 (file)
index 0000000..40e3d62
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Hidraw Userspace Example
+ *
+ * Copyright (c) 2010 Alan Ott <alan@signal11.us>
+ * Copyright (c) 2010 Signal 11 Software
+ *
+ * The code may be used by anyone for any purpose,
+ * and can serve as a starting point for developing
+ * applications using hidraw.
+ */
+
+/* Linux */
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/hidraw.h>
+
+/* Unix */
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* C */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+const char *bus_str(int bus);
+
+int main(int argc, char **argv)
+{
+       int fd;
+       int i, res, desc_size = 0;
+       char buf[256];
+       struct hidraw_report_descriptor rpt_desc;
+       struct hidraw_devinfo info;
+
+       /* Open the Device with non-blocking reads. In real life,
+          don't use a hard coded path; use libudev instead. */
+       fd = open("/dev/hidraw0", O_RDWR|O_NONBLOCK);
+
+       if (fd < 0) {
+               perror("Unable to open device");
+               return 1;
+       }
+
+       memset(&rpt_desc, 0x0, sizeof(rpt_desc));
+       memset(&info, 0x0, sizeof(info));
+       memset(buf, 0x0, sizeof(buf));
+
+       /* Get Report Descriptor Size */
+       res = ioctl(fd, HIDIOCGRDESCSIZE, &desc_size);
+       if (res < 0)
+               perror("HIDIOCGRDESCSIZE");
+       else
+               printf("Report Descriptor Size: %d\n", desc_size);
+
+       /* Get Report Descriptor */
+       rpt_desc.size = desc_size;
+       res = ioctl(fd, HIDIOCGRDESC, &rpt_desc);
+       if (res < 0) {
+               perror("HIDIOCGRDESC");
+       } else {
+               printf("Report Descriptor:\n");
+               for (i = 0; i < rpt_desc.size; i++)
+                       printf("%hhx ", rpt_desc.value[i]);
+               puts("\n");
+       }
+
+       /* Get Raw Name */
+       res = ioctl(fd, HIDIOCGRAWNAME(256), buf);
+       if (res < 0)
+               perror("HIDIOCGRAWNAME");
+       else
+               printf("Raw Name: %s\n", buf);
+
+       /* Get Physical Location */
+       res = ioctl(fd, HIDIOCGRAWPHYS(256), buf);
+       if (res < 0)
+               perror("HIDIOCGRAWPHYS");
+       else
+               printf("Raw Phys: %s\n", buf);
+
+       /* Get Raw Info */
+       res = ioctl(fd, HIDIOCGRAWINFO, &info);
+       if (res < 0) {
+               perror("HIDIOCGRAWINFO");
+       } else {
+               printf("Raw Info:\n");
+               printf("\tbustype: %d (%s)\n",
+                       info.bustype, bus_str(info.bustype));
+               printf("\tvendor: 0x%04hx\n", info.vendor);
+               printf("\tproduct: 0x%04hx\n", info.product);
+       }
+
+       /* Set Feature */
+       buf[0] = 0x9; /* Report Number */
+       buf[1] = 0xff;
+       buf[2] = 0xff;
+       buf[3] = 0xff;
+       res = ioctl(fd, HIDIOCSFEATURE(4), buf);
+       if (res < 0)
+               perror("HIDIOCSFEATURE");
+       else
+               printf("ioctl HIDIOCGFEATURE returned: %d\n", res);
+
+       /* Get Feature */
+       buf[0] = 0x9; /* Report Number */
+       res = ioctl(fd, HIDIOCGFEATURE(256), buf);
+       if (res < 0) {
+               perror("HIDIOCGFEATURE");
+       } else {
+               printf("ioctl HIDIOCGFEATURE returned: %d\n", res);
+               printf("Report data (not containing the report number):\n\t");
+               for (i = 0; i < res; i++)
+                       printf("%hhx ", buf[i]);
+               puts("\n");
+       }
+
+       /* Send a Report to the Device */
+       buf[0] = 0x1; /* Report Number */
+       buf[1] = 0x77;
+       res = write(fd, buf, 2);
+       if (res < 0) {
+               printf("Error: %d\n", errno);
+               perror("write");
+       } else {
+               printf("write() wrote %d bytes\n", res);
+       }
+
+       /* Get a report from the device */
+       res = read(fd, buf, 16);
+       if (res < 0) {
+               perror("read");
+       } else {
+               printf("read() read %d bytes:\n\t", res);
+               for (i = 0; i < res; i++)
+                       printf("%hhx ", buf[i]);
+               puts("\n");
+       }
+       close(fd);
+       return 0;
+}
+
+const char *
+bus_str(int bus)
+{
+       switch (bus) {
+       case BUS_USB:
+               return "USB";
+               break;
+       case BUS_HIL:
+               return "HIL";
+               break;
+       case BUS_BLUETOOTH:
+               return "Bluetooth";
+               break;
+       case BUS_VIRTUAL:
+               return "Virtual";
+               break;
+       default:
+               return "Other";
+               break;
+       }
+}