Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 773e42d

Browse files
committed
Issue #15359: Add CAN_BCM protocol support to the socket module. Patch by Brian
Thorne.
1 parent c44911f commit 773e42d

8 files changed

Lines changed: 151 additions & 18 deletions

File tree

Doc/library/socket.rst

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ created. Socket addresses are represented as follows:
107107

108108
.. versionadded:: 3.3
109109

110-
- Certain other address families (:const:`AF_BLUETOOTH`, :const:`AF_PACKET`)
111-
support specific representations.
110+
- Certain other address families (:const:`AF_BLUETOOTH`, :const:`AF_PACKET`,
111+
:const:`AF_CAN`) support specific representations.
112112

113113
.. XXX document them!
114114
@@ -257,6 +257,16 @@ The module :mod:`socket` exports the following constants and functions:
257257

258258
.. versionadded:: 3.3
259259

260+
.. data:: CAN_BCM
261+
CAN_BCM_*
262+
263+
CAN_BCM, in the CAN protocol family, is the broadcast manager (BCM) protocol.
264+
Broadcast manager constants, documented in the Linux documentation, are also
265+
defined in the socket module.
266+
267+
Availability: Linux >= 2.6.25.
268+
269+
.. versionadded:: 3.4
260270

261271
.. data:: AF_RDS
262272
PF_RDS
@@ -452,13 +462,16 @@ The module :mod:`socket` exports the following constants and functions:
452462
:const:`AF_INET6`, :const:`AF_UNIX`, :const:`AF_CAN` or :const:`AF_RDS`. The
453463
socket type should be :const:`SOCK_STREAM` (the default),
454464
:const:`SOCK_DGRAM`, :const:`SOCK_RAW` or perhaps one of the other ``SOCK_``
455-
constants. The protocol number is usually zero and may be omitted in that
456-
case or :const:`CAN_RAW` in case the address family is :const:`AF_CAN`.
465+
constants. The protocol number is usually zero and may be omitted or in the
466+
case where the address family is :const:`AF_CAN` the protocol should be one
467+
of :const:`CAN_RAW` or :const:`CAN_BCM`.
457468

458469
.. versionchanged:: 3.3
459470
The AF_CAN family was added.
460471
The AF_RDS family was added.
461472

473+
.. versionchanged:: 3.4
474+
The CAN_BCM protocol was added.
462475

463476
.. function:: socketpair([family[, type[, proto]]])
464477

@@ -1331,7 +1344,16 @@ the interface::
13311344
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
13321345

13331346
The last example shows how to use the socket interface to communicate to a CAN
1334-
network. This example might require special priviledge::
1347+
network using the raw socket protocol. To use CAN with the broadcast
1348+
manager protocol instead, open a socket with::
1349+
1350+
socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_BCM)
1351+
1352+
After binding (:const:`CAN_RAW`) or connecting (:const:`CAN_BCM`) the socket, you
1353+
can use the :method:`socket.send`, and the :method:`socket.recv` operations (and
1354+
their counterparts) on the socket object as usual.
1355+
1356+
This example might require special priviledge::
13351357

13361358
import socket
13371359
import struct

Lib/test/test_socket.py

Lines changed: 95 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,36 @@ class SocketCANTest(unittest.TestCase):
121121
interface = 'vcan0'
122122
bufsize = 128
123123

124+
"""The CAN frame structure is defined in <linux/can.h>:
125+
126+
struct can_frame {
127+
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
128+
__u8 can_dlc; /* data length code: 0 .. 8 */
129+
__u8 data[8] __attribute__((aligned(8)));
130+
};
131+
"""
132+
can_frame_fmt = "=IB3x8s"
133+
can_frame_size = struct.calcsize(can_frame_fmt)
134+
135+
"""The Broadcast Management Command frame structure is defined
136+
in <linux/can/bcm.h>:
137+
138+
struct bcm_msg_head {
139+
__u32 opcode;
140+
__u32 flags;
141+
__u32 count;
142+
struct timeval ival1, ival2;
143+
canid_t can_id;
144+
__u32 nframes;
145+
struct can_frame frames[0];
146+
}
147+
148+
`bcm_msg_head` must be 8 bytes aligned because of the `frames` member (see
149+
`struct can_frame` definition). Must use native not standard types for packing.
150+
"""
151+
bcm_cmd_msg_fmt = "@3I4l2I"
152+
bcm_cmd_msg_fmt += "x" * (struct.calcsize(bcm_cmd_msg_fmt) % 8)
153+
124154
def setUp(self):
125155
self.s = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
126156
self.addCleanup(self.s.close)
@@ -1291,10 +1321,35 @@ def testCrucialConstants(self):
12911321
socket.PF_CAN
12921322
socket.CAN_RAW
12931323

1324+
@unittest.skipUnless(hasattr(socket, "CAN_BCM"),
1325+
'socket.CAN_BCM required for this test.')
1326+
def testBCMConstants(self):
1327+
socket.CAN_BCM
1328+
1329+
# opcodes
1330+
socket.CAN_BCM_TX_SETUP # create (cyclic) transmission task
1331+
socket.CAN_BCM_TX_DELETE # remove (cyclic) transmission task
1332+
socket.CAN_BCM_TX_READ # read properties of (cyclic) transmission task
1333+
socket.CAN_BCM_TX_SEND # send one CAN frame
1334+
socket.CAN_BCM_RX_SETUP # create RX content filter subscription
1335+
socket.CAN_BCM_RX_DELETE # remove RX content filter subscription
1336+
socket.CAN_BCM_RX_READ # read properties of RX content filter subscription
1337+
socket.CAN_BCM_TX_STATUS # reply to TX_READ request
1338+
socket.CAN_BCM_TX_EXPIRED # notification on performed transmissions (count=0)
1339+
socket.CAN_BCM_RX_STATUS # reply to RX_READ request
1340+
socket.CAN_BCM_RX_TIMEOUT # cyclic message is absent
1341+
socket.CAN_BCM_RX_CHANGED # updated CAN frame (detected content change)
1342+
12941343
def testCreateSocket(self):
12951344
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
12961345
pass
12971346

1347+
@unittest.skipUnless(hasattr(socket, "CAN_BCM"),
1348+
'socket.CAN_BCM required for this test.')
1349+
def testCreateBCMSocket(self):
1350+
with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_BCM) as s:
1351+
pass
1352+
12981353
def testBindAny(self):
12991354
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
13001355
s.bind(('', ))
@@ -1327,19 +1382,8 @@ def testFilter(self):
13271382

13281383

13291384
@unittest.skipUnless(HAVE_SOCKET_CAN, 'SocketCan required for this test.')
1330-
@unittest.skipUnless(thread, 'Threading required for this test.')
13311385
class CANTest(ThreadedCANSocketTest):
13321386

1333-
"""The CAN frame structure is defined in <linux/can.h>:
1334-
1335-
struct can_frame {
1336-
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
1337-
__u8 can_dlc; /* data length code: 0 .. 8 */
1338-
__u8 data[8] __attribute__((aligned(8)));
1339-
};
1340-
"""
1341-
can_frame_fmt = "=IB3x8s"
1342-
13431387
def __init__(self, methodName='runTest'):
13441388
ThreadedCANSocketTest.__init__(self, methodName=methodName)
13451389

@@ -1388,6 +1432,46 @@ def _testSendMultiFrames(self):
13881432
self.cf2 = self.build_can_frame(0x12, b'\x99\x22\x33')
13891433
self.cli.send(self.cf2)
13901434

1435+
@unittest.skipUnless(hasattr(socket, "CAN_BCM"),
1436+
'socket.CAN_BCM required for this test.')
1437+
def _testBCM(self):
1438+
cf, addr = self.cli.recvfrom(self.bufsize)
1439+
self.assertEqual(self.cf, cf)
1440+
can_id, can_dlc, data = self.dissect_can_frame(cf)
1441+
self.assertEqual(self.can_id, can_id)
1442+
self.assertEqual(self.data, data)
1443+
1444+
@unittest.skipUnless(hasattr(socket, "CAN_BCM"),
1445+
'socket.CAN_BCM required for this test.')
1446+
def testBCM(self):
1447+
bcm = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_BCM)
1448+
self.addCleanup(bcm.close)
1449+
bcm.connect((self.interface,))
1450+
self.can_id = 0x123
1451+
self.data = bytes([0xc0, 0xff, 0xee])
1452+
self.cf = self.build_can_frame(self.can_id, self.data)
1453+
opcode = socket.CAN_BCM_TX_SEND
1454+
flags = 0
1455+
count = 0
1456+
ival1_seconds = ival1_usec = ival2_seconds = ival2_usec = 0
1457+
bcm_can_id = 0x0222
1458+
nframes = 1
1459+
assert len(self.cf) == 16
1460+
header = struct.pack(self.bcm_cmd_msg_fmt,
1461+
opcode,
1462+
flags,
1463+
count,
1464+
ival1_seconds,
1465+
ival1_usec,
1466+
ival2_seconds,
1467+
ival2_usec,
1468+
bcm_can_id,
1469+
nframes,
1470+
)
1471+
header_plus_frame = header + self.cf
1472+
bytes_sent = bcm.send(header_plus_frame)
1473+
self.assertEqual(bytes_sent, len(header_plus_frame))
1474+
13911475

13921476
@unittest.skipUnless(HAVE_SOCKET_RDS, 'RDS sockets required for this test.')
13931477
class BasicRDSTest(unittest.TestCase):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ Core and Builtins
235235
Library
236236
-------
237237

238+
- Issue #15359: Add CAN_BCM protocol support to the socket module. Patch by
239+
Brian Thorne.
240+
238241
- Issue #16948: Fix quoted printable body encoding for non-latin1 character
239242
sets in the email package.
240243

Modules/socketmodule.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1598,6 +1598,8 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
15981598
case AF_CAN:
15991599
switch (s->sock_proto) {
16001600
case CAN_RAW:
1601+
/* fall-through */
1602+
case CAN_BCM:
16011603
{
16021604
struct sockaddr_can *addr;
16031605
PyObject *interfaceName;
@@ -6031,6 +6033,21 @@ PyInit__socket(void)
60316033
PyModule_AddIntConstant(m, "CAN_RAW_LOOPBACK", CAN_RAW_LOOPBACK);
60326034
PyModule_AddIntConstant(m, "CAN_RAW_RECV_OWN_MSGS", CAN_RAW_RECV_OWN_MSGS);
60336035
#endif
6036+
#ifdef HAVE_LINUX_CAN_BCM_H
6037+
PyModule_AddIntConstant(m, "CAN_BCM", CAN_BCM);
6038+
PyModule_AddIntConstant(m, "CAN_BCM_TX_SETUP", TX_SETUP);
6039+
PyModule_AddIntConstant(m, "CAN_BCM_TX_DELETE", TX_DELETE);
6040+
PyModule_AddIntConstant(m, "CAN_BCM_TX_READ", TX_READ);
6041+
PyModule_AddIntConstant(m, "CAN_BCM_TX_SEND", TX_SEND);
6042+
PyModule_AddIntConstant(m, "CAN_BCM_RX_SETUP", RX_SETUP);
6043+
PyModule_AddIntConstant(m, "CAN_BCM_RX_DELETE", RX_DELETE);
6044+
PyModule_AddIntConstant(m, "CAN_BCM_RX_READ", RX_READ);
6045+
PyModule_AddIntConstant(m, "CAN_BCM_TX_STATUS", TX_STATUS);
6046+
PyModule_AddIntConstant(m, "CAN_BCM_TX_EXPIRED", TX_EXPIRED);
6047+
PyModule_AddIntConstant(m, "CAN_BCM_RX_STATUS", RX_STATUS);
6048+
PyModule_AddIntConstant(m, "CAN_BCM_RX_TIMEOUT", RX_TIMEOUT);
6049+
PyModule_AddIntConstant(m, "CAN_BCM_RX_CHANGED", RX_CHANGED);
6050+
#endif
60346051
#ifdef SOL_RDS
60356052
PyModule_AddIntConstant(m, "SOL_RDS", SOL_RDS);
60366053
#endif

Modules/socketmodule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ typedef int socklen_t;
8080
#include <linux/can/raw.h>
8181
#endif
8282

83+
#ifdef HAVE_LINUX_CAN_BCM_H
84+
#include <linux/can/bcm.h>
85+
#endif
86+
8387
#ifdef HAVE_SYS_SYS_DOMAIN_H
8488
#include <sys/sys_domain.h>
8589
#endif

configure

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7224,7 +7224,7 @@ done
72247224

72257225

72267226
# On Linux, can.h and can/raw.h require sys/socket.h
7227-
for ac_header in linux/can.h linux/can/raw.h
7227+
for ac_header in linux/can.h linux/can/raw.h linux/can/bcm.h
72287228
do :
72297229
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
72307230
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1568,7 +1568,7 @@ AC_CHECK_HEADERS(linux/netlink.h,,,[
15681568
])
15691569

15701570
# On Linux, can.h and can/raw.h require sys/socket.h
1571-
AC_CHECK_HEADERS(linux/can.h linux/can/raw.h,,,[
1571+
AC_CHECK_HEADERS(linux/can.h linux/can/raw.h linux/can/bcm.h,,,[
15721572
#ifdef HAVE_SYS_SOCKET_H
15731573
#include <sys/socket.h>
15741574
#endif

pyconfig.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,9 @@
501501
/* Define to 1 if you have the `linkat' function. */
502502
#undef HAVE_LINKAT
503503

504+
/* Define to 1 if you have the <linux/can/bcm.h> header file. */
505+
#undef HAVE_LINUX_CAN_BCM_H
506+
504507
/* Define to 1 if you have the <linux/can.h> header file. */
505508
#undef HAVE_LINUX_CAN_H
506509

0 commit comments

Comments
 (0)