commit ad8f8b198a7b9f5e25564b369ac77680d6286bb3 Author: Danny Robson Date: Sat Feb 23 12:54:46 2019 +1100 initial import diff --git a/cleanup.py b/cleanup.py new file mode 100755 index 0000000..bc44cb6 --- /dev/null +++ b/cleanup.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import os +import sys +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('name', type=str) +args = parser.parse_args() + +name = args.name +root = os.path.join('/sys/kernel/config/usb_gadget/', name) + +# Follows the cleanup routine outlined in: +# https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt + +for config in os.scandir(f"{root}/configs"): + # rm configs/./ + for function in os.scandir(config.path): + if function.is_symlink(): + os.remove(function.path) + + # rmdir configs/./strings/ + for lang in os.scandir(f"{config.path}/strings/"): + os.rmdir(lang.path) + + # rmdir configs/. + os.rmdir(config.path) + +# rmdir functions/. +for function in os.scandir(f"{root}/functions/"): + os.rmdir(function.path) + +# rmdir strings/ +for strings in os.scandir(f"{root}/strings/"): + os.rmdir(strings.path) + +# rmdir +os.rmdir(root) diff --git a/generate.py b/generate.py new file mode 100755 index 0000000..7deb107 --- /dev/null +++ b/generate.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 + +import binascii +import os +import sys + +keyboard = { + 'protocol': 1, + 'subclass': 1, + 'report_length': 8, + 'report_desc': bytes([ + 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) + 0x09, 0x06, # Usage (Keyboard) + 0xA1, 0x01, # Collection (Application) + 0x05, 0x07, # Usage Page (Kbrd/Keypad) + 0x19, 0xE0, # Usage Minimum (0xE0) + 0x29, 0xE7, # Usage Maximum (0xE7) + 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,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x01, # Report Count (1) + 0x75, 0x08, # Report Size (8) + 0x81, 0x03, # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 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,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x95, 0x01, # Report Count (1) + 0x75, 0x03, # Report Size (3) + 0x91, 0x03, # Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x95, 0x06, # Report Count (6) + 0x75, 0x08, # Report Size (8) + 0x15, 0x00, # Logical Minimum (0) + 0x25, 0x65, # Logical Maximum (101) + 0x05, 0x07, # Usage Page (Kbrd/Keypad) + 0x19, 0x00, # Usage Minimum (0x00) + 0x29, 0x65, # Usage Maximum (0x65) + 0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, # End Collection + ]), +} + + +mouse = { + 'protocol': 2, + 'subclass': 1, + 'report_length': 4, + 'report_desc': bytes([ + 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) + 0x09, 0x02, # Usage (Mouse) + 0xA1, 0x01, # Collection (Application) + 0x09, 0x01, # Usage (Pointer) + 0xA1, 0x00, # Collection (Physical) + 0x05, 0x09, # Usage Page (Button) + 0x19, 0x01, # Usage Minimum (0x01) + 0x29, 0x05, # Usage Maximum (0x05) + 0x15, 0x00, # Logical Minimum (0) + 0x25, 0x01, # Logical Maximum (1) + 0x95, 0x05, # Report Count (5) + 0x75, 0x01, # Report Size (1) + 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x01, # Report Count (1) + 0x75, 0x03, # Report Size (3) + 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) + 0x09, 0x30, # Usage (X) + 0x09, 0x31, # Usage (Y) + 0x09, 0x38, # Usage (Wheel) + 0x15, 0x81, # Logical Minimum (-127) + 0x25, 0x7F, # Logical Maximum (127) + 0x75, 0x08, # Report Size (8) + 0x95, 0x03, # Report Count (3) + 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, # End Collection + 0xC0, # End Collection + ]) +} + + +try: + UDC = os.listdir('/sys/class/udc') + if len(UDC) != 1: + print("Expected a single entry in '/sys/class/udc'") + sys.exit(1) + UDC = UDC[0] +except FileNotFoundError: + print ("Unable to query UDC") + UDC='foo' + #sys.exit (1) + + +files = { + 'idVendor': '0x1d6b', # Linux Foundation + 'idProduct': '0x0104', # Multifunction Composite Gadget + 'bcdDevice': '0x0100', # v1.0.0 + 'bcdUSB': '0x0200', + + 'strings': { + '0x409': { + 'serialnumber': "fedcba9876543210", + 'manufacturer': "Raspberry Pi", + 'product': "pizero keyboard Device", + } + }, + + 'configs': { + 'c.1': { + 'strings': { + '0x0409': { + 'configuration': 'Config 1: ECM network' + } + }, + 'MaxPower': 250 + } + }, + + 'functions': { + 'hid.usb0': keyboard, + 'hid.usb1': mouse, + } +} + + +name = 'pitooth' +root = os.path.join('/sys/kernel/config/usb_gadget/', name) +try: + os.mkdir(root) +except FileExistsError: + pass + +def serialise(root, tree): + for key,val in tree.items(): + child = os.path.join(root, key) + + if isinstance(val, dict): + try: + os.mkdir(child) + except FileExistsError: + pass + + serialise(child, val) + else: + with open(child, 'wb') as dst: + print(child,val) + if not isinstance(val, bytes): + val = bytes(str(val).encode()) + dst.write(val) + +serialise(root, files) + +# Link the functions in +for key in files['functions'].keys(): + src = os.path.join(root, 'functions', key) + dst = os.path.join(root, 'configs', 'c.1', key) + os.symlink(src, dst) + +# UDC needs to be written last +with open(f"{root}/UDC", 'w') as dst: + dst.write(UDC) diff --git a/relay.py b/relay.py new file mode 100755 index 0000000..a35481a --- /dev/null +++ b/relay.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 + +import evdev +import argparse +import os +import sys + +scan_to_hid = { + evdev.ecodes.KEY_A: 0x04, + evdev.ecodes.KEY_B: 0x05, + evdev.ecodes.KEY_C: 0x06, + evdev.ecodes.KEY_D: 0x07, + evdev.ecodes.KEY_E: 0x08, + evdev.ecodes.KEY_F: 0x09, + evdev.ecodes.KEY_G: 0x0a, + evdev.ecodes.KEY_H: 0x0b, + evdev.ecodes.KEY_I: 0x0c, + evdev.ecodes.KEY_J: 0x0d, + evdev.ecodes.KEY_K: 0x0e, + evdev.ecodes.KEY_L: 0x0f, + evdev.ecodes.KEY_M: 0x10, + evdev.ecodes.KEY_N: 0x11, + evdev.ecodes.KEY_O: 0x12, + evdev.ecodes.KEY_P: 0x13, + evdev.ecodes.KEY_Q: 0x14, + evdev.ecodes.KEY_R: 0x16, + evdev.ecodes.KEY_S: 0x15, + evdev.ecodes.KEY_T: 0x17, + evdev.ecodes.KEY_U: 0x18, + evdev.ecodes.KEY_V: 0x19, + evdev.ecodes.KEY_W: 0x1a, + evdev.ecodes.KEY_X: 0x1b, + evdev.ecodes.KEY_Y: 0x1c, + evdev.ecodes.KEY_Z: 0x1d, +} + +LEFT_CTRL = 1 << 0 +LEFT_SHIFT = 1 << 1 +LEFT_ALT = 1 << 2 +LEFT_GUI = 1 << 3 +RIGHT_CTRL = 1 << 4 +RIGHT_SHIFT = 1 << 5 +RIGHT_ALT = 1 << 6 +RIGHT_GUI = 1 << 7 + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Forward input events to a USB descriptor') + parser.add_argument('input', type=str, help='The source evdev device') + parser.add_argument('output', type=str, help='The output HID device') + + args = parser.parse_args() + + dst = os.open(args.output, os.O_WRONLY) + src = evdev.InputDevice(args.input) + src.grab() + + modifiers = 0 + keys_down = set() + + for event in src.read_loop(): + if event.type != evdev.ecodes.EV_KEY: + continue + + data = evdev.categorize(event) + print("Got key", data.scancode) + + code = scan_to_hid.get(data.scancode, None) + if code is None: + print("Ignoring unknown key") + continue + + if data.keystate == data.key_down: + if len(keys_down) >= 6: + print("Ignoring key due to rollover") + continue + print("Adding key") + keys_down.add(code) + + if data.keystate == data.key_up: + print("Removing key") + keys_down.remove(code) + + # Build the packet + packet = [0] + [0] + [k for k in keys_down] + packet += [0] * (8 - len(packet)) + + assert(len(packet) == 8) + + print("Sending", packet) + os.write(dst, bytes(packet)) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..97a5e58 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +evdev