mirror of https://github.com/python/cpython
Issue #15359: Add CAN_BCM protocol support to the socket module. Patch by Brian
Thorne.
This commit is contained in:
parent
c44911f49a
commit
773e42dff8
|
@ -107,8 +107,8 @@ created. Socket addresses are represented as follows:
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
- Certain other address families (:const:`AF_BLUETOOTH`, :const:`AF_PACKET`)
|
- Certain other address families (:const:`AF_BLUETOOTH`, :const:`AF_PACKET`,
|
||||||
support specific representations.
|
:const:`AF_CAN`) support specific representations.
|
||||||
|
|
||||||
.. XXX document them!
|
.. XXX document them!
|
||||||
|
|
||||||
|
@ -257,6 +257,16 @@ The module :mod:`socket` exports the following constants and functions:
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. data:: CAN_BCM
|
||||||
|
CAN_BCM_*
|
||||||
|
|
||||||
|
CAN_BCM, in the CAN protocol family, is the broadcast manager (BCM) protocol.
|
||||||
|
Broadcast manager constants, documented in the Linux documentation, are also
|
||||||
|
defined in the socket module.
|
||||||
|
|
||||||
|
Availability: Linux >= 2.6.25.
|
||||||
|
|
||||||
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
.. data:: AF_RDS
|
.. data:: AF_RDS
|
||||||
PF_RDS
|
PF_RDS
|
||||||
|
@ -452,13 +462,16 @@ The module :mod:`socket` exports the following constants and functions:
|
||||||
:const:`AF_INET6`, :const:`AF_UNIX`, :const:`AF_CAN` or :const:`AF_RDS`. The
|
:const:`AF_INET6`, :const:`AF_UNIX`, :const:`AF_CAN` or :const:`AF_RDS`. The
|
||||||
socket type should be :const:`SOCK_STREAM` (the default),
|
socket type should be :const:`SOCK_STREAM` (the default),
|
||||||
:const:`SOCK_DGRAM`, :const:`SOCK_RAW` or perhaps one of the other ``SOCK_``
|
:const:`SOCK_DGRAM`, :const:`SOCK_RAW` or perhaps one of the other ``SOCK_``
|
||||||
constants. The protocol number is usually zero and may be omitted in that
|
constants. The protocol number is usually zero and may be omitted or in the
|
||||||
case or :const:`CAN_RAW` in case the address family is :const:`AF_CAN`.
|
case where the address family is :const:`AF_CAN` the protocol should be one
|
||||||
|
of :const:`CAN_RAW` or :const:`CAN_BCM`.
|
||||||
|
|
||||||
.. versionchanged:: 3.3
|
.. versionchanged:: 3.3
|
||||||
The AF_CAN family was added.
|
The AF_CAN family was added.
|
||||||
The AF_RDS family was added.
|
The AF_RDS family was added.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.4
|
||||||
|
The CAN_BCM protocol was added.
|
||||||
|
|
||||||
.. function:: socketpair([family[, type[, proto]]])
|
.. function:: socketpair([family[, type[, proto]]])
|
||||||
|
|
||||||
|
@ -1331,7 +1344,16 @@ the interface::
|
||||||
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
|
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
|
||||||
|
|
||||||
The last example shows how to use the socket interface to communicate to a CAN
|
The last example shows how to use the socket interface to communicate to a CAN
|
||||||
network. This example might require special priviledge::
|
network using the raw socket protocol. To use CAN with the broadcast
|
||||||
|
manager protocol instead, open a socket with::
|
||||||
|
|
||||||
|
socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_BCM)
|
||||||
|
|
||||||
|
After binding (:const:`CAN_RAW`) or connecting (:const:`CAN_BCM`) the socket, you
|
||||||
|
can use the :method:`socket.send`, and the :method:`socket.recv` operations (and
|
||||||
|
their counterparts) on the socket object as usual.
|
||||||
|
|
||||||
|
This example might require special priviledge::
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
|
|
|
@ -121,6 +121,36 @@ class SocketCANTest(unittest.TestCase):
|
||||||
interface = 'vcan0'
|
interface = 'vcan0'
|
||||||
bufsize = 128
|
bufsize = 128
|
||||||
|
|
||||||
|
"""The CAN frame structure is defined in <linux/can.h>:
|
||||||
|
|
||||||
|
struct can_frame {
|
||||||
|
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
|
||||||
|
__u8 can_dlc; /* data length code: 0 .. 8 */
|
||||||
|
__u8 data[8] __attribute__((aligned(8)));
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
can_frame_fmt = "=IB3x8s"
|
||||||
|
can_frame_size = struct.calcsize(can_frame_fmt)
|
||||||
|
|
||||||
|
"""The Broadcast Management Command frame structure is defined
|
||||||
|
in <linux/can/bcm.h>:
|
||||||
|
|
||||||
|
struct bcm_msg_head {
|
||||||
|
__u32 opcode;
|
||||||
|
__u32 flags;
|
||||||
|
__u32 count;
|
||||||
|
struct timeval ival1, ival2;
|
||||||
|
canid_t can_id;
|
||||||
|
__u32 nframes;
|
||||||
|
struct can_frame frames[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
`bcm_msg_head` must be 8 bytes aligned because of the `frames` member (see
|
||||||
|
`struct can_frame` definition). Must use native not standard types for packing.
|
||||||
|
"""
|
||||||
|
bcm_cmd_msg_fmt = "@3I4l2I"
|
||||||
|
bcm_cmd_msg_fmt += "x" * (struct.calcsize(bcm_cmd_msg_fmt) % 8)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.s = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
|
self.s = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
|
||||||
self.addCleanup(self.s.close)
|
self.addCleanup(self.s.close)
|
||||||
|
@ -1291,10 +1321,35 @@ class BasicCANTest(unittest.TestCase):
|
||||||
socket.PF_CAN
|
socket.PF_CAN
|
||||||
socket.CAN_RAW
|
socket.CAN_RAW
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(socket, "CAN_BCM"),
|
||||||
|
'socket.CAN_BCM required for this test.')
|
||||||
|
def testBCMConstants(self):
|
||||||
|
socket.CAN_BCM
|
||||||
|
|
||||||
|
# opcodes
|
||||||
|
socket.CAN_BCM_TX_SETUP # create (cyclic) transmission task
|
||||||
|
socket.CAN_BCM_TX_DELETE # remove (cyclic) transmission task
|
||||||
|
socket.CAN_BCM_TX_READ # read properties of (cyclic) transmission task
|
||||||
|
socket.CAN_BCM_TX_SEND # send one CAN frame
|
||||||
|
socket.CAN_BCM_RX_SETUP # create RX content filter subscription
|
||||||
|
socket.CAN_BCM_RX_DELETE # remove RX content filter subscription
|
||||||
|
socket.CAN_BCM_RX_READ # read properties of RX content filter subscription
|
||||||
|
socket.CAN_BCM_TX_STATUS # reply to TX_READ request
|
||||||
|
socket.CAN_BCM_TX_EXPIRED # notification on performed transmissions (count=0)
|
||||||
|
socket.CAN_BCM_RX_STATUS # reply to RX_READ request
|
||||||
|
socket.CAN_BCM_RX_TIMEOUT # cyclic message is absent
|
||||||
|
socket.CAN_BCM_RX_CHANGED # updated CAN frame (detected content change)
|
||||||
|
|
||||||
def testCreateSocket(self):
|
def testCreateSocket(self):
|
||||||
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
|
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(socket, "CAN_BCM"),
|
||||||
|
'socket.CAN_BCM required for this test.')
|
||||||
|
def testCreateBCMSocket(self):
|
||||||
|
with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_BCM) as s:
|
||||||
|
pass
|
||||||
|
|
||||||
def testBindAny(self):
|
def testBindAny(self):
|
||||||
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
|
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
|
||||||
s.bind(('', ))
|
s.bind(('', ))
|
||||||
|
@ -1327,19 +1382,8 @@ class BasicCANTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAVE_SOCKET_CAN, 'SocketCan required for this test.')
|
@unittest.skipUnless(HAVE_SOCKET_CAN, 'SocketCan required for this test.')
|
||||||
@unittest.skipUnless(thread, 'Threading required for this test.')
|
|
||||||
class CANTest(ThreadedCANSocketTest):
|
class CANTest(ThreadedCANSocketTest):
|
||||||
|
|
||||||
"""The CAN frame structure is defined in <linux/can.h>:
|
|
||||||
|
|
||||||
struct can_frame {
|
|
||||||
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
|
|
||||||
__u8 can_dlc; /* data length code: 0 .. 8 */
|
|
||||||
__u8 data[8] __attribute__((aligned(8)));
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
can_frame_fmt = "=IB3x8s"
|
|
||||||
|
|
||||||
def __init__(self, methodName='runTest'):
|
def __init__(self, methodName='runTest'):
|
||||||
ThreadedCANSocketTest.__init__(self, methodName=methodName)
|
ThreadedCANSocketTest.__init__(self, methodName=methodName)
|
||||||
|
|
||||||
|
@ -1388,6 +1432,46 @@ class CANTest(ThreadedCANSocketTest):
|
||||||
self.cf2 = self.build_can_frame(0x12, b'\x99\x22\x33')
|
self.cf2 = self.build_can_frame(0x12, b'\x99\x22\x33')
|
||||||
self.cli.send(self.cf2)
|
self.cli.send(self.cf2)
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(socket, "CAN_BCM"),
|
||||||
|
'socket.CAN_BCM required for this test.')
|
||||||
|
def _testBCM(self):
|
||||||
|
cf, addr = self.cli.recvfrom(self.bufsize)
|
||||||
|
self.assertEqual(self.cf, cf)
|
||||||
|
can_id, can_dlc, data = self.dissect_can_frame(cf)
|
||||||
|
self.assertEqual(self.can_id, can_id)
|
||||||
|
self.assertEqual(self.data, data)
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(socket, "CAN_BCM"),
|
||||||
|
'socket.CAN_BCM required for this test.')
|
||||||
|
def testBCM(self):
|
||||||
|
bcm = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_BCM)
|
||||||
|
self.addCleanup(bcm.close)
|
||||||
|
bcm.connect((self.interface,))
|
||||||
|
self.can_id = 0x123
|
||||||
|
self.data = bytes([0xc0, 0xff, 0xee])
|
||||||
|
self.cf = self.build_can_frame(self.can_id, self.data)
|
||||||
|
opcode = socket.CAN_BCM_TX_SEND
|
||||||
|
flags = 0
|
||||||
|
count = 0
|
||||||
|
ival1_seconds = ival1_usec = ival2_seconds = ival2_usec = 0
|
||||||
|
bcm_can_id = 0x0222
|
||||||
|
nframes = 1
|
||||||
|
assert len(self.cf) == 16
|
||||||
|
header = struct.pack(self.bcm_cmd_msg_fmt,
|
||||||
|
opcode,
|
||||||
|
flags,
|
||||||
|
count,
|
||||||
|
ival1_seconds,
|
||||||
|
ival1_usec,
|
||||||
|
ival2_seconds,
|
||||||
|
ival2_usec,
|
||||||
|
bcm_can_id,
|
||||||
|
nframes,
|
||||||
|
)
|
||||||
|
header_plus_frame = header + self.cf
|
||||||
|
bytes_sent = bcm.send(header_plus_frame)
|
||||||
|
self.assertEqual(bytes_sent, len(header_plus_frame))
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAVE_SOCKET_RDS, 'RDS sockets required for this test.')
|
@unittest.skipUnless(HAVE_SOCKET_RDS, 'RDS sockets required for this test.')
|
||||||
class BasicRDSTest(unittest.TestCase):
|
class BasicRDSTest(unittest.TestCase):
|
||||||
|
|
|
@ -235,6 +235,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #15359: Add CAN_BCM protocol support to the socket module. Patch by
|
||||||
|
Brian Thorne.
|
||||||
|
|
||||||
- Issue #16948: Fix quoted printable body encoding for non-latin1 character
|
- Issue #16948: Fix quoted printable body encoding for non-latin1 character
|
||||||
sets in the email package.
|
sets in the email package.
|
||||||
|
|
||||||
|
|
|
@ -1598,6 +1598,8 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
|
||||||
case AF_CAN:
|
case AF_CAN:
|
||||||
switch (s->sock_proto) {
|
switch (s->sock_proto) {
|
||||||
case CAN_RAW:
|
case CAN_RAW:
|
||||||
|
/* fall-through */
|
||||||
|
case CAN_BCM:
|
||||||
{
|
{
|
||||||
struct sockaddr_can *addr;
|
struct sockaddr_can *addr;
|
||||||
PyObject *interfaceName;
|
PyObject *interfaceName;
|
||||||
|
@ -6031,6 +6033,21 @@ PyInit__socket(void)
|
||||||
PyModule_AddIntConstant(m, "CAN_RAW_LOOPBACK", CAN_RAW_LOOPBACK);
|
PyModule_AddIntConstant(m, "CAN_RAW_LOOPBACK", CAN_RAW_LOOPBACK);
|
||||||
PyModule_AddIntConstant(m, "CAN_RAW_RECV_OWN_MSGS", CAN_RAW_RECV_OWN_MSGS);
|
PyModule_AddIntConstant(m, "CAN_RAW_RECV_OWN_MSGS", CAN_RAW_RECV_OWN_MSGS);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_LINUX_CAN_BCM_H
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM", CAN_BCM);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_TX_SETUP", TX_SETUP);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_TX_DELETE", TX_DELETE);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_TX_READ", TX_READ);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_TX_SEND", TX_SEND);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_RX_SETUP", RX_SETUP);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_RX_DELETE", RX_DELETE);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_RX_READ", RX_READ);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_TX_STATUS", TX_STATUS);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_TX_EXPIRED", TX_EXPIRED);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_RX_STATUS", RX_STATUS);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_RX_TIMEOUT", RX_TIMEOUT);
|
||||||
|
PyModule_AddIntConstant(m, "CAN_BCM_RX_CHANGED", RX_CHANGED);
|
||||||
|
#endif
|
||||||
#ifdef SOL_RDS
|
#ifdef SOL_RDS
|
||||||
PyModule_AddIntConstant(m, "SOL_RDS", SOL_RDS);
|
PyModule_AddIntConstant(m, "SOL_RDS", SOL_RDS);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -80,6 +80,10 @@ typedef int socklen_t;
|
||||||
#include <linux/can/raw.h>
|
#include <linux/can/raw.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_LINUX_CAN_BCM_H
|
||||||
|
#include <linux/can/bcm.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SYS_SYS_DOMAIN_H
|
#ifdef HAVE_SYS_SYS_DOMAIN_H
|
||||||
#include <sys/sys_domain.h>
|
#include <sys/sys_domain.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7224,7 +7224,7 @@ done
|
||||||
|
|
||||||
|
|
||||||
# On Linux, can.h and can/raw.h require sys/socket.h
|
# On Linux, can.h and can/raw.h require sys/socket.h
|
||||||
for ac_header in linux/can.h linux/can/raw.h
|
for ac_header in linux/can.h linux/can/raw.h linux/can/bcm.h
|
||||||
do :
|
do :
|
||||||
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
|
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
|
||||||
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "
|
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "
|
||||||
|
|
|
@ -1568,7 +1568,7 @@ AC_CHECK_HEADERS(linux/netlink.h,,,[
|
||||||
])
|
])
|
||||||
|
|
||||||
# On Linux, can.h and can/raw.h require sys/socket.h
|
# On Linux, can.h and can/raw.h require sys/socket.h
|
||||||
AC_CHECK_HEADERS(linux/can.h linux/can/raw.h,,,[
|
AC_CHECK_HEADERS(linux/can.h linux/can/raw.h linux/can/bcm.h,,,[
|
||||||
#ifdef HAVE_SYS_SOCKET_H
|
#ifdef HAVE_SYS_SOCKET_H
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -501,6 +501,9 @@
|
||||||
/* Define to 1 if you have the `linkat' function. */
|
/* Define to 1 if you have the `linkat' function. */
|
||||||
#undef HAVE_LINKAT
|
#undef HAVE_LINKAT
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <linux/can/bcm.h> header file. */
|
||||||
|
#undef HAVE_LINUX_CAN_BCM_H
|
||||||
|
|
||||||
/* Define to 1 if you have the <linux/can.h> header file. */
|
/* Define to 1 if you have the <linux/can.h> header file. */
|
||||||
#undef HAVE_LINUX_CAN_H
|
#undef HAVE_LINUX_CAN_H
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue