1b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhovimport asyncore 2b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhovimport btsocket 3b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhovimport struct 4b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhovimport logging 5b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 6b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 7b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem RakhovBT_ATT_DEFAULT_LE_MTU = 23 8b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 9b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem RakhovBT_ATT_OP_MTU_REQ = 0x02 10b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem RakhovBT_ATT_OP_MTU_RSP = 0x03 11b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 12b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 13b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhovclass BluetoothGATTServerError(Exception): 14b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov """Error raised for GATT-related issues with BluetoothGATTServer.""" 15b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov pass 16b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 17b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 18b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhovclass BluetoothGATTServer(asyncore.dispatcher): 19b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov """Bluetooth GATT Server. 20b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 21b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov This class inherits asyncore.dispatcher to handle I/O on BLE socket. 22b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov It is essentially a socket service server. The class implements 23b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov initialization, read/write requests, notifications and indications. 24b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 25b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov Before creating an instance of this class, the machine must be set up 26b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov (with appropriate HCI commands) to be at least advertising, be powered, 27b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov and have LE turned on. 28b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 29b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov """ 30b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 31b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov def __init__(self, mtu=BT_ATT_DEFAULT_LE_MTU): 32b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov self.mtu = max(mtu, BT_ATT_DEFAULT_LE_MTU) 33b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 34b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov sock, addr = btsocket.create_le_gatt_server_socket() 35b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov logging.debug('incoming connection from address %s', addr) 36b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 37b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov asyncore.dispatcher.__init__(self, sock=sock) 38b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov asyncore.loop() 39b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 40b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 41b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov def exchange_mtu(self, data): 42b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov """Handle exchange MTU request. 43b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 44b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov Exchange MTU request/response usually initiates client-server 45b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov communication. The method sends exchange MTU response back to client. 46b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov It also sets value for MTU attribute. 47b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 48b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov @param data: Raw data received from socket (without opcode). 49b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 50b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov """ 51b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov if len(data) != 2: 52b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov raise BluetoothGATTServerError( 53b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 'Invalid MTU size: expected 2 bytes for Exchange MTU Request') 54b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 55b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov client_rx_mtu = struct.unpack('<H', data) 56b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov if client_rx_mtu < BT_ATT_DEFAULT_LE_MTU: 57b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov raise BluetoothGATTServerError('Invalid MTU size: %d < %d' % 58b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov client_rx_mtu, BT_ATT_DEFAULT_LE_MTU) 59b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 60b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov self.mtu = min(client_rx_mtu, self.mtu) 61b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 62b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov response = struct.pack('<BH', BT_ATT_OP_MTU_RSP, self.mtu) 63b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov self.send(response) 64b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 65b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 66b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov def handle_read(self): 67b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov """Receive and handle a single message from the socket. 68b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 69b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov This method gets called when the asynchronous loop detects that a read() 70b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov call on the channel's socket will succeed. It overrides handle_read() 71b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov from asyncore.dispatcher class. 72b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 73b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov """ 74b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov data = self.recv(self.mtu) 75b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 76b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov opcode = ord(data[0]) 77b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov data = data[1:] 78b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 79b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov func_map = {BT_ATT_OP_MTU_REQ: self.exchange_mtu} 80b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov if opcode not in func_map: 81b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov raise BluetoothGATTServerError('Invalid Opcode: %d' % opcode) 82b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov 83b0cdf3b3cdda00ac6c2683ec3ad3e8b9ceadbad9Artem Rakhov func_map[opcode](data) 84