Choices for socket use

138 views
Skip to first unread message

IanH

unread,
Oct 14, 2017, 4:30:46 PM10/14/17
to python-bleson
For reference, here's the background research I did on kernel APIs ---

So it seems Linux gives us a number of ways of actually talking to the kernel, with various pros and cons. The basic difference is the value set in the
addr.hci_channel field when calling bind() after creating a BTPROTO_HCI socket.

HCI_CHANNEL_RAW

This is the 'traditional' option, which is what both python-hcipy and bluepy.device are using at the minute. These let you send controller commands as defined in the Bluetooth spec (e.g. Set Scan Parameters) and ACL data to the device (e.g. GATT commands). Seems you can either do this via ioctl (hcipy) or reading/writing socket data (bluepy.device).

HCI_CHANNEL_CONTROL

This is used by BlueZ code when issuing 'management' commands. It's documented at e.g. https://github.com/pauloborges/bluez/blob/master/doc/mgmt-api.txt
It seems this has a Linux-specific command set which doesn't match up to anything in the Bluetooth spec. There's some background to why this was added at http://www.bluez.org/the-management-interface/

HCI_CHANNEL_USER

This is best described here: https://www.spinics.net/lists/linux-bluetooth/msg37345.html
Seems like this allows controller commands to be sent (like HCI_CHANNEL_RAW) but the kernel gives you exclusive access.


There might be advantages to using the newer HCI_CHANNEL_CONTROL and HCI_CHANNEL_USER methods, but there's a big problem that current Python's socket module doesn't let you get at it. The relevant code is in makesockaddr() in CPython's socketmodule.c -  see https://github.com/python/cpython/blob/master/Modules/socketmodule.c#L1276. - it only ever seems to set the dev and bdaddr members, leaving the hci_channel field set to 0 (HCI_CHANNEL_RAW).

We could conceivably submit a patch to a future version of CPython to fix this, or build our small own extension module with a better bind(), or try something slightly crazy using CTypes to load and call directly into the C library. None of these seem particularly appealing at the minute, so we might be best sticking with HCI_CHANNEL_RAW.
 

Wayne Keenan

unread,
Oct 15, 2017, 4:02:23 AM10/15/17
to python-bleson
thanks very much for this, I'd seen these references to these socket types in Sandeeps nodeJS C helper and hadn't gotten round to figuring them out.  
hcipy uses just one type and for the things that POC does it 'gets by', which obviously takes a risk that the wheels might come off down the road.


I think one of the as yet unwritten goals for bleson is to be pure python (and have no external dependancies), ideally.

IanH

unread,
Oct 22, 2017, 10:04:52 AM10/22/17
to python-bleson
For reference, there's one last way you can connect, which is what bluez is currently doing when making a connection to a peripheral:

* Create a socket with socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)
* bind() it using a struct sockaddr_l2 structure; the l2_bdaddr is our adapter's MAC address, and the l2_cid field is set to ATT_CID
* connect() it, again with struct sockaddr_l2; l2_bdaddr is the target device address, and l2_cid is again ATT_CID.

The whole ATT_CID thing seems a bit unofficial, to be honest - it's defined in the BlueZ sourcers. The kernel headers seem to have a L2CAP_CID_ATT definition, formerly known as L2CAP_CID_LE_DATA, with the same value.

However ... this doesn't help us. l2_cid isn't something we can set from Python - current socketmodule.c lets us set the l2_psm field, but not the l2_cid one.

So - after all that - we will just have to stick to HCI_CHANNEL_RAW for now.

Reply all
Reply to author
Forward
0 new messages