Skip to content

Commit 14886eb

Browse files
pythongh-70145: Add support for channels in Bluetooth HCI protocol
1 parent 1d97488 commit 14886eb

4 files changed

Lines changed: 73 additions & 10 deletions

File tree

‎Doc/library/socket.rst‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,10 @@ created. Socket addresses are represented as follows:
156156

157157
- :const:`BTPROTO_HCI` accepts a format that depends on your OS.
158158

159-
- On Linux it accepts a tuple ``(device_id,)`` where ``device_id``
160-
is an integer specifying the number of the Bluetooth device.
159+
- On Linux it accepts a tuple ``(device_id, [channel])`` where ``device_id``
160+
is an integer specifying the number of the Bluetooth device,
161+
and ``channel`` is an optional integer specifying the HCI channel
162+
(:const:`HCI_CHANNEL_RAW` by default).
161163
- On FreeBSD, NetBSD and DragonFly BSD it accepts ``bdaddr`` where ``bdaddr``
162164
is a :class:`bytes` object containing the Bluetooth address in a
163165
string format. (ex. ``b'12:23:34:45:56:67'``)
@@ -168,6 +170,9 @@ created. Socket addresses are represented as follows:
168170
.. versionchanged:: 3.13.3
169171
FreeBSD support added.
170172

173+
.. versionchanged:: next
174+
Added ``channel`` field.
175+
171176
- :const:`BTPROTO_SCO` accepts ``bdaddr`` where ``bdaddr`` is a
172177
:class:`bytes` object containing the Bluetooth address in a
173178
string format. (ex. ``b'12:23:34:45:56:67'``)
@@ -678,6 +683,18 @@ Constants
678683
available on Linux and FreeBSD. :const:`!HCI_TIME_STAMP` and
679684
:const:`!HCI_DATA_DIR` are only available on Linux.
680685

686+
.. data:: HCI_CHANNEL_RAW
687+
HCI_CHANNEL_USER
688+
HCI_CHANNEL_MONITOR
689+
HCI_CHANNEL_CONTROL
690+
HCI_CHANNEL_LOGGING
691+
692+
Possible values for ``channel`` field in the :const:`BTPROTO_HCI` address.
693+
694+
.. availability:: Linux
695+
696+
.. versionadded:: next
697+
681698
.. data:: AF_QIPCRTR
682699

683700
Constant for Qualcomm's IPC router protocol, used to communicate with

‎Lib/test/test_socket.py‎

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2622,6 +2622,13 @@ def testBluetoothConstants(self):
26222622
socket.BTPROTO_L2CAP
26232623
socket.BTPROTO_SCO
26242624

2625+
if sys.platform == "linux":
2626+
socket.HCI_CHANNEL_RAW
2627+
socket.HCI_CHANNEL_USER
2628+
socket.HCI_CHANNEL_MONITOR
2629+
socket.HCI_CHANNEL_CONTROL
2630+
socket.HCI_CHANNEL_LOGGING
2631+
26252632
def testCreateRfcommSocket(self):
26262633
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM) as s:
26272634
pass
@@ -2717,13 +2724,14 @@ def testBadRfcommAddr(self):
27172724

27182725
@unittest.skipUnless(hasattr(socket, 'BTPROTO_HCI'), 'Bluetooth HCI sockets required for this test')
27192726
def testBindHciSocket(self):
2720-
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s:
2721-
if sys.platform.startswith(('netbsd', 'dragonfly', 'freebsd')):
2727+
if sys.platform.startswith(('netbsd', 'dragonfly', 'freebsd')):
2728+
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s:
27222729
s.bind(socket.BDADDR_ANY.encode())
27232730
addr = s.getsockname()
27242731
self.assertEqual(addr, socket.BDADDR_ANY)
2725-
else:
2726-
dev = 0
2732+
else:
2733+
dev = 0
2734+
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s:
27272735
try:
27282736
s.bind((dev,))
27292737
except OSError as err:
@@ -2733,6 +2741,26 @@ def testBindHciSocket(self):
27332741
addr = s.getsockname()
27342742
self.assertEqual(addr, dev)
27352743

2744+
with (self.subTest('channel=HCI_CHANNEL_RAW'),
2745+
socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s):
2746+
channel = socket.HCI_CHANNEL_RAW
2747+
s.bind((dev, channel))
2748+
addr = s.getsockname()
2749+
self.assertEqual(addr, dev)
2750+
2751+
with (self.subTest('channel=HCI_CHANNEL_USER'),
2752+
socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s):
2753+
channel = socket.HCI_CHANNEL_USER
2754+
try:
2755+
s.bind((dev, channel))
2756+
except OSError as err:
2757+
# Needs special permissions.
2758+
if err.errno in (errno.EPERM, errno.EBUSY, errno.ERFKILL):
2759+
self.skipTest(str(err))
2760+
raise
2761+
addr = s.getsockname()
2762+
self.assertEqual(addr, (dev, channel))
2763+
27362764
@unittest.skipUnless(hasattr(socket, 'BTPROTO_HCI'), 'Bluetooth HCI sockets required for this test')
27372765
def testBadHciAddr(self):
27382766
with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) as s:
@@ -2753,7 +2781,7 @@ def testBadHciAddr(self):
27532781
with self.assertRaises(OSError):
27542782
s.bind(())
27552783
with self.assertRaises(OSError):
2756-
s.bind((dev, 0))
2784+
s.bind((dev, socket.HCI_CHANNEL_RAW, 0, 0))
27572785
with self.assertRaises(OSError):
27582786
s.bind(dev)
27592787
with self.assertRaises(OSError):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add support for channels in Bluetooth HCI protocol
2+
(:const:`~socket.BTPROTO_HCI`).

‎Modules/socketmodule.c‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,7 +1541,14 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
15411541
struct sockaddr_hci *a = (struct sockaddr_hci *) addr;
15421542
#if defined(HAVE_BLUETOOTH_BLUETOOTH_H)
15431543
PyObject *ret = NULL;
1544-
ret = Py_BuildValue("i", _BT_HCI_MEMB(a, dev));
1544+
if (_BT_HCI_MEMB(a, channel) == HCI_CHANNEL_RAW) {
1545+
return Py_BuildValue("i", _BT_HCI_MEMB(a, dev));
1546+
}
1547+
else {
1548+
return Py_BuildValue("ii",
1549+
_BT_HCI_MEMB(a, dev),
1550+
_BT_HCI_MEMB(a, channel));
1551+
}
15451552
return ret;
15461553
#elif defined(__FreeBSD__)
15471554
const char *node = _BT_HCI_MEMB(a, node);
@@ -2138,13 +2145,15 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
21382145
memset(addr, 0, sizeof(struct sockaddr_hci));
21392146
_BT_HCI_MEMB(addr, family) = AF_BLUETOOTH;
21402147
#if defined(HAVE_BLUETOOTH_BLUETOOTH_H)
2141-
unsigned short dev = _BT_HCI_MEMB(addr, dev);
2142-
if (!PyArg_ParseTuple(args, "H", &dev)) {
2148+
unsigned short dev;
2149+
unsigned short channel = HCI_CHANNEL_RAW;
2150+
if (!PyArg_ParseTuple(args, "H|H", &dev, &channel)) {
21432151
PyErr_Format(PyExc_OSError,
21442152
"%s(): wrong format", caller);
21452153
return 0;
21462154
}
21472155
_BT_HCI_MEMB(addr, dev) = dev;
2156+
_BT_HCI_MEMB(addr, channel) = channel;
21482157
#elif defined(__FreeBSD__)
21492158
if (!PyBytes_Check(args)) {
21502159
PyErr_Format(PyExc_OSError, "%s: "
@@ -7875,6 +7884,13 @@ socket_exec(PyObject *m)
78757884
#ifdef BTPROTO_HCI
78767885
ADD_INT_MACRO(m, BTPROTO_HCI);
78777886
ADD_INT_MACRO(m, SOL_HCI);
7887+
#if defined(HCI_CHANNEL_RAW)
7888+
ADD_INT_MACRO(m, HCI_CHANNEL_RAW);
7889+
ADD_INT_MACRO(m, HCI_CHANNEL_USER);
7890+
ADD_INT_MACRO(m, HCI_CHANNEL_MONITOR);
7891+
ADD_INT_MACRO(m, HCI_CHANNEL_CONTROL);
7892+
ADD_INT_MACRO(m, HCI_CHANNEL_LOGGING);
7893+
#endif
78787894
#if defined(HCI_FILTER)
78797895
ADD_INT_MACRO(m, HCI_FILTER);
78807896
#endif

0 commit comments

Comments
 (0)