Expose Bluetooth LE HID emulation to Autotest

This patch adds the necessary framework to allow a Bluetooth LE kit to
be used in place of a Bluetooth BR/EDR kit, attempting to follow the
work done when adding a Bluetooth BR/EDR flow as closely as possible.

Note that Chameleon boards may only have one of the current kits
attached at a time, as we have not yet resolved the detection issue.

Also fix existing typo: gaamepad -> gamepad

BUG=chromium:752719
TEST=First, locate the source for chameleond on a Chameleon board, cd
there, and run the self-test with only a Bluefruit LE kit attached:
$ IFS="^^^";export PS1=`echo $PS1|sed s/w/W/`;unset IFS
$ cd \
/usr/lib/python2.7/site-packages/chameleond-0.0.2-py2.7.egg/chameleond/
$ python utils/bluetooth_bluefruitle.py
Check that it doesn't crash, prints sensible results.

Second, test that current functionality is not broken:
Run this code on the Chameleon board, with only RN-42 attached:
$ make && make remote-install CHAMELEON_HOST=$CHAMELEON_IP
Execute the non-flaky non-stress tests that use this code,
see that the tests pass:
$ test_that --board ${BOARD} --args "chameleon_host=${CHAMELEON_IP}" \
${DUT_IP} bluetooth_AdapterPairing.mouse \
bluetooth_AdapterPairing.mouse.pairing_twice \
bluetooth_AdapterHIDReports.mouse

Third, test that this actually WAI with future patches:
Apply CL:602900 and CL:639850 to your chroot.
Execute the non-flaky non-stress tests that use this code,
see that things work as much as possible without Autotest adjustments.
Note that only the Bluefruit LE Friend should be attached.
$ test_that --board ${BOARD} --args "chameleon_host=${CHAMELEON_IP}" \
${DUT_IP} bluetooth_AdapterPairing.le.mouse \
bluetooth_AdapterPairing.le.mouse.pairing_twice \
bluetooth_AdapterHIDReports.le.mouse

Change-Id: I885c63caf8bb80f9b04f88a736ead8ee8ebf1329
Reviewed-on: https://chromium-review.googlesource.com/611780
Commit-Ready: Alexander Lent <alent@google.com>
Tested-by: Alexander Lent <alent@google.com>
Reviewed-by: Shyh-In Hwang <josephsih@chromium.org>
diff --git a/chameleond/devices/bluetooth_hid_flow.py b/chameleond/devices/bluetooth_hid_flow.py
index b112eb5..a01588c 100644
--- a/chameleond/devices/bluetooth_hid_flow.py
+++ b/chameleond/devices/bluetooth_hid_flow.py
@@ -8,6 +8,7 @@
 from chameleond.devices import chameleon_device
 from chameleond.utils import common
 from chameleond.utils import serial_utils
+from chameleond.utils.bluetooth_bluefruitle import BluefruitLE
 from chameleond.utils.bluetooth_hid import BluetoothHIDMouse
 from chameleond.utils.bluetooth_peripheral_kit import PeripheralKit
 from chameleond.utils.bluetooth_rn42 import RN42
@@ -116,7 +117,7 @@
 
 
 class BluetoothHIDMouseFlow(BluetoothHIDFlow, BluetoothHIDMouse):
-  """A flow object that emulates a classic bluetooth mouse device."""
+  """A flow object that emulates a Bluetooth BR/EDR mouse device."""
 
   def __init__(self, port_id, usb_ctrl):
     """Initializes a BluetoothHIDMouseFlow object.
@@ -130,3 +131,20 @@
     # should be in BluetoothHID*, but that doesn't currently work due to cyclic
     # imports. Remove this when constants are moved to BluetoothHID.
     BluetoothHIDMouse.__init__(self, PeripheralKit.PIN_CODE_MODE, RN42)
+
+
+class BluetoothHOGMouseFlow(BluetoothHIDFlow, BluetoothHIDMouse):
+  """A flow object that emulates a Bluetooth Low Energy mouse device."""
+
+  def __init__(self, port_id, usb_ctrl):
+    """Initializes a BluetoothHOGMouseFlow object.
+
+    (HOG meaning HID over GATT)
+
+    Args:
+      port_id: the port id that represents the type of port used.
+      usb_ctrl: a USBController object that BluetoothHOGFlow references to.
+    """
+    BluetoothHIDFlow.__init__(self, port_id, 'BluetoothLEMouse', usb_ctrl)
+    BluetoothHIDMouse.__init__(self, PeripheralKit.SSP_JUST_WORK_MODE,
+                               BluefruitLE)
diff --git a/chameleond/drivers/fpga_tio.py b/chameleond/drivers/fpga_tio.py
index 58af41b..233f4cf 100644
--- a/chameleond/drivers/fpga_tio.py
+++ b/chameleond/drivers/fpga_tio.py
@@ -80,6 +80,7 @@
     usb_audio_ctrl = usb.USBAudioController()
     usb_hid_ctrl = usb.USBController('g_hid')
     bluetooth_hid_ctrl = usb.USBController('ftdi_sio')
+    # TODO(alent): Need a different controller for dual BT USB kits?
 
     self._devices = {
         ids.DP1: input_flow.DpInputFlow(ids.DP1, main_bus, fpga_ctrl),
@@ -101,6 +102,8 @@
             ids.USB_TOUCH, usb_hid_ctrl),
         ids.BLUETOOTH_HID_MOUSE: bluetooth_hid_flow.BluetoothHIDMouseFlow(
             ids.BLUETOOTH_HID_MOUSE, bluetooth_hid_ctrl),
+        ids.BLUETOOTH_HOG_MOUSE: bluetooth_hid_flow.BluetoothHOGMouseFlow(
+            ids.BLUETOOTH_HOG_MOUSE, bluetooth_hid_ctrl),
         ids.AVSYNC_PROBE: avsync_probe.AVSyncProbe(ids.AVSYNC_PROBE),
         ids.AUDIO_BOARD: audio_board.AudioBoard(ext_board_bus),
         ids.MOTOR_BOARD: motor_board.MotorBoard(ext_board_bus)
@@ -115,6 +118,8 @@
     self.audio_board = self._device_manager.GetChameleonDevice(ids.AUDIO_BOARD)
     self.bluetooth_mouse = self._device_manager.GetChameleonDevice(
         ids.BLUETOOTH_HID_MOUSE)
+    self.bluetooth_hog_mouse = self._device_manager.GetChameleonDevice(
+        ids.BLUETOOTH_HOG_MOUSE)
     self.avsync_probe = self._device_manager.GetChameleonDevice(
         ids.AVSYNC_PROBE)
     self.motor_board = self._device_manager.GetChameleonDevice(ids.MOTOR_BOARD)
diff --git a/chameleond/utils/ids.py b/chameleond/utils/ids.py
index c3a7544..791a794 100644
--- a/chameleond/utils/ids.py
+++ b/chameleond/utils/ids.py
@@ -23,6 +23,11 @@
 AVSYNC_PROBE = 17
 AUDIO_BOARD = 18
 MOTOR_BOARD = 19
+BLUETOOTH_HOG_KEYBOARD = 20
+BLUETOOTH_HOG_GAMEPAD = 21
+BLUETOOTH_HOG_MOUSE = 22
+BLUETOOTH_HOG_COMBO = 23
+BLUETOOTH_HOG_JOYSTICK = 24
 
 # device names
 DEVICE_NAMES = {
@@ -38,13 +43,18 @@
     USB_KEYBOARD: 'usb_keyboard',
     USB_TOUCH: 'usb_touch',
     BLUETOOTH_HID_KEYBOARD: 'bluetooth_hid_keyboard',
-    BLUETOOTH_HID_GAMEPAD: 'bluetooth_hid_gaamepad',
+    BLUETOOTH_HID_GAMEPAD: 'bluetooth_hid_gamepad',
     BLUETOOTH_HID_MOUSE: 'bluetooth_hid_mouse',
     BLUETOOTH_HID_COMBO: 'bluetooth_hid_combo',
     BLUETOOTH_HID_JOYSTICK: 'bluetooth_hid_joystick',
     AVSYNC_PROBE: 'avsync_probe',
     AUDIO_BOARD: 'audio_board',
-    MOTOR_BOARD: 'motor_board'
+    MOTOR_BOARD: 'motor_board',
+    BLUETOOTH_HOG_KEYBOARD: 'bluetooth_hog_keyboard',
+    BLUETOOTH_HOG_GAMEPAD: 'bluetooth_hog_gamepad',
+    BLUETOOTH_HOG_MOUSE: 'bluetooth_hog_mouse',
+    BLUETOOTH_HOG_COMBO: 'bluetooth_hog_combo',
+    BLUETOOTH_HOG_JOYSTICK: 'bluetooth_hog_joystick'
 }
 
 
@@ -69,6 +79,13 @@
                        BLUETOOTH_HID_COMBO,
                        BLUETOOTH_HID_JOYSTICK]
 
+# Ports that support BLUETOOTH HID over GATT (LE)
+BLUETOOTH_HOG_PORTS = [BLUETOOTH_HOG_KEYBOARD,
+                       BLUETOOTH_HOG_GAMEPAD,
+                       BLUETOOTH_HOG_MOUSE,
+                       BLUETOOTH_HOG_COMBO,
+                       BLUETOOTH_HOG_JOYSTICK]
+
 # Convenience methods
 IsInputPort = lambda port_id: port_id in INPUT_PORTS
 IsOutputPort = lambda port_id: port_id in OUTPUT_PORTS
@@ -77,6 +94,7 @@
 IsUSBAudioPort = lambda port_id: port_id in USB_AUDIO_PORTS
 IsUSBHIDPort = lambda port_id: port_id in USB_HID_PORTS
 IsBluetoothHIDPort = lambda port_id: port_id in BLUETOOTH_HID_PORTS
+IsBluetoothHOGPort = lambda port_id: port_id in BLUETOOTH_HOG_PORTS
 
 # IDs of EDIDs
 EDID_ID_DEFAULT = 0