Submitting review comment fix: header function and extra lines.

Executed formatter on file.
This commit is contained in:
skarnataki
2023-10-10 11:43:57 +00:00
committed by Lucas Abel
parent 963fa41a49
commit 651e44e0b6
4 changed files with 452 additions and 342 deletions

View File

@@ -208,11 +208,11 @@ class Host(EventEmitter):
def on_l2cap_channel_open(self, l2cap_channel: l2cap.Channel) -> None: def on_l2cap_channel_open(self, l2cap_channel: l2cap.Channel) -> None:
if l2cap_channel.psm == HID_CONTROL_PSM: if l2cap_channel.psm == HID_CONTROL_PSM:
self.l2cap_ctrl_channel = l2cap_channel self.l2cap_ctrl_channel = l2cap_channel # type: ignore
self.l2cap_ctrl_channel.sink = self.on_ctrl_pdu self.l2cap_ctrl_channel.sink = self.on_ctrl_pdu # type: ignore
else: else:
self.l2cap_intr_channel = l2cap_channel self.l2cap_intr_channel = l2cap_channel # type: ignore
self.l2cap_intr_channel.sink = self.on_intr_pdu self.l2cap_intr_channel.sink = self.on_intr_pdu # type: ignore
logger.debug(f'$$$ L2CAP channel open: {l2cap_channel}') logger.debug(f'$$$ L2CAP channel open: {l2cap_channel}')
def on_ctrl_pdu(self, pdu: bytes) -> None: def on_ctrl_pdu(self, pdu: bytes) -> None:
@@ -300,4 +300,3 @@ class Host(EventEmitter):
msg = bytearray([header]) msg = bytearray([header])
logger.debug(f'>>> HID CONTROL VIRTUAL CABLE UNPLUG, PDU: {msg.hex()}') logger.debug(f'>>> HID CONTROL VIRTUAL CABLE UNPLUG, PDU: {msg.hex()}')
self.l2cap_ctrl_channel.send_pdu(msg) # type: ignore self.l2cap_ctrl_channel.send_pdu(msg) # type: ignore

View File

@@ -2,247 +2,247 @@
# letters # letters
shift_map = { shift_map = {
'a' : 'A', 'a': 'A',
'b' : 'B', 'b': 'B',
'c' : 'C', 'c': 'C',
'd' : 'D', 'd': 'D',
'e' : 'E', 'e': 'E',
'f' : 'F', 'f': 'F',
'g' : 'G', 'g': 'G',
'h' : 'H', 'h': 'H',
'i' : 'I', 'i': 'I',
'j' : 'J', 'j': 'J',
'k' : 'K', 'k': 'K',
'l' : 'L', 'l': 'L',
'm' : 'M', 'm': 'M',
'n' : 'N', 'n': 'N',
'o' : 'O', 'o': 'O',
'p' : 'P', 'p': 'P',
'q' : 'Q', 'q': 'Q',
'r' : 'R', 'r': 'R',
's' : 'S', 's': 'S',
't' : 'T', 't': 'T',
'u' : 'U', 'u': 'U',
'v' : 'V', 'v': 'V',
'w' : 'W', 'w': 'W',
'x' : 'X', 'x': 'X',
'y' : 'Y', 'y': 'Y',
'z' : 'Z', 'z': 'Z',
# numbers # numbers
'1' : '!', '1': '!',
'2' : '@', '2': '@',
'3' : '#', '3': '#',
'4' : '$', '4': '$',
'5' : '%', '5': '%',
'6' : '^', '6': '^',
'7' : '&', '7': '&',
'8' : '*', '8': '*',
'9' : '(', '9': '(',
'0' : ')', '0': ')',
# symbols # symbols
'-' : '_', '-': '_',
'=' : '+', '=': '+',
'[' : '{', '[': '{',
']' : '}', ']': '}',
'\\' : '|', '\\': '|',
';' : ':', ';': ':',
'\'' : '"', '\'': '"',
',' : '<', ',': '<',
'.' : '>', '.': '>',
'/' : '?', '/': '?',
'`' : '~' '`': '~',
} }
# hex map # hex map
# modifier keys # modifier keys
mod_keys = { mod_keys = {
'00' : '', '00': '',
'01' : 'left_ctrl', '01': 'left_ctrl',
'02' : 'left_shift', '02': 'left_shift',
'04' : 'left_alt', '04': 'left_alt',
'08' : 'left_meta', '08': 'left_meta',
'10' : 'right_ctrl', '10': 'right_ctrl',
'20' : 'right_shift', '20': 'right_shift',
'40' : 'right_alt', '40': 'right_alt',
'80' : 'right_meta' '80': 'right_meta',
} }
# base keys # base keys
base_keys = { base_keys = {
# meta # meta
'00' : '', # none '00': '', # none
'01' : 'error_ovf', '01': 'error_ovf',
# letters # letters
'04' : 'a', '04': 'a',
'05' : 'b', '05': 'b',
'06' : 'c', '06': 'c',
'07' : 'd', '07': 'd',
'08' : 'e', '08': 'e',
'09' : 'f', '09': 'f',
'0a' : 'g', '0a': 'g',
'0b' : 'h', '0b': 'h',
'0c' : 'i', '0c': 'i',
'0d' : 'j', '0d': 'j',
'0e' : 'k', '0e': 'k',
'0f' : 'l', '0f': 'l',
'10' : 'm', '10': 'm',
'11' : 'n', '11': 'n',
'12' : 'o', '12': 'o',
'13' : 'p', '13': 'p',
'14' : 'q', '14': 'q',
'15' : 'r', '15': 'r',
'16' : 's', '16': 's',
'17' : 't', '17': 't',
'18' : 'u', '18': 'u',
'19' : 'v', '19': 'v',
'1a' : 'w', '1a': 'w',
'1b' : 'x', '1b': 'x',
'1c' : 'y', '1c': 'y',
'1d' : 'z', '1d': 'z',
# numbers # numbers
'1e' : '1', '1e': '1',
'1f' : '2', '1f': '2',
'20' : '3', '20': '3',
'21' : '4', '21': '4',
'22' : '5', '22': '5',
'23' : '6', '23': '6',
'24' : '7', '24': '7',
'25' : '8', '25': '8',
'26' : '9', '26': '9',
'27' : '0', '27': '0',
# misc # misc
'28' : 'enter', #enter \n '28': 'enter', # enter \n
'29' : 'esc', '29': 'esc',
'2a' : 'backspace', '2a': 'backspace',
'2b' : 'tab', '2b': 'tab',
'2c' : 'spacebar', #space '2c': 'spacebar', # space
'2d' : '-', '2d': '-',
'2e' : '=', '2e': '=',
'2f' : '[', '2f': '[',
'30' : ']', '30': ']',
'31' : '\\', '31': '\\',
'32' : '=', '32': '=',
'33' : '_SEMICOLON', '33': '_SEMICOLON',
'34' : 'KEY_APOSTROPHE', '34': 'KEY_APOSTROPHE',
'35' : 'KEY_GRAVE', '35': 'KEY_GRAVE',
'36' : 'KEY_COMMA', '36': 'KEY_COMMA',
'37' : 'KEY_DOT', '37': 'KEY_DOT',
'38' : 'KEY_SLASH', '38': 'KEY_SLASH',
'39' : 'KEY_CAPSLOCK', '39': 'KEY_CAPSLOCK',
'3a' : 'KEY_F1', '3a': 'KEY_F1',
'3b' : 'KEY_F2', '3b': 'KEY_F2',
'3c' : 'KEY_F3', '3c': 'KEY_F3',
'3d' : 'KEY_F4', '3d': 'KEY_F4',
'3e' : 'KEY_F5', '3e': 'KEY_F5',
'3f' : 'KEY_F6', '3f': 'KEY_F6',
'40' : 'KEY_F7', '40': 'KEY_F7',
'41' : 'KEY_F8', '41': 'KEY_F8',
'42' : 'KEY_F9', '42': 'KEY_F9',
'43' : 'KEY_F10', '43': 'KEY_F10',
'44' : 'KEY_F11', '44': 'KEY_F11',
'45' : 'KEY_F12', '45': 'KEY_F12',
'46' : 'KEY_SYSRQ', '46': 'KEY_SYSRQ',
'47' : 'KEY_SCROLLLOCK', '47': 'KEY_SCROLLLOCK',
'48' : 'KEY_PAUSE', '48': 'KEY_PAUSE',
'49' : 'KEY_INSERT', '49': 'KEY_INSERT',
'4a' : 'KEY_HOME', '4a': 'KEY_HOME',
'4b' : 'KEY_PAGEUP', '4b': 'KEY_PAGEUP',
'4c' : 'KEY_DELETE', '4c': 'KEY_DELETE',
'4d' : 'KEY_END', '4d': 'KEY_END',
'4e' : 'KEY_PAGEDOWN', '4e': 'KEY_PAGEDOWN',
'4f' : 'KEY_RIGHT', '4f': 'KEY_RIGHT',
'50' : 'KEY_LEFT', '50': 'KEY_LEFT',
'51' : 'KEY_DOWN', '51': 'KEY_DOWN',
'52' : 'KEY_UP', '52': 'KEY_UP',
'53' : 'KEY_NUMLOCK', '53': 'KEY_NUMLOCK',
'54' : 'KEY_KPSLASH', '54': 'KEY_KPSLASH',
'55' : 'KEY_KPASTERISK', '55': 'KEY_KPASTERISK',
'56' : 'KEY_KPMINUS', '56': 'KEY_KPMINUS',
'57' : 'KEY_KPPLUS', '57': 'KEY_KPPLUS',
'58' : 'KEY_KPENTER', '58': 'KEY_KPENTER',
'59' : 'KEY_KP1', '59': 'KEY_KP1',
'5a' : 'KEY_KP2', '5a': 'KEY_KP2',
'5b' : 'KEY_KP3', '5b': 'KEY_KP3',
'5c' : 'KEY_KP4', '5c': 'KEY_KP4',
'5d' : 'KEY_KP5', '5d': 'KEY_KP5',
'5e' : 'KEY_KP6', '5e': 'KEY_KP6',
'5f' : 'KEY_KP7', '5f': 'KEY_KP7',
'60' : 'KEY_KP8', '60': 'KEY_KP8',
'61' : 'KEY_KP9', '61': 'KEY_KP9',
'62' : 'KEY_KP0', '62': 'KEY_KP0',
'63' : 'KEY_KPDOT', '63': 'KEY_KPDOT',
'64' : 'KEY_102ND', '64': 'KEY_102ND',
'65' : 'KEY_COMPOSE', '65': 'KEY_COMPOSE',
'66' : 'KEY_POWER', '66': 'KEY_POWER',
'67' : 'KEY_KPEQUAL', '67': 'KEY_KPEQUAL',
'68' : 'KEY_F13', '68': 'KEY_F13',
'69' : 'KEY_F14', '69': 'KEY_F14',
'6a' : 'KEY_F15', '6a': 'KEY_F15',
'6b' : 'KEY_F16', '6b': 'KEY_F16',
'6c' : 'KEY_F17', '6c': 'KEY_F17',
'6d' : 'KEY_F18', '6d': 'KEY_F18',
'6e' : 'KEY_F19', '6e': 'KEY_F19',
'6f' : 'KEY_F20', '6f': 'KEY_F20',
'70' : 'KEY_F21', '70': 'KEY_F21',
'71' : 'KEY_F22', '71': 'KEY_F22',
'72' : 'KEY_F23', '72': 'KEY_F23',
'73' : 'KEY_F24', '73': 'KEY_F24',
'74' : 'KEY_OPEN', '74': 'KEY_OPEN',
'75' : 'KEY_HELP', '75': 'KEY_HELP',
'76' : 'KEY_PROPS', '76': 'KEY_PROPS',
'77' : 'KEY_FRONT', '77': 'KEY_FRONT',
'78' : 'KEY_STOP', '78': 'KEY_STOP',
'79' : 'KEY_AGAIN', '79': 'KEY_AGAIN',
'7a' : 'KEY_UNDO', '7a': 'KEY_UNDO',
'7b' : 'KEY_CUT', '7b': 'KEY_CUT',
'7c' : 'KEY_COPY', '7c': 'KEY_COPY',
'7d' : 'KEY_PASTE', '7d': 'KEY_PASTE',
'7e' : 'KEY_FIND', '7e': 'KEY_FIND',
'7f' : 'KEY_MUTE', '7f': 'KEY_MUTE',
'80' : 'KEY_VOLUMEUP', '80': 'KEY_VOLUMEUP',
'81' : 'KEY_VOLUMEDOWN', '81': 'KEY_VOLUMEDOWN',
'85' : 'KEY_KPCOMMA', '85': 'KEY_KPCOMMA',
'87' : 'KEY_RO', '87': 'KEY_RO',
'88' : 'KEY_KATAKANAHIRAGANA', '88': 'KEY_KATAKANAHIRAGANA',
'89' : 'KEY_YEN', '89': 'KEY_YEN',
'8a' : 'KEY_HENKAN', '8a': 'KEY_HENKAN',
'8b' : 'KEY_MUHENKAN', '8b': 'KEY_MUHENKAN',
'8c' : 'KEY_KPJPCOMMA', '8c': 'KEY_KPJPCOMMA',
'90' : 'KEY_HANGEUL', '90': 'KEY_HANGEUL',
'91' : 'KEY_HANJA', '91': 'KEY_HANJA',
'92' : 'KEY_KATAKANA', '92': 'KEY_KATAKANA',
'93' : 'KEY_HIRAGANA', '93': 'KEY_HIRAGANA',
'94' : 'KEY_ZENKAKUHANKAKU', '94': 'KEY_ZENKAKUHANKAKU',
'b6' : 'KEY_KPLEFTPAREN', 'b6': 'KEY_KPLEFTPAREN',
'b7' : 'KEY_KPRIGHTPAREN', 'b7': 'KEY_KPRIGHTPAREN',
'e0' : 'KEY_LEFTCTRL', 'e0': 'KEY_LEFTCTRL',
'e1' : 'KEY_LEFTSHIFT', 'e1': 'KEY_LEFTSHIFT',
'e2' : 'KEY_LEFTALT', 'e2': 'KEY_LEFTALT',
'e3' : 'KEY_LEFTMETA', 'e3': 'KEY_LEFTMETA',
'e4' : 'KEY_RIGHTCTRL', 'e4': 'KEY_RIGHTCTRL',
'e5' : 'KEY_RIGHTSHIFT', 'e5': 'KEY_RIGHTSHIFT',
'e6' : 'KEY_RIGHTALT', 'e6': 'KEY_RIGHTALT',
'e7' : 'KEY_RIGHTMETA', 'e7': 'KEY_RIGHTMETA',
'e8' : 'KEY_MEDIA_PLAYPAUSE', 'e8': 'KEY_MEDIA_PLAYPAUSE',
'e9' : 'KEY_MEDIA_STOPCD', 'e9': 'KEY_MEDIA_STOPCD',
'ea' : 'KEY_MEDIA_PREVIOUSSONG', 'ea': 'KEY_MEDIA_PREVIOUSSONG',
'eb' : 'KEY_MEDIA_NEXTSONG', 'eb': 'KEY_MEDIA_NEXTSONG',
'ec' : 'KEY_MEDIA_EJECTCD', 'ec': 'KEY_MEDIA_EJECTCD',
'ed' : 'KEY_MEDIA_VOLUMEUP', 'ed': 'KEY_MEDIA_VOLUMEUP',
'ee' : 'KEY_MEDIA_VOLUMEDOWN', 'ee': 'KEY_MEDIA_VOLUMEDOWN',
'ef' : 'KEY_MEDIA_MUTE', 'ef': 'KEY_MEDIA_MUTE',
'f0' : 'KEY_MEDIA_WWW', 'f0': 'KEY_MEDIA_WWW',
'f1' : 'KEY_MEDIA_BACK', 'f1': 'KEY_MEDIA_BACK',
'f2' : 'KEY_MEDIA_FORWARD', 'f2': 'KEY_MEDIA_FORWARD',
'f3' : 'KEY_MEDIA_STOP', 'f3': 'KEY_MEDIA_STOP',
'f4' : 'KEY_MEDIA_FIND', 'f4': 'KEY_MEDIA_FIND',
'f5' : 'KEY_MEDIA_SCROLLUP', 'f5': 'KEY_MEDIA_SCROLLUP',
'f6' : 'KEY_MEDIA_SCROLLDOWN', 'f6': 'KEY_MEDIA_SCROLLDOWN',
'f7' : 'KEY_MEDIA_EDIT', 'f7': 'KEY_MEDIA_EDIT',
'f8' : 'KEY_MEDIA_SLEEP', 'f8': 'KEY_MEDIA_SLEEP',
'f9' : 'KEY_MEDIA_COFFEE', 'f9': 'KEY_MEDIA_COFFEE',
'fa' : 'KEY_MEDIA_REFRESH', 'fa': 'KEY_MEDIA_REFRESH',
'fb' : 'KEY_MEDIA_CALC' 'fb': 'KEY_MEDIA_CALC',
} }

View File

@@ -23,7 +23,7 @@ def get_key(modifier: str, key: str) -> str:
class Keyboard: class Keyboard:
def __init__(self): # type: ignore def __init__(self): # type: ignore
self.report = [ self.report = [
[ # Bit array for Modifier keys [ # Bit array for Modifier keys
0, # Right GUI - (usually the Windows key) 0, # Right GUI - (usually the Windows key)
0, # Right ALT 0, # Right ALT
0, # Right Shift 0, # Right Shift
@@ -33,8 +33,8 @@ class Keyboard:
0, # Left Shift 0, # Left Shift
0, # Left Control 0, # Left Control
], ],
0x00, # Vendor reserved 0x00, # Vendor reserved
'', # Rest is space for 6 keys '', # Rest is space for 6 keys
'', '',
'', '',
'', '',
@@ -60,17 +60,27 @@ class Keyboard:
print(color('\tKeyboard Input Received', 'green', None, 'bold')) print(color('\tKeyboard Input Received', 'green', None, 'bold'))
print(color(f'Keys:', 'white', None, 'bold')) print(color(f'Keys:', 'white', None, 'bold'))
for i in range(1, 7): for i in range(1, 7):
print(color(f' Key{i}{" ":>8s}= ', 'cyan', None, 'bold'), self.report[i + 1]) print(
color(f' Key{i}{" ":>8s}= ', 'cyan', None, 'bold'), self.report[i + 1]
)
print(color(f'\nModifier Keys:', 'white', None, 'bold')) print(color(f'\nModifier Keys:', 'white', None, 'bold'))
print( print(
color(f' Left Ctrl : ', 'cyan'), f'{self.report[0][0] == 1!s:<5}', # type: ignore color(f' Left Ctrl : ', 'cyan'),
color(f' Left Shift : ', 'cyan'), f'{self.report[0][1] == 1!s:<5}', # type: ignore f'{self.report[0][0] == 1!s:<5}', # type: ignore
color(f' Left ALT : ', 'cyan'), f'{self.report[0][2] == 1!s:<5}', # type: ignore color(f' Left Shift : ', 'cyan'),
color(f' Left GUI : ', 'cyan'), f'{self.report[0][3] == 1!s:<5}\n', # type: ignore f'{self.report[0][1] == 1!s:<5}', # type: ignore
color(f' Right Ctrl : ', 'cyan'), f'{self.report[0][4] == 1!s:<5}', # type: ignore color(f' Left ALT : ', 'cyan'),
color(f' Right Shift : ', 'cyan'), f'{self.report[0][5] == 1!s:<5}', # type: ignore f'{self.report[0][2] == 1!s:<5}', # type: ignore
color(f' Right ALT : ', 'cyan'), f'{self.report[0][6] == 1!s:<5}', # type: ignore color(f' Left GUI : ', 'cyan'),
color(f' Right GUI : ', 'cyan'), f'{self.report[0][7] == 1!s:<5}', # type: ignore f'{self.report[0][3] == 1!s:<5}\n', # type: ignore
color(f' Right Ctrl : ', 'cyan'),
f'{self.report[0][4] == 1!s:<5}', # type: ignore
color(f' Right Shift : ', 'cyan'),
f'{self.report[0][5] == 1!s:<5}', # type: ignore
color(f' Right ALT : ', 'cyan'),
f'{self.report[0][6] == 1!s:<5}', # type: ignore
color(f' Right GUI : ', 'cyan'),
f'{self.report[0][7] == 1!s:<5}', # type: ignore
) )
@@ -78,7 +88,7 @@ class Keyboard:
class Mouse: class Mouse:
def __init__(self): # type: ignore def __init__(self): # type: ignore
self.report = [ self.report = [
[ # Bit array for Buttons [ # Bit array for Buttons
0, # Button 1 (primary/trigger 0, # Button 1 (primary/trigger
0, # Button 2 (secondary) 0, # Button 2 (secondary)
0, # Button 3 (tertiary) 0, # Button 3 (tertiary)
@@ -106,23 +116,32 @@ class Mouse:
def print_mouse_report(self) -> None: def print_mouse_report(self) -> None:
print(color('\tMouse Input Received', 'green', None, 'bold')) print(color('\tMouse Input Received', 'green', None, 'bold'))
print( print(
color(f' Button 1 (primary/trigger) = ', 'cyan'), self.report[0][0] == 1, # type: ignore color(f' Button 1 (primary/trigger) = ', 'cyan'),
color(f'\n Button 2 (secondary) = ', 'cyan'), self.report[0][1] == 1, # type: ignore self.report[0][0] == 1, # type: ignore
color(f'\n Button 3 (tertiary) = ', 'cyan'), self.report[0][2] == 1, # type: ignore color(f'\n Button 2 (secondary) = ', 'cyan'),
color(f'\n Button4 = ', 'cyan'), self.report[0][3] == 1, # type: ignore self.report[0][1] == 1, # type: ignore
color(f'\n Button5 = ', 'cyan'), self.report[0][4] == 1, # type: ignore color(f'\n Button 3 (tertiary) = ', 'cyan'),
color(f'\n X (X-axis displacement) = ', 'cyan'), self.report[1], self.report[0][2] == 1, # type: ignore
color(f'\n Y (Y-axis displacement) = ', 'cyan'), self.report[2], color(f'\n Button4 = ', 'cyan'),
color(f'\n Wheel = ', 'cyan'), self.report[3], self.report[0][3] == 1, # type: ignore
color(f'\n AC PAN = ', 'cyan'), self.report[4], color(f'\n Button5 = ', 'cyan'),
self.report[0][4] == 1, # type: ignore
color(f'\n X (X-axis displacement) = ', 'cyan'),
self.report[1],
color(f'\n Y (Y-axis displacement) = ', 'cyan'),
self.report[2],
color(f'\n Wheel = ', 'cyan'),
self.report[3],
color(f'\n AC PAN = ', 'cyan'),
self.report[4],
) )
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
class ReportParser: class ReportParser:
def parse_input_report(input_report: bytes) -> None: # type: ignore def parse_input_report(self, input_report: bytes) -> None: # type: ignore
report_id = input_report[0] report_id = input_report[0] # pylint: disable=unsubscriptable-object
report_length = len(input_report) report_length = len(input_report)
# Keyboard input report (report id = 1) # Keyboard input report (report id = 1)

View File

@@ -53,7 +53,7 @@ from hid_report_parser import ReportParser
SDP_HID_SERVICE_NAME_ATTRIBUTE_ID = 0x0100 SDP_HID_SERVICE_NAME_ATTRIBUTE_ID = 0x0100
SDP_HID_SERVICE_DESCRIPTION_ATTRIBUTE_ID = 0x0101 SDP_HID_SERVICE_DESCRIPTION_ATTRIBUTE_ID = 0x0101
SDP_HID_PROVIDER_NAME_ATTRIBUTE_ID = 0x0102 SDP_HID_PROVIDER_NAME_ATTRIBUTE_ID = 0x0102
SDP_HID_DEVICE_RELEASE_NUMBER_ATTRIBUTE_ID = 0x0200 # [DEPRECATED] SDP_HID_DEVICE_RELEASE_NUMBER_ATTRIBUTE_ID = 0x0200 # [DEPRECATED]
SDP_HID_PARSER_VERSION_ATTRIBUTE_ID = 0x0201 SDP_HID_PARSER_VERSION_ATTRIBUTE_ID = 0x0201
SDP_HID_DEVICE_SUBCLASS_ATTRIBUTE_ID = 0x0202 SDP_HID_DEVICE_SUBCLASS_ATTRIBUTE_ID = 0x0202
SDP_HID_COUNTRY_CODE_ATTRIBUTE_ID = 0x0203 SDP_HID_COUNTRY_CODE_ATTRIBUTE_ID = 0x0203
@@ -61,12 +61,12 @@ SDP_HID_VIRTUAL_CABLE_ATTRIBUTE_ID = 0x0204
SDP_HID_RECONNECT_INITIATE_ATTRIBUTE_ID = 0x0205 SDP_HID_RECONNECT_INITIATE_ATTRIBUTE_ID = 0x0205
SDP_HID_DESCRIPTOR_LIST_ATTRIBUTE_ID = 0x0206 SDP_HID_DESCRIPTOR_LIST_ATTRIBUTE_ID = 0x0206
SDP_HID_LANGID_BASE_LIST_ATTRIBUTE_ID = 0x0207 SDP_HID_LANGID_BASE_LIST_ATTRIBUTE_ID = 0x0207
SDP_HID_SDP_DISABLE_ATTRIBUTE_ID = 0x0208 # [DEPRECATED] SDP_HID_SDP_DISABLE_ATTRIBUTE_ID = 0x0208 # [DEPRECATED]
SDP_HID_BATTERY_POWER_ATTRIBUTE_ID = 0x0209 SDP_HID_BATTERY_POWER_ATTRIBUTE_ID = 0x0209
SDP_HID_REMOTE_WAKE_ATTRIBUTE_ID = 0x020A SDP_HID_REMOTE_WAKE_ATTRIBUTE_ID = 0x020A
SDP_HID_PROFILE_VERSION_ATTRIBUTE_ID = 0x020B # DEPRECATED] SDP_HID_PROFILE_VERSION_ATTRIBUTE_ID = 0x020B # DEPRECATED]
SDP_HID_SUPERVISION_TIMEOUT_ATTRIBUTE_ID = 0x020C SDP_HID_SUPERVISION_TIMEOUT_ATTRIBUTE_ID = 0x020C
SDP_HID_NORMALLY_CONNECTABLE_ATTRIBUTE_ID = 0x020D SDP_HID_NORMALLY_CONNECTABLE_ATTRIBUTE_ID = 0x020D
SDP_HID_BOOT_DEVICE_ATTRIBUTE_ID = 0x020E SDP_HID_BOOT_DEVICE_ATTRIBUTE_ID = 0x020E
SDP_HID_SSR_HOST_MAX_LATENCY_ATTRIBUTE_ID = 0x020F SDP_HID_SSR_HOST_MAX_LATENCY_ATTRIBUTE_ID = 0x020F
SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID = 0x0210 SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID = 0x0210
@@ -74,6 +74,7 @@ SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID = 0x0210
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
async def get_hid_device_sdp_record(device, connection): async def get_hid_device_sdp_record(device, connection):
# Connect to the SDP Server # Connect to the SDP Server
@@ -84,114 +85,187 @@ async def get_hid_device_sdp_record(device, connection):
else: else:
print(color('Failed to connect to SDP Server', 'red')) print(color('Failed to connect to SDP Server', 'red'))
# List BT HID Device service in the root browse group # List BT HID Device service in the root browse group
service_record_handles = await sdp_client.search_services( service_record_handles = await sdp_client.search_services(
[BT_HUMAN_INTERFACE_DEVICE_SERVICE] [BT_HUMAN_INTERFACE_DEVICE_SERVICE]
) )
if (len(service_record_handles) < 1): if len(service_record_handles) < 1:
await sdp_client.disconnect() await sdp_client.disconnect()
raise Exception(color(f'BT HID Device service not found on peer device!!!!','red')) raise Exception(
color(f'BT HID Device service not found on peer device!!!!', 'red')
)
# For BT_HUMAN_INTERFACE_DEVICE_SERVICE service, get all its attributes # For BT_HUMAN_INTERFACE_DEVICE_SERVICE service, get all its attributes
for service_record_handle in service_record_handles: for service_record_handle in service_record_handles:
attributes = await sdp_client.get_attributes( attributes = await sdp_client.get_attributes(
service_record_handle, [SDP_ALL_ATTRIBUTES_RANGE] service_record_handle, [SDP_ALL_ATTRIBUTES_RANGE]
) )
print( print(color(f'SERVICE {service_record_handle:04X} attributes:', 'yellow'))
color(f'SERVICE {service_record_handle:04X} attributes:', 'yellow') print(color(f'SDP attributes for HID device', 'magenta'))
)
print(color(f'SDP attributes for HID device','magenta'))
for attribute in attributes: for attribute in attributes:
if attribute.id == SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID : if attribute.id == SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID:
print(color(' Service Record Handle : ', 'cyan'), hex(attribute.value.value)) print(
color(' Service Record Handle : ', 'cyan'),
hex(attribute.value.value),
)
elif attribute.id == SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID : elif attribute.id == SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID:
print(color(' Service Class : ', 'cyan'), attribute.value.value[0].value) print(
color(' Service Class : ', 'cyan'), attribute.value.value[0].value
)
elif attribute.id == SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID : elif attribute.id == SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID:
print(color(' SDP Browse Group List : ', 'cyan'), attribute.value.value[0].value) print(
color(' SDP Browse Group List : ', 'cyan'),
attribute.value.value[0].value,
)
elif attribute.id == SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID : elif attribute.id == SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID:
print(color(' BT_L2CAP_PROTOCOL_ID : ', 'cyan'), attribute.value.value[0].value[0].value) print(
print(color(' PSM for Bluetooth HID Control channel : ', 'cyan'), hex(attribute.value.value[0].value[1].value)) color(' BT_L2CAP_PROTOCOL_ID : ', 'cyan'),
print(color(' BT_HIDP_PROTOCOL_ID : ', 'cyan'), attribute.value.value[1].value[0].value) attribute.value.value[0].value[0].value,
)
print(
color(' PSM for Bluetooth HID Control channel : ', 'cyan'),
hex(attribute.value.value[0].value[1].value),
)
print(
color(' BT_HIDP_PROTOCOL_ID : ', 'cyan'),
attribute.value.value[1].value[0].value,
)
elif attribute.id == SDP_LANGUAGE_BASE_ATTRIBUTE_ID_LIST_ATTRIBUTE_ID : elif attribute.id == SDP_LANGUAGE_BASE_ATTRIBUTE_ID_LIST_ATTRIBUTE_ID:
print(color(' Lanugage : ', 'cyan'), hex(attribute.value.value[0].value)) print(
print(color(' Encoding : ', 'cyan'), hex(attribute.value.value[1].value)) color(' Lanugage : ', 'cyan'), hex(attribute.value.value[0].value)
print(color(' PrimaryLanguageBaseID : ', 'cyan'), hex(attribute.value.value[2].value)) )
print(
color(' Encoding : ', 'cyan'), hex(attribute.value.value[1].value)
)
print(
color(' PrimaryLanguageBaseID : ', 'cyan'),
hex(attribute.value.value[2].value),
)
elif attribute.id == SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID : elif attribute.id == SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID:
print(color(' BT_HUMAN_INTERFACE_DEVICE_SERVICE ', 'cyan'), attribute.value.value[0].value[0].value) print(
print(color(' HID Profileversion number : ', 'cyan'), hex(attribute.value.value[0].value[1].value)) color(' BT_HUMAN_INTERFACE_DEVICE_SERVICE ', 'cyan'),
attribute.value.value[0].value[0].value,
)
print(
color(' HID Profileversion number : ', 'cyan'),
hex(attribute.value.value[0].value[1].value),
)
elif attribute.id == SDP_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID : elif attribute.id == SDP_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID:
print(color(' BT_L2CAP_PROTOCOL_ID : ', 'cyan'), attribute.value.value[0].value[0].value[0].value) print(
print(color(' PSM for Bluetooth HID Interrupt channel : ', 'cyan'), hex(attribute.value.value[0].value[0].value[1].value)) color(' BT_L2CAP_PROTOCOL_ID : ', 'cyan'),
print(color(' BT_HIDP_PROTOCOL_ID : ', 'cyan'), attribute.value.value[0].value[1].value[0].value) attribute.value.value[0].value[0].value[0].value,
)
print(
color(' PSM for Bluetooth HID Interrupt channel : ', 'cyan'),
hex(attribute.value.value[0].value[0].value[1].value),
)
print(
color(' BT_HIDP_PROTOCOL_ID : ', 'cyan'),
attribute.value.value[0].value[1].value[0].value,
)
elif attribute.id == SDP_HID_SERVICE_NAME_ATTRIBUTE_ID : elif attribute.id == SDP_HID_SERVICE_NAME_ATTRIBUTE_ID:
print(color(' Service Name: ', 'cyan'), attribute.value.value) print(color(' Service Name: ', 'cyan'), attribute.value.value)
elif attribute.id == SDP_HID_SERVICE_DESCRIPTION_ATTRIBUTE_ID : elif attribute.id == SDP_HID_SERVICE_DESCRIPTION_ATTRIBUTE_ID:
print(color(' Service Description: ', 'cyan'), attribute.value.value) print(color(' Service Description: ', 'cyan'), attribute.value.value)
elif attribute.id == SDP_HID_PROVIDER_NAME_ATTRIBUTE_ID : elif attribute.id == SDP_HID_PROVIDER_NAME_ATTRIBUTE_ID:
print(color(' Provider Name: ', 'cyan'), attribute.value.value) print(color(' Provider Name: ', 'cyan'), attribute.value.value)
elif attribute.id == SDP_HID_DEVICE_RELEASE_NUMBER_ATTRIBUTE_ID : elif attribute.id == SDP_HID_DEVICE_RELEASE_NUMBER_ATTRIBUTE_ID:
print(color(' Release Number: ', 'cyan'), hex(attribute.value.value)) print(color(' Release Number: ', 'cyan'), hex(attribute.value.value))
elif attribute.id == SDP_HID_PARSER_VERSION_ATTRIBUTE_ID : elif attribute.id == SDP_HID_PARSER_VERSION_ATTRIBUTE_ID:
print(color(' HID Parser Version: ', 'cyan'), hex(attribute.value.value)) print(
color(' HID Parser Version: ', 'cyan'), hex(attribute.value.value)
)
elif attribute.id == SDP_HID_DEVICE_SUBCLASS_ATTRIBUTE_ID : elif attribute.id == SDP_HID_DEVICE_SUBCLASS_ATTRIBUTE_ID:
print(color(' HIDDeviceSubclass: ', 'cyan'), hex(attribute.value.value)) print(
color(' HIDDeviceSubclass: ', 'cyan'), hex(attribute.value.value)
)
elif attribute.id == SDP_HID_COUNTRY_CODE_ATTRIBUTE_ID : elif attribute.id == SDP_HID_COUNTRY_CODE_ATTRIBUTE_ID:
print(color(' HIDCountryCode: ', 'cyan'), hex(attribute.value.value)) print(color(' HIDCountryCode: ', 'cyan'), hex(attribute.value.value))
elif attribute.id == SDP_HID_VIRTUAL_CABLE_ATTRIBUTE_ID : elif attribute.id == SDP_HID_VIRTUAL_CABLE_ATTRIBUTE_ID:
print(color(' HIDVirtualCable: ', 'cyan'), attribute.value.value) print(color(' HIDVirtualCable: ', 'cyan'), attribute.value.value)
elif attribute.id == SDP_HID_RECONNECT_INITIATE_ATTRIBUTE_ID : elif attribute.id == SDP_HID_RECONNECT_INITIATE_ATTRIBUTE_ID:
print(color(' HIDReconnectInitiate: ', 'cyan'), attribute.value.value) print(color(' HIDReconnectInitiate: ', 'cyan'), attribute.value.value)
elif attribute.id == SDP_HID_DESCRIPTOR_LIST_ATTRIBUTE_ID : elif attribute.id == SDP_HID_DESCRIPTOR_LIST_ATTRIBUTE_ID:
print(color(' HID Report Descriptor type: ', 'cyan'), hex(attribute.value.value[0].value[0].value)) print(
print(color(' HID Report DescriptorList: ', 'cyan'), attribute.value.value[0].value[1].value) color(' HID Report Descriptor type: ', 'cyan'),
hex(attribute.value.value[0].value[0].value),
)
print(
color(' HID Report DescriptorList: ', 'cyan'),
attribute.value.value[0].value[1].value,
)
elif attribute.id == SDP_HID_LANGID_BASE_LIST_ATTRIBUTE_ID : elif attribute.id == SDP_HID_LANGID_BASE_LIST_ATTRIBUTE_ID:
print(color(' HID LANGID Base Language: ', 'cyan'), hex(attribute.value.value[0].value[0].value)) print(
print(color(' HID LANGID Base Bluetooth String Offset: ', 'cyan'), hex(attribute.value.value[0].value[1].value)) color(' HID LANGID Base Language: ', 'cyan'),
hex(attribute.value.value[0].value[0].value),
)
print(
color(' HID LANGID Base Bluetooth String Offset: ', 'cyan'),
hex(attribute.value.value[0].value[1].value),
)
elif attribute.id == SDP_HID_BATTERY_POWER_ATTRIBUTE_ID : elif attribute.id == SDP_HID_BATTERY_POWER_ATTRIBUTE_ID:
print(color(' HIDBatteryPower: ', 'cyan'), attribute.value.value) print(color(' HIDBatteryPower: ', 'cyan'), attribute.value.value)
elif attribute.id == SDP_HID_REMOTE_WAKE_ATTRIBUTE_ID : elif attribute.id == SDP_HID_REMOTE_WAKE_ATTRIBUTE_ID:
print(color(' HIDRemoteWake: ', 'cyan'), attribute.value.value) print(color(' HIDRemoteWake: ', 'cyan'), attribute.value.value)
elif attribute.id == SDP_HID_PROFILE_VERSION_ATTRIBUTE_ID : elif attribute.id == SDP_HID_PROFILE_VERSION_ATTRIBUTE_ID:
print(color(' HIDProfileVersion : ', 'cyan'), hex(attribute.value.value)) print(
color(' HIDProfileVersion : ', 'cyan'), hex(attribute.value.value)
)
elif attribute.id == SDP_HID_SUPERVISION_TIMEOUT_ATTRIBUTE_ID : elif attribute.id == SDP_HID_SUPERVISION_TIMEOUT_ATTRIBUTE_ID:
print(color(' HIDSupervisionTimeout: ', 'cyan'), hex(attribute.value.value)) print(
color(' HIDSupervisionTimeout: ', 'cyan'),
hex(attribute.value.value),
)
elif attribute.id == SDP_HID_NORMALLY_CONNECTABLE_ATTRIBUTE_ID : elif attribute.id == SDP_HID_NORMALLY_CONNECTABLE_ATTRIBUTE_ID:
print(color(' HIDNormallyConnectable: ', 'cyan'), attribute.value.value) print(
color(' HIDNormallyConnectable: ', 'cyan'), attribute.value.value
)
elif attribute.id == SDP_HID_BOOT_DEVICE_ATTRIBUTE_ID : elif attribute.id == SDP_HID_BOOT_DEVICE_ATTRIBUTE_ID:
print(color(' HIDBootDevice: ', 'cyan'), attribute.value.value) print(color(' HIDBootDevice: ', 'cyan'), attribute.value.value)
elif attribute.id == SDP_HID_SSR_HOST_MAX_LATENCY_ATTRIBUTE_ID : elif attribute.id == SDP_HID_SSR_HOST_MAX_LATENCY_ATTRIBUTE_ID:
print(color(' HIDSSRHostMaxLatency: ', 'cyan'), hex(attribute.value.value)) print(
color(' HIDSSRHostMaxLatency: ', 'cyan'),
hex(attribute.value.value),
)
elif attribute.id == SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID : elif attribute.id == SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID:
print(color(' HIDSSRHostMinTimeout: ', 'cyan'), hex(attribute.value.value)) print(
color(' HIDSSRHostMinTimeout: ', 'cyan'),
hex(attribute.value.value),
)
else: else:
print(color(f' Warning: Attribute ID: {attribute.id} match not found.\n Attribute Info: {attribute}', 'yellow')) print(
color(
f' Warning: Attribute ID: {attribute.id} match not found.\n Attribute Info: {attribute}',
'yellow',
)
)
await sdp_client.disconnect() await sdp_client.disconnect()
@@ -217,26 +291,33 @@ async def main():
return return
def on_hid_data_cb(pdu): def on_hid_data_cb(pdu):
report_type = pdu[0] & 0x0f report_type = pdu[0] & 0x0F
if (len(pdu) == 1): if len(pdu) == 1:
print(color(f'Warning: No report received','yellow')) print(color(f'Warning: No report received', 'yellow'))
return return
report_length = len(pdu[1:]) report_length = len(pdu[1:])
report_id = pdu[1] report_id = pdu[1]
if (report_type != Message.ReportType.OTHER_REPORT): if report_type != Message.ReportType.OTHER_REPORT:
print(color(f' Report type = {report_type}, Report length = {report_length}, Report id = {report_id}', 'blue', None, 'bold')) print(
color(
f' Report type = {report_type}, Report length = {report_length}, Report id = {report_id}',
'blue',
None,
'bold',
)
)
if ((report_length <= 1) or (report_id == 0)): if (report_length <= 1) or (report_id == 0):
return return
if report_type == Message.ReportType.INPUT_REPORT: if report_type == Message.ReportType.INPUT_REPORT:
ReportParser.parse_input_report(pdu[1:]) #type: ignore ReportParser.parse_input_report(pdu[1:]) # type: ignore
async def handle_virtual_cable_unplug(): async def handle_virtual_cable_unplug():
await hid_host.disconnect_interrupt_channel() await hid_host.disconnect_interrupt_channel()
await hid_host.disconnect_control_channel() await hid_host.disconnect_control_channel()
await device.keystore.delete(target_address) #type: ignore await device.keystore.delete(target_address) # type: ignore
await connection.disconnect() await connection.disconnect()
def on_hid_virtual_cable_unplug_cb(): def on_hid_virtual_cable_unplug_cb():
@@ -282,7 +363,9 @@ async def main():
async def menu(): async def menu():
reader = await get_stream_reader(sys.stdin) reader = await get_stream_reader(sys.stdin)
while True: while True:
print("\n************************ HID Host Menu *****************************\n") print(
"\n************************ HID Host Menu *****************************\n"
)
print(" 1. Connect Control Channel") print(" 1. Connect Control Channel")
print(" 2. Connect Interrupt Channel") print(" 2. Connect Interrupt Channel")
print(" 3. Disconnect Control Channel") print(" 3. Disconnect Control Channel")
@@ -343,15 +426,17 @@ async def main():
if choice1 == '1': if choice1 == '1':
# data includes first octet as report id # data includes first octet as report id
data = bytearray([0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]) data = bytearray(
[0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]
)
hid_host.set_report(1, data) hid_host.set_report(1, data)
elif choice1 == '2': elif choice1 == '2':
data = bytearray([0x03, 0x01, 0x01]) data = bytearray([0x03, 0x01, 0x01])
hid_host.set_report(2, data) hid_host.set_report(2, data)
elif choice1 == '3': elif choice1 == '3':
data = bytearray([0x05, 0x01, 0x01, 0x01]) data = bytearray([0x05, 0x01, 0x01, 0x01])
hid_host.set_report(3, data) hid_host.set_report(3, data)
else: else:
@@ -382,11 +467,13 @@ async def main():
choice1 = choice1.decode('utf-8').strip() choice1 = choice1.decode('utf-8').strip()
if choice1 == '1': if choice1 == '1':
data = bytearray([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) data = bytearray(
[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
)
hid_host.send_data(data) hid_host.send_data(data)
elif choice1 == '2': elif choice1 == '2':
data = bytearray([0x03, 0x00, 0x0d, 0xfd, 0x00, 0x00]) data = bytearray([0x03, 0x00, 0x0D, 0xFD, 0x00, 0x00])
hid_host.send_data(data) hid_host.send_data(data)
else: else:
@@ -406,8 +493,12 @@ async def main():
print('Device not found or Device already unpaired.') print('Device not found or Device already unpaired.')
elif choice == '13': elif choice == '13':
peer_address = Address.from_string_for_transport(target_address, transport=BT_BR_EDR_TRANSPORT) peer_address = Address.from_string_for_transport(
connection = device.find_connection_by_bd_addr(peer_address, transport=BT_BR_EDR_TRANSPORT) target_address, transport=BT_BR_EDR_TRANSPORT
)
connection = device.find_connection_by_bd_addr(
peer_address, transport=BT_BR_EDR_TRANSPORT
)
if connection is not None: if connection is not None:
await connection.disconnect() await connection.disconnect()
else: else:
@@ -421,7 +512,9 @@ async def main():
print('Device not found or Device already unpaired.') print('Device not found or Device already unpaired.')
elif choice == '15': elif choice == '15':
connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT) connection = await device.connect(
target_address, transport=BT_BR_EDR_TRANSPORT
)
await connection.authenticate() await connection.authenticate()
await connection.encrypt() await connection.encrypt()
@@ -445,4 +538,3 @@ async def main():
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
asyncio.run(main()) asyncio.run(main())