blob: 32b9a0095ea5a9687fa088e3a961f4cd9dfd959c [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""The control interface of Bluetooth HID flow module driver."""
import logging
import subprocess
from chameleond.devices import chameleon_device
from chameleond.utils import common
from chameleond.utils import serial_utils
from chameleond.utils import system_tools
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
class BluetoothHIDFlow(chameleon_device.Flow):
"""The control interface of bluetooth HID flow module driver."""
# Subclasses must override this DRIVER attribute
DRIVER = None
# TODO(crbug.com/763504): Can we lower detection time? Or maybe wait longer
# only when enabling the driver the first time, since the first detect was
# timing out.
# NOTE: This timeout was increased because the first detection after
# enabling the driver was taking too long. This may increase startup times
# by ~10+ seconds on Chameleons without a Bluetooth kit.
DETECT_TIMEOUT_SECS = 5 # the timeout in detection
DETECT_INTERVAL_SECS = 1 # the time to wait before retrying in detection
def __init__(self, port_id, connector_type, usb_ctrl, kit_vid_hex,
kit_pid_hex):
"""Initializes a BluetoothHIDFlow object.
Args:
port_id: the port id that represents the type of port used.
connector_type: the string obtained by GetConnectorType().
usb_ctrl: a USBController object that BluetoothHIDFlow references to.
serial_driver: the serial driver name for the kit
kit_vid_hex: The USB VID (Vendor ID) of the kit, as a hexadecimal string
kit_pid_hex: The USB PID (Product ID) of the kit, as a hexadecimal string
"""
self._port_id = port_id
self._connector_type = connector_type
self._usb_ctrl = usb_ctrl
self._tty = None
self._kit_vid_hex = kit_vid_hex
self._kit_pid_hex = kit_pid_hex
super(BluetoothHIDFlow, self).__init__()
def _FindAndSetTty(self):
self._tty = serial_utils.FindTtyByUsbVidPid(self._kit_vid_hex,
self._kit_pid_hex,
driver_name=self.DRIVER)
return self._tty
def IsUSBHostMode(self):
"""Check if the platform is in USB host mode.
Returns:
True if the platform is in USB host mode; otherwise, False.
"""
try:
pci_info = system_tools.SystemTools.Output('lspci', '-v')
except subprocess.CalledProcessError:
logging.info('Failed to use lspci')
return False
for line in pci_info.splitlines():
if 'xhci_hcd' in line:
logging.info('USB host mode: %s', line)
return True
logging.info('Not in USB host mode')
return False
def IsDetected(self):
"""Returns if the device can be detected."""
# Enables Bluetooth HID port controller.
# If the platform is 'chromeos' which always acts in the USB host mode,
# there is no need to enable the USB OTG driver.
if not self.IsUSBHostMode():
self._usb_ctrl.EnableUSBOTGDriver()
self._usb_ctrl.EnableDriver()
# Our Bluetooth HID flow differs substantially from other flows.
# Everything needed for IsDetected does the job of InitDevice:
# Initialize a TTY, and report detecting it instead of a driver.
# (Other USB flows simulate Plug/Unplug by Enabling/Diabling the driver.)
# Ultimately, this is reasonable given that we expect the kit to stay
# plugged in to chameleon, but Plug/Unplug for resetting the USB device
# might be useful.
# TODO(josephsih): Investigate plug/unplug for the Bluetooth HID Flow.
try:
common.WaitForCondition(lambda: bool(self._FindAndSetTty()), True,
self.DETECT_INTERVAL_SECS,
self.DETECT_TIMEOUT_SECS)
return True
except common.TimeoutError:
logging.warn("Did not detect bluetooth kit for %s before timing out.",
self.__class__.__name__)
return False
def InitDevice(self):
"""Init the tty of the kit attached to the chameleon board."""
logging.debug("InitDevice in Bluetooth HID Flow #%d is a no-op.",
self._port_id)
def Reset(self):
"""Reset chameleon device.
Do nothing for BlueoothHIDFlow.
"""
logging.debug('Bluetooth HID flow #%d: Reset() called.', self._port_id)
def Select(self):
"""Selects the USB HID flow."""
logging.debug('Selected Bluetooth HID flow #%d.', self._port_id)
def GetConnectorType(self):
"""Returns the human readable string for the connector type."""
return self._connector_type
def IsPhysicalPlugged(self):
"""Returns if the physical cable is plugged.
Always returns True.
"""
logging.debug('Bluetooth HID flow #%d: IsPhysicalPlugged() called.',
self._port_id)
return True
def IsPlugged(self):
"""Returns a Boolean value about the status of bluetooth hid serial driver.
Returns:
True if Bluetooth hid serial driver is enabled and the tty is found.
False otherwise.
"""
return self._usb_ctrl.DriverIsEnabled() and bool(self._tty)
def Plug(self):
"""Emulates plug by enabling the bluetooth hid serial driver."""
logging.debug('Bluetooth HID flow #%d: Plug() called.', self._port_id)
def Unplug(self):
"""Emulates unplug by disabling bluetooth hid serial driver.
Do nothing for BlueoothHIDFlow.
"""
logging.debug('Bluetooth HID flow #%d: Unplug() called.', self._port_id)
def DoFSM(self):
"""fpga_tio calls DoFSM after a flow is selected.
Do nothing for BlueoothHIDFlow.
"""
logging.debug('Bluetooth HID flow #%d: DoFSM() called.', self._port_id)
class BluetoothHIDMouseFlow(BluetoothHIDFlow, BluetoothHIDMouse):
"""A flow object that emulates a Bluetooth BR/EDR mouse device."""
DRIVER = RN42.DRIVER
def __init__(self, port_id, usb_ctrl):
"""Initializes a BluetoothHIDMouseFlow object.
Args:
port_id: the port id that represents the type of port used.
usb_ctrl: a USBController object that BluetoothHIDFlow references to.
"""
BluetoothHIDFlow.__init__(self, port_id, 'BluetoothBR/EDR', usb_ctrl,
RN42.USB_VID, RN42.USB_PID)
# TODO(josephsih): Ideally constants at this level of Bluetooth abstraction
# 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."""
DRIVER = BluefruitLE.DRIVER
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,
BluefruitLE.USB_VID, BluefruitLE.USB_PID)
BluetoothHIDMouse.__init__(self, PeripheralKit.SSP_JUST_WORK_MODE,
BluefruitLE)