^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) ===========================
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) Linux USB HID gadget driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) ===========================
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) Introduction
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) ============
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) The HID Gadget driver provides emulation of USB Human Interface
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) Devices (HID). The basic HID handling is done in the kernel,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) and HID reports can be sent/received through I/O on the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) /dev/hidgX character devices.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) For more details about HID, see the developer page on
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) https://www.usb.org/developers/hidpage/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) Configuration
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) =============
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) g_hid is a platform driver, so to use it you need to add
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) struct platform_device(s) to your platform code defining the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) HID function descriptors you want to use - E.G. something
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) like::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/usb/g_hid.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) /* hid descriptor for a keyboard */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) static struct hidg_func_descriptor my_hid_data = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) .subclass = 0, /* No subclass */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) .protocol = 1, /* Keyboard */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) .report_length = 8,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) .report_desc_length = 63,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) .report_desc = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) 0x09, 0x06, /* USAGE (Keyboard) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) 0xa1, 0x01, /* COLLECTION (Application) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) 0x05, 0x07, /* USAGE_PAGE (Keyboard) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) 0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) 0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) 0x75, 0x01, /* REPORT_SIZE (1) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) 0x95, 0x08, /* REPORT_COUNT (8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) 0x81, 0x02, /* INPUT (Data,Var,Abs) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) 0x95, 0x01, /* REPORT_COUNT (1) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) 0x75, 0x08, /* REPORT_SIZE (8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) 0x95, 0x05, /* REPORT_COUNT (5) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) 0x75, 0x01, /* REPORT_SIZE (1) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) 0x05, 0x08, /* USAGE_PAGE (LEDs) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) 0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) 0x29, 0x05, /* USAGE_MAXIMUM (Kana) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) 0x95, 0x01, /* REPORT_COUNT (1) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) 0x75, 0x03, /* REPORT_SIZE (3) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) 0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) 0x95, 0x06, /* REPORT_COUNT (6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) 0x75, 0x08, /* REPORT_SIZE (8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) 0x25, 0x65, /* LOGICAL_MAXIMUM (101) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) 0x05, 0x07, /* USAGE_PAGE (Keyboard) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) 0x19, 0x00, /* USAGE_MINIMUM (Reserved) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) 0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) 0x81, 0x00, /* INPUT (Data,Ary,Abs) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) 0xc0 /* END_COLLECTION */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) static struct platform_device my_hid = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) .name = "hidg",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) .id = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) .num_resources = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) .resource = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) .dev.platform_data = &my_hid_data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) You can add as many HID functions as you want, only limited by
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) the amount of interrupt endpoints your gadget driver supports.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) Configuration with configfs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) ===========================
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) Instead of adding fake platform devices and drivers in order to pass
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) some data to the kernel, if HID is a part of a gadget composed with
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) configfs the hidg_func_descriptor.report_desc is passed to the kernel
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) by writing the appropriate stream of bytes to a configfs attribute.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) Send and receive HID reports
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) ============================
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) HID reports can be sent/received using read/write on the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) /dev/hidgX character devices. See below for an example program
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) to do this.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) hid_gadget_test is a small interactive program to test the HID
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) gadget driver. To use, point it at a hidg device and set the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) device type (keyboard / mouse / joystick) - E.G.::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) # hid_gadget_test /dev/hidg0 keyboard
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) You are now in the prompt of hid_gadget_test. You can type any
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) combination of options and values. Available options and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) values are listed at program start. In keyboard mode you can
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) send up to six values.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) For example type: g i s t r --left-shift
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) Hit return and the corresponding report will be sent by the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) HID gadget.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) Another interesting example is the caps lock test. Type
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) --caps-lock and hit return. A report is then sent by the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) gadget and you should receive the host answer, corresponding
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) to the caps lock LED status::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) --caps-lock
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) recv report:2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) With this command::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) # hid_gadget_test /dev/hidg1 mouse
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) You can test the mouse emulation. Values are two signed numbers.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) Sample code::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) /* hid_gadget_test */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) #include <pthread.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) #include <string.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) #include <stdio.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) #include <ctype.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) #include <fcntl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) #include <errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) #include <stdio.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) #include <stdlib.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) #include <unistd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) #define BUF_LEN 512
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) struct options {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) const char *opt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) unsigned char val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) static struct options kmod[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) {.opt = "--left-ctrl", .val = 0x01},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) {.opt = "--right-ctrl", .val = 0x10},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) {.opt = "--left-shift", .val = 0x02},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) {.opt = "--right-shift", .val = 0x20},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) {.opt = "--left-alt", .val = 0x04},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) {.opt = "--right-alt", .val = 0x40},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) {.opt = "--left-meta", .val = 0x08},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) {.opt = "--right-meta", .val = 0x80},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) {.opt = NULL}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) static struct options kval[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) {.opt = "--return", .val = 0x28},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) {.opt = "--esc", .val = 0x29},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) {.opt = "--bckspc", .val = 0x2a},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) {.opt = "--tab", .val = 0x2b},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) {.opt = "--spacebar", .val = 0x2c},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) {.opt = "--caps-lock", .val = 0x39},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) {.opt = "--f1", .val = 0x3a},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) {.opt = "--f2", .val = 0x3b},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) {.opt = "--f3", .val = 0x3c},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) {.opt = "--f4", .val = 0x3d},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) {.opt = "--f5", .val = 0x3e},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) {.opt = "--f6", .val = 0x3f},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) {.opt = "--f7", .val = 0x40},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) {.opt = "--f8", .val = 0x41},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) {.opt = "--f9", .val = 0x42},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) {.opt = "--f10", .val = 0x43},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) {.opt = "--f11", .val = 0x44},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) {.opt = "--f12", .val = 0x45},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) {.opt = "--insert", .val = 0x49},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) {.opt = "--home", .val = 0x4a},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) {.opt = "--pageup", .val = 0x4b},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) {.opt = "--del", .val = 0x4c},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) {.opt = "--end", .val = 0x4d},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) {.opt = "--pagedown", .val = 0x4e},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) {.opt = "--right", .val = 0x4f},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) {.opt = "--left", .val = 0x50},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) {.opt = "--down", .val = 0x51},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) {.opt = "--kp-enter", .val = 0x58},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) {.opt = "--up", .val = 0x52},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) {.opt = "--num-lock", .val = 0x53},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) {.opt = NULL}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) char *tok = strtok(buf, " ");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) int key = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) int i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) for (; tok != NULL; tok = strtok(NULL, " ")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) if (strcmp(tok, "--quit") == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) if (strcmp(tok, "--hold") == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) *hold = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) if (key < 6) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) for (i = 0; kval[i].opt != NULL; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) if (strcmp(tok, kval[i].opt) == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) report[2 + key++] = kval[i].val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) if (kval[i].opt != NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) if (key < 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) if (islower(tok[0])) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) report[2 + key++] = (tok[0] - ('a' - 0x04));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) for (i = 0; kmod[i].opt != NULL; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) if (strcmp(tok, kmod[i].opt) == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) report[0] = report[0] | kmod[i].val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) if (kmod[i].opt != NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) if (key < 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) fprintf(stderr, "unknown option: %s\n", tok);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) return 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) static struct options mmod[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) {.opt = "--b1", .val = 0x01},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) {.opt = "--b2", .val = 0x02},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) {.opt = "--b3", .val = 0x04},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) {.opt = NULL}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) char *tok = strtok(buf, " ");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) int mvt = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) int i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) for (; tok != NULL; tok = strtok(NULL, " ")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) if (strcmp(tok, "--quit") == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) if (strcmp(tok, "--hold") == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) *hold = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) for (i = 0; mmod[i].opt != NULL; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) if (strcmp(tok, mmod[i].opt) == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) report[0] = report[0] | mmod[i].val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) if (mmod[i].opt != NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) if (!(tok[0] == '-' && tok[1] == '-') && mvt < 2) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) errno = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) report[1 + mvt++] = (char)strtol(tok, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) if (errno != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) fprintf(stderr, "Bad value:'%s'\n", tok);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) report[1 + mvt--] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) fprintf(stderr, "unknown option: %s\n", tok);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) return 3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) static struct options jmod[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) {.opt = "--b1", .val = 0x10},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) {.opt = "--b2", .val = 0x20},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) {.opt = "--b3", .val = 0x40},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) {.opt = "--b4", .val = 0x80},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) {.opt = "--hat1", .val = 0x00},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) {.opt = "--hat2", .val = 0x01},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) {.opt = "--hat3", .val = 0x02},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) {.opt = "--hat4", .val = 0x03},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) {.opt = "--hatneutral", .val = 0x04},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) {.opt = NULL}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) char *tok = strtok(buf, " ");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) int mvt = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) int i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) *hold = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) /* set default hat position: neutral */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) report[3] = 0x04;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) for (; tok != NULL; tok = strtok(NULL, " ")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) if (strcmp(tok, "--quit") == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) for (i = 0; jmod[i].opt != NULL; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) if (strcmp(tok, jmod[i].opt) == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) report[3] = (report[3] & 0xF0) | jmod[i].val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) if (jmod[i].opt != NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) if (!(tok[0] == '-' && tok[1] == '-') && mvt < 3) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) errno = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) report[mvt++] = (char)strtol(tok, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) if (errno != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) fprintf(stderr, "Bad value:'%s'\n", tok);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) report[mvt--] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) fprintf(stderr, "unknown option: %s\n", tok);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) return 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) void print_options(char c)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) int i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) if (c == 'k') {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) printf(" keyboard options:\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) " --hold\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) for (i = 0; kmod[i].opt != NULL; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) printf("\t\t%s\n", kmod[i].opt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) printf("\n keyboard values:\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) " [a-z] or\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) for (i = 0; kval[i].opt != NULL; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) printf("\t\t%-8s%s", kval[i].opt, i % 2 ? "\n" : "");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) printf("\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) } else if (c == 'm') {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) printf(" mouse options:\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) " --hold\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) for (i = 0; mmod[i].opt != NULL; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) printf("\t\t%s\n", mmod[i].opt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) printf("\n mouse values:\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) " Two signed numbers\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) "--quit to close\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) printf(" joystick options:\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) for (i = 0; jmod[i].opt != NULL; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) printf("\t\t%s\n", jmod[i].opt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) printf("\n joystick values:\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) " three signed numbers\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) "--quit to close\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) int main(int argc, const char *argv[])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) const char *filename = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) int fd = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) char buf[BUF_LEN];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) int cmd_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) char report[8];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) int to_send = 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) int hold = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) fd_set rfds;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) int retval, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) if (argc < 3) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) fprintf(stderr, "Usage: %s devname mouse|keyboard|joystick\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) argv[0]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) if (argv[2][0] != 'k' && argv[2][0] != 'm' && argv[2][0] != 'j')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) return 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) filename = argv[1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) if ((fd = open(filename, O_RDWR, 0666)) == -1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) perror(filename);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) return 3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) print_options(argv[2][0]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) while (42) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) FD_ZERO(&rfds);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) FD_SET(STDIN_FILENO, &rfds);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) FD_SET(fd, &rfds);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) retval = select(fd + 1, &rfds, NULL, NULL, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) if (retval == -1 && errno == EINTR)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) if (retval < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) perror("select()");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) return 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) if (FD_ISSET(fd, &rfds)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) cmd_len = read(fd, buf, BUF_LEN - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) printf("recv report:");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) for (i = 0; i < cmd_len; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) printf(" %02x", buf[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) printf("\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) if (FD_ISSET(STDIN_FILENO, &rfds)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) memset(report, 0x0, sizeof(report));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) cmd_len = read(STDIN_FILENO, buf, BUF_LEN - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) if (cmd_len == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) buf[cmd_len - 1] = '\0';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) hold = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) memset(report, 0x0, sizeof(report));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) if (argv[2][0] == 'k')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) to_send = keyboard_fill_report(report, buf, &hold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) else if (argv[2][0] == 'm')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) to_send = mouse_fill_report(report, buf, &hold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) to_send = joystick_fill_report(report, buf, &hold);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) if (to_send == -1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) if (write(fd, report, to_send) != to_send) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) perror(filename);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) return 5;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) if (!hold) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) memset(report, 0x0, sizeof(report));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) if (write(fd, report, to_send) != to_send) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) perror(filename);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) return 6;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) close(fd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) }