# Copyright (c) 2013 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. import array import btsocket import fcntl import logging import socket import struct # Constants from lib/mgmt.h in BlueZ source MGMT_INDEX_NONE = 0xFFFF MGMT_HDR_SIZE = 6 MGMT_STATUS_SUCCESS = 0x00 MGMT_STATUS_UNKNOWN_COMMAND = 0x01 MGMT_STATUS_NOT_CONNECTED = 0x02 MGMT_STATUS_FAILED = 0x03 MGMT_STATUS_CONNECT_FAILED = 0x04 MGMT_STATUS_AUTH_FAILED = 0x05 MGMT_STATUS_NOT_PAIRED = 0x06 MGMT_STATUS_NO_RESOURCES = 0x07 MGMT_STATUS_TIMEOUT = 0x08 MGMT_STATUS_ALREADY_CONNECTED = 0x09 MGMT_STATUS_BUSY = 0x0a MGMT_STATUS_REJECTED = 0x0b MGMT_STATUS_NOT_SUPPORTED = 0x0c MGMT_STATUS_INVALID_PARAMS = 0x0d MGMT_STATUS_DISCONNECTED = 0x0e MGMT_STATUS_NOT_POWERED = 0x0f MGMT_STATUS_CANCELLED = 0x10 MGMT_STATUS_INVALID_INDEX = 0x11 MGMT_STATUS_RFKILLED = 0x12 MGMT_OP_READ_VERSION = 0x0001 MGMT_OP_READ_COMMANDS = 0x0002 MGMT_OP_READ_INDEX_LIST = 0x0003 MGMT_OP_READ_INFO = 0x0004 MGMT_OP_SET_POWERED = 0x0005 MGMT_OP_SET_DISCOVERABLE = 0x0006 MGMT_OP_SET_CONNECTABLE = 0x0007 MGMT_OP_SET_FAST_CONNECTABLE = 0x0008 MGMT_OP_SET_PAIRABLE = 0x0009 MGMT_OP_SET_LINK_SECURITY = 0x000A MGMT_OP_SET_SSP = 0x000B MGMT_OP_SET_HS = 0x000C MGMT_OP_SET_LE = 0x000D MGMT_OP_SET_DEV_CLASS = 0x000E MGMT_OP_SET_LOCAL_NAME = 0x000F MGMT_OP_ADD_UUID = 0x0010 MGMT_OP_REMOVE_UUID = 0x0011 MGMT_OP_LOAD_LINK_KEYS = 0x0012 MGMT_OP_LOAD_LONG_TERM_KEYS = 0x0013 MGMT_OP_DISCONNECT = 0x0014 MGMT_OP_GET_CONNECTIONS = 0x0015 MGMT_OP_PIN_CODE_REPLY = 0x0016 MGMT_OP_PIN_CODE_NEG_REPLY = 0x0017 MGMT_OP_SET_IO_CAPABILITY = 0x0018 MGMT_OP_PAIR_DEVICE = 0x0019 MGMT_OP_CANCEL_PAIR_DEVICE = 0x001A MGMT_OP_UNPAIR_DEVICE = 0x001B MGMT_OP_USER_CONFIRM_REPLY = 0x001C MGMT_OP_USER_CONFIRM_NEG_REPLY = 0x001D MGMT_OP_USER_PASSKEY_REPLY = 0x001E MGMT_OP_USER_PASSKEY_NEG_REPLY = 0x001F MGMT_OP_READ_LOCAL_OOB_DATA = 0x0020 MGMT_OP_ADD_REMOTE_OOB_DATA = 0x0021 MGMT_OP_REMOVE_REMOTE_OOB_DATA = 0x0022 MGMT_OP_START_DISCOVERY = 0x0023 MGMT_OP_STOP_DISCOVERY = 0x0024 MGMT_OP_CONFIRM_NAME = 0x0025 MGMT_OP_BLOCK_DEVICE = 0x0026 MGMT_OP_UNBLOCK_DEVICE = 0x0027 MGMT_OP_SET_DEVICE_ID = 0x0028 MGMT_OP_SET_ADVERTISING = 0x0029 MGMT_OP_SET_BREDR = 0x002A MGMT_OP_SET_STATIC_ADDRESS = 0x002B MGMT_OP_SET_SCAN_PARAMS = 0x002C MGMT_OP_SET_SECURE_CONN = 0x002D MGMT_OP_SET_DEBUG_KEYS = 0x002E MGMT_OP_SET_PRIVACY = 0x002F MGMT_OP_LOAD_IRKS = 0x0030 MGMT_OP_GET_CONN_INFO = 0x0031 MGMT_OP_GET_CLOCK_INFO = 0x0032 MGMT_OP_ADD_DEVICE = 0x0033 MGMT_OP_REMOVE_DEVICE = 0x0034 MGMT_OP_LOAD_CONN_PARAM = 0x0035 MGMT_OP_READ_UNCONF_INDEX_LIST = 0x0036 MGMT_OP_READ_CONFIG_INFO = 0x0037 MGMT_OP_SET_EXTERNAL_CONFIG = 0x0038 MGMT_OP_SET_PUBLIC_ADDRESS = 0x0039 MGMT_EV_CMD_COMPLETE = 0x0001 MGMT_EV_CMD_STATUS = 0x0002 MGMT_EV_CONTROLLER_ERROR = 0x0003 MGMT_EV_INDEX_ADDED = 0x0004 MGMT_EV_INDEX_REMOVED = 0x0005 MGMT_EV_NEW_SETTINGS = 0x0006 MGMT_EV_CLASS_OF_DEV_CHANGED = 0x0007 MGMT_EV_LOCAL_NAME_CHANGED = 0x0008 MGMT_EV_NEW_LINK_KEY = 0x0009 MGMT_EV_NEW_LONG_TERM_KEY = 0x000A MGMT_EV_DEVICE_CONNECTED = 0x000B MGMT_EV_DEVICE_DISCONNECTED = 0x000C MGMT_EV_CONNECT_FAILED = 0x000D MGMT_EV_PIN_CODE_REQUEST = 0x000E MGMT_EV_USER_CONFIRM_REQUEST = 0x000F MGMT_EV_USER_PASSKEY_REQUEST = 0x0010 MGMT_EV_AUTH_FAILED = 0x0011 MGMT_EV_DEVICE_FOUND = 0x0012 MGMT_EV_DISCOVERING = 0x0013 MGMT_EV_DEVICE_BLOCKED = 0x0014 MGMT_EV_DEVICE_UNBLOCKED = 0x0015 MGMT_EV_DEVICE_UNPAIRED = 0x0016 MGMT_EV_PASSKEY_NOTIFY = 0x0017 MGMT_EV_NEW_IRK = 0x0018 MGMT_EV_NEW_CSRK = 0x0019 MGMT_EV_DEVICE_ADDED = 0x001a MGMT_EV_DEVICE_REMOVED = 0x001b MGMT_EV_NEW_CONN_PARAM = 0x001c MGMT_EV_UNCONF_INDEX_ADDED = 0x001d MGMT_EV_UNCONF_INDEX_REMOVED = 0x001e MGMT_EV_NEW_CONFIG_OPTIONS = 0x001f # Settings returned by MGMT_OP_READ_INFO MGMT_SETTING_POWERED = 0x00000001 MGMT_SETTING_CONNECTABLE = 0x00000002 MGMT_SETTING_FAST_CONNECTABLE = 0x00000004 MGMT_SETTING_DISCOVERABLE = 0x00000008 MGMT_SETTING_PAIRABLE = 0x00000010 MGMT_SETTING_LINK_SECURITY = 0x00000020 MGMT_SETTING_SSP = 0x00000040 MGMT_SETTING_BREDR = 0x00000080 MGMT_SETTING_HS = 0x00000100 MGMT_SETTING_LE = 0x00000200 MGMT_SETTING_ADVERTISING = 0x00000400 MGMT_SETTING_SECURE_CONNECTIONS = 0x00000800 MGMT_SETTING_DEBUG_KEYS = 0x00001000 MGMT_SETTING_PRIVACY = 0x00002000 MGMT_SETTING_CONTROLLER_CONFIG = 0x00004000 # Options returned by MGMT_OP_READ_CONFIG_INFO MGMT_OPTION_EXTERNAL_CONFIG = 0x00000001 MGMT_OPTION_PUBLIC_ADDRESS = 0x00000002 # Disconnect reason returned in MGMT_EV_DEVICE_DISCONNECTED MGMT_DEV_DISCONN_UNKNOWN = 0x00 MGMT_DEV_DISCONN_TIMEOUT = 0x01 MGMT_DEV_DISCONN_LOCAL_HOST = 0x02 MGMT_DEV_DISCONN_REMOTE = 0x03 # Flags returned in MGMT_EV_DEVICE_FOUND MGMT_DEV_FOUND_CONFIRM_NAME = 0x01 MGMT_DEV_FOUND_LEGACY_PAIRING = 0x02 # EIR Data field types EIR_FLAGS = 0x01 EIR_UUID16_SOME = 0x02 EIR_UUID16_ALL = 0x03 EIR_UUID32_SOME = 0x04 EIR_UUID32_ALL = 0x05 EIR_UUID128_SOME = 0x06 EIR_UUID128_ALL = 0x07 EIR_NAME_SHORT = 0x08 EIR_NAME_COMPLETE = 0x09 EIR_TX_POWER = 0x0A EIR_CLASS_OF_DEV = 0x0D EIR_SSP_HASH = 0x0E EIR_SSP_RANDOMIZER = 0x0F EIR_DEVICE_ID = 0x10 EIR_GAP_APPEARANCE = 0x19 # Derived from lib/hci.h HCIGETDEVLIST = 0x800448d2 HCIGETDEVINFO = 0x800448d3 HCI_UP = 1 << 0 HCI_INIT = 1 << 1 HCI_RUNNING = 1 << 2 HCI_PSCAN = 1 << 3 HCI_ISCAN = 1 << 4 HCI_AUTH = 1 << 5 HCI_ENCRYPT = 1 << 6 HCI_INQUIRY = 1 << 7 HCI_RAW = 1 << 8 def parse_eir(eirdata): """Parse Bluetooth Extended Inquiry Result (EIR) data structuree. @param eirdata: Encoded eir data structure. @return Dictionary equivalent to the expanded structure keyed by EIR_* fields, with any data members parsed to useful formats. """ fields = {} pos = 0 while pos < len(eirdata): # Byte at the current position is the field length, which should be # zero at the end of the structure. (field_len,) = struct.unpack('B', buffer(eirdata, pos, 1)) if field_len == 0: break # Next byte is the field type, and the rest of the field is the data. # Note that the length field doesn't include itself so that's why the # offsets and lengths look a little odd. (field_type,) = struct.unpack('B', buffer(eirdata, pos + 1, 1)) data = eirdata[pos+2:pos+field_len+1] pos += field_len + 1 # Parse the individual fields to make the data meaningful. if field_type == EIR_NAME_SHORT or field_type == EIR_NAME_COMPLETE: data = data.rstrip('\0') # Place in the dictionary keyed by type. fields[field_type] = data return fields class BluetoothSocketError(Exception): """Error raised for general issues with BluetoothSocket.""" pass class BluetoothInvalidPacketError(Exception): """Error raised when an invalid packet is received from the socket.""" pass class BluetoothControllerError(Exception): """Error raised when the Controller Error event is received.""" pass class BluetoothSocket(btsocket.socket): """Bluetooth Socket. BluetoothSocket wraps the btsocket.socket() class, and thus the system socket.socket() class, to implement the necessary send and receive methods for the HCI Control and Monitor protocols (aka mgmt_ops) of the Linux Kernel. Instantiate either BluetoothControlSocket or BluetoothRawSocket rather than this class directly. See bluez/doc/mgmt_api.txt for details. """ def __init__(self): super(BluetoothSocket, self).__init__(family=btsocket.AF_BLUETOOTH, type=socket.SOCK_RAW, proto=btsocket.BTPROTO_HCI) self.events = [] def send_command(self, code, index, data=''): """Send a command to the socket. To send a command, wait for the reply event, and parse it use send_command_and_wait() instead. @param code: Command Code. @param index: Controller index, may be MGMT_INDEX_NONE. @param data: Parameters as bytearray or str (optional). """ # Send the command to the kernel msg = struct.pack(' 1) or ((version == 1) and (revision >= 5))) def read_version(self): """Read the version of the management interface. @return tuple (version, revision) on success, None on failure. """ (status, data) = self.send_command_and_wait( MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, expected_length=3) if status != MGMT_STATUS_SUCCESS: return None (version, revision) = struct.unpack_from('> 4, dev_type & 0x0f, features, pkt_type, link_policy, link_mode, acl_mtu, acl_pkts, sco_mtu, sco_pkts, err_rx, err_tx, cmd_tx, evt_rx, acl_tx, acl_rx, sco_tx, sco_rx, byte_rx, byte_tx)