def set_di_cos(self, lines): """ Enable COS IRQ on specific Digital Input lines. COS (change of state) IRQ can be enabled on each Digital Input line. :params lines: The DI lines as bit mask. """ if lines < 0 or lines > 0xff: raise RuntimeError("Lines is not a 8 bit bitmask") # enable interrupt on the CPLD bmreq = build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) self.dev.ctrl_transfer(bmreq, BREQUEST_CPLD, wValue=0xd000, wIndex=0, data_or_wLength=[lines], timeout=self.timeout) # maybe clear interrupt? bmreq = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) self.dev.ctrl_transfer(bmreq, BREQUEST_COS, wValue=0x00, wIndex=0x00, data_or_wLength=[], timeout=self.timeout)
def set_counter_min_pulse_width(self, counter, pulse): """ :params counter: Define which counter should work. :params pulse: Define the pulse width in 1/48Mhz steps (16 bit). """ value = struct.pack('<H', pulse) wValue = 0xd028 # bit 3 define which counter if counter == 0: wValue |= 0x4 elif counter == 1: wValue |= 0x0 else: raise RuntimeError( "Argument counter is out of range. counter must be 0-1") bmreq = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) self.dev.ctrl_transfer(bmreq, BREQUEST_CPLD, wValue, wIndex=0, data_or_wLength=value, timeout=self.timeout)
def set_descriptor(dev, desc, desc_type, desc_index, wIndex=None): r"""Update an existing descriptor or add a new one. dev is the Device object to which the request will be sent to. The desc parameter is the descriptor to be sent to the device. desc_type and desc_index are the descriptor type and index, respectively. wIndex index is used for string descriptors and represents the Language ID. For other types of descriptors, it is zero. """ wValue = desc_index | (desc_type << 8) bmRequestType = util.build_request_type( util.CTRL_OUT, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_DEVICE ) dev.ctrl_transfer( bmRequestType=bmRequestType, bRequest=0x07, wValue=wValue, wIndex=wIndex, data_or_wLength=desc )
def get_descriptor(dev, desc_size, desc_type, desc_index, request_type = util.CTRL_TYPE_STANDARD, wIndex = 0): r"""Return the specified descriptor. dev is the Device object to which the request will be sent to. desc_size is the descriptor size. desc_type and desc_index are the descriptor type and index, respectively. request_type is one of CTRL_TYPE_STANDARD, CTRL_TYPE_CLASS, CTRL_TYPE_VENDOR, or CTRL_TYPE_RESERVED. wIndex index is used for string descriptors and represents the Language ID. For other types of descriptors, it is zero. """ wValue = desc_index | (desc_type << 8) bmRequestType = util.build_request_type( util.CTRL_IN, request_type, util.CTRL_RECIPIENT_DEVICE) return dev.ctrl_transfer( bmRequestType = bmRequestType, bRequest = 0x06, wValue = wValue, wIndex = wIndex, data_or_wLength = desc_size)
def get_descriptor(dev, desc_size, desc_type, desc_index, wIndex=0): r"""Return the specified descriptor. dev is the Device object to which the request will be sent to. desc_size is the descriptor size. desc_type and desc_index are the descriptor type and index, respectively. wIndex index is used for string descriptors and represents the Language ID. For other types of descriptors, it is zero. """ wValue = desc_index | (desc_type << 8) bmRequestType = util.build_request_type( util.CTRL_IN, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_DEVICE ) return dev.ctrl_transfer( bmRequestType=bmRequestType, bRequest=0x06, wValue=wValue, wIndex=wIndex, data_or_wLength=desc_size )
def set_descriptor(dev, desc, desc_type, desc_index, request_type = util.CTRL_TYPE_STANDARD, wIndex = None): r"""Update an existing descriptor or add a new one. dev is the Device object to which the request will be sent to. The desc parameter is the descriptor to be sent to the device. desc_type and desc_index are the descriptor type and index, respectively. request_type is one of CTRL_TYPE_STANDARD, CTRL_TYPE_CLASS, CTRL_TYPE_VENDOR, or CTRL_TYPE_RESERVED. wIndex index is used for string descriptors and represents the Language ID. For other types of descriptors, it is zero. """ wValue = desc_index | (desc_type << 8) bmRequestType = util.build_request_type( util.CTRL_OUT, request_type, util.CTRL_RECIPIENT_DEVICE) dev.ctrl_transfer( bmRequestType = bmRequestType, bRequest = 0x07, wValue = wValue, wIndex = wIndex, data_or_wLength = desc)
def update_displays(self): """ Converts strings to radio panel bytes, and updates the displays. """ display_state = "" for value in self.lcd_state.values(): display_state += value text_bytes = convert_string_to_bytes(display_state) outType = util.build_request_type( util.CTRL_OUT, util.CTRL_TYPE_CLASS, util.CTRL_RECIPIENT_INTERFACE) # 0x21 self.device.ctrl_transfer(outType, 0x09, 0x03, 0x00, text_bytes)
def _send_query(self): command = bytearray(8) command[0] = 0x08 command[-1] = 0x02 bmRequestType = build_request_type( CTRL_OUT, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE) self.device.ctrl_transfer( bmRequestType=bmRequestType, bRequest=0x09, wValue=(2 << 8 | 0x08), wIndex=0, data_or_wLength=command, )
def get_interface(dev, bInterfaceNumber): r"""Get the current alternate setting of the interface. dev is the Device object to which the request will be sent to. """ bmRequestType = util.build_request_type(util.CTRL_IN, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_INTERFACE) return dev.ctrl_transfer(bmRequestType=bmRequestType, bRequest=0x0a, wIndex=bInterfaceNumber, data_or_wLength=1)[0]
def get_counter(self, counter, ctype=COUNTER_TYPE_COUNTER): """ Return the counter frequency of a counter. :param ctype: The type of the counter. COUNTER_TYPE_FREQUENCY or COUNTER_TYPE_COUNTER :param counter: The counter to retrieve. 0 or 1. :returns: Depending on ctype a count or 20ns clock period. """ wValue = 0xd000 if ctype == COUNTER_TYPE_FREQUENCY: wValue |= 0x18 elif ctype == COUNTER_TYPE_COUNTER: wValue |= 0x20 else: raise RuntimeError("Unknown counter type!") # bit 3 define which counter if counter == 0: wValue |= 0x4 elif counter == 1: wValue |= 0x0 else: raise RuntimeError( "Argument counter is out of range. counter must be 0-1") bmreq = build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) transfer = self.dev.ctrl_transfer(bmreq, BREQUEST_CPLD, wValue, wIndex=0, data_or_wLength=4, timeout=self.timeout) # counter: # 5263 # [ 0x8f, 0x14, 0x00, 0x00 ] # freq: # 9.647365 Hz. # [ 0x5c, 0xeb, 0x4b, 0x00 ] # freq: # 968.054199 Hz. # [ 0xb0, 0xc1, 0x00, 0x00 ] return struct.unpack('<I', transfer[0:4])[0]
def get_configuration(dev): r"""Get the current active configuration of the device. dev is the Device object to which the request will be sent to. This function differs from the Device.get_active_configuration method because the later may use cached data, while this function always does a device request. """ bmRequestType = util.build_request_type(util.CTRL_IN, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_DEVICE) return dev.ctrl_transfer(bmRequestType, bRequest=0x08, data_or_wLength=1)[0]
def get_dos(self): """ Retrieve the Digital Output state from the device. :returns: A bit mask. Each bit represents a single line. """ bmreq = build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) transfer = self.dev.ctrl_transfer(bmreq, BREQUEST_DO, wValue=0x4, wIndex=0, data_or_wLength=1, timeout=self.timeout) return transfer[0]
def _parse_recipient(recipient, direction): if recipient is None: r = util.CTRL_RECIPIENT_DEVICE wIndex = 0 elif isinstance(recipient, core.Interface): r = util.CTRL_RECIPIENT_INTERFACE wIndex = recipient.bInterfaceNumber elif isinstance(recipient, core.Endpoint): r = util.CTRL_RECIPIENT_ENDPOINT wIndex = recipient.bEndpointAddress else: raise ValueError('Invalid recipient.') bmRequestType = util.build_request_type(direction, util.CTRL_TYPE_STANDARD, r) return (bmRequestType, wIndex)
def set_counter(self, counter, counter_filter=False, polarity=False, reset_frequency_counter=False, reset_edge_counter=False): """ Change counter properties or reset the counter or frequenct value. :params counter_filter: Enable digital input filter. :params polarity: Set polarity to high (True) or low (False) :params reset_frequency_counter: Reset the frequency counter. :params reset_edge_counter: Reset the edge counter. """ wValue = 0xd010 # bit 3 define which counter if counter == 0: wValue |= 0x4 elif counter == 1: wValue |= 0x0 else: raise RuntimeError( "Argument counter is out of range. counter must be 0-1") value = 0 if counter_filter: value |= 0x1 if reset_edge_counter: value |= 0x2 if reset_frequency_counter: value |= 0x4 if polarity: value |= 0x8 bmreq = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) self.dev.ctrl_transfer(bmreq, BREQUEST_CPLD, wValue, wIndex=0x00, data_or_wLength=[value, 0x00, 0x00, 0x00], timeout=self.timeout)
def get_device_id(self): """ Read the device id, which can be set via a rotary switch. :returns: device id (0-7) """ bmreq = build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) transfer = self.dev.ctrl_transfer(bmreq, BREQUEST_DEVICE_ID, 0x4, wIndex=0, data_or_wLength=1, timeout=self.timeout) return transfer[0]
def get_interface(dev, bInterfaceNumber): r"""Get the current alternate setting of the interface. dev is the Device object to which the request will be sent to. """ bmRequestType = util.build_request_type( util.CTRL_IN, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_INTERFACE) return dev.ctrl_transfer( bmRequestType = bmRequestType, bRequest = 0x0a, wIndex = bInterfaceNumber, data_or_wLength = 1)[0]
def get_dos_initial(self): """ Get the initial Digital Output state after power up. :returns: A bit msak. Each bit represents a single line. """ bmreq = build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) transfer = self.dev.ctrl_transfer(bmreq, BREQUEST_INITIAL, wValue=0xd028, wIndex=0, data_or_wLength=2, timeout=self.timeout) return struct.unpack('<H', transfer[0:2])[0]
def get_dis(self): """ Read the state of all Digital Inputs :returns: A bit mask. Each bit represents a single line. """ bmreq = build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) transfer = self.dev.ctrl_transfer(bmreq, BREQUEST_CPLD, wValue=0xd00a, wIndex=0, data_or_wLength=1, timeout=self.timeout) return transfer[0]
def cb_test_get_cfg_desc(self, key, value, timestamp): """Verify the device provides required HID descriptors. USB Device Class Definition for HID, v1.11, paragraph 7.1: When a Get_Descriptor(Configuration) request is issued, it returns (...), and the HID descriptor for each interface. """ kwargs_cfg_desc_req = { 'bmRequestType': build_request_type(CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_DEVICE), 'bRequest': USB_REQUEST_GET_DESCRIPTOR, # Descriptor Index (part of wValue) is reset to zero. 'wValue': build_get_desc_value(DESC_TYPE_CONFIG, 0x00), # wIndex is reset to zero. 'wIndex': 0x00, # wLength unknown, set to 1024. 'data_or_wLength': 1024 } mbed_hid_dev = None try: mbed_hid_dev = retry_fun_call( fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable num_retries=20, retry_delay=0.05) except RetryError as exc: self.notify_error(exc) return try: # Request the Configuration descriptor. cfg_desc = mbed_hid_dev.ctrl_transfer(**kwargs_cfg_desc_req) # pylint: disable=not-callable raise_if_false( DESC_TYPE_HID_HID in get_descriptor_types(cfg_desc), 'No HID class descriptor in the Configuration descriptor.') except usb.core.USBError as exc: self.notify_failure( 'Get_Descriptor request failed. {}'.format(exc)) except RuntimeError as exc: self.notify_failure(exc) else: self.notify_success()
def set_dos_initial(self, lines): """ Set the initial Digital Output state after power up. :params lines: A bit msak. Each bit represents a single line. """ if lines < 0 or lines > 0xff: raise RuntimeError("Invalid Value for lines. lines must be 8 bit") bmreq = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) self.dev.ctrl_transfer(bmreq, BREQUEST_INITIAL, wValue=0xd028, wIndex=lines, data_or_wLength=[], timeout=self.timeout)
def setReport(reportId, data): #PYUSB_DEBUG=debug #LIBUSB_DEBUG=3 #print "test" #print util.build_request_type(util.CTRL_OUT, util.CTRL_TYPE_CLASS, util.CTRL_RECIPIENT_INTERFACE) r = dev.ctrl_transfer( util.build_request_type( util.CTRL_OUT, util.CTRL_TYPE_CLASS, util.CTRL_RECIPIENT_INTERFACE), # bmRequestType, REQ_HID_SET_REPORT, 0x0300, #HID_REPORT_TYPE_OUTPUT | reportId, 0, # feature_interface_num data, # data timeout=6000 # timeout (1000 was too small for C_FREE) ) #ctrl_transfer(0x21, 0x09, 0x209, 0, data, timeout) #dev.ctrl_transfer(0x21, 0x09, 0x0300, 0, data, timeout) return r
def _parse_recipient(recipient, direction): if recipient is None: r = util.CTRL_RECIPIENT_DEVICE wIndex = 0 elif isinstance(recipient, core.Interface): r = util.CTRL_RECIPIENT_INTERFACE wIndex = recipient.bInterfaceNumber elif isinstance(recipient, core.Endpoint): r = util.CTRL_RECIPIENT_ENDPOINT wIndex = recipient.bEndpointAddress else: raise ValueError('Invalid recipient.') bmRequestType = util.build_request_type( direction, util.CTRL_TYPE_STANDARD, r ) return (bmRequestType, wIndex)
def set_dos(self, lines): """ Set the Digital Outputs. :param lines: A bit mask describing the new state. """ if lines < 0 or lines > 0xff: raise RuntimeError("Invalid Value for lines. lines must be 8 bit") # there is only one port with 8 lines bmreq = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) self.dev.ctrl_transfer(bmreq, BREQUEST_DO, wValue=0xff03, wIndex=lines, data_or_wLength=[], timeout=self.timeout)
def set_button_control(dev): # Transfer control to the button HID interface bm_request_type = util.build_request_type( util.CTRL_OUT, util.CTRL_TYPE_CLASS, util.CTRL_RECIPIENT_INTERFACE ) state = array('B', [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]) ret = dev.ctrl_transfer(bm_request_type, 0x09, 0x00, 0x00, state) if ret < 0: print "Error reading control response" return -1 if ret == 0: print "Device didn't sent enough data" return -1 return 0
def get_configuration(dev): r"""Get the current active configuration of the device. dev is the Device object to which the request will be sent to. This function differs from the Device.get_active_configuration method because the later may use cached data, while this function always does a device request. """ bmRequestType = util.build_request_type( util.CTRL_IN, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_DEVICE) return dev.ctrl_transfer( bmRequestType, bRequest = 0x08, data_or_wLength = 1)[0]
def set_di_min_pulse_width(self, pulse): """ Set the minimum pulse width for the digital filter. The CPLD can filter digital inputs to ensure a level change last at least some time. The pulse width length can be only set for all inputs, but it can be enabled or disabled for each line indivitual. :params pulse: Define the pulse width in 1/48Mhz steps (16 bit). """ value = struct.pack('<H', pulse) bmreq = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) self.dev.ctrl_transfer(bmreq, BREQUEST_CPLD, wValue=0xd00c, wIndex=0, data_or_wLength=value, timeout=self.timeout)
def set_di_digital_filter(self, lines): """ Enable minimum pulse width filter on specific Digital Inputs. See set_di_min_pulse_width for further information on the digital filter. :params lines: The DI lines as bit mask. """ if lines < 0 or lines > 0xff: raise RuntimeError("Invalid Value for lines. lines must be 8 bit") bmreq = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE) self.dev.ctrl_transfer(bmreq, BREQUEST_CPLD, wValue=0xd00c, wIndex=0, data_or_wLength=[lines], timeout=self.timeout)
def cb_test_get_cfg_desc(self, key, value, timestamp): """Verify the device provides required HID descriptors. USB Device Class Definition for HID, v1.11, paragraph 7.1: When a Get_Descriptor(Configuration) request is issued, it returns (...), and the HID descriptor for each interface. """ kwargs_cfg_desc_req = { 'bmRequestType': build_request_type( CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_DEVICE), 'bRequest': USB_REQUEST_GET_DESCRIPTOR, # Descriptor Index (part of wValue) is reset to zero. 'wValue': build_get_desc_value(DESC_TYPE_CONFIG, 0x00), # wIndex is reset to zero. 'wIndex': 0x00, # wLength unknown, set to 1024. 'data_or_wLength': 1024} mbed_hid_dev = None try: mbed_hid_dev = retry_fun_call( fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable num_retries=20, retry_delay=0.05) except RetryError as exc: self.notify_error(exc) return try: # Request the Configuration descriptor. cfg_desc = mbed_hid_dev.ctrl_transfer(**kwargs_cfg_desc_req) # pylint: disable=not-callable raise_if_false(DESC_TYPE_HID_HID in get_descriptor_types(cfg_desc), 'No HID class descriptor in the Configuration descriptor.') except usb.core.USBError as exc: self.notify_failure('Get_Descriptor request failed. {}'.format(exc)) except RuntimeError as exc: self.notify_failure(exc) else: self.notify_success()
def cb_test_class_requests(self, key, value, timestamp): """Verify all required HID requests are supported. USB Device Class Definition for HID, v1.11, Appendix G: 1. Get_Report -- required for all types, 2. Set_Report -- not required if dev doesn't declare an Output Report, 3. Get_Idle -- required for keyboards, 4. Set_Idle -- required for keyboards, 5. Get_Protocol -- required for boot_keyboard and boot_mouse, 6. Set_Protocol -- required for boot_keyboard and boot_mouse. Details in USB Device Class Definition for HID, v1.11, paragraph 7.2. """ kwargs_get_report_request = { 'bmRequestType': build_request_type(CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE), 'bRequest': HID_REQUEST_GET_REPORT, # wValue: ReportType = Input, ReportID = 0 (not used) 'wValue': (0x01 << 8) | 0x00, # wIndex: InterfaceNumber (defined later) 'wIndex': None, # wLength: unknown, set to 1024 'data_or_wLength': 1024 } kwargs_get_idle_request = { 'bmRequestType': build_request_type(CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE), 'bRequest': HID_REQUEST_GET_IDLE, # wValue: 0, ReportID = 0 (not used) 'wValue': (0x00 << 8) | 0x00, # wIndex: InterfaceNumber (defined later) 'wIndex': None, 'data_or_wLength': 1 } kwargs_set_idle_request = { 'bmRequestType': build_request_type(CTRL_OUT, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE), 'bRequest': HID_REQUEST_SET_IDLE, # wValue: Duration, ReportID = 0 (all input reports) 'wValue': (KEYBOARD_IDLE_RATE_TO_SET << 8) | 0x00, # wIndex: InterfaceNumber (defined later) 'wIndex': None, 'data_or_wLength': 0 } kwargs_get_protocol_request = { 'bmRequestType': build_request_type(CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE), 'bRequest': HID_REQUEST_GET_PROTOCOL, 'wValue': 0x00, # wIndex: InterfaceNumber (defined later) 'wIndex': None, 'data_or_wLength': 1 } kwargs_set_protocol_request = { 'bmRequestType': build_request_type(CTRL_OUT, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE), 'bRequest': HID_REQUEST_SET_PROTOCOL, 'wValue': HID_PROTOCOL_TO_SET, # wIndex: InterfaceNumber (defined later) 'wIndex': None, 'data_or_wLength': 0 } mbed_hid_dev = None try: mbed_hid_dev = retry_fun_call( fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable num_retries=20, retry_delay=0.05) except RetryError as exc: self.notify_error(exc) return hid_dev_type = None tested_request_name = None try: for intf in mbed_hid_dev.get_active_configuration(): # pylint: disable=not-callable hid_dev_type = get_usbhid_dev_type(intf) if hid_dev_type is None: continue try: if mbed_hid_dev.is_kernel_driver_active( intf.bInterfaceNumber): mbed_hid_dev.detach_kernel_driver( intf.bInterfaceNumber) # pylint: disable=not-callable except (NotImplementedError, AttributeError): pass if hid_dev_type == 'boot_keyboard': # 4. Set_Idle tested_request_name = 'Set_Idle' kwargs_set_idle_request['wIndex'] = intf.bInterfaceNumber mbed_hid_dev.ctrl_transfer(**kwargs_set_idle_request) # pylint: disable=not-callable # 3. Get_Idle tested_request_name = 'Get_Idle' kwargs_get_idle_request['wIndex'] = intf.bInterfaceNumber idle_rate = mbed_hid_dev.ctrl_transfer( **kwargs_get_idle_request) # pylint: disable=not-callable raise_if_different(KEYBOARD_IDLE_RATE_TO_SET, idle_rate, 'Invalid idle rate received. ') if hid_dev_type in ('boot_keyboard', 'boot_mouse'): # 6. Set_Protocol tested_request_name = 'Set_Protocol' kwargs_set_protocol_request[ 'wIndex'] = intf.bInterfaceNumber mbed_hid_dev.ctrl_transfer(**kwargs_set_protocol_request) # pylint: disable=not-callable # 5. Get_Protocol tested_request_name = 'Get_Protocol' kwargs_get_protocol_request[ 'wIndex'] = intf.bInterfaceNumber protocol = mbed_hid_dev.ctrl_transfer( **kwargs_get_protocol_request) # pylint: disable=not-callable raise_if_different(HID_PROTOCOL_TO_SET, protocol, 'Invalid protocol received. ') # 1. Get_Report tested_request_name = 'Get_Report' kwargs_get_report_request['wIndex'] = intf.bInterfaceNumber mbed_hid_dev.ctrl_transfer(**kwargs_get_report_request) # pylint: disable=not-callable except usb.core.USBError as exc: self.notify_failure( 'The {!r} does not support the {!r} HID class request ({}).'. format(hid_dev_type, tested_request_name, exc)) except RuntimeError as exc: self.notify_failure( 'Set/Get data mismatch for {!r} for the {!r} HID class request ({}).' .format(hid_dev_type, tested_request_name, exc)) else: self.notify_success()
def cb_test_get_hid_desc(self, key, value, timestamp): """Verify the device handles Get_Descriptor request correctly. Two requests are tested for every HID interface: 1. Get_Descriptor(HID), 2. Get_Descriptor(Report). Details in USB Device Class Definition for HID, v1.11, paragraph 7.1. """ kwargs_hid_desc_req = { 'bmRequestType': build_request_type(CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_INTERFACE), 'bRequest': USB_REQUEST_GET_DESCRIPTOR, # Descriptor Index (part of wValue) is reset to zero for # HID class descriptors other than Physical ones. 'wValue': build_get_desc_value(DESC_TYPE_HID_HID, 0x00), # wIndex is replaced with the Interface Number in the loop. 'wIndex': None, 'data_or_wLength': DESC_LEN_HID_HID } kwargs_report_desc_req = { 'bmRequestType': build_request_type(CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_INTERFACE), 'bRequest': USB_REQUEST_GET_DESCRIPTOR, # Descriptor Index (part of wValue) is reset to zero for # HID class descriptors other than Physical ones. 'wValue': build_get_desc_value(DESC_TYPE_HID_REPORT, 0x00), # wIndex is replaced with the Interface Number in the loop. 'wIndex': None, # wLength is replaced with the Report Descriptor Length in the loop. 'data_or_wLength': None } mbed_hid_dev = None report_desc_lengths = [] try: mbed_hid_dev = retry_fun_call( fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable num_retries=20, retry_delay=0.05) except RetryError as exc: self.notify_error(exc) return try: for intf in mbed_hid_dev.get_active_configuration(): # pylint: disable=not-callable if intf.bInterfaceClass != USB_CLASS_HID: continue try: if mbed_hid_dev.is_kernel_driver_active( intf.bInterfaceNumber): mbed_hid_dev.detach_kernel_driver( intf.bInterfaceNumber) # pylint: disable=not-callable except (NotImplementedError, AttributeError): pass # Request the HID descriptor. kwargs_hid_desc_req['wIndex'] = intf.bInterfaceNumber hid_desc = mbed_hid_dev.ctrl_transfer(**kwargs_hid_desc_req) # pylint: disable=not-callable try: bNumDescriptors, bDescriptorType, wDescriptorLength = get_hid_descriptor_parts( hid_desc) # pylint: disable=invalid-name except TypeError as exc: self.notify_error(exc) return raise_if_different( 1, bNumDescriptors, 'Exactly one HID Report descriptor expected. ') raise_if_different(DESC_TYPE_HID_REPORT, bDescriptorType, 'Invalid HID class descriptor type. ') raise_if_false(wDescriptorLength > 0, 'Invalid HID Report descriptor length. ') # Request the Report descriptor. kwargs_report_desc_req['wIndex'] = intf.bInterfaceNumber kwargs_report_desc_req['data_or_wLength'] = wDescriptorLength report_desc = mbed_hid_dev.ctrl_transfer( **kwargs_report_desc_req) # pylint: disable=not-callable raise_if_different( wDescriptorLength, len(report_desc), 'The size of data received does not match the HID Report descriptor length. ' ) report_desc_lengths.append(len(report_desc)) except usb.core.USBError as exc: self.notify_failure( 'Get_Descriptor request failed. {}'.format(exc)) except RuntimeError as exc: self.notify_failure(exc) else: # Send the report desc len to the device. # USBHID::report_desc_length() returns uint16_t msg_value = '{0:04x}'.format(max(report_desc_lengths)) self.notify_success(msg_value)
def cb_test_get_hid_desc(self, key, value, timestamp): """Verify the device handles Get_Descriptor request correctly. Two requests are tested for every HID interface: 1. Get_Descriptor(HID), 2. Get_Descriptor(Report). Details in USB Device Class Definition for HID, v1.11, paragraph 7.1. """ kwargs_hid_desc_req = { 'bmRequestType': build_request_type( CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_INTERFACE), 'bRequest': USB_REQUEST_GET_DESCRIPTOR, # Descriptor Index (part of wValue) is reset to zero for # HID class descriptors other than Physical ones. 'wValue': build_get_desc_value(DESC_TYPE_HID_HID, 0x00), # wIndex is replaced with the Interface Number in the loop. 'wIndex': None, 'data_or_wLength': DESC_LEN_HID_HID} kwargs_report_desc_req = { 'bmRequestType': build_request_type( CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_INTERFACE), 'bRequest': USB_REQUEST_GET_DESCRIPTOR, # Descriptor Index (part of wValue) is reset to zero for # HID class descriptors other than Physical ones. 'wValue': build_get_desc_value(DESC_TYPE_HID_REPORT, 0x00), # wIndex is replaced with the Interface Number in the loop. 'wIndex': None, # wLength is replaced with the Report Descriptor Length in the loop. 'data_or_wLength': None} mbed_hid_dev = None report_desc_lengths = [] try: mbed_hid_dev = retry_fun_call( fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable num_retries=20, retry_delay=0.05) except RetryError as exc: self.notify_error(exc) return try: for intf in mbed_hid_dev.get_active_configuration(): # pylint: disable=not-callable if intf.bInterfaceClass != USB_CLASS_HID: continue try: if mbed_hid_dev.is_kernel_driver_active(intf.bInterfaceNumber): mbed_hid_dev.detach_kernel_driver(intf.bInterfaceNumber) # pylint: disable=not-callable except (NotImplementedError, AttributeError): pass # Request the HID descriptor. kwargs_hid_desc_req['wIndex'] = intf.bInterfaceNumber hid_desc = mbed_hid_dev.ctrl_transfer(**kwargs_hid_desc_req) # pylint: disable=not-callable try: bNumDescriptors, bDescriptorType, wDescriptorLength = get_hid_descriptor_parts(hid_desc) # pylint: disable=invalid-name except TypeError as exc: self.notify_error(exc) return raise_if_different(1, bNumDescriptors, 'Exactly one HID Report descriptor expected. ') raise_if_different(DESC_TYPE_HID_REPORT, bDescriptorType, 'Invalid HID class descriptor type. ') raise_if_false(wDescriptorLength > 0, 'Invalid HID Report descriptor length. ') # Request the Report descriptor. kwargs_report_desc_req['wIndex'] = intf.bInterfaceNumber kwargs_report_desc_req['data_or_wLength'] = wDescriptorLength report_desc = mbed_hid_dev.ctrl_transfer(**kwargs_report_desc_req) # pylint: disable=not-callable raise_if_different(wDescriptorLength, len(report_desc), 'The size of data received does not match the HID Report descriptor length. ') report_desc_lengths.append(len(report_desc)) except usb.core.USBError as exc: self.notify_failure('Get_Descriptor request failed. {}'.format(exc)) except RuntimeError as exc: self.notify_failure(exc) else: # Send the report desc len to the device. # USBHID::report_desc_length() returns uint16_t msg_value = '{0:04x}'.format(max(report_desc_lengths)) self.notify_success(msg_value)
def cb_test_class_requests(self, key, value, timestamp): """Verify all required HID requests are supported. USB Device Class Definition for HID, v1.11, Appendix G: 1. Get_Report -- required for all types, 2. Set_Report -- not required if dev doesn't declare an Output Report, 3. Get_Idle -- required for keyboards, 4. Set_Idle -- required for keyboards, 5. Get_Protocol -- required for boot_keyboard and boot_mouse, 6. Set_Protocol -- required for boot_keyboard and boot_mouse. Details in USB Device Class Definition for HID, v1.11, paragraph 7.2. """ kwargs_get_report_request = { 'bmRequestType': build_request_type( CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE), 'bRequest': HID_REQUEST_GET_REPORT, # wValue: ReportType = Input, ReportID = 0 (not used) 'wValue': (0x01 << 8) | 0x00, # wIndex: InterfaceNumber (defined later) 'wIndex': None, # wLength: unknown, set to 1024 'data_or_wLength': 1024} kwargs_get_idle_request = { 'bmRequestType': build_request_type( CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE), 'bRequest': HID_REQUEST_GET_IDLE, # wValue: 0, ReportID = 0 (not used) 'wValue': (0x00 << 8) | 0x00, # wIndex: InterfaceNumber (defined later) 'wIndex': None, 'data_or_wLength': 1} kwargs_set_idle_request = { 'bmRequestType': build_request_type( CTRL_OUT, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE), 'bRequest': HID_REQUEST_SET_IDLE, # wValue: Duration, ReportID = 0 (all input reports) 'wValue': (KEYBOARD_IDLE_RATE_TO_SET << 8) | 0x00, # wIndex: InterfaceNumber (defined later) 'wIndex': None, 'data_or_wLength': 0} kwargs_get_protocol_request = { 'bmRequestType': build_request_type( CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE), 'bRequest': HID_REQUEST_GET_PROTOCOL, 'wValue': 0x00, # wIndex: InterfaceNumber (defined later) 'wIndex': None, 'data_or_wLength': 1} kwargs_set_protocol_request = { 'bmRequestType': build_request_type( CTRL_OUT, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE), 'bRequest': HID_REQUEST_SET_PROTOCOL, 'wValue': HID_PROTOCOL_TO_SET, # wIndex: InterfaceNumber (defined later) 'wIndex': None, 'data_or_wLength': 0} mbed_hid_dev = None try: mbed_hid_dev = retry_fun_call( fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable num_retries=20, retry_delay=0.05) except RetryError as exc: self.notify_error(exc) return hid_dev_type = None tested_request_name = None try: for intf in mbed_hid_dev.get_active_configuration(): # pylint: disable=not-callable hid_dev_type = get_usbhid_dev_type(intf) if hid_dev_type is None: continue try: if mbed_hid_dev.is_kernel_driver_active(intf.bInterfaceNumber): mbed_hid_dev.detach_kernel_driver(intf.bInterfaceNumber) # pylint: disable=not-callable except (NotImplementedError, AttributeError): pass if hid_dev_type == 'boot_keyboard': # 4. Set_Idle tested_request_name = 'Set_Idle' kwargs_set_idle_request['wIndex'] = intf.bInterfaceNumber mbed_hid_dev.ctrl_transfer(**kwargs_set_idle_request) # pylint: disable=not-callable # 3. Get_Idle tested_request_name = 'Get_Idle' kwargs_get_idle_request['wIndex'] = intf.bInterfaceNumber idle_rate = mbed_hid_dev.ctrl_transfer(**kwargs_get_idle_request) # pylint: disable=not-callable raise_if_different(KEYBOARD_IDLE_RATE_TO_SET, idle_rate, 'Invalid idle rate received. ') if hid_dev_type in ('boot_keyboard', 'boot_mouse'): # 6. Set_Protocol tested_request_name = 'Set_Protocol' kwargs_set_protocol_request['wIndex'] = intf.bInterfaceNumber mbed_hid_dev.ctrl_transfer(**kwargs_set_protocol_request) # pylint: disable=not-callable # 5. Get_Protocol tested_request_name = 'Get_Protocol' kwargs_get_protocol_request['wIndex'] = intf.bInterfaceNumber protocol = mbed_hid_dev.ctrl_transfer(**kwargs_get_protocol_request) # pylint: disable=not-callable raise_if_different(HID_PROTOCOL_TO_SET, protocol, 'Invalid protocol received. ') # 1. Get_Report tested_request_name = 'Get_Report' kwargs_get_report_request['wIndex'] = intf.bInterfaceNumber mbed_hid_dev.ctrl_transfer(**kwargs_get_report_request) # pylint: disable=not-callable except usb.core.USBError as exc: self.notify_failure('The {!r} does not support the {!r} HID class request ({}).' .format(hid_dev_type, tested_request_name, exc)) except RuntimeError as exc: self.notify_failure('Set/Get data mismatch for {!r} for the {!r} HID class request ({}).' .format(hid_dev_type, tested_request_name, exc)) else: self.notify_success()