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