blob: 29dacbe26cb14c15e850a31ae46c579005dcf757 [file] [log] [blame]
Joseph Hwang5d0dc642016-01-19 10:21:12 +08001# Copyright 2016 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""This module provides emulation of bluetooth HID devices."""
6
7import logging
8import sys
9import time
10
11from bluetooth_rn42 import RN42
12
13
14class BluetoothHIDException(Exception):
15 """A dummpy exception class for Bluetooth HID class."""
16 pass
17
18
19class BluetoothHID(RN42):
20 """A base bluetooth HID emulator class using RN-42 evaluation kit."""
21
22 # Suppoerted device types
23 KEYBOARD = 'KEYBOARD'
24 GAMEPAD = 'GAMEPAD'
25 MOUSE = 'MOUSE'
26 COMBO = 'COMBO'
27 JOYSTICK = 'JOYSTICK'
28
29 SEND_DELAY_SECS = 0.2 # Need to sleep for a short while otherwise
30 # the bits may get lost during transmission.
31
32 def __init__(self, device_type, authentication_mode,
33 send_delay=SEND_DELAY_SECS):
34 """Initialization of BluetoothHID
35
36 Args:
37 device_type: the device type for emulation
38 authentication_mode: the authentication mode
39 send_delay: wait a while after sending data
40 """
41 super(BluetoothHID, self).__init__()
42 self.device_type = device_type
43 self.send_delay = send_delay
44
45 # Enter command mode for configuration.
46 self.EnterCommandMode()
47
48 # Set HID as the service profile.
49 self.SetServiceProfileHID()
50
51 # Set the HID device type.
52 self.SetHIDDevice(device_type)
53
54 # Set authentication to the specified mode.
55 self.SetAuthenticationMode(authentication_mode)
56
57 # Set RN-42 to work as a slave.
58 self.SetSlaveMode()
59
60 # Enable the connection status message so that we could get the message
61 # of connection/disconnection status.
62 self.EnableConnectionStatusMessage()
63
64 # Reboot so that the configurations above take in effect.
65 self.Reboot()
66
67 # Should enter command mode again after reboot.
68 self.EnterCommandMode()
69
70 logging.info('A HID "%s" device is created successfully.', device_type)
71
72 def __del__(self):
73 self.Close()
74
75 def SetHIDDevice(self, device_type):
76 """Set HID device to the specified device type.
77
78 Args:
79 device_type: the HID device type to emulate
80 """
81 if device_type == self.KEYBOARD:
82 self.SetHIDKeyboard()
83 elif device_type == self.GAMEPAD:
84 self.SetHIDGamepad()
85 elif device_type == self.MOUSE:
86 self.SetHIDMouse()
87 elif device_type == self.COMBO:
88 self.SetHIDCombo()
89 elif device_type == self.JOYSTICK:
90 self.SetHIDJoystick()
91
92 def Send(self, data):
93 """Send HID reports to the remote host.
94
95 Args:
96 data: the data to send
97 """
98 raise NotImplementedError('An HID subclass must override this method')
99
100
101class BluetoothHIDKeyboard(BluetoothHID):
102 """A bluetooth HID keyboard emulator class."""
103
104 def __init__(self, authentication_mode):
105 """Initialization of BluetoothHIDKeyboard
106
107 Args:
108 authentication_mode: the authentication mode
109 """
110 super(BluetoothHIDKeyboard, self).__init__(BluetoothHID.KEYBOARD,
111 authentication_mode)
112
113 def Send(self, data):
114 """Send data to the remote host.
115
116 Args:
117 data: data to send to the remote host
118 """
119 # TODO(josephsih): should have a method to check the connection status.
120 # Currently, once RN-42 is connected to a remote host, all characters
121 # except chr(0) transmitted through the serial port are interpreted
122 # as characters to send to the remote host.
123 # TODO(josephsih): Will support special keys and modifier keys soon.
124 # Currently, only printable ASCII characters are supported.
125 logging.debug('HID device sending %r...', data)
126 self.SerialSendReceive(data, msg='BluetoothHID.Send')
127 time.sleep(self.send_delay)
128
129
130def _UsageAndExit():
131 """The usage of this module."""
132 print 'Usage: python bluetooth_hid.py remote_address text_to_send'
133 print 'Example:'
134 print ' python bluetooth_hid.py 6C:29:95:1A:D4:6F "echo hello world"'
135 exit(1)
136
137
138def DemoBluetoothHIDKeyboard(remote_address, chars):
139 """A simple demo of acting as a HID keyboard.
140
141 This simple demo works only after the HID device has already paired
142 with the remote device such that a link key has been exchanged. Then
143 the HID device could connect directly to the remote host without
144 pin code and sends the message.
145
146 A full flow would be letting a remote host pair with the HID device
147 with the pin code of the HID device. Thereafter, either the host or
148 the HID device could request to connect. This is out of the scope of
149 this simple demo.
150
151 Args:
152 remote_address: the bluetooth address of the target remote device
153 chars: the characters to send
154 """
155 print 'Creating an emulated bluetooth keyboard...'
156 keyboard = BluetoothHIDKeyboard(BluetoothHID.PIN_CODE_MODE)
157
158 print 'Connecting to the remote address %s...' % remote_address
159 try:
160 if keyboard.ConnectToRemoteAddress(remote_address):
161 for i in range(1, 11):
162 print 'Sending "%s" for the %dth time...' % (chars, i)
163 keyboard.Send(chars + ' ' + str(i))
164 else:
165 print 'Something is wrong. Not able to connect to the remote address.'
166 print 'Have you already paired RN-42 with the remote host?'
167 finally:
168 print 'Disconnecting...'
169 keyboard.Disconnect()
170
171 print 'Closing the keyboard...'
172 keyboard.Close()
173
174
175if __name__ == '__main__':
176 if len(sys.argv) != 3:
177 _UsageAndExit()
178
179 remote_host_address = sys.argv[1]
180 chars_to_send = sys.argv[2]
181
182 if len(remote_host_address.replace(':', '')) != 12:
183 print '"%s" is not a valid bluetooth address.' % remote_host_address
184 _UsageAndExit()
185
186 DemoBluetoothHIDKeyboard(remote_host_address, chars_to_send)