Linux gadget driver usb

Linux USB HID gadget driver¶

The HID Gadget driver provides emulation of USB Human Interface Devices (HID). The basic HID handling is done in the kernel, and HID reports can be sent/received through I/O on the /dev/hidgX character devices.

For more details about HID, see the developer page on https://www.usb.org/developers/hidpage/

Configuration¶

g_hid is a platform driver, so to use it you need to add struct platform_device(s) to your platform code defining the HID function descriptors you want to use — E.G. something like:

#include #include /* hid descriptor for a keyboard */ static struct hidg_func_descriptor my_hid_data = < .subclass = 0, /* No subclass */ .protocol = 1, /* Keyboard */ .report_length = 8, .report_desc_length = 63, .report_desc = < 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 0x09, 0x06, /* USAGE (Keyboard) */ 0xa1, 0x01, /* COLLECTION (Application) */ 0x05, 0x07, /* USAGE_PAGE (Keyboard) */ 0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */ 0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 0x75, 0x01, /* REPORT_SIZE (1) */ 0x95, 0x08, /* REPORT_COUNT (8) */ 0x81, 0x02, /* INPUT (Data,Var,Abs) */ 0x95, 0x01, /* REPORT_COUNT (1) */ 0x75, 0x08, /* REPORT_SIZE (8) */ 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */ 0x95, 0x05, /* REPORT_COUNT (5) */ 0x75, 0x01, /* REPORT_SIZE (1) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */ 0x29, 0x05, /* USAGE_MAXIMUM (Kana) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0x95, 0x01, /* REPORT_COUNT (1) */ 0x75, 0x03, /* REPORT_SIZE (3) */ 0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */ 0x95, 0x06, /* REPORT_COUNT (6) */ 0x75, 0x08, /* REPORT_SIZE (8) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 0x25, 0x65, /* LOGICAL_MAXIMUM (101) */ 0x05, 0x07, /* USAGE_PAGE (Keyboard) */ 0x19, 0x00, /* USAGE_MINIMUM (Reserved) */ 0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */ 0x81, 0x00, /* INPUT (Data,Ary,Abs) */ 0xc0 /* END_COLLECTION */ >>; static struct platform_device my_hid = < .name = "hidg", .id = 0, .num_resources = 0, .resource = 0, .dev.platform_data = &my_hid_data, >;

You can add as many HID functions as you want, only limited by the amount of interrupt endpoints your gadget driver supports.

Configuration with configfs¶

Instead of adding fake platform devices and drivers in order to pass some data to the kernel, if HID is a part of a gadget composed with configfs the hidg_func_descriptor.report_desc is passed to the kernel by writing the appropriate stream of bytes to a configfs attribute.

Send and receive HID reports¶

HID reports can be sent/received using read/write on the /dev/hidgX character devices. See below for an example program to do this.

hid_gadget_test is a small interactive program to test the HID gadget driver. To use, point it at a hidg device and set the device type (keyboard / mouse / joystick) — E.G.:

# hid_gadget_test /dev/hidg0 keyboard

You are now in the prompt of hid_gadget_test. You can type any combination of options and values. Available options and values are listed at program start. In keyboard mode you can send up to six values.

For example type: g i s t r —left-shift

Hit return and the corresponding report will be sent by the HID gadget.

Another interesting example is the caps lock test. Type —caps-lock and hit return. A report is then sent by the gadget and you should receive the host answer, corresponding to the caps lock LED status:

# hid_gadget_test /dev/hidg1 mouse

You can test the mouse emulation. Values are two signed numbers.

/* hid_gadget_test */ #include #include #include #include #include #include #include #include #include #define BUF_LEN 512 struct options < const char *opt; unsigned char val; >; static struct options kmod[] = < , , , , , , , , >; static struct options kval[] = < , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , >; int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold) < char *tok = strtok(buf, " "); int key = 0; int i = 0; for (; tok != NULL; tok = strtok(NULL, " ")) < if (strcmp(tok, "--quit") == 0) return -1; if (strcmp(tok, "--hold") == 0) < *hold = 1; continue; >if (key < 6) < for (i = 0; kval[i].opt != NULL; i++) if (strcmp(tok, kval[i].opt) == 0) < report[2 + key++] = kval[i].val; break; >if (kval[i].opt != NULL) continue; > if (key < 6) if (islower(tok[0])) < report[2 + key++] = (tok[0] - ('a' - 0x04)); continue; >for (i = 0; kmod[i].opt != NULL; i++) if (strcmp(tok, kmod[i].opt) == 0) < report[0] = report[0] | kmod[i].val; break; >if (kmod[i].opt != NULL) continue; if (key < 6) fprintf(stderr, "unknown option: %s\n", tok); >return 8; > static struct options mmod[] = < , , , >; int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold) < char *tok = strtok(buf, " "); int mvt = 0; int i = 0; for (; tok != NULL; tok = strtok(NULL, " ")) < if (strcmp(tok, "--quit") == 0) return -1; if (strcmp(tok, "--hold") == 0) < *hold = 1; continue; >for (i = 0; mmod[i].opt != NULL; i++) if (strcmp(tok, mmod[i].opt) == 0) < report[0] = report[0] | mmod[i].val; break; >if (mmod[i].opt != NULL) continue; if (!(tok[0] == '-' && tok[1] == '-') && mvt < 2) < errno = 0; report[1 + mvt++] = (char)strtol(tok, NULL, 0); if (errno != 0) < fprintf(stderr, "Bad value:'%s'\n", tok); report[1 + mvt--] = 0; >continue; > fprintf(stderr, "unknown option: %s\n", tok); > return 3; > static struct options jmod[] = < , , , , , , , , , >; int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold) < char *tok = strtok(buf, " "); int mvt = 0; int i = 0; *hold = 1; /* set default hat position: neutral */ report[3] = 0x04; for (; tok != NULL; tok = strtok(NULL, " ")) < if (strcmp(tok, "--quit") == 0) return -1; for (i = 0; jmod[i].opt != NULL; i++) if (strcmp(tok, jmod[i].opt) == 0) < report[3] = (report[3] & 0xF0) | jmod[i].val; break; >if (jmod[i].opt != NULL) continue; if (!(tok[0] == '-' && tok[1] == '-') && mvt < 3) < errno = 0; report[mvt++] = (char)strtol(tok, NULL, 0); if (errno != 0) < fprintf(stderr, "Bad value:'%s'\n", tok); report[mvt--] = 0; >continue; > fprintf(stderr, "unknown option: %s\n", tok); > return 4; > void print_options(char c) < int i = 0; if (c == 'k') < printf(" keyboard options:\n" " --hold\n"); for (i = 0; kmod[i].opt != NULL; i++) printf("\t\t%s\n", kmod[i].opt); printf("\n keyboard values:\n" " [a-z] or\n"); for (i = 0; kval[i].opt != NULL; i++) printf("\t\t%-8s%s", kval[i].opt, i % 2 ? "\n" : ""); printf("\n"); >else if (c == 'm') < printf(" mouse options:\n" " --hold\n"); for (i = 0; mmod[i].opt != NULL; i++) printf("\t\t%s\n", mmod[i].opt); printf("\n mouse values:\n" " Two signed numbers\n" "--quit to close\n"); >else < printf(" joystick options:\n"); for (i = 0; jmod[i].opt != NULL; i++) printf("\t\t%s\n", jmod[i].opt); printf("\n joystick values:\n" " three signed numbers\n" "--quit to close\n"); >> int main(int argc, const char *argv[]) < const char *filename = NULL; int fd = 0; char buf[BUF_LEN]; int cmd_len; char report[8]; int to_send = 8; int hold = 0; fd_set rfds; int retval, i; if (argc < 3) < fprintf(stderr, "Usage: %s devname mouse|keyboard|joystick\n", argv[0]); return 1; >if (argv[2][0] != 'k' && argv[2][0] != 'm' && argv[2][0] != 'j') return 2; filename = argv[1]; if ((fd = open(filename, O_RDWR, 0666)) == -1) < perror(filename); return 3; >print_options(argv[2][0]); while (42) < FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); FD_SET(fd, &rfds); retval = select(fd + 1, &rfds, NULL, NULL, NULL); if (retval == -1 && errno == EINTR) continue; if (retval < 0) < perror("select()"); return 4; >if (FD_ISSET(fd, &rfds)) < cmd_len = read(fd, buf, BUF_LEN - 1); printf("recv report:"); for (i = 0; i < cmd_len; i++) printf(" %02x", buf[i]); printf("\n"); >if (FD_ISSET(STDIN_FILENO, &rfds)) < memset(report, 0x0, sizeof(report)); cmd_len = read(STDIN_FILENO, buf, BUF_LEN - 1); if (cmd_len == 0) break; buf[cmd_len - 1] = '\0'; hold = 0; memset(report, 0x0, sizeof(report)); if (argv[2][0] == 'k') to_send = keyboard_fill_report(report, buf, &hold); else if (argv[2][0] == 'm') to_send = mouse_fill_report(report, buf, &hold); else to_send = joystick_fill_report(report, buf, &hold); if (to_send == -1) break; if (write(fd, report, to_send) != to_send) < perror(filename); return 5; >if (!hold) < memset(report, 0x0, sizeof(report)); if (write(fd, report, to_send) != to_send) < perror(filename); return 6; >> > > close(fd); return 0; >

Источник

Читайте также:  Настройка ip адресации linux

Linux USB gadget configured through configfs¶

A USB Linux Gadget is a device which has a UDC (USB Device Controller) and can be connected to a USB Host to extend it with additional functions like a serial port or a mass storage capability.

A gadget is seen by its host as a set of configurations, each of which contains a number of interfaces which, from the gadget’s perspective, are known as functions, each function representing e.g. a serial connection or a SCSI disk.

Linux provides a number of functions for gadgets to use.

Creating a gadget means deciding what configurations there will be and which functions each configuration will provide.

Configfs (please see Configfs — Userspace-driven Kernel Object Configuration ) lends itself nicely for the purpose of telling the kernel about the above mentioned decision. This document is about how to do it.

It also describes how configfs integration into gadget is designed.

Requirements¶

In order for this to work configfs must be available, so CONFIGFS_FS must be ‘y’ or ‘m’ in .config. As of this writing USB_LIBCOMPOSITE selects CONFIGFS_FS.

Usage¶

(The original post describing the first function made available through configfs can be seen here: http://www.spinics.net/lists/linux-usb/msg76388.html)

$ modprobe libcomposite $ mount none $CONFIGFS_HOME -t configfs

where CONFIGFS_HOME is the mount point for configfs

1. Creating the gadgets¶

For each gadget to be created its corresponding directory must be created:

$ mkdir $CONFIGFS_HOME/usb_gadget/

$ mkdir $CONFIGFS_HOME/usb_gadget/g1 . . . $ cd $CONFIGFS_HOME/usb_gadget/g1

Each gadget needs to have its vendor id and product id specified:

$ echo > idVendor $ echo > idProduct

A gadget also needs its serial number, manufacturer and product strings. In order to have a place to store them, a strings subdirectory must be created for each language, e.g.:

Читайте также:  Alt linux точка монтирования

Then the strings can be specified:

$ echo > strings/0x409/serialnumber $ echo > strings/0x409/manufacturer $ echo > strings/0x409/product

Further custom string descriptors can be created as directories within the language’s directory, with the string text being written to the «s» attribute within the string’s directory:

$ mkdir strings/0x409/xu.0 $ echo > strings/0x409/xu.0/s

Where function drivers support it, functions may allow symlinks to these custom string descriptors to associate those strings with class descriptors.

2. Creating the configurations¶

Each gadget will consist of a number of configurations, their corresponding directories must be created:

where can be any string which is legal in a filesystem and the is the configuration’s number, e.g.:

Each configuration also needs its strings, so a subdirectory must be created for each language, e.g.:

$ mkdir configs/c.1/strings/0x409

Then the configuration string can be specified:

$ echo > configs/c.1/strings/0x409/configuration

Some attributes can also be set for a configuration, e.g.:

$ echo 120 > configs/c.1/MaxPower

3. Creating the functions¶

The gadget will provide some functions, for each function its corresponding directory must be created:

where corresponds to one of allowed function names and instance name is an arbitrary string allowed in a filesystem, e.g.:

$ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module() . . .

Each function provides its specific set of attributes, with either read-only or read-write access. Where applicable they need to be written to as appropriate. Please refer to Documentation/ABI/testing/configfs-usb-gadget for more information.

4. Associating the functions with their configurations¶

At this moment a number of gadgets is created, each of which has a number of configurations specified and a number of functions available. What remains is specifying which function is available in which configuration (the same function can be used in multiple configurations). This is achieved with creating symbolic links:

$ ln -s functions/ncm.usb0 configs/c.1 . . .

5. Enabling the gadget¶

All the above steps serve the purpose of composing the gadget of configurations and functions.

An example directory structure might look like this:

. ./strings ./strings/0x409 ./strings/0x409/serialnumber ./strings/0x409/product ./strings/0x409/manufacturer ./configs ./configs/c.1 ./configs/c.1/ncm.usb0 -> ../../../../usb_gadget/g1/functions/ncm.usb0 ./configs/c.1/strings ./configs/c.1/strings/0x409 ./configs/c.1/strings/0x409/configuration ./configs/c.1/bmAttributes ./configs/c.1/MaxPower ./functions ./functions/ncm.usb0 ./functions/ncm.usb0/ifname ./functions/ncm.usb0/qmult ./functions/ncm.usb0/host_addr ./functions/ncm.usb0/dev_addr ./UDC ./bcdUSB ./bcdDevice ./idProduct ./idVendor ./bMaxPacketSize0 ./bDeviceProtocol ./bDeviceSubClass ./bDeviceClass

Such a gadget must be finally enabled so that the USB host can enumerate it.

Читайте также:  Системное администрирование linux ubuntu

In order to enable the gadget it must be bound to a UDC (USB Device Controller):

where is one of those found in /sys/class/udc/* e.g.:

6. Disabling the gadget¶

7. Cleaning up¶

Remove functions from configurations:

where . specify the configuration and is a symlink to a function being removed from the configuration, e.g.:

Remove strings directories in configurations:

$ rmdir configs/c.1/strings/0x409 . . .

and remove the configurations:

Remove functions (function modules are not unloaded, though):

Remove strings directories in the gadget:

and finally remove the gadget:

Implementation design¶

Below the idea of how configfs works is presented. In configfs there are items and groups, both represented as directories. The difference between an item and a group is that a group can contain other groups. In the picture below only an item is shown. Both items and groups can have attributes, which are represented as files. The user can create and remove directories, but cannot remove files, which can be read-only or read-write, depending on what they represent.

The filesystem part of configfs operates on config_items/groups and configfs_attributes which are generic and of the same type for all configured elements. However, they are embedded in usage-specific larger structures. In the picture below there is a «cs» which contains a config_item and an «sa» which contains a configfs_attribute.

The filesystem view would be like this:

Whenever a user reads/writes the «sa» file, a function is called which accepts a struct config_item and a struct configfs_attribute. In the said function the «cs» and «sa» are retrieved using the well known container_of technique and an appropriate sa’s function (show or store) is called and passed the «cs» and a character buffer. The «show» is for displaying the file’s contents (copy data from the cs to the buffer), while the «store» is for modifying the file’s contents (copy data from the buffer to the cs), but it is up to the implementer of the two functions to decide what they actually do.

typedef struct configured_structure cs; typedef struct specific_attribute sa; sa +----------------------------------+ cs | (*show)(cs *, buffer); | +-----------------+ | (*store)(cs *, buffer, length); | | | | | | +-------------+ | | +------------------+ | | | struct |-|----|------>|struct | | | | config_item | | | |configfs_attribute| | | +-------------+ | | +------------------+ | | | +----------------------------------+ | data to be set | . | | . +-----------------+ .

The file names are decided by the config item/group designer, while the directories in general can be named at will. A group can have a number of its default sub-groups created automatically.

The concepts described above translate to USB gadgets like this:

1. A gadget has its config group, which has some attributes (idVendor, idProduct etc) and default sub-groups (configs, functions, strings). Writing to the attributes causes the information to be stored in appropriate locations. In the configs, functions and strings sub-groups a user can create their sub-groups to represent configurations, functions, and groups of strings in a given language.

2. The user creates configurations and functions, in the configurations creates symbolic links to functions. This information is used when the gadget’s UDC attribute is written to, which means binding the gadget to the UDC. The code in drivers/usb/gadget/configfs.c iterates over all configurations, and in each configuration it iterates over all functions and binds them. This way the whole gadget is bound.

    The file drivers/usb/gadget/configfs.c contains code for

  • gadget’s config_group
  • gadget’s default groups (configs, functions, strings)
  • associating functions with configurations (symlinks)

4. Each USB function naturally has its own view of what it wants configured, so config_groups for particular functions are defined in the functions implementation files drivers/usb/gadget/f_*.c.

usb_get_function_instance(), which, in turn, calls request_module. So, provided that modprobe works, modules for particular functions are loaded automatically. Please note that the converse is not true: after a gadget is disabled and torn down, the modules remain loaded.

Источник

Оцените статью
Adblock
detector