SHARE
TWEET

Odroid C2 HID test script

a guest Nov 14th, 2019 24 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import sys
  2. import os
  3. import shutil
  4. import pwd
  5. import asyncio
  6. import subprocess
  7. import argparse
  8. import atexit
  9.  
  10.  
  11. class HIDReportDescriptorKeyboard(object):
  12.     def __len__(self):
  13.         return 8
  14.  
  15.     def __bytes__(self):
  16.         return bytes([
  17.             0x05, 0x01,  # Usage Page (Generic Desktop Ctrls)
  18.             0x09, 0x06,  # Usage (Keyboard)
  19.             0xA1, 0x01,  # Collection (Application)
  20.             0x05, 0x07,  # Usage Page (Kbrd/Keypad)
  21.             0x19, 0xE0,  # Usage Minimum (0xE0)
  22.             0x29, 0xE7,  # Usage Maximum (0xE7)
  23.             0x15, 0x00,  # Logical Minimum (0)
  24.             0x25, 0x01,  # Logical Maximum (1)
  25.             0x75, 0x01,  # Report Size (1)
  26.             0x95, 0x08,  # Report Count (8)
  27.             0x81, 0x02,  # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
  28.             0x95, 0x01,  # Report Count (1)
  29.             0x75, 0x08,  # Report Size (8)
  30.             0x81, 0x03,  # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
  31.             0x95, 0x05,  # Report Count (5)
  32.             0x75, 0x01,  # Report Size (1)
  33.             0x05, 0x08,  # Usage Page (LEDs)
  34.             0x19, 0x01,  # Usage Minimum (Num Lock)
  35.             0x29, 0x05,  # Usage Maximum (Kana)
  36.             0x91, 0x02,  # Output (Data,Var,Abs)
  37.             0x95, 0x01,  # Report Count (1)
  38.             0x75, 0x03,  # Report Size (3)
  39.             0x91, 0x03,  # Output (Const,Var,Abs)
  40.             0x95, 0x06,  # Report Count (6)
  41.             0x75, 0x08,  # Report Size (8)
  42.             0x15, 0x00,  # Logical Minimum (0)
  43.             0x25, 0x65,  # Logical Maximum (101)
  44.             0x05, 0x07,  # Usage Page (Kbrd/Keypad)
  45.             0x19, 0x00,  # Usage Minimum (0x00)
  46.             0x29, 0x65,  # Usage Maximum (0x65)
  47.             0x81, 0x00,  # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
  48.             0xC0,        # End Collection
  49.         ])
  50.  
  51.  
  52. class HIDReportDescriptorGamepad(object):
  53.     def __len__(self):
  54.         return 4
  55.  
  56.     def __bytes__(self):
  57.         return bytes([
  58.             0x05, 0x01,  # USAGE_PAGE (Generic Desktop)
  59.             0x15, 0x00,  # LOGICAL_MINIMUM (0)
  60.             0x09, 0x04,  # USAGE (Joystick)
  61.             0xa1, 0x01,  # COLLECTION (Application)
  62.             0x05, 0x02,  # USAGE_PAGE (Simulation Controls)
  63.             0x09, 0xbb,  # USAGE (Throttle)
  64.             0x15, 0x81,  # LOGICAL_MINIMUM (-127)
  65.             0x25, 0x7f,  # LOGICAL_MAXIMUM (127)
  66.             0x75, 0x08,  # REPORT_SIZE (8)
  67.             0x95, 0x01,  # REPORT_COUNT (1)
  68.             0x81, 0x02,  # INPUT (Data,Var,Abs)
  69.             0x05, 0x01,  # USAGE_PAGE (Generic Desktop)
  70.             0x09, 0x01,  # USAGE (Pointer)
  71.             0xa1, 0x00,  # COLLECTION (Physical)
  72.             0x09, 0x30,  # USAGE (X)
  73.             0x09, 0x31,  # USAGE (Y)
  74.             0x95, 0x02,  # REPORT_COUNT (2)
  75.             0x81, 0x02,  # INPUT (Data,Var,Abs)
  76.             0xc0,        # END_COLLECTION
  77.             0x09, 0x39,  # USAGE (Hat switch)
  78.             0x15, 0x00,  # LOGICAL_MINIMUM (0)
  79.             0x25, 0x03,  # LOGICAL_MAXIMUM (3)
  80.             0x35, 0x00,  # PHYSICAL_MINIMUM (0)
  81.             0x46, 0x0e, 0x01,  # PHYSICAL_MAXIMUM (270)
  82.             0x65, 0x14,  # UNIT (Eng Rot:Angular Pos)
  83.             0x75, 0x04,  # REPORT_SIZE (4)
  84.             0x95, 0x01,  # REPORT_COUNT (1)
  85.             0x81, 0x02,  # INPUT (Data,Var,Abs)
  86.             0x05, 0x09,  # USAGE_PAGE (Button)
  87.             0x19, 0x01,  # USAGE_MINIMUM (Button 1)
  88.             0x29, 0x04,  # USAGE_MAXIMUM (Button 4)
  89.             0x15, 0x00,  # LOGICAL_MINIMUM (0)
  90.             0x25, 0x01,  # LOGICAL_MAXIMUM (1)
  91.             0x75, 0x01,  # REPORT_SIZE (1)
  92.             0x95, 0x04,  # REPORT_COUNT (4)
  93.             0x55, 0x00,  # UNIT_EXPONENT (0)
  94.             0x65, 0x00,  # UNIT (None)
  95.             0x81, 0x02,  # INPUT (Data,Var,Abs)
  96.             0xc0         # END_COLLECTION
  97.         ])
  98.  
  99.  
  100. class HidDaemon(object):
  101.     def __init__(self, vendor_id, product_id, manufacturer, description, serial_number, hid_report_class):
  102.         self._descriptor = hid_report_class()
  103.         self._hid_devname = 'odroidc2_hid'
  104.         self._vendor = vendor_id
  105.         self._product = product_id
  106.         self._manufacturer = manufacturer
  107.         self._desc = description
  108.         self._serial = serial_number
  109.         self._libcomposite_already_running = self.check_libcomposite()
  110.         self._usb_f_hid_already_running = self.check_usb_f_hid()
  111.         self._loop = asyncio.get_event_loop()
  112.         self._devname = 'hidg0'
  113.         self._devpath = '/dev/%s' % self._devname
  114.  
  115.     def _cleanup(self):
  116.         udc_path = '/sys/kernel/config/usb_gadget/%s/UDC' % self._hid_devname
  117.         if os.path.exists(udc_path):
  118.             with open(udc_path, 'w') as fd:
  119.                 fd.truncate()
  120.             try:
  121.                 shutil.rmtree('/sys/kernel/config/usb_gadget/%s' % self._hid_devname, ignore_errors=True)
  122.             except:
  123.                 pass
  124.         if not self._usb_f_hid_already_running and self.check_usb_f_hid():
  125.             self.unload_usb_f_hid()
  126.         if not self._libcomposite_already_running and self.check_libcomposite():
  127.             self.unload_libcomposite()
  128.  
  129.     @staticmethod
  130.     def check_libcomposite():
  131.         r = int(subprocess.check_output("lsmod | grep 'libcomposite' | wc -l", shell=True, close_fds=True).decode().strip())
  132.         return r != 0
  133.  
  134.     @staticmethod
  135.     def load_libcomposite():
  136.         if not HidDaemon.check_libcomposite():
  137.             subprocess.check_call("modprobe libcomposite", shell=True, close_fds=True)
  138.  
  139.     @staticmethod
  140.     def unload_libcomposite():
  141.         if HidDaemon.check_libcomposite():
  142.             subprocess.check_call("rmmod libcomposite", shell=True, close_fds=True)
  143.  
  144.     @staticmethod
  145.     def check_usb_f_hid():
  146.         r = int(
  147.             subprocess.check_output("lsmod | grep 'usb_f_hid' | wc -l", shell=True, close_fds=True).decode().strip())
  148.         return r != 0
  149.  
  150.     @staticmethod
  151.     def load_usb_f_hid():
  152.         if not HidDaemon.check_libcomposite():
  153.             subprocess.check_call("modprobe usb_f_hid", shell=True, close_fds=True)
  154.  
  155.     @staticmethod
  156.     def unload_usb_f_hid():
  157.         if HidDaemon.check_libcomposite():
  158.             subprocess.check_call("rmmod usb_f_hid", shell=True, close_fds=True)
  159.  
  160.     def _setup(self):
  161.         f_dev_name = self._hid_devname
  162.         os.makedirs('/sys/kernel/config/usb_gadget/%s/strings/0x409' % f_dev_name, exist_ok=True)
  163.         os.makedirs('/sys/kernel/config/usb_gadget/%s/configs/c.1/strings/0x409' % f_dev_name, exist_ok=True)
  164.         os.makedirs('/sys/kernel/config/usb_gadget/%s/functions/hid.usb0' % f_dev_name, exist_ok=True)
  165.         with open('/sys/kernel/config/usb_gadget/%s/idVendor' % f_dev_name, 'w') as fd:
  166.             fd.write('0x%04x' % self._vendor)
  167.         with open('/sys/kernel/config/usb_gadget/%s/idProduct' % f_dev_name, 'w') as fd:
  168.             fd.write('0x%04x' % self._product)
  169.         with open('/sys/kernel/config/usb_gadget/%s/bcdDevice' % f_dev_name, 'w') as fd:
  170.             fd.write('0x0100')
  171.         with open('/sys/kernel/config/usb_gadget/%s/bcdUSB' % f_dev_name, 'w') as fd:
  172.             fd.write('0x0200')
  173.  
  174.         with open('/sys/kernel/config/usb_gadget/%s/strings/0x409/serialnumber' % f_dev_name, 'w') as fd:
  175.             fd.write(self._serial)
  176.         with open('/sys/kernel/config/usb_gadget/%s/strings/0x409/manufacturer' % f_dev_name, 'w') as fd:
  177.             fd.write(self._manufacturer)
  178.         with open('/sys/kernel/config/usb_gadget/%s/strings/0x409/product' % f_dev_name, 'w') as fd:
  179.             fd.write(self._desc)
  180.  
  181.         with open('/sys/kernel/config/usb_gadget/%s/configs/c.1/strings/0x409/configuration' % f_dev_name, 'w') as fd:
  182.             fd.write('Config 1 : %s' % self._desc)
  183.         with open('/sys/kernel/config/usb_gadget/%s/configs/c.1/MaxPower' % f_dev_name,'w') as fd:
  184.             fd.write('250')
  185.  
  186.         with open('/sys/kernel/config/usb_gadget/%s/functions/hid.usb0/protocol' % f_dev_name, 'w') as fd:
  187.             fd.write('1')
  188.         with open('/sys/kernel/config/usb_gadget/%s/functions/hid.usb0/subclass' % f_dev_name, 'w') as fd:
  189.             fd.write('1')
  190.         with open('/sys/kernel/config/usb_gadget/%s/functions/hid.usb0/report_length' % f_dev_name, 'w') as fd:
  191.             fd.write(str(len(self._descriptor)))
  192.         with open('/sys/kernel/config/usb_gadget/%s/functions/hid.usb0/report_desc' % f_dev_name, 'wb') as fd:
  193.             fd.write(bytes(self._descriptor))
  194.  
  195.         os.symlink(
  196.             '/sys/kernel/config/usb_gadget/%s/functions/hid.usb0' % f_dev_name,
  197.             '/sys/kernel/config/usb_gadget/%s/configs/c.1/hid.usb0' % f_dev_name,
  198.             target_is_directory=True
  199.         )
  200.  
  201.         with open('/sys/kernel/config/usb_gadget/%s/UDC' % f_dev_name, 'w') as fd: fd.write('\r\n'.join(os.listdir('/sys/class/udc')))
  202.  
  203.     def run(self):
  204.         if not self._libcomposite_already_running:
  205.             self.load_libcomposite()
  206.         atexit.register(self._cleanup)
  207.  
  208.         # Setup HID gadget (keyboard)
  209.         self._setup()
  210.  
  211.         # Use asyncio because we can then do thing on the side (web ui, polling attached devices using pyusb ...)
  212.         try:
  213.             self._loop.run_forever()
  214.         except KeyboardInterrupt:
  215.             pass
  216.  
  217. if __name__ == '__main__':
  218.     user_root = pwd.getpwuid(0)
  219.     user_curr = pwd.getpwuid(os.getuid())
  220.     print('Running as <%s>' % user_curr.pw_name)
  221.     if os.getuid() != 0:
  222.         print('Attempting to run as <root>')
  223.         sys.exit(os.system("/usr/bin/sudo /usr/bin/su root -c '%s %s'" % (sys.executable, ' '.join(sys.argv))))
  224.     parser = argparse.ArgumentParser()
  225.     parser.add_argument('hid_type', choices=['keyboard', 'gamepad'])
  226.     args = parser.parse_args()
  227.     if args.hid_type == 'keyboard':
  228.         print('Emulating: Keyboard')
  229.         # Generic keyboard
  230.         hid = HidDaemon(0x16c0, 0x0488, 'author', 'ODROID C2 KBD HID', 'fedcba9876543210', HIDReportDescriptorKeyboard)
  231.         hid.run()
  232.     elif args.hid_type == 'gamepad':
  233.         print('Emulating: Gamepad')
  234.         # Teensy FlightSim for the purpose of this example (and since it's intended for DIY, it fits ou purpose)
  235.         hid = HidDaemon(0x16c0, 0x0488, 'author', 'ODROID C2 GAMEPAD HID', 'fedcba9876543210', HIDReportDescriptorGamepad)
  236.         hid.run()
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top