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