2019-02-23 12:54:46 +11:00
|
|
|
#!/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)
|
2019-03-09 11:11:23 +11:00
|
|
|
|
|
|
|
# Control key bitmask
|
2019-02-23 12:54:46 +11:00
|
|
|
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)
|
2019-03-09 11:11:23 +11:00
|
|
|
|
|
|
|
# Reserved byte
|
2019-02-23 12:54:46 +11:00
|
|
|
0x95, 0x01, # Report Count (1)
|
|
|
|
0x75, 0x08, # Report Size (8)
|
|
|
|
0x81, 0x03, # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
2019-03-09 11:11:23 +11:00
|
|
|
|
|
|
|
# LED status
|
2019-02-23 12:54:46 +11:00
|
|
|
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)
|
2019-03-09 11:11:23 +11:00
|
|
|
|
|
|
|
# Padding
|
2019-02-23 12:54:46 +11:00
|
|
|
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)
|
2019-03-09 11:11:23 +11:00
|
|
|
|
|
|
|
# 6 general buttons
|
2019-02-23 12:54:46 +11:00
|
|
|
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)
|
2019-03-09 11:11:23 +11:00
|
|
|
|
2019-02-23 12:54:46 +11:00
|
|
|
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",
|
2019-03-09 11:11:47 +11:00
|
|
|
'manufacturer': "Quimby",
|
|
|
|
'product': "Virtual Keyboard",
|
2019-02-23 12:54:46 +11:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
'configs': {
|
|
|
|
'c.1': {
|
|
|
|
'strings': {
|
|
|
|
'0x0409': {
|
|
|
|
'configuration': 'Config 1: ECM network'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'MaxPower': 250
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
'functions': {
|
|
|
|
'hid.usb0': keyboard,
|
|
|
|
'hid.usb1': mouse,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-02-23 21:48:41 +11:00
|
|
|
name = 'quimby'
|
2019-02-23 12:54:46 +11:00
|
|
|
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)
|