Add usb-gadget support (PR #76).
parent
57e194d0c8
commit
2967fb54f8
6 changed files with 393 additions and 1 deletions
@ -0,0 +1,156 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <stdint.h> |
||||
#include <string.h> |
||||
#include <fcntl.h> |
||||
#include <unistd.h> |
||||
#include "../keys.h" |
||||
#include "usb-gadget.h" |
||||
|
||||
static uint8_t mods = 0; |
||||
|
||||
static uint8_t keys[6] = {0}; |
||||
|
||||
struct vkbd { |
||||
int fd; |
||||
}; |
||||
|
||||
struct hid_report { |
||||
uint8_t hid_mods; |
||||
uint8_t reserved; |
||||
uint8_t hid_code[6]; |
||||
}; |
||||
|
||||
static int create_virtual_keyboard(void) |
||||
{ |
||||
|
||||
int fd = open("/dev/hidg0", O_WRONLY | O_NONBLOCK); |
||||
if (fd < 0) { |
||||
perror("open"); |
||||
exit(-1); |
||||
} |
||||
|
||||
return fd; |
||||
} |
||||
|
||||
struct vkbd *vkbd_init(const char *name) |
||||
{ |
||||
struct vkbd *vkbd = calloc(1, sizeof vkbd); |
||||
vkbd->fd = create_virtual_keyboard(); |
||||
|
||||
return vkbd; |
||||
} |
||||
|
||||
uint16_t hid_code(uint16_t code) |
||||
{ |
||||
if(hid_table[code]) |
||||
return hid_table[code]; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void send_hid_report (const struct vkbd *vkbd) |
||||
{ |
||||
|
||||
struct hid_report report; |
||||
|
||||
for (int i = 0; i < 6; i++) |
||||
report.hid_code[i] = keys[i]; |
||||
|
||||
report.hid_mods = mods; |
||||
|
||||
write(vkbd->fd,&report,sizeof(report)); |
||||
|
||||
} |
||||
|
||||
static uint8_t get_modifier(int code) |
||||
{ |
||||
switch (code) { |
||||
case KEY_LEFTSHIFT: |
||||
return HID_SHIFT; |
||||
break; |
||||
case KEY_RIGHTSHIFT: |
||||
return HID_RIGHTSHIFT; |
||||
break; |
||||
case KEY_LEFTCTRL: |
||||
return HID_CTRL; |
||||
break; |
||||
case KEY_RIGHTCTRL: |
||||
return HID_RIGHTCTRL; |
||||
break; |
||||
case KEY_LEFTALT: |
||||
return HID_ALT; |
||||
break; |
||||
case KEY_RIGHTALT: |
||||
return HID_ALT_GR; |
||||
break; |
||||
case KEY_LEFTMETA: |
||||
return HID_SUPER; |
||||
break; |
||||
case KEY_RIGHTMETA: |
||||
return HID_RIGHTSUPER; |
||||
break; |
||||
default: |
||||
return 0; |
||||
break; |
||||
} |
||||
|
||||
} |
||||
|
||||
static int update_modifier_state(int code, int state) |
||||
{ |
||||
uint16_t mod = get_modifier(code); |
||||
|
||||
if(mod) { |
||||
if (state) |
||||
mods |= mod; |
||||
else |
||||
mods &= ~mod; |
||||
return 0; |
||||
} |
||||
|
||||
return -1; |
||||
|
||||
} |
||||
|
||||
void update_key_state(int code, int state) |
||||
{ |
||||
int i; |
||||
int set = 0; |
||||
|
||||
for (i = 0; i < 6; i++) { |
||||
if(keys[i] == code) { |
||||
set = 1; |
||||
if(state == 0) |
||||
keys[i] = 0; |
||||
} |
||||
} |
||||
if(state && !set) { |
||||
for (i = 0; i < 6; i++) { |
||||
if(keys[i] == 0) { |
||||
keys[i] = code; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
void vkbd_send(const struct vkbd *vkbd, int code, int state) |
||||
{ |
||||
if(update_modifier_state(code, state) < 0) |
||||
update_key_state(hid_code(code), state); |
||||
|
||||
send_hid_report(vkbd); |
||||
} |
||||
|
||||
void vkbd_move_mouse(const struct vkbd *vkbd, int x, int y) |
||||
{ |
||||
// Not implemented
|
||||
} |
||||
|
||||
void free_vkbd(struct vkbd *vkbd) |
||||
{ |
||||
close(vkbd->fd); |
||||
free(vkbd); |
||||
} |
||||
@ -0,0 +1,168 @@ |
||||
#ifndef _USBGADGET_H_ |
||||
#define _USBGADGET_H_ |
||||
#define _USBGADGET_H_ |
||||
|
||||
#ifdef __FreeBSD__ |
||||
#include <dev/evdev/input-event-codes.h> |
||||
#else |
||||
#include <linux/input-event-codes.h> |
||||
#endif |
||||
|
||||
#define HID_CTRL 0x1 |
||||
#define HID_RIGHTCTRL 0x10 |
||||
#define HID_SHIFT 0x2 |
||||
#define HID_RIGHTSHIFT 0x20 |
||||
#define HID_ALT 0x4 |
||||
#define HID_ALT_GR 0x40 |
||||
#define HID_RIGHTSUPER 0x80 |
||||
#define HID_SUPER 0x8 |
||||
|
||||
static const uint16_t hid_table[] = { |
||||
[KEY_ESC] = 0x29, |
||||
[KEY_1] = 0x1e, |
||||
[KEY_2] = 0x1f, |
||||
[KEY_3] = 0x20, |
||||
[KEY_4] = 0x21, |
||||
[KEY_5] = 0x22, |
||||
[KEY_6] = 0x23, |
||||
[KEY_7] = 0x24, |
||||
[KEY_8] = 0x25, |
||||
[KEY_9] = 0x26, |
||||
[KEY_0] = 0x27, |
||||
[KEY_MINUS] = 0x2d, |
||||
[KEY_EQUAL] = 0x2e, |
||||
[KEY_BACKSPACE] = 0x2a, |
||||
[KEY_TAB] = 0x2b, |
||||
[KEY_Q] = 0x14, |
||||
[KEY_W] = 0x1a, |
||||
[KEY_E] = 0x08, |
||||
[KEY_R] = 0x15, |
||||
[KEY_T] = 0x17, |
||||
[KEY_Y] = 0x1c, |
||||
[KEY_U] = 0x18, |
||||
[KEY_I] = 0x0c, |
||||
[KEY_O] = 0x12, |
||||
[KEY_P] = 0x13, |
||||
[KEY_LEFTBRACE] = 0x2f, |
||||
[KEY_RIGHTBRACE] = 0x30, |
||||
[KEY_ENTER] = 0x28, |
||||
[KEY_LEFTCTRL] = 0xe0, |
||||
[KEY_A] = 0x04, |
||||
[KEY_S] = 0x16, |
||||
[KEY_D] = 0x07, |
||||
[KEY_F] = 0x09, |
||||
[KEY_G] = 0x0a, |
||||
[KEY_H] = 0x0b, |
||||
[KEY_J] = 0x0d, |
||||
[KEY_K] = 0x0e, |
||||
[KEY_L] = 0x0f, |
||||
[KEY_SEMICOLON] = 0x33, |
||||
[KEY_APOSTROPHE] = 0x34, |
||||
[KEY_GRAVE] = 0x35, |
||||
[KEY_LEFTSHIFT] = 0xe1, |
||||
[KEY_BACKSLASH] = 0x31, |
||||
[KEY_Z] = 0x1d, |
||||
[KEY_X] = 0x1b, |
||||
[KEY_C] = 0x06, |
||||
[KEY_V] = 0x19, |
||||
[KEY_B] = 0x05, |
||||
[KEY_N] = 0x11, |
||||
[KEY_M] = 0x10, |
||||
[KEY_COMMA] = 0x36, |
||||
[KEY_DOT] = 0x37, |
||||
[KEY_SLASH] = 0x38, |
||||
[KEY_RIGHTSHIFT] = 0xe5, |
||||
[KEY_KPASTERISK] = 0x55, |
||||
[KEY_LEFTALT] = 0xe2, |
||||
[KEY_SPACE] = 0x2c, |
||||
[KEY_CAPSLOCK] = 0x39, |
||||
[KEY_F1] = 0x3a, |
||||
[KEY_F2] = 0x3b, |
||||
[KEY_F3] = 0x3c, |
||||
[KEY_F4] = 0x3d, |
||||
[KEY_F5] = 0x3e, |
||||
[KEY_F6] = 0x3f, |
||||
[KEY_F7] = 0x40, |
||||
[KEY_F8] = 0x41, |
||||
[KEY_F9] = 0x42, |
||||
[KEY_F10] = 0x43, |
||||
[KEY_NUMLOCK] = 0x53, |
||||
[KEY_SCROLLLOCK] = 0x47, |
||||
[KEY_KP7] = 0x5f, |
||||
[KEY_KP8] = 0x60, |
||||
[KEY_KP9] = 0x61, |
||||
[KEY_KPMINUS] = 0x56, |
||||
[KEY_KP4] = 0x5c, |
||||
[KEY_KP5] = 0x5d, |
||||
[KEY_KP6] = 0x5e, |
||||
[KEY_KPPLUS] = 0x57, |
||||
[KEY_KP1] = 0x59, |
||||
[KEY_KP2] = 0x5a, |
||||
[KEY_KP3] = 0x5b, |
||||
[KEY_KP0] = 0x62, |
||||
[KEY_KPDOT] = 0x63, |
||||
[KEY_ZENKAKUHANKAKU] = 0x94, |
||||
[KEY_102ND] = 0x64, |
||||
[KEY_F11] = 0x44, |
||||
[KEY_F12] = 0x45, |
||||
[KEY_RO] = 0x87, |
||||
[KEY_KATAKANA] = 0x92, |
||||
[KEY_HIRAGANA] = 0x93, |
||||
[KEY_HENKAN] = 0x8a, |
||||
[KEY_KATAKANAHIRAGANA] = 0x88, |
||||
[KEY_MUHENKAN] = 0x8b, |
||||
[KEY_KPENTER] = 0x58, |
||||
[KEY_RIGHTCTRL] = 0xe4, |
||||
[KEY_KPSLASH] = 0x54, |
||||
[KEY_SYSRQ] = 0x46, |
||||
[KEY_RIGHTALT] = 0xe6, |
||||
[KEY_HOME] = 0x4a, |
||||
[KEY_UP] = 0x52, |
||||
[KEY_PAGEUP] = 0x4b, |
||||
[KEY_LEFT] = 0x50, |
||||
[KEY_RIGHT] = 0x4f, |
||||
[KEY_END] = 0x4d, |
||||
[KEY_DOWN] = 0x51, |
||||
[KEY_PAGEDOWN] = 0x4e, |
||||
[KEY_INSERT] = 0x49, |
||||
[KEY_DELETE] = 0x4c, |
||||
[KEY_MUTE] = 0x7f, |
||||
[KEY_VOLUMEDOWN] = 0x81, |
||||
[KEY_VOLUMEUP] = 0x80, |
||||
[KEY_POWER] = 0x66, |
||||
[KEY_KPEQUAL] = 0x67, |
||||
[KEY_KPPLUSMINUS] = 0xd7, |
||||
[KEY_PAUSE] = 0x48, |
||||
[KEY_KPCOMMA] = 0x85, |
||||
[KEY_HANGEUL] = 0x90, |
||||
[KEY_HANJA] = 0x91, |
||||
[KEY_YEN] = 0x89, |
||||
[KEY_LEFTMETA] = 0xe3, |
||||
[KEY_RIGHTMETA] = 0xe7, |
||||
[KEY_COMPOSE] = 0x65, |
||||
[KEY_AGAIN] = 0x79, |
||||
[KEY_UNDO] = 0x7a, |
||||
[KEY_FRONT] = 0x77, |
||||
[KEY_COPY] = 0x7c, |
||||
[KEY_OPEN] = 0x74, |
||||
[KEY_PASTE] = 0x7d, |
||||
[KEY_FIND] = 0x7e, |
||||
[KEY_CUT] = 0x7b, |
||||
[KEY_HELP] = 0x75, |
||||
[KEY_KPLEFTPAREN] = 0xb6, |
||||
[KEY_KPRIGHTPAREN] = 0xb7, |
||||
[KEY_F13] = 0x68, |
||||
[KEY_F14] = 0x69, |
||||
[KEY_F15] = 0x6a, |
||||
[KEY_F16] = 0x6b, |
||||
[KEY_F17] = 0x6c, |
||||
[KEY_F18] = 0x6d, |
||||
[KEY_F19] = 0x6e, |
||||
[KEY_F20] = 0x6f, |
||||
[KEY_F21] = 0x70, |
||||
[KEY_F22] = 0x71, |
||||
[KEY_F23] = 0x72, |
||||
[KEY_F24] = 0x73 |
||||
}; |
||||
|
||||
#endif |
||||
@ -0,0 +1,29 @@ |
||||
#!/bin/bash |
||||
modprobe libcomposite |
||||
cd /sys/kernel/config/usb_gadget || exit |
||||
mkdir g1 |
||||
cd g1 |
||||
mkdir configs/c.1 |
||||
mkdir functions/hid.usb0 |
||||
echo 1 > functions/hid.usb0/protocol |
||||
echo 1 > functions/hid.usb0/subclass |
||||
echo 8 > functions/hid.usb0/report_length |
||||
echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.usb0/report_desc |
||||
mkdir strings/0x409 |
||||
mkdir configs/c.1/strings/0x409 |
||||
echo 0x0100 > bcdDevice |
||||
echo 0x0200 > bcdUSB |
||||
echo 0x00 > bDeviceClass |
||||
echo 0x00 > bDeviceProtocol |
||||
echo 0x00 > bDeviceSubClass |
||||
echo 0x08 > bMaxPacketSize0 |
||||
echo 0x0104 > idProduct |
||||
echo 0x1d6b > idVendor |
||||
echo "0123456789" > strings/0x409/serialnumber |
||||
echo "Tux" > strings/0x409/manufacturer |
||||
echo "USB Gadget Keyboard" > strings/0x409/product |
||||
echo "Conf 1" > configs/c.1/strings/0x409/configuration |
||||
echo 0x80 > configs/c.1/bmAttributes |
||||
echo 500 > configs/c.1/MaxPower |
||||
ln -s functions/hid.usb0 configs/c.1 |
||||
ls /sys/class/udc > UDC |
||||
@ -0,0 +1,19 @@ |
||||
# USB HID gadget |
||||
|
||||
Linux devices with host and either USB OTG or device port can be used as USB to USB converter boards, with keyboard connected to USB host port and PC to USB OTG or device port. |
||||
|
||||
In that kind of setup Linux USB HID gadget driver can be used to emulate HID device and `keyd` can be configured to translate evdev input events to HID reports. |
||||
|
||||
|
||||
# Installation |
||||
|
||||
sudo apt-get install libudev-dev # Debian specific, install the corresponding package on your distribution |
||||
|
||||
git clone https://github.com/rvaiya/keyd |
||||
cd keyd |
||||
make vkbd-usb-gadget && sudo make install-usb-gadget |
||||
sudo systemctl enable usb-gadget && sudo systemctl start usb-gadget |
||||
sudo systemctl enable keyd && sudo systemctl start keyd |
||||
|
||||
Device should show up on `lsusb` list as `1d6b:0104 Linux Foundation Multifunction Composite Gadget`. |
||||
One can also see it in `/dev/input/by-id/` under `Tux_USB_Gadget_Keyboard` name. |
||||
@ -0,0 +1,14 @@ |
||||
[Unit] |
||||
Description=usb gadget setup |
||||
Requires=systemd-modules-load.service,keyd.service |
||||
Before=keyd.service |
||||
After=systemd-modules-load.service |
||||
|
||||
|
||||
[Service] |
||||
Type=oneshot |
||||
RemainAfterExit=yes |
||||
ExecStart=/bin/bash /usr/bin/keyd-usb-gadget.sh |
||||
|
||||
[Install] |
||||
WantedBy=keyd.service |
||||
Loading…
Reference in new issue