mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-29 13:53:33 +00:00
linux-can-next-for-5.20-20220703
-----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEBsvAIBsPu6mG7thcrX5LkNig010FAmLBZ3sTHG1rbEBwZW5n dXRyb25peC5kZQAKCRCtfkuQ2KDTXe2oB/wJ1T3/1tZ0+Jx7Hk84MXUTO9W05KqH sie+yehu9A5+RNlA6/zRJBEmA4pyvOjtN0qynh3f25LfIesxUC5Tma1Epzp/Gnay cYBrypD2iKKMNpJoiBtVVXh6Kin7+5oSLr4zxeaYcXjGfUlPIOqMnrwbGu/5ZtMH 7FL092tkQ/S1j/LDTiZk+OdjpuaM9D/7Z0t0iJ6NJb70/VEk6K3bY5XdmjLayYIJ IBmxBbUXvBevVRDiSyMqNzA+RzQ5egrTrE3gPZZPneISN3Qu6cG+5xUqYGOxyjrJ hzinoC48wEEHEMAeiM28AXjv9voIPvSoqeOvgZmSS0JTkfPZqirYpdzd =9458 -----END PGP SIGNATURE----- Merge tag 'linux-can-next-for-5.20-20220703' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next Marc Kleine-Budde says: ==================== pull-request: can-next 2022-07-03 this is a pull request of 15 patches for net-next/master. The first 2 patches are by Max Staudt and add the can327 serial CAN driver along with a new line discipline ID. The next patch is by me an fixes a typo in the ctucanfd driver. The last 12 patches are by Dario Binacchi and integrate slcan CAN serial driver better into the existing CAN driver API. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
a48e789dd2
14 changed files with 2037 additions and 110 deletions
331
Documentation/networking/device_drivers/can/can327.rst
Normal file
331
Documentation/networking/device_drivers/can/can327.rst
Normal file
|
@ -0,0 +1,331 @@
|
|||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
|
||||
|
||||
can327: ELM327 driver for Linux SocketCAN
|
||||
==========================================
|
||||
|
||||
Authors
|
||||
--------
|
||||
|
||||
Max Staudt <max@enpas.org>
|
||||
|
||||
|
||||
|
||||
Motivation
|
||||
-----------
|
||||
|
||||
This driver aims to lower the initial cost for hackers interested in
|
||||
working with CAN buses.
|
||||
|
||||
CAN adapters are expensive, few, and far between.
|
||||
ELM327 interfaces are cheap and plentiful.
|
||||
Let's use ELM327s as CAN adapters.
|
||||
|
||||
|
||||
|
||||
Introduction
|
||||
-------------
|
||||
|
||||
This driver is an effort to turn abundant ELM327 based OBD interfaces
|
||||
into full fledged (as far as possible) CAN interfaces.
|
||||
|
||||
Since the ELM327 was never meant to be a stand alone CAN controller,
|
||||
the driver has to switch between its modes as quickly as possible in
|
||||
order to fake full-duplex operation.
|
||||
|
||||
As such, can327 is a best effort driver. However, this is more than
|
||||
enough to implement simple request-response protocols (such as OBD II),
|
||||
and to monitor broadcast messages on a bus (such as in a vehicle).
|
||||
|
||||
Most ELM327s come as nondescript serial devices, attached via USB or
|
||||
Bluetooth. The driver cannot recognize them by itself, and as such it
|
||||
is up to the user to attach it in form of a TTY line discipline
|
||||
(similar to PPP, SLIP, slcan, ...).
|
||||
|
||||
This driver is meant for ELM327 versions 1.4b and up, see below for
|
||||
known limitations in older controllers and clones.
|
||||
|
||||
|
||||
|
||||
Data sheet
|
||||
-----------
|
||||
|
||||
The official data sheets can be found at ELM electronics' home page:
|
||||
|
||||
https://www.elmelectronics.com/
|
||||
|
||||
|
||||
|
||||
How to attach the line discipline
|
||||
----------------------------------
|
||||
|
||||
Every ELM327 chip is factory programmed to operate at a serial setting
|
||||
of 38400 baud/s, 8 data bits, no parity, 1 stopbit.
|
||||
|
||||
If you have kept this default configuration, the line discipline can
|
||||
be attached on a command prompt as follows::
|
||||
|
||||
sudo ldattach \
|
||||
--debug \
|
||||
--speed 38400 \
|
||||
--eightbits \
|
||||
--noparity \
|
||||
--onestopbit \
|
||||
--iflag -ICRNL,INLCR,-IXOFF \
|
||||
30 \
|
||||
/dev/ttyUSB0
|
||||
|
||||
To change the ELM327's serial settings, please refer to its data
|
||||
sheet. This needs to be done before attaching the line discipline.
|
||||
|
||||
Once the ldisc is attached, the CAN interface starts out unconfigured.
|
||||
Set the speed before starting it::
|
||||
|
||||
# The interface needs to be down to change parameters
|
||||
sudo ip link set can0 down
|
||||
sudo ip link set can0 type can bitrate 500000
|
||||
sudo ip link set can0 up
|
||||
|
||||
500000 bit/s is a common rate for OBD-II diagnostics.
|
||||
If you're connecting straight to a car's OBD port, this is the speed
|
||||
that most cars (but not all!) expect.
|
||||
|
||||
After this, you can set out as usual with candump, cansniffer, etc.
|
||||
|
||||
|
||||
|
||||
How to check the controller version
|
||||
------------------------------------
|
||||
|
||||
Use a terminal program to attach to the controller.
|
||||
|
||||
After issuing the "``AT WS``" command, the controller will respond with
|
||||
its version::
|
||||
|
||||
>AT WS
|
||||
|
||||
|
||||
ELM327 v1.4b
|
||||
|
||||
>
|
||||
|
||||
Note that clones may claim to be any version they like.
|
||||
It is not indicative of their actual feature set.
|
||||
|
||||
|
||||
|
||||
|
||||
Communication example
|
||||
----------------------
|
||||
|
||||
This is a short and incomplete introduction on how to talk to an ELM327.
|
||||
It is here to guide understanding of the controller's and the driver's
|
||||
limitation (listed below) as well as manual testing.
|
||||
|
||||
|
||||
The ELM327 has two modes:
|
||||
|
||||
- Command mode
|
||||
- Reception mode
|
||||
|
||||
In command mode, it expects one command per line, terminated by CR.
|
||||
By default, the prompt is a "``>``", after which a command can be
|
||||
entered::
|
||||
|
||||
>ATE1
|
||||
OK
|
||||
>
|
||||
|
||||
The init script in the driver switches off several configuration options
|
||||
that are only meaningful in the original OBD scenario the chip is meant
|
||||
for, and are actually a hindrance for can327.
|
||||
|
||||
|
||||
When a command is not recognized, such as by an older version of the
|
||||
ELM327, a question mark is printed as a response instead of OK::
|
||||
|
||||
>ATUNKNOWN
|
||||
?
|
||||
>
|
||||
|
||||
At present, can327 does not evaluate this response. See the section
|
||||
below on known limitations for details.
|
||||
|
||||
|
||||
When a CAN frame is to be sent, the target address is configured, after
|
||||
which the frame is sent as a command that consists of the data's hex
|
||||
dump::
|
||||
|
||||
>ATSH123
|
||||
OK
|
||||
>DEADBEEF12345678
|
||||
OK
|
||||
>
|
||||
|
||||
The above interaction sends the SFF frame "``DE AD BE EF 12 34 56 78``"
|
||||
with (11 bit) CAN ID ``0x123``.
|
||||
For this to function, the controller must be configured for SFF sending
|
||||
mode (using "``AT PB``", see code or datasheet).
|
||||
|
||||
|
||||
Once a frame has been sent and wait-for-reply mode is on (``ATR1``,
|
||||
configured on ``listen-only=off``), or when the reply timeout expires
|
||||
and the driver sets the controller into monitoring mode (``ATMA``),
|
||||
the ELM327 will send one line for each received CAN frame, consisting
|
||||
of CAN ID, DLC, and data::
|
||||
|
||||
123 8 DEADBEEF12345678
|
||||
|
||||
For EFF (29 bit) CAN frames, the address format is slightly different,
|
||||
which can327 uses to tell the two apart::
|
||||
|
||||
12 34 56 78 8 DEADBEEF12345678
|
||||
|
||||
The ELM327 will receive both SFF and EFF frames - the current CAN
|
||||
config (``ATPB``) does not matter.
|
||||
|
||||
|
||||
If the ELM327's internal UART sending buffer runs full, it will abort
|
||||
the monitoring mode, print "BUFFER FULL" and drop back into command
|
||||
mode. Note that in this case, unlike with other error messages, the
|
||||
error message may appear on the same line as the last (usually
|
||||
incomplete) data frame::
|
||||
|
||||
12 34 56 78 8 DEADBEEF123 BUFFER FULL
|
||||
|
||||
|
||||
|
||||
Known limitations of the controller
|
||||
------------------------------------
|
||||
|
||||
- Clone devices ("v1.5" and others)
|
||||
|
||||
Sending RTR frames is not supported and will be dropped silently.
|
||||
|
||||
Receiving RTR with DLC 8 will appear to be a regular frame with
|
||||
the last received frame's DLC and payload.
|
||||
|
||||
"``AT CSM``" (CAN Silent Monitoring, i.e. don't send CAN ACKs) is
|
||||
not supported, and is hard coded to ON. Thus, frames are not ACKed
|
||||
while listening: "``AT MA``" (Monitor All) will always be "silent".
|
||||
However, immediately after sending a frame, the ELM327 will be in
|
||||
"receive reply" mode, in which it *does* ACK any received frames.
|
||||
Once the bus goes silent, or an error occurs (such as BUFFER FULL),
|
||||
or the receive reply timeout runs out, the ELM327 will end reply
|
||||
reception mode on its own and can327 will fall back to "``AT MA``"
|
||||
in order to keep monitoring the bus.
|
||||
|
||||
Other limitations may apply, depending on the clone and the quality
|
||||
of its firmware.
|
||||
|
||||
|
||||
- All versions
|
||||
|
||||
No full duplex operation is supported. The driver will switch
|
||||
between input/output mode as quickly as possible.
|
||||
|
||||
The length of outgoing RTR frames cannot be set. In fact, some
|
||||
clones (tested with one identifying as "``v1.5``") are unable to
|
||||
send RTR frames at all.
|
||||
|
||||
We don't have a way to get real-time notifications on CAN errors.
|
||||
While there is a command (``AT CS``) to retrieve some basic stats,
|
||||
we don't poll it as it would force us to interrupt reception mode.
|
||||
|
||||
|
||||
- Versions prior to 1.4b
|
||||
|
||||
These versions do not send CAN ACKs when in monitoring mode (AT MA).
|
||||
However, they do send ACKs while waiting for a reply immediately
|
||||
after sending a frame. The driver maximizes this time to make the
|
||||
controller as useful as possible.
|
||||
|
||||
Starting with version 1.4b, the ELM327 supports the "``AT CSM``"
|
||||
command, and the "listen-only" CAN option will take effect.
|
||||
|
||||
|
||||
- Versions prior to 1.4
|
||||
|
||||
These chips do not support the "``AT PB``" command, and thus cannot
|
||||
change bitrate or SFF/EFF mode on-the-fly. This will have to be
|
||||
programmed by the user before attaching the line discipline. See the
|
||||
data sheet for details.
|
||||
|
||||
|
||||
- Versions prior to 1.3
|
||||
|
||||
These chips cannot be used at all with can327. They do not support
|
||||
the "``AT D1``" command, which is necessary to avoid parsing conflicts
|
||||
on incoming data, as well as distinction of RTR frame lengths.
|
||||
|
||||
Specifically, this allows for easy distinction of SFF and EFF
|
||||
frames, and to check whether frames are complete. While it is possible
|
||||
to deduce the type and length from the length of the line the ELM327
|
||||
sends us, this method fails when the ELM327's UART output buffer
|
||||
overruns. It may abort sending in the middle of the line, which will
|
||||
then be mistaken for something else.
|
||||
|
||||
|
||||
|
||||
Known limitations of the driver
|
||||
--------------------------------
|
||||
|
||||
- No 8/7 timing.
|
||||
|
||||
ELM327 can only set CAN bitrates that are of the form 500000/n, where
|
||||
n is an integer divisor.
|
||||
However there is an exception: With a separate flag, it may set the
|
||||
speed to be 8/7 of the speed indicated by the divisor.
|
||||
This mode is not currently implemented.
|
||||
|
||||
- No evaluation of command responses.
|
||||
|
||||
The ELM327 will reply with OK when a command is understood, and with ?
|
||||
when it is not. The driver does not currently check this, and simply
|
||||
assumes that the chip understands every command.
|
||||
The driver is built such that functionality degrades gracefully
|
||||
nevertheless. See the section on known limitations of the controller.
|
||||
|
||||
- No use of hardware CAN ID filtering
|
||||
|
||||
An ELM327's UART sending buffer will easily overflow on heavy CAN bus
|
||||
load, resulting in the "``BUFFER FULL``" message. Using the hardware
|
||||
filters available through "``AT CF xxx``" and "``AT CM xxx``" would be
|
||||
helpful here, however SocketCAN does not currently provide a facility
|
||||
to make use of such hardware features.
|
||||
|
||||
|
||||
|
||||
Rationale behind the chosen configuration
|
||||
------------------------------------------
|
||||
|
||||
``AT E1``
|
||||
Echo on
|
||||
|
||||
We need this to be able to get a prompt reliably.
|
||||
|
||||
``AT S1``
|
||||
Spaces on
|
||||
|
||||
We need this to distinguish 11/29 bit CAN addresses received.
|
||||
|
||||
Note:
|
||||
We can usually do this using the line length (odd/even),
|
||||
but this fails if the line is not transmitted fully to
|
||||
the host (BUFFER FULL).
|
||||
|
||||
``AT D1``
|
||||
DLC on
|
||||
|
||||
We need this to tell the "length" of RTR frames.
|
||||
|
||||
|
||||
|
||||
A note on CAN bus termination
|
||||
------------------------------
|
||||
|
||||
Your adapter may have resistors soldered in which are meant to terminate
|
||||
the bus. This is correct when it is plugged into a OBD-II socket, but
|
||||
not helpful when trying to tap into the middle of an existing CAN bus.
|
||||
|
||||
If communications don't work with the adapter connected, check for the
|
||||
termination resistors on its PCB and try removing them.
|
|
@ -10,6 +10,7 @@ Contents:
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
can327
|
||||
ctu/ctucanfd-driver
|
||||
freescale/flexcan
|
||||
|
||||
|
|
|
@ -7320,6 +7320,13 @@ L: netdev@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/net/ethernet/ibm/ehea/
|
||||
|
||||
ELM327 CAN NETWORK DRIVER
|
||||
M: Max Staudt <max@enpas.org>
|
||||
L: linux-can@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/networking/device_drivers/can/can327.rst
|
||||
F: drivers/net/can/can327.c
|
||||
|
||||
EM28XX VIDEO4LINUX DRIVER
|
||||
M: Mauro Carvalho Chehab <mchehab@kernel.org>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
|
|
@ -49,26 +49,6 @@ config CAN_VXCAN
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called vxcan.
|
||||
|
||||
config CAN_SLCAN
|
||||
tristate "Serial / USB serial CAN Adaptors (slcan)"
|
||||
depends on TTY
|
||||
help
|
||||
CAN driver for several 'low cost' CAN interfaces that are attached
|
||||
via serial lines or via USB-to-serial adapters using the LAWICEL
|
||||
ASCII protocol. The driver implements the tty linediscipline N_SLCAN.
|
||||
|
||||
As only the sending and receiving of CAN frames is implemented, this
|
||||
driver should work with the (serial/USB) CAN hardware from:
|
||||
www.canusb.com / www.can232.com / www.mictronics.de / www.canhack.de
|
||||
|
||||
Userspace tools to attach the SLCAN line discipline (slcan_attach,
|
||||
slcand) can be found in the can-utils at the linux-can project, see
|
||||
https://github.com/linux-can/can-utils for details.
|
||||
|
||||
The slcan driver supports up to 10 CAN netdevices by default which
|
||||
can be changed by the 'maxdev=xx' module option. This driver can
|
||||
also be built as a module. If so, the module will be called slcan.
|
||||
|
||||
config CAN_NETLINK
|
||||
bool "CAN device drivers with Netlink support"
|
||||
default y
|
||||
|
@ -113,6 +93,24 @@ config CAN_AT91
|
|||
This is a driver for the SoC CAN controller in Atmel's AT91SAM9263
|
||||
and AT91SAM9X5 processors.
|
||||
|
||||
config CAN_CAN327
|
||||
tristate "Serial / USB serial ELM327 based OBD-II Interfaces (can327)"
|
||||
depends on TTY
|
||||
select CAN_RX_OFFLOAD
|
||||
help
|
||||
CAN driver for several 'low cost' OBD-II interfaces based on the
|
||||
ELM327 OBD-II interpreter chip.
|
||||
|
||||
This is a best effort driver - the ELM327 interface was never
|
||||
designed to be used as a standalone CAN interface. However, it can
|
||||
still be used for simple request-response protocols (such as OBD II),
|
||||
and to monitor broadcast messages on a bus (such as in a vehicle).
|
||||
|
||||
Please refer to the documentation for information on how to use it:
|
||||
Documentation/networking/device_drivers/can/can327.rst
|
||||
|
||||
If this driver is built as a module, it will be called can327.
|
||||
|
||||
config CAN_FLEXCAN
|
||||
tristate "Support for Freescale FLEXCAN based chips"
|
||||
depends on OF || COLDFIRE || COMPILE_TEST
|
||||
|
@ -154,6 +152,26 @@ config CAN_KVASER_PCIEFD
|
|||
Kvaser Mini PCI Express HS v2
|
||||
Kvaser Mini PCI Express 2xHS v2
|
||||
|
||||
config CAN_SLCAN
|
||||
tristate "Serial / USB serial CAN Adaptors (slcan)"
|
||||
depends on TTY
|
||||
help
|
||||
CAN driver for several 'low cost' CAN interfaces that are attached
|
||||
via serial lines or via USB-to-serial adapters using the LAWICEL
|
||||
ASCII protocol. The driver implements the tty linediscipline N_SLCAN.
|
||||
|
||||
As only the sending and receiving of CAN frames is implemented, this
|
||||
driver should work with the (serial/USB) CAN hardware from:
|
||||
www.canusb.com / www.can232.com / www.mictronics.de / www.canhack.de
|
||||
|
||||
Userspace tools to attach the SLCAN line discipline (slcan_attach,
|
||||
slcand) can be found in the can-utils at the linux-can project, see
|
||||
https://github.com/linux-can/can-utils for details.
|
||||
|
||||
The slcan driver supports up to 10 CAN netdevices by default which
|
||||
can be changed by the 'maxdev=xx' module option. This driver can
|
||||
also be built as a module. If so, the module will be called slcan.
|
||||
|
||||
config CAN_SUN4I
|
||||
tristate "Allwinner A10 CAN controller"
|
||||
depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
obj-$(CONFIG_CAN_VCAN) += vcan.o
|
||||
obj-$(CONFIG_CAN_VXCAN) += vxcan.o
|
||||
obj-$(CONFIG_CAN_SLCAN) += slcan.o
|
||||
obj-$(CONFIG_CAN_SLCAN) += slcan/
|
||||
|
||||
obj-y += dev/
|
||||
obj-y += rcar/
|
||||
|
@ -14,6 +14,7 @@ obj-y += usb/
|
|||
obj-y += softing/
|
||||
|
||||
obj-$(CONFIG_CAN_AT91) += at91_can.o
|
||||
obj-$(CONFIG_CAN_CAN327) += can327.o
|
||||
obj-$(CONFIG_CAN_CC770) += cc770/
|
||||
obj-$(CONFIG_CAN_C_CAN) += c_can/
|
||||
obj-$(CONFIG_CAN_CTUCANFD) += ctucanfd/
|
||||
|
|
1137
drivers/net/can/can327.c
Normal file
1137
drivers/net/can/can327.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1087,7 +1087,7 @@ static void ctucan_tx_interrupt(struct net_device *ndev)
|
|||
/**
|
||||
* ctucan_interrupt() - CAN Isr
|
||||
* @irq: irq number
|
||||
* @dev_id: device id poniter
|
||||
* @dev_id: device id pointer
|
||||
*
|
||||
* This is the CTU CAN FD ISR. It checks for the type of interrupt
|
||||
* and invokes the corresponding ISR.
|
||||
|
|
|
@ -511,7 +511,8 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
|||
if (priv->do_get_state)
|
||||
priv->do_get_state(dev, &state);
|
||||
|
||||
if ((priv->bittiming.bitrate &&
|
||||
if ((priv->bittiming.bitrate != CAN_BITRATE_UNSET &&
|
||||
priv->bittiming.bitrate != CAN_BITRATE_UNKNOWN &&
|
||||
nla_put(skb, IFLA_CAN_BITTIMING,
|
||||
sizeof(priv->bittiming), &priv->bittiming)) ||
|
||||
|
||||
|
|
7
drivers/net/can/slcan/Makefile
Normal file
7
drivers/net/can/slcan/Makefile
Normal file
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_CAN_SLCAN) += slcan.o
|
||||
|
||||
slcan-objs :=
|
||||
slcan-objs += slcan-core.o
|
||||
slcan-objs += slcan-ethtool.o
|
|
@ -54,8 +54,10 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/skb.h>
|
||||
#include <linux/can/can-ml.h>
|
||||
|
||||
#include "slcan.h"
|
||||
|
||||
MODULE_ALIAS_LDISC(N_SLCAN);
|
||||
MODULE_DESCRIPTION("serial line CAN interface");
|
||||
|
@ -76,8 +78,13 @@ MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces");
|
|||
#define SLC_CMD_LEN 1
|
||||
#define SLC_SFF_ID_LEN 3
|
||||
#define SLC_EFF_ID_LEN 8
|
||||
|
||||
#define SLC_STATE_LEN 1
|
||||
#define SLC_STATE_BE_RXCNT_LEN 3
|
||||
#define SLC_STATE_BE_TXCNT_LEN 3
|
||||
#define SLC_STATE_FRAME_LEN (1 + SLC_CMD_LEN + SLC_STATE_BE_RXCNT_LEN + \
|
||||
SLC_STATE_BE_TXCNT_LEN)
|
||||
struct slcan {
|
||||
struct can_priv can;
|
||||
int magic;
|
||||
|
||||
/* Various fields. */
|
||||
|
@ -96,10 +103,42 @@ struct slcan {
|
|||
unsigned long flags; /* Flag values/ mode etc */
|
||||
#define SLF_INUSE 0 /* Channel in use */
|
||||
#define SLF_ERROR 1 /* Parity, etc. error */
|
||||
#define SLF_XCMD 2 /* Command transmission */
|
||||
unsigned long cmd_flags; /* Command flags */
|
||||
#define CF_ERR_RST 0 /* Reset errors on open */
|
||||
wait_queue_head_t xcmd_wait; /* Wait queue for commands */
|
||||
/* transmission */
|
||||
};
|
||||
|
||||
static struct net_device **slcan_devs;
|
||||
|
||||
static const u32 slcan_bitrate_const[] = {
|
||||
10000, 20000, 50000, 100000, 125000,
|
||||
250000, 500000, 800000, 1000000
|
||||
};
|
||||
|
||||
bool slcan_err_rst_on_open(struct net_device *ndev)
|
||||
{
|
||||
struct slcan *sl = netdev_priv(ndev);
|
||||
|
||||
return !!test_bit(CF_ERR_RST, &sl->cmd_flags);
|
||||
}
|
||||
|
||||
int slcan_enable_err_rst_on_open(struct net_device *ndev, bool on)
|
||||
{
|
||||
struct slcan *sl = netdev_priv(ndev);
|
||||
|
||||
if (netif_running(ndev))
|
||||
return -EBUSY;
|
||||
|
||||
if (on)
|
||||
set_bit(CF_ERR_RST, &sl->cmd_flags);
|
||||
else
|
||||
clear_bit(CF_ERR_RST, &sl->cmd_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* SLCAN ENCAPSULATION FORMAT *
|
||||
************************************************************************/
|
||||
|
@ -140,88 +179,289 @@ static struct net_device **slcan_devs;
|
|||
************************************************************************/
|
||||
|
||||
/* Send one completely decapsulated can_frame to the network layer */
|
||||
static void slc_bump(struct slcan *sl)
|
||||
static void slc_bump_frame(struct slcan *sl)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct can_frame cf;
|
||||
struct can_frame *cf;
|
||||
int i, tmp;
|
||||
u32 tmpid;
|
||||
char *cmd = sl->rbuff;
|
||||
|
||||
memset(&cf, 0, sizeof(cf));
|
||||
skb = alloc_can_skb(sl->dev, &cf);
|
||||
if (unlikely(!skb)) {
|
||||
sl->dev->stats.rx_dropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (*cmd) {
|
||||
case 'r':
|
||||
cf.can_id = CAN_RTR_FLAG;
|
||||
cf->can_id = CAN_RTR_FLAG;
|
||||
fallthrough;
|
||||
case 't':
|
||||
/* store dlc ASCII value and terminate SFF CAN ID string */
|
||||
cf.len = sl->rbuff[SLC_CMD_LEN + SLC_SFF_ID_LEN];
|
||||
cf->len = sl->rbuff[SLC_CMD_LEN + SLC_SFF_ID_LEN];
|
||||
sl->rbuff[SLC_CMD_LEN + SLC_SFF_ID_LEN] = 0;
|
||||
/* point to payload data behind the dlc */
|
||||
cmd += SLC_CMD_LEN + SLC_SFF_ID_LEN + 1;
|
||||
break;
|
||||
case 'R':
|
||||
cf.can_id = CAN_RTR_FLAG;
|
||||
cf->can_id = CAN_RTR_FLAG;
|
||||
fallthrough;
|
||||
case 'T':
|
||||
cf.can_id |= CAN_EFF_FLAG;
|
||||
cf->can_id |= CAN_EFF_FLAG;
|
||||
/* store dlc ASCII value and terminate EFF CAN ID string */
|
||||
cf.len = sl->rbuff[SLC_CMD_LEN + SLC_EFF_ID_LEN];
|
||||
cf->len = sl->rbuff[SLC_CMD_LEN + SLC_EFF_ID_LEN];
|
||||
sl->rbuff[SLC_CMD_LEN + SLC_EFF_ID_LEN] = 0;
|
||||
/* point to payload data behind the dlc */
|
||||
cmd += SLC_CMD_LEN + SLC_EFF_ID_LEN + 1;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
goto decode_failed;
|
||||
}
|
||||
|
||||
if (kstrtou32(sl->rbuff + SLC_CMD_LEN, 16, &tmpid))
|
||||
return;
|
||||
goto decode_failed;
|
||||
|
||||
cf.can_id |= tmpid;
|
||||
cf->can_id |= tmpid;
|
||||
|
||||
/* get len from sanitized ASCII value */
|
||||
if (cf.len >= '0' && cf.len < '9')
|
||||
cf.len -= '0';
|
||||
if (cf->len >= '0' && cf->len < '9')
|
||||
cf->len -= '0';
|
||||
else
|
||||
return;
|
||||
goto decode_failed;
|
||||
|
||||
/* RTR frames may have a dlc > 0 but they never have any data bytes */
|
||||
if (!(cf.can_id & CAN_RTR_FLAG)) {
|
||||
for (i = 0; i < cf.len; i++) {
|
||||
if (!(cf->can_id & CAN_RTR_FLAG)) {
|
||||
for (i = 0; i < cf->len; i++) {
|
||||
tmp = hex_to_bin(*cmd++);
|
||||
if (tmp < 0)
|
||||
return;
|
||||
cf.data[i] = (tmp << 4);
|
||||
goto decode_failed;
|
||||
|
||||
cf->data[i] = (tmp << 4);
|
||||
tmp = hex_to_bin(*cmd++);
|
||||
if (tmp < 0)
|
||||
return;
|
||||
cf.data[i] |= tmp;
|
||||
goto decode_failed;
|
||||
|
||||
cf->data[i] |= tmp;
|
||||
}
|
||||
}
|
||||
|
||||
skb = dev_alloc_skb(sizeof(struct can_frame) +
|
||||
sizeof(struct can_skb_priv));
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
skb->dev = sl->dev;
|
||||
skb->protocol = htons(ETH_P_CAN);
|
||||
skb->pkt_type = PACKET_BROADCAST;
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
||||
can_skb_reserve(skb);
|
||||
can_skb_prv(skb)->ifindex = sl->dev->ifindex;
|
||||
can_skb_prv(skb)->skbcnt = 0;
|
||||
|
||||
skb_put_data(skb, &cf, sizeof(struct can_frame));
|
||||
|
||||
sl->dev->stats.rx_packets++;
|
||||
if (!(cf.can_id & CAN_RTR_FLAG))
|
||||
sl->dev->stats.rx_bytes += cf.len;
|
||||
if (!(cf->can_id & CAN_RTR_FLAG))
|
||||
sl->dev->stats.rx_bytes += cf->len;
|
||||
|
||||
netif_rx(skb);
|
||||
return;
|
||||
|
||||
decode_failed:
|
||||
sl->dev->stats.rx_errors++;
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/* A change state frame must contain state info and receive and transmit
|
||||
* error counters.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* sb256256 : state bus-off: rx counter 256, tx counter 256
|
||||
* sa057033 : state active, rx counter 57, tx counter 33
|
||||
*/
|
||||
static void slc_bump_state(struct slcan *sl)
|
||||
{
|
||||
struct net_device *dev = sl->dev;
|
||||
struct sk_buff *skb;
|
||||
struct can_frame *cf;
|
||||
char *cmd = sl->rbuff;
|
||||
u32 rxerr, txerr;
|
||||
enum can_state state, rx_state, tx_state;
|
||||
|
||||
switch (cmd[1]) {
|
||||
case 'a':
|
||||
state = CAN_STATE_ERROR_ACTIVE;
|
||||
break;
|
||||
case 'w':
|
||||
state = CAN_STATE_ERROR_WARNING;
|
||||
break;
|
||||
case 'p':
|
||||
state = CAN_STATE_ERROR_PASSIVE;
|
||||
break;
|
||||
case 'b':
|
||||
state = CAN_STATE_BUS_OFF;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == sl->can.state || sl->rcount < SLC_STATE_FRAME_LEN)
|
||||
return;
|
||||
|
||||
cmd += SLC_STATE_BE_RXCNT_LEN + SLC_CMD_LEN + 1;
|
||||
cmd[SLC_STATE_BE_TXCNT_LEN] = 0;
|
||||
if (kstrtou32(cmd, 10, &txerr))
|
||||
return;
|
||||
|
||||
*cmd = 0;
|
||||
cmd -= SLC_STATE_BE_RXCNT_LEN;
|
||||
if (kstrtou32(cmd, 10, &rxerr))
|
||||
return;
|
||||
|
||||
skb = alloc_can_err_skb(dev, &cf);
|
||||
if (skb) {
|
||||
cf->data[6] = txerr;
|
||||
cf->data[7] = rxerr;
|
||||
} else {
|
||||
cf = NULL;
|
||||
}
|
||||
|
||||
tx_state = txerr >= rxerr ? state : 0;
|
||||
rx_state = txerr <= rxerr ? state : 0;
|
||||
can_change_state(dev, cf, tx_state, rx_state);
|
||||
|
||||
if (state == CAN_STATE_BUS_OFF)
|
||||
can_bus_off(dev);
|
||||
|
||||
if (skb)
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
/* An error frame can contain more than one type of error.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* e1a : len 1, errors: ACK error
|
||||
* e3bcO: len 3, errors: Bit0 error, CRC error, Tx overrun error
|
||||
*/
|
||||
static void slc_bump_err(struct slcan *sl)
|
||||
{
|
||||
struct net_device *dev = sl->dev;
|
||||
struct sk_buff *skb;
|
||||
struct can_frame *cf;
|
||||
char *cmd = sl->rbuff;
|
||||
bool rx_errors = false, tx_errors = false, rx_over_errors = false;
|
||||
int i, len;
|
||||
|
||||
/* get len from sanitized ASCII value */
|
||||
len = cmd[1];
|
||||
if (len >= '0' && len < '9')
|
||||
len -= '0';
|
||||
else
|
||||
return;
|
||||
|
||||
if ((len + SLC_CMD_LEN + 1) > sl->rcount)
|
||||
return;
|
||||
|
||||
skb = alloc_can_err_skb(dev, &cf);
|
||||
|
||||
if (skb)
|
||||
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
|
||||
|
||||
cmd += SLC_CMD_LEN + 1;
|
||||
for (i = 0; i < len; i++, cmd++) {
|
||||
switch (*cmd) {
|
||||
case 'a':
|
||||
netdev_dbg(dev, "ACK error\n");
|
||||
tx_errors = true;
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_ACK;
|
||||
cf->data[3] = CAN_ERR_PROT_LOC_ACK;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'b':
|
||||
netdev_dbg(dev, "Bit0 error\n");
|
||||
tx_errors = true;
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_BIT0;
|
||||
|
||||
break;
|
||||
case 'B':
|
||||
netdev_dbg(dev, "Bit1 error\n");
|
||||
tx_errors = true;
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_BIT1;
|
||||
|
||||
break;
|
||||
case 'c':
|
||||
netdev_dbg(dev, "CRC error\n");
|
||||
rx_errors = true;
|
||||
if (skb) {
|
||||
cf->data[2] |= CAN_ERR_PROT_BIT;
|
||||
cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'f':
|
||||
netdev_dbg(dev, "Form Error\n");
|
||||
rx_errors = true;
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_FORM;
|
||||
|
||||
break;
|
||||
case 'o':
|
||||
netdev_dbg(dev, "Rx overrun error\n");
|
||||
rx_over_errors = true;
|
||||
rx_errors = true;
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'O':
|
||||
netdev_dbg(dev, "Tx overrun error\n");
|
||||
tx_errors = true;
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] = CAN_ERR_CRTL_TX_OVERFLOW;
|
||||
}
|
||||
|
||||
break;
|
||||
case 's':
|
||||
netdev_dbg(dev, "Stuff error\n");
|
||||
rx_errors = true;
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_STUFF;
|
||||
|
||||
break;
|
||||
default:
|
||||
if (skb)
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (rx_errors)
|
||||
dev->stats.rx_errors++;
|
||||
|
||||
if (rx_over_errors)
|
||||
dev->stats.rx_over_errors++;
|
||||
|
||||
if (tx_errors)
|
||||
dev->stats.tx_errors++;
|
||||
|
||||
if (skb)
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
static void slc_bump(struct slcan *sl)
|
||||
{
|
||||
switch (sl->rbuff[0]) {
|
||||
case 'r':
|
||||
fallthrough;
|
||||
case 't':
|
||||
fallthrough;
|
||||
case 'R':
|
||||
fallthrough;
|
||||
case 'T':
|
||||
return slc_bump_frame(sl);
|
||||
case 'e':
|
||||
return slc_bump_err(sl);
|
||||
case 's':
|
||||
return slc_bump_state(sl);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* parse tty input stream */
|
||||
|
@ -318,12 +558,22 @@ static void slcan_transmit(struct work_struct *work)
|
|||
|
||||
spin_lock_bh(&sl->lock);
|
||||
/* First make sure we're connected. */
|
||||
if (!sl->tty || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev)) {
|
||||
if (!sl->tty || sl->magic != SLCAN_MAGIC ||
|
||||
(unlikely(!netif_running(sl->dev)) &&
|
||||
likely(!test_bit(SLF_XCMD, &sl->flags)))) {
|
||||
spin_unlock_bh(&sl->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sl->xleft <= 0) {
|
||||
if (unlikely(test_bit(SLF_XCMD, &sl->flags))) {
|
||||
clear_bit(SLF_XCMD, &sl->flags);
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
|
||||
spin_unlock_bh(&sl->lock);
|
||||
wake_up(&sl->xcmd_wait);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now serial buffer is almost free & we can start
|
||||
* transmission of another packet */
|
||||
sl->dev->stats.tx_packets++;
|
||||
|
@ -365,7 +615,7 @@ static netdev_tx_t slc_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
spin_lock(&sl->lock);
|
||||
if (!netif_running(dev)) {
|
||||
spin_unlock(&sl->lock);
|
||||
printk(KERN_WARNING "%s: xmit: iface is down\n", dev->name);
|
||||
netdev_warn(dev, "xmit: iface is down\n");
|
||||
goto out;
|
||||
}
|
||||
if (sl->tty == NULL) {
|
||||
|
@ -387,17 +637,63 @@ static netdev_tx_t slc_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
* Routines looking at netdevice side.
|
||||
******************************************/
|
||||
|
||||
static int slcan_transmit_cmd(struct slcan *sl, const unsigned char *cmd)
|
||||
{
|
||||
int ret, actual, n;
|
||||
|
||||
spin_lock(&sl->lock);
|
||||
if (!sl->tty) {
|
||||
spin_unlock(&sl->lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
n = snprintf(sl->xbuff, sizeof(sl->xbuff), "%s", cmd);
|
||||
set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
|
||||
actual = sl->tty->ops->write(sl->tty, sl->xbuff, n);
|
||||
sl->xleft = n - actual;
|
||||
sl->xhead = sl->xbuff + actual;
|
||||
set_bit(SLF_XCMD, &sl->flags);
|
||||
spin_unlock(&sl->lock);
|
||||
ret = wait_event_interruptible_timeout(sl->xcmd_wait,
|
||||
!test_bit(SLF_XCMD, &sl->flags),
|
||||
HZ);
|
||||
clear_bit(SLF_XCMD, &sl->flags);
|
||||
if (ret == -ERESTARTSYS)
|
||||
return ret;
|
||||
|
||||
if (ret == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Netdevice UP -> DOWN routine */
|
||||
static int slc_close(struct net_device *dev)
|
||||
{
|
||||
struct slcan *sl = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
spin_lock_bh(&sl->lock);
|
||||
if (sl->tty) {
|
||||
if (sl->can.bittiming.bitrate &&
|
||||
sl->can.bittiming.bitrate != CAN_BITRATE_UNKNOWN) {
|
||||
spin_unlock_bh(&sl->lock);
|
||||
err = slcan_transmit_cmd(sl, "C\r");
|
||||
spin_lock_bh(&sl->lock);
|
||||
if (err)
|
||||
netdev_warn(dev,
|
||||
"failed to send close command 'C\\r'\n");
|
||||
}
|
||||
|
||||
/* TTY discipline is running. */
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
|
||||
}
|
||||
netif_stop_queue(dev);
|
||||
close_candev(dev);
|
||||
sl->can.state = CAN_STATE_STOPPED;
|
||||
if (sl->can.bittiming.bitrate == CAN_BITRATE_UNKNOWN)
|
||||
sl->can.bittiming.bitrate = CAN_BITRATE_UNSET;
|
||||
|
||||
sl->rcount = 0;
|
||||
sl->xleft = 0;
|
||||
spin_unlock_bh(&sl->lock);
|
||||
|
@ -409,20 +705,77 @@ static int slc_close(struct net_device *dev)
|
|||
static int slc_open(struct net_device *dev)
|
||||
{
|
||||
struct slcan *sl = netdev_priv(dev);
|
||||
unsigned char cmd[SLC_MTU];
|
||||
int err, s;
|
||||
|
||||
if (sl->tty == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
sl->flags &= (1 << SLF_INUSE);
|
||||
/* The baud rate is not set with the command
|
||||
* `ip link set <iface> type can bitrate <baud>' and therefore
|
||||
* can.bittiming.bitrate is CAN_BITRATE_UNSET (0), causing
|
||||
* open_candev() to fail. So let's set to a fake value.
|
||||
*/
|
||||
if (sl->can.bittiming.bitrate == CAN_BITRATE_UNSET)
|
||||
sl->can.bittiming.bitrate = CAN_BITRATE_UNKNOWN;
|
||||
|
||||
err = open_candev(dev);
|
||||
if (err) {
|
||||
netdev_err(dev, "failed to open can device\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
sl->flags &= BIT(SLF_INUSE);
|
||||
|
||||
if (sl->can.bittiming.bitrate != CAN_BITRATE_UNKNOWN) {
|
||||
for (s = 0; s < ARRAY_SIZE(slcan_bitrate_const); s++) {
|
||||
if (sl->can.bittiming.bitrate == slcan_bitrate_const[s])
|
||||
break;
|
||||
}
|
||||
|
||||
/* The CAN framework has already validate the bitrate value,
|
||||
* so we can avoid to check if `s' has been properly set.
|
||||
*/
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "C\rS%d\r", s);
|
||||
err = slcan_transmit_cmd(sl, cmd);
|
||||
if (err) {
|
||||
netdev_err(dev,
|
||||
"failed to send bitrate command 'C\\rS%d\\r'\n",
|
||||
s);
|
||||
goto cmd_transmit_failed;
|
||||
}
|
||||
|
||||
if (test_bit(CF_ERR_RST, &sl->cmd_flags)) {
|
||||
err = slcan_transmit_cmd(sl, "F\r");
|
||||
if (err) {
|
||||
netdev_err(dev,
|
||||
"failed to send error command 'F\\r'\n");
|
||||
goto cmd_transmit_failed;
|
||||
}
|
||||
}
|
||||
|
||||
err = slcan_transmit_cmd(sl, "O\r");
|
||||
if (err) {
|
||||
netdev_err(dev, "failed to send open command 'O\\r'\n");
|
||||
goto cmd_transmit_failed;
|
||||
}
|
||||
}
|
||||
|
||||
sl->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
|
||||
cmd_transmit_failed:
|
||||
close_candev(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Hook the destructor so we can free slcan devs at the right point in time */
|
||||
static void slc_free_netdev(struct net_device *dev)
|
||||
static void slc_dealloc(struct slcan *sl)
|
||||
{
|
||||
int i = dev->base_addr;
|
||||
int i = sl->dev->base_addr;
|
||||
|
||||
free_candev(sl->dev);
|
||||
slcan_devs[i] = NULL;
|
||||
}
|
||||
|
||||
|
@ -438,24 +791,6 @@ static const struct net_device_ops slc_netdev_ops = {
|
|||
.ndo_change_mtu = slcan_change_mtu,
|
||||
};
|
||||
|
||||
static void slc_setup(struct net_device *dev)
|
||||
{
|
||||
dev->netdev_ops = &slc_netdev_ops;
|
||||
dev->needs_free_netdev = true;
|
||||
dev->priv_destructor = slc_free_netdev;
|
||||
|
||||
dev->hard_header_len = 0;
|
||||
dev->addr_len = 0;
|
||||
dev->tx_queue_len = 10;
|
||||
|
||||
dev->mtu = CAN_MTU;
|
||||
dev->type = ARPHRD_CAN;
|
||||
|
||||
/* New-style flags. */
|
||||
dev->flags = IFF_NOARP;
|
||||
dev->features = NETIF_F_HW_CSUM;
|
||||
}
|
||||
|
||||
/******************************************
|
||||
Routines looking at TTY side.
|
||||
******************************************/
|
||||
|
@ -518,11 +853,8 @@ static void slc_sync(void)
|
|||
static struct slcan *slc_alloc(void)
|
||||
{
|
||||
int i;
|
||||
char name[IFNAMSIZ];
|
||||
struct net_device *dev = NULL;
|
||||
struct can_ml_priv *can_ml;
|
||||
struct slcan *sl;
|
||||
int size;
|
||||
|
||||
for (i = 0; i < maxdev; i++) {
|
||||
dev = slcan_devs[i];
|
||||
|
@ -535,22 +867,24 @@ static struct slcan *slc_alloc(void)
|
|||
if (i >= maxdev)
|
||||
return NULL;
|
||||
|
||||
sprintf(name, "slcan%d", i);
|
||||
size = ALIGN(sizeof(*sl), NETDEV_ALIGN) + sizeof(struct can_ml_priv);
|
||||
dev = alloc_netdev(size, name, NET_NAME_UNKNOWN, slc_setup);
|
||||
dev = alloc_candev(sizeof(*sl), 1);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
snprintf(dev->name, sizeof(dev->name), "slcan%d", i);
|
||||
dev->netdev_ops = &slc_netdev_ops;
|
||||
dev->base_addr = i;
|
||||
slcan_set_ethtool_ops(dev);
|
||||
sl = netdev_priv(dev);
|
||||
can_ml = (void *)sl + ALIGN(sizeof(*sl), NETDEV_ALIGN);
|
||||
can_set_ml_priv(dev, can_ml);
|
||||
|
||||
/* Initialize channel control data */
|
||||
sl->magic = SLCAN_MAGIC;
|
||||
sl->dev = dev;
|
||||
sl->can.bitrate_const = slcan_bitrate_const;
|
||||
sl->can.bitrate_const_cnt = ARRAY_SIZE(slcan_bitrate_const);
|
||||
spin_lock_init(&sl->lock);
|
||||
INIT_WORK(&sl->tx_work, slcan_transmit);
|
||||
init_waitqueue_head(&sl->xcmd_wait);
|
||||
slcan_devs[i] = dev;
|
||||
|
||||
return sl;
|
||||
|
@ -609,26 +943,28 @@ static int slcan_open(struct tty_struct *tty)
|
|||
|
||||
set_bit(SLF_INUSE, &sl->flags);
|
||||
|
||||
err = register_netdevice(sl->dev);
|
||||
if (err)
|
||||
rtnl_unlock();
|
||||
err = register_candev(sl->dev);
|
||||
if (err) {
|
||||
pr_err("slcan: can't register candev\n");
|
||||
goto err_free_chan;
|
||||
}
|
||||
} else {
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
/* Done. We have linked the TTY line to a channel. */
|
||||
rtnl_unlock();
|
||||
tty->receive_room = 65536; /* We don't flow control */
|
||||
|
||||
/* TTY layer expects 0 on success */
|
||||
return 0;
|
||||
|
||||
err_free_chan:
|
||||
rtnl_lock();
|
||||
sl->tty = NULL;
|
||||
tty->disc_data = NULL;
|
||||
clear_bit(SLF_INUSE, &sl->flags);
|
||||
slc_free_netdev(sl->dev);
|
||||
/* do not call free_netdev before rtnl_unlock */
|
||||
slc_dealloc(sl);
|
||||
rtnl_unlock();
|
||||
free_netdev(sl->dev);
|
||||
return err;
|
||||
|
||||
err_exit:
|
||||
|
@ -662,9 +998,11 @@ static void slcan_close(struct tty_struct *tty)
|
|||
synchronize_rcu();
|
||||
flush_work(&sl->tx_work);
|
||||
|
||||
/* Flush network side */
|
||||
unregister_netdev(sl->dev);
|
||||
/* This will complete via sl_free_netdev */
|
||||
slc_close(sl->dev);
|
||||
unregister_candev(sl->dev);
|
||||
rtnl_lock();
|
||||
slc_dealloc(sl);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static void slcan_hangup(struct tty_struct *tty)
|
||||
|
@ -772,15 +1110,15 @@ static void __exit slcan_exit(void)
|
|||
dev = slcan_devs[i];
|
||||
if (!dev)
|
||||
continue;
|
||||
slcan_devs[i] = NULL;
|
||||
|
||||
sl = netdev_priv(dev);
|
||||
if (sl->tty) {
|
||||
printk(KERN_ERR "%s: tty discipline still running\n",
|
||||
dev->name);
|
||||
netdev_err(dev, "tty discipline still running\n");
|
||||
}
|
||||
|
||||
unregister_netdev(dev);
|
||||
slc_close(dev);
|
||||
unregister_candev(dev);
|
||||
slc_dealloc(sl);
|
||||
}
|
||||
|
||||
kfree(slcan_devs);
|
65
drivers/net/can/slcan/slcan-ethtool.c
Normal file
65
drivers/net/can/slcan/slcan-ethtool.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Copyright (c) 2022 Amarula Solutions, Dario Binacchi <dario.binacchi@amarulasolutions.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "slcan.h"
|
||||
|
||||
static const char slcan_priv_flags_strings[][ETH_GSTRING_LEN] = {
|
||||
#define SLCAN_PRIV_FLAGS_ERR_RST_ON_OPEN BIT(0)
|
||||
"err-rst-on-open",
|
||||
};
|
||||
|
||||
static void slcan_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
|
||||
{
|
||||
switch (stringset) {
|
||||
case ETH_SS_PRIV_FLAGS:
|
||||
memcpy(data, slcan_priv_flags_strings,
|
||||
sizeof(slcan_priv_flags_strings));
|
||||
}
|
||||
}
|
||||
|
||||
static u32 slcan_get_priv_flags(struct net_device *ndev)
|
||||
{
|
||||
u32 flags = 0;
|
||||
|
||||
if (slcan_err_rst_on_open(ndev))
|
||||
flags |= SLCAN_PRIV_FLAGS_ERR_RST_ON_OPEN;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static int slcan_set_priv_flags(struct net_device *ndev, u32 flags)
|
||||
{
|
||||
bool err_rst_op_open = !!(flags & SLCAN_PRIV_FLAGS_ERR_RST_ON_OPEN);
|
||||
|
||||
return slcan_enable_err_rst_on_open(ndev, err_rst_op_open);
|
||||
}
|
||||
|
||||
static int slcan_get_sset_count(struct net_device *netdev, int sset)
|
||||
{
|
||||
switch (sset) {
|
||||
case ETH_SS_PRIV_FLAGS:
|
||||
return ARRAY_SIZE(slcan_priv_flags_strings);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ethtool_ops slcan_ethtool_ops = {
|
||||
.get_strings = slcan_get_strings,
|
||||
.get_priv_flags = slcan_get_priv_flags,
|
||||
.set_priv_flags = slcan_set_priv_flags,
|
||||
.get_sset_count = slcan_get_sset_count,
|
||||
};
|
||||
|
||||
void slcan_set_ethtool_ops(struct net_device *netdev)
|
||||
{
|
||||
netdev->ethtool_ops = &slcan_ethtool_ops;
|
||||
}
|
18
drivers/net/can/slcan/slcan.h
Normal file
18
drivers/net/can/slcan/slcan.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0
|
||||
* slcan.h - serial line CAN interface driver
|
||||
*
|
||||
* Copyright (C) Laurence Culhane <loz@holmes.demon.co.uk>
|
||||
* Copyright (C) Fred N. van Kempen <waltje@uwalt.nl.mugnet.org>
|
||||
* Copyright (C) Oliver Hartkopp <socketcan@hartkopp.net>
|
||||
* Copyright (C) 2022 Amarula Solutions, Dario Binacchi <dario.binacchi@amarulasolutions.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SLCAN_H
|
||||
#define _SLCAN_H
|
||||
|
||||
bool slcan_err_rst_on_open(struct net_device *ndev);
|
||||
int slcan_enable_err_rst_on_open(struct net_device *ndev, bool on);
|
||||
void slcan_set_ethtool_ops(struct net_device *ndev);
|
||||
|
||||
#endif /* _SLCAN_H */
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#define CAN_SYNC_SEG 1
|
||||
|
||||
#define CAN_BITRATE_UNSET 0
|
||||
#define CAN_BITRATE_UNKNOWN (-1U)
|
||||
|
||||
#define CAN_CTRLMODE_TDC_MASK \
|
||||
(CAN_CTRLMODE_TDC_AUTO | CAN_CTRLMODE_TDC_MANUAL)
|
||||
|
|
|
@ -38,8 +38,9 @@
|
|||
#define N_NULL 27 /* Null ldisc used for error handling */
|
||||
#define N_MCTP 28 /* MCTP-over-serial */
|
||||
#define N_DEVELOPMENT 29 /* Manual out-of-tree testing */
|
||||
#define N_CAN327 30 /* ELM327 based OBD-II interfaces */
|
||||
|
||||
/* Always the newest line discipline + 1 */
|
||||
#define NR_LDISCS 30
|
||||
#define NR_LDISCS 31
|
||||
|
||||
#endif /* _UAPI_LINUX_TTY_H */
|
||||
|
|
Loading…
Reference in a new issue