15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved. 25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# found in the LICENSE file. 45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)"""Implementation of a USB HID mouse. 65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)Two classes are provided by this module. The MouseFeature class implements 85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)the core functionality of a HID mouse and can be included in any HID gadget. 95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)The MouseGadget class implements an example mouse gadget. 105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)""" 115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import struct 135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import hid_constants 155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import hid_descriptors 165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import hid_gadget 175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import usb_constants 185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class MouseFeature(hid_gadget.HidFeature): 215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """HID feature implementation for a mouse. 225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) REPORT_DESC provides an example HID report descriptor for a device including 245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) this functionality. 255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) REPORT_DESC = hid_descriptors.ReportDescriptor( 285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.UsagePage(0x01), # Generic Desktop 295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Usage(0x02), # Mouse 305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Collection( 315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_constants.CollectionType.APPLICATION, 325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Usage(0x01), # Pointer 335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Collection( 345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_constants.CollectionType.PHYSICAL, 355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.UsagePage(0x09), # Buttons 365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.UsageMinimum(1), 375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.UsageMaximum(3), 385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.LogicalMinimum(0, force_length=1), 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.LogicalMaximum(1), 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.ReportCount(3), 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.ReportSize(1), 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Input(hid_descriptors.Data, 435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Variable, 445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Absolute), 455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.ReportCount(1), 465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.ReportSize(5), 475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Input(hid_descriptors.Constant), 485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.UsagePage(0x01), # Generic Desktop 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Usage(0x30), # X 505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Usage(0x31), # Y 515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.LogicalMinimum(0x81), # -127 525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.LogicalMaximum(127), 535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.ReportSize(8), 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.ReportCount(2), 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Input(hid_descriptors.Data, 565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Variable, 575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) hid_descriptors.Relative) 585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ) 595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ) 605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ) 615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __init__(self): 635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) super(MouseFeature, self).__init__() 645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._buttons = 0 655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def ButtonDown(self, button): 675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._buttons |= button 685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if self.IsConnected(): 695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.SendReport(self.EncodeInputReport()) 705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def ButtonUp(self, button): 725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._buttons &= ~button 735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if self.IsConnected(): 745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.SendReport(self.EncodeInputReport()) 755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def Move(self, x_displacement, y_displacement): 775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if self.IsConnected(): 785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.SendReport(self.EncodeInputReport(x_displacement, y_displacement)) 795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def EncodeInputReport(self, x_displacement=0, y_displacement=0): 815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return struct.pack('Bbb', self._buttons, x_displacement, y_displacement) 825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def GetInputReport(self): 845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Construct an input report. 855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) See Device Class Definition for Human Interface Devices (HID) Version 1.11 875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Appendix B.2. 885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Returns: 905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) A packed input report. 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self.EncodeInputReport() 935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class MouseGadget(hid_gadget.HidGadget): 965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """USB gadget implementation of a HID mouse.""" 975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __init__(self): 995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._feature = MouseFeature() 1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) super(MouseGadget, self).__init__( 1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) report_desc=MouseFeature.REPORT_DESC, 1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) features={0: self._feature}, 1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) packet_size=8, 1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) interval_ms=1, 1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) out_endpoint=False, 1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) vendor_id=usb_constants.VendorID.GOOGLE, 1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) product_id=usb_constants.ProductID.GOOGLE_MOUSE_GADGET, 1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) device_version=0x0100) 1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.AddStringDescriptor(1, 'Google Inc.') 1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.AddStringDescriptor(2, 'Mouse Gadget') 1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def ButtonDown(self, button): 1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._feature.ButtonDown(button) 1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def ButtonUp(self, button): 1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._feature.ButtonUp(button) 1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def Move(self, x_displacement, y_displacement): 1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._feature.Move(x_displacement, y_displacement) 1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def RegisterHandlers(): 1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Registers web request handlers with the application server.""" 1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) from tornado import web 1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) class WebConfigureHandler(web.RequestHandler): 1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def post(self): 1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) gadget = MouseGadget() 1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) server.SwitchGadget(gadget) 1325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) class WebClickHandler(web.RequestHandler): 1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def post(self): 1365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) BUTTONS = { 1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) '1': hid_constants.Mouse.BUTTON_1, 1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) '2': hid_constants.Mouse.BUTTON_2, 1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) '3': hid_constants.Mouse.BUTTON_3, 1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) button = BUTTONS[self.get_argument('button')] 1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) server.gadget.ButtonDown(button) 1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) server.gadget.ButtonUp(button) 1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) class WebMoveHandler(web.RequestHandler): 1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def post(self): 1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) x = int(self.get_argument('x')) 1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) y = int(self.get_argument('y')) 1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) server.gadget.Move(x, y) 1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) import server 1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) server.app.add_handlers('.*$', [ 1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) (r'/mouse/configure', WebConfigureHandler), 1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) (r'/mouse/move', WebMoveHandler), 1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) (r'/mouse/click', WebClickHandler), 1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ]) 159