Add usb-gadget support (PR #76).

master
Giorgi Chavchanidze 4 years ago committed by Raheman Vaiya
parent 57e194d0c8
commit 2967fb54f8
  1. 8
      Makefile
  2. 156
      src/vkbd/usb-gadget.c
  3. 168
      src/vkbd/usb-gadget.h
  4. 29
      src/vkbd/usb-gadget.sh
  5. 19
      usb-gadget.md
  6. 14
      usb-gadget.service

@ -1,4 +1,4 @@
.PHONY: all clean install uninstall debug man
.PHONY: all clean install install-usb-gadget uninstall uninstall-usb-gadget debug man
DESTDIR=
PREFIX=/usr
@ -36,10 +36,16 @@ install:
install -m644 keyd.service $(DESTDIR)$(PREFIX)/lib/systemd/system
install -m755 bin/keyd $(DESTDIR)$(PREFIX)/bin
install -m644 keyd.1.gz $(DESTDIR)$(PREFIX)/share/man/man1
install-usb-gadget: install
install -m644 usb-gadget.service $(DESTDIR)$(PREFIX)/lib/systemd/system/keyd-usb-gadget.service
install -m755 src/vkbd/usb-gadget.sh $(DESTDIR)$(PREFIX)/bin/keyd-usb-gadget.sh
uninstall:
rm -f $(DESTDIR)$(PREFIX)/lib/systemd/system/keyd.service\
bin/keyd $(DESTDIR)$(PREFIX)/bin/keyd\
$(DESTDIR)$(PREFIX)/share/man/man1/keyd.1.gz
uninstall-vkbd-usb-gadget: uninstall
rm -f $(DESTDIR)$(PREFIX)/lib/systemd/system/keyd-usb-gadget.service\
$(DESTDIR)$(PREFIX)/bin/keyd-usb-gadget.sh
test: all
@cd t; \
for f in *.sh; do \

@ -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…
Cancel
Save