linux-can-next-for-4.12-20170425

-----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCgAxFiEE4bay/IylYqM/npjQHv7KIOw4HPYFAlj/Cw8THG1rbEBwZW5n
 dXRyb25peC5kZQAKCRAe/sog7Dgc9keOB/9dFFKUSqXEbevRCVj8Hc/tpmUnAYDP
 xcIpz/8GRHPrkOx/tpqtzkAQjeiNzcrT5LDPLDoMSpubZDJjNTKGfcb5sOvGqK9P
 IyY4dv0DO9/z1zxdpkK7CkR+g9Z3w9mEdQl2OS0yxbOXRPgX5Sl44Tp5xWgvJhOc
 s60m/Y60PQ22CSee7EBYWCwvJPfLIdsr5AIM6wtbEveZU13afAFbyIqoP/97RZKF
 sJ8NfGwQmRcD+AHw1nB/YfhNh4NEBE52IiBKf3zgC8Y8lDId/Wve/j/MnJGzeK48
 eXPU3g1QaJTuEddn4xC0RRMKycR9klwfGJkY1cKSLtCWQxd1cfHKLdWY
 =P474
 -----END PGP SIGNATURE-----

Merge tag 'linux-can-next-for-4.12-20170425' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next

Marc Kleine-Budde says:

====================
pull-request: can-next 2017-04-25

this is a pull request of 21 patches for net-next/master.

There are 4 patches by Stephane Grosjean for the PEAK PCAN-PCIe FD
CAN-FD boards. The next 7 patches are by Mario Huettel, which add
support for M_CAN IP version >= v3.1.x to the m_can driver. A patch by
Remigiusz Kołłątaj adds support for the Microchip CAN BUS Analyzer. 8
patches by Oliver Hartkopp complete the initial CAN network namespace
support. Wei Yongjun's patch for the ti_hecc driver fixes the return
value check in the probe function.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-04-25 10:52:47 -04:00
commit 45a6f3bca6
24 changed files with 3889 additions and 373 deletions

View File

@ -9,6 +9,24 @@ config CAN_VCAN
This driver can also be built as a module. If so, the module
will be called vcan.
config CAN_VXCAN
tristate "Virtual CAN Tunnel (vxcan)"
---help---
Similar to the virtual ethernet driver veth, vxcan implements a
local CAN traffic tunnel between two virtual CAN network devices.
When creating a vxcan, two vxcan devices are created as pair.
When one end receives the packet it appears on its pair and vice
versa. The vxcan can be used for cross namespace communication.
In opposite to vcan loopback devices the vxcan only forwards CAN
frames to its pair and does *not* provide a local echo of sent
CAN frames. To disable a potential echo in af_can.c the vxcan driver
announces IFF_ECHO in the interface flags. To have a clean start
in each namespace the CAN GW hop counter is set to zero.
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
@ -142,6 +160,7 @@ source "drivers/net/can/cc770/Kconfig"
source "drivers/net/can/ifi_canfd/Kconfig"
source "drivers/net/can/m_can/Kconfig"
source "drivers/net/can/mscan/Kconfig"
source "drivers/net/can/peak_canfd/Kconfig"
source "drivers/net/can/rcar/Kconfig"
source "drivers/net/can/sja1000/Kconfig"
source "drivers/net/can/softing/Kconfig"

View File

@ -3,6 +3,7 @@
#
obj-$(CONFIG_CAN_VCAN) += vcan.o
obj-$(CONFIG_CAN_VXCAN) += vxcan.o
obj-$(CONFIG_CAN_SLCAN) += slcan.o
obj-$(CONFIG_CAN_DEV) += can-dev.o
@ -26,6 +27,7 @@ obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/
obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
obj-$(CONFIG_CAN_MSCAN) += mscan/
obj-$(CONFIG_CAN_M_CAN) += m_can/
obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/
obj-$(CONFIG_CAN_SJA1000) += sja1000/
obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
config CAN_PEAK_PCIEFD
depends on PCI
tristate "PEAK-System PCAN-PCIe FD cards"
---help---
This driver adds support for the PEAK-System PCI Express FD
CAN-FD cards family.
These 1x or 2x CAN-FD channels cards offer CAN 2.0 a/b as well as
CAN-FD access to the CAN bus. Besides the nominal bitrate of up to
1 Mbit/s, the data bytes of CAN-FD frames can be transmitted with
up to 12 Mbit/s. A galvanic isolation of the CAN ports protects the
electronics of the card and the respective computer against
disturbances of up to 500 Volts. The PCAN-PCI Express FD can be
operated with ambient temperatures in a range of -40 to +85 °C.

View File

@ -0,0 +1,5 @@
#
# Makefile for the PEAK-System CAN-FD IP module drivers
#
obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_pciefd.o
peak_pciefd-y := peak_pciefd_main.o peak_canfd.o

View File

@ -0,0 +1,801 @@
/*
* Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com>
* Copyright (C) 2012 Stephane Grosjean <s.grosjean@peak-system.com>
*
* Copyright (C) 2016 PEAK System-Technik GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the version 2 of the GNU General Public License
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/can.h>
#include <linux/can/dev.h>
#include "peak_canfd_user.h"
/* internal IP core cache size (used as default echo skbs max number) */
#define PCANFD_ECHO_SKB_MAX 24
/* bittiming ranges of the PEAK-System PC CAN-FD interfaces */
static const struct can_bittiming_const peak_canfd_nominal_const = {
.name = "peak_canfd",
.tseg1_min = 1,
.tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS),
.tseg2_min = 1,
.tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS),
.sjw_max = (1 << PUCAN_TSLOW_SJW_BITS),
.brp_min = 1,
.brp_max = (1 << PUCAN_TSLOW_BRP_BITS),
.brp_inc = 1,
};
static const struct can_bittiming_const peak_canfd_data_const = {
.name = "peak_canfd",
.tseg1_min = 1,
.tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS),
.tseg2_min = 1,
.tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS),
.sjw_max = (1 << PUCAN_TFAST_SJW_BITS),
.brp_min = 1,
.brp_max = (1 << PUCAN_TFAST_BRP_BITS),
.brp_inc = 1,
};
static struct peak_canfd_priv *pucan_init_cmd(struct peak_canfd_priv *priv)
{
priv->cmd_len = 0;
return priv;
}
static void *pucan_add_cmd(struct peak_canfd_priv *priv, int cmd_op)
{
struct pucan_command *cmd;
if (priv->cmd_len + sizeof(*cmd) > priv->cmd_maxlen)
return NULL;
cmd = priv->cmd_buffer + priv->cmd_len;
/* reset all unused bit to default */
memset(cmd, 0, sizeof(*cmd));
cmd->opcode_channel = pucan_cmd_opcode_channel(priv->index, cmd_op);
priv->cmd_len += sizeof(*cmd);
return cmd;
}
static int pucan_write_cmd(struct peak_canfd_priv *priv)
{
int err;
if (priv->pre_cmd) {
err = priv->pre_cmd(priv);
if (err)
return err;
}
err = priv->write_cmd(priv);
if (err)
return err;
if (priv->post_cmd)
err = priv->post_cmd(priv);
return err;
}
/* uCAN commands interface functions */
static int pucan_set_reset_mode(struct peak_canfd_priv *priv)
{
pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RESET_MODE);
return pucan_write_cmd(priv);
}
static int pucan_set_normal_mode(struct peak_canfd_priv *priv)
{
int err;
pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_NORMAL_MODE);
err = pucan_write_cmd(priv);
if (!err)
priv->can.state = CAN_STATE_ERROR_ACTIVE;
return err;
}
static int pucan_set_listen_only_mode(struct peak_canfd_priv *priv)
{
int err;
pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_LISTEN_ONLY_MODE);
err = pucan_write_cmd(priv);
if (!err)
priv->can.state = CAN_STATE_ERROR_ACTIVE;
return err;
}
static int pucan_set_timing_slow(struct peak_canfd_priv *priv,
const struct can_bittiming *pbt)
{
struct pucan_timing_slow *cmd;
cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TIMING_SLOW);
cmd->sjw_t = PUCAN_TSLOW_SJW_T(pbt->sjw - 1,
priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES);
cmd->tseg1 = PUCAN_TSLOW_TSEG1(pbt->prop_seg + pbt->phase_seg1 - 1);
cmd->tseg2 = PUCAN_TSLOW_TSEG2(pbt->phase_seg2 - 1);
cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(pbt->brp - 1));
cmd->ewl = 96; /* default */
netdev_dbg(priv->ndev,
"nominal: brp=%u tseg1=%u tseg2=%u sjw=%u\n",
le16_to_cpu(cmd->brp), cmd->tseg1, cmd->tseg2, cmd->sjw_t);
return pucan_write_cmd(priv);
}
static int pucan_set_timing_fast(struct peak_canfd_priv *priv,
const struct can_bittiming *pbt)
{
struct pucan_timing_fast *cmd;
cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TIMING_FAST);
cmd->sjw = PUCAN_TFAST_SJW(pbt->sjw - 1);
cmd->tseg1 = PUCAN_TFAST_TSEG1(pbt->prop_seg + pbt->phase_seg1 - 1);
cmd->tseg2 = PUCAN_TFAST_TSEG2(pbt->phase_seg2 - 1);
cmd->brp = cpu_to_le16(PUCAN_TFAST_BRP(pbt->brp - 1));
netdev_dbg(priv->ndev,
"data: brp=%u tseg1=%u tseg2=%u sjw=%u\n",
le16_to_cpu(cmd->brp), cmd->tseg1, cmd->tseg2, cmd->sjw);
return pucan_write_cmd(priv);
}
static int pucan_set_std_filter(struct peak_canfd_priv *priv, u8 row, u32 mask)
{
struct pucan_std_filter *cmd;
cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_STD_FILTER);
/* all the 11-bits CAN ID values are represented by one bit in a
* 64 rows array of 32 bits: the upper 6 bits of the CAN ID select the
* row while the lowest 5 bits select the bit in that row.
*
* bit filter
* 1 passed
* 0 discarded
*/
/* select the row */
cmd->idx = row;
/* set/unset bits in the row */
cmd->mask = cpu_to_le32(mask);
return pucan_write_cmd(priv);
}
static int pucan_tx_abort(struct peak_canfd_priv *priv, u16 flags)
{
struct pucan_tx_abort *cmd;
cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TX_ABORT);
cmd->flags = cpu_to_le16(flags);
return pucan_write_cmd(priv);
}
static int pucan_clr_err_counters(struct peak_canfd_priv *priv)
{
struct pucan_wr_err_cnt *cmd;
cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_WR_ERR_CNT);
cmd->sel_mask = cpu_to_le16(PUCAN_WRERRCNT_TE | PUCAN_WRERRCNT_RE);
cmd->tx_counter = 0;
cmd->rx_counter = 0;
return pucan_write_cmd(priv);
}
static int pucan_set_options(struct peak_canfd_priv *priv, u16 opt_mask)
{
struct pucan_options *cmd;
cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_EN_OPTION);
cmd->options = cpu_to_le16(opt_mask);
return pucan_write_cmd(priv);
}
static int pucan_clr_options(struct peak_canfd_priv *priv, u16 opt_mask)
{
struct pucan_options *cmd;
cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_CLR_DIS_OPTION);
cmd->options = cpu_to_le16(opt_mask);
return pucan_write_cmd(priv);
}
static int pucan_setup_rx_barrier(struct peak_canfd_priv *priv)
{
pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RX_BARRIER);
return pucan_write_cmd(priv);
}
/* handle the reception of one CAN frame */
static int pucan_handle_can_rx(struct peak_canfd_priv *priv,
struct pucan_rx_msg *msg)
{
struct net_device_stats *stats = &priv->ndev->stats;
struct canfd_frame *cf;
struct sk_buff *skb;
const u16 rx_msg_flags = le16_to_cpu(msg->flags);
u8 cf_len;
if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN)
cf_len = can_dlc2len(get_canfd_dlc(pucan_msg_get_dlc(msg)));
else
cf_len = get_can_dlc(pucan_msg_get_dlc(msg));
/* if this frame is an echo, */
if ((rx_msg_flags & PUCAN_MSG_LOOPED_BACK) &&
!(rx_msg_flags & PUCAN_MSG_SELF_RECEIVE)) {
int n;
unsigned long flags;
spin_lock_irqsave(&priv->echo_lock, flags);
n = can_get_echo_skb(priv->ndev, msg->client);
spin_unlock_irqrestore(&priv->echo_lock, flags);
/* count bytes of the echo instead of skb */
stats->tx_bytes += cf_len;
stats->tx_packets++;
if (n) {
/* restart tx queue only if a slot is free */
netif_wake_queue(priv->ndev);
}
return 0;
}
/* otherwise, it should be pushed into rx fifo */
if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) {
/* CANFD frame case */
skb = alloc_canfd_skb(priv->ndev, &cf);
if (!skb)
return -ENOMEM;
if (rx_msg_flags & PUCAN_MSG_BITRATE_SWITCH)
cf->flags |= CANFD_BRS;
if (rx_msg_flags & PUCAN_MSG_ERROR_STATE_IND)
cf->flags |= CANFD_ESI;
} else {
/* CAN 2.0 frame case */
skb = alloc_can_skb(priv->ndev, (struct can_frame **)&cf);
if (!skb)
return -ENOMEM;
}
cf->can_id = le32_to_cpu(msg->can_id);
cf->len = cf_len;
if (rx_msg_flags & PUCAN_MSG_EXT_ID)
cf->can_id |= CAN_EFF_FLAG;
if (rx_msg_flags & PUCAN_MSG_RTR)
cf->can_id |= CAN_RTR_FLAG;
else
memcpy(cf->data, msg->d, cf->len);
stats->rx_bytes += cf->len;
stats->rx_packets++;
netif_rx(skb);
return 0;
}
/* handle rx/tx error counters notification */
static int pucan_handle_error(struct peak_canfd_priv *priv,
struct pucan_error_msg *msg)
{
priv->bec.txerr = msg->tx_err_cnt;
priv->bec.rxerr = msg->rx_err_cnt;
return 0;
}
/* handle status notification */
static int pucan_handle_status(struct peak_canfd_priv *priv,
struct pucan_status_msg *msg)
{
struct net_device *ndev = priv->ndev;
struct net_device_stats *stats = &ndev->stats;
struct can_frame *cf;
struct sk_buff *skb;
/* this STATUS is the CNF of the RX_BARRIER: Tx path can be setup */
if (pucan_status_is_rx_barrier(msg)) {
unsigned long flags;
if (priv->enable_tx_path) {
int err = priv->enable_tx_path(priv);
if (err)
return err;
}
/* restart network queue only if echo skb array is free */
spin_lock_irqsave(&priv->echo_lock, flags);
if (!priv->can.echo_skb[priv->echo_idx]) {
spin_unlock_irqrestore(&priv->echo_lock, flags);
netif_wake_queue(ndev);
} else {
spin_unlock_irqrestore(&priv->echo_lock, flags);
}
return 0;
}
skb = alloc_can_err_skb(ndev, &cf);
/* test state error bits according to their priority */
if (pucan_status_is_busoff(msg)) {
netdev_dbg(ndev, "Bus-off entry status\n");
priv->can.state = CAN_STATE_BUS_OFF;
priv->can.can_stats.bus_off++;
can_bus_off(ndev);
if (skb)
cf->can_id |= CAN_ERR_BUSOFF;
} else if (pucan_status_is_passive(msg)) {
netdev_dbg(ndev, "Error passive status\n");
priv->can.state = CAN_STATE_ERROR_PASSIVE;
priv->can.can_stats.error_passive++;
if (skb) {
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ?
CAN_ERR_CRTL_TX_PASSIVE :
CAN_ERR_CRTL_RX_PASSIVE;
cf->data[6] = priv->bec.txerr;
cf->data[7] = priv->bec.rxerr;
}
} else if (pucan_status_is_warning(msg)) {
netdev_dbg(ndev, "Error warning status\n");
priv->can.state = CAN_STATE_ERROR_WARNING;
priv->can.can_stats.error_warning++;
if (skb) {
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ?
CAN_ERR_CRTL_TX_WARNING :
CAN_ERR_CRTL_RX_WARNING;
cf->data[6] = priv->bec.txerr;
cf->data[7] = priv->bec.rxerr;
}
} else if (priv->can.state != CAN_STATE_ERROR_ACTIVE) {
/* back to ERROR_ACTIVE */
netdev_dbg(ndev, "Error active status\n");
can_change_state(ndev, cf, CAN_STATE_ERROR_ACTIVE,
CAN_STATE_ERROR_ACTIVE);
} else {
dev_kfree_skb(skb);
return 0;
}
if (!skb) {
stats->rx_dropped++;
return -ENOMEM;
}
stats->rx_packets++;
stats->rx_bytes += cf->can_dlc;
netif_rx(skb);
return 0;
}
/* handle uCAN Rx overflow notification */
static int pucan_handle_cache_critical(struct peak_canfd_priv *priv)
{
struct net_device_stats *stats = &priv->ndev->stats;
struct can_frame *cf;
struct sk_buff *skb;
stats->rx_over_errors++;
stats->rx_errors++;
skb = alloc_can_err_skb(priv->ndev, &cf);
if (!skb) {
stats->rx_dropped++;
return -ENOMEM;
}
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
cf->data[6] = priv->bec.txerr;
cf->data[7] = priv->bec.rxerr;
stats->rx_bytes += cf->can_dlc;
stats->rx_packets++;
netif_rx(skb);
return 0;
}
/* handle a single uCAN message */
int peak_canfd_handle_msg(struct peak_canfd_priv *priv,
struct pucan_rx_msg *msg)
{
u16 msg_type = le16_to_cpu(msg->type);
int msg_size = le16_to_cpu(msg->size);
int err;
if (!msg_size || !msg_type) {
/* null packet found: end of list */
goto exit;
}
switch (msg_type) {
case PUCAN_MSG_CAN_RX:
err = pucan_handle_can_rx(priv, (struct pucan_rx_msg *)msg);
break;
case PUCAN_MSG_ERROR:
err = pucan_handle_error(priv, (struct pucan_error_msg *)msg);
break;
case PUCAN_MSG_STATUS:
err = pucan_handle_status(priv, (struct pucan_status_msg *)msg);
break;
case PUCAN_MSG_CACHE_CRITICAL:
err = pucan_handle_cache_critical(priv);
break;
default:
err = 0;
}
if (err < 0)
return err;
exit:
return msg_size;
}
/* handle a list of rx_count messages from rx_msg memory address */
int peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv,
struct pucan_rx_msg *msg_list, int msg_count)
{
void *msg_ptr = msg_list;
int i, msg_size;
for (i = 0; i < msg_count; i++) {
msg_size = peak_canfd_handle_msg(priv, msg_ptr);
/* a null packet can be found at the end of a list */
if (msg_size <= 0)
break;
msg_ptr += msg_size;
}
if (msg_size < 0)
return msg_size;
return i;
}
static int peak_canfd_start(struct peak_canfd_priv *priv)
{
int err;
err = pucan_clr_err_counters(priv);
if (err)
goto err_exit;
priv->echo_idx = 0;
priv->bec.txerr = 0;
priv->bec.rxerr = 0;
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
err = pucan_set_listen_only_mode(priv);
else
err = pucan_set_normal_mode(priv);
err_exit:
return err;
}
static void peak_canfd_stop(struct peak_canfd_priv *priv)
{
int err;
/* go back to RESET mode */
err = pucan_set_reset_mode(priv);
if (err) {
netdev_err(priv->ndev, "channel %u reset failed\n",
priv->index);
} else {
/* abort last Tx (MUST be done in RESET mode only!) */
pucan_tx_abort(priv, PUCAN_TX_ABORT_FLUSH);
}
}
static int peak_canfd_set_mode(struct net_device *ndev, enum can_mode mode)
{
struct peak_canfd_priv *priv = netdev_priv(ndev);
switch (mode) {
case CAN_MODE_START:
peak_canfd_start(priv);
netif_wake_queue(ndev);
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int peak_canfd_get_berr_counter(const struct net_device *ndev,
struct can_berr_counter *bec)
{
struct peak_canfd_priv *priv = netdev_priv(ndev);
*bec = priv->bec;
return 0;
}
static int peak_canfd_open(struct net_device *ndev)
{
struct peak_canfd_priv *priv = netdev_priv(ndev);
int i, err = 0;
err = open_candev(ndev);
if (err) {
netdev_err(ndev, "open_candev() failed, error %d\n", err);
goto err_exit;
}
err = pucan_set_reset_mode(priv);
if (err)
goto err_close;
if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)
err = pucan_clr_options(priv, PUCAN_OPTION_CANDFDISO);
else
err = pucan_set_options(priv, PUCAN_OPTION_CANDFDISO);
if (err)
goto err_close;
}
/* set option: get rx/tx error counters */
err = pucan_set_options(priv, PUCAN_OPTION_ERROR);
if (err)
goto err_close;
/* accept all standard CAN ID */
for (i = 0; i <= PUCAN_FLTSTD_ROW_IDX_MAX; i++)
pucan_set_std_filter(priv, i, 0xffffffff);
err = peak_canfd_start(priv);
if (err)
goto err_close;
/* receiving the RB status says when Tx path is ready */
err = pucan_setup_rx_barrier(priv);
if (!err)
goto err_exit;
err_close:
close_candev(ndev);
err_exit:
return err;
}
static int peak_canfd_set_bittiming(struct net_device *ndev)
{
struct peak_canfd_priv *priv = netdev_priv(ndev);
return pucan_set_timing_slow(priv, &priv->can.bittiming);
}
static int peak_canfd_set_data_bittiming(struct net_device *ndev)
{
struct peak_canfd_priv *priv = netdev_priv(ndev);
return pucan_set_timing_fast(priv, &priv->can.data_bittiming);
}
static int peak_canfd_close(struct net_device *ndev)
{
struct peak_canfd_priv *priv = netdev_priv(ndev);
netif_stop_queue(ndev);
peak_canfd_stop(priv);
close_candev(ndev);
return 0;
}
static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
struct peak_canfd_priv *priv = netdev_priv(ndev);
struct net_device_stats *stats = &ndev->stats;
struct canfd_frame *cf = (struct canfd_frame *)skb->data;
struct pucan_tx_msg *msg;
u16 msg_size, msg_flags;
unsigned long flags;
bool should_stop_tx_queue;
int room_left;
u8 can_dlc;
if (can_dropped_invalid_skb(ndev, skb))
return NETDEV_TX_OK;
msg_size = ALIGN(sizeof(*msg) + cf->len, 4);
msg = priv->alloc_tx_msg(priv, msg_size, &room_left);
/* should never happen except under bus-off condition and (auto-)restart
* mechanism
*/
if (!msg) {
stats->tx_dropped++;
netif_stop_queue(ndev);
return NETDEV_TX_BUSY;
}
msg->size = cpu_to_le16(msg_size);
msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX);
msg_flags = 0;
if (cf->can_id & CAN_EFF_FLAG) {
msg_flags |= PUCAN_MSG_EXT_ID;
msg->can_id = cpu_to_le32(cf->can_id & CAN_EFF_MASK);
} else {
msg->can_id = cpu_to_le32(cf->can_id & CAN_SFF_MASK);
}
if (can_is_canfd_skb(skb)) {
/* CAN FD frame format */
can_dlc = can_len2dlc(cf->len);
msg_flags |= PUCAN_MSG_EXT_DATA_LEN;
if (cf->flags & CANFD_BRS)
msg_flags |= PUCAN_MSG_BITRATE_SWITCH;
if (cf->flags & CANFD_ESI)
msg_flags |= PUCAN_MSG_ERROR_STATE_IND;
} else {
/* CAN 2.0 frame format */
can_dlc = cf->len;
if (cf->can_id & CAN_RTR_FLAG)
msg_flags |= PUCAN_MSG_RTR;
}
/* always ask loopback for echo management */
msg_flags |= PUCAN_MSG_LOOPED_BACK;
/* set driver specific bit to differentiate with application loopback */
if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
msg_flags |= PUCAN_MSG_SELF_RECEIVE;
msg->flags = cpu_to_le16(msg_flags);
msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(priv->index, can_dlc);
memcpy(msg->d, cf->data, cf->len);
/* struct msg client field is used as an index in the echo skbs ring */
msg->client = priv->echo_idx;
spin_lock_irqsave(&priv->echo_lock, flags);
/* prepare and save echo skb in internal slot */
can_put_echo_skb(skb, ndev, priv->echo_idx);
/* move echo index to the next slot */
priv->echo_idx = (priv->echo_idx + 1) % priv->can.echo_skb_max;
/* if next slot is not free, stop network queue (no slot free in echo
* skb ring means that the controller did not write these frames on
* the bus: no need to continue).
*/
should_stop_tx_queue = !!(priv->can.echo_skb[priv->echo_idx]);
spin_unlock_irqrestore(&priv->echo_lock, flags);
/* write the skb on the interface */
priv->write_tx_msg(priv, msg);
/* stop network tx queue if not enough room to save one more msg too */
if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
should_stop_tx_queue |= (room_left <
(sizeof(*msg) + CANFD_MAX_DLEN));
else
should_stop_tx_queue |= (room_left <
(sizeof(*msg) + CAN_MAX_DLEN));
if (should_stop_tx_queue)
netif_stop_queue(ndev);
return NETDEV_TX_OK;
}
static const struct net_device_ops peak_canfd_netdev_ops = {
.ndo_open = peak_canfd_open,
.ndo_stop = peak_canfd_close,
.ndo_start_xmit = peak_canfd_start_xmit,
.ndo_change_mtu = can_change_mtu,
};
struct net_device *alloc_peak_canfd_dev(int sizeof_priv, int index,
int echo_skb_max)
{
struct net_device *ndev;
struct peak_canfd_priv *priv;
/* we DO support local echo */
if (echo_skb_max < 0)
echo_skb_max = PCANFD_ECHO_SKB_MAX;
/* allocate the candev object */
ndev = alloc_candev(sizeof_priv, echo_skb_max);
if (!ndev)
return NULL;
priv = netdev_priv(ndev);
/* complete now socket-can initialization side */
priv->can.state = CAN_STATE_STOPPED;
priv->can.bittiming_const = &peak_canfd_nominal_const;
priv->can.data_bittiming_const = &peak_canfd_data_const;
priv->can.do_set_mode = peak_canfd_set_mode;
priv->can.do_get_berr_counter = peak_canfd_get_berr_counter;
priv->can.do_set_bittiming = peak_canfd_set_bittiming;
priv->can.do_set_data_bittiming = peak_canfd_set_data_bittiming;
priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
CAN_CTRLMODE_LISTENONLY |
CAN_CTRLMODE_3_SAMPLES |
CAN_CTRLMODE_FD |
CAN_CTRLMODE_FD_NON_ISO |
CAN_CTRLMODE_BERR_REPORTING;
priv->ndev = ndev;
priv->index = index;
priv->cmd_len = 0;
spin_lock_init(&priv->echo_lock);
ndev->flags |= IFF_ECHO;
ndev->netdev_ops = &peak_canfd_netdev_ops;
ndev->dev_id = index;
return ndev;
}

View File

@ -0,0 +1,55 @@
/*
* CAN driver for PEAK System micro-CAN based adapters
*
* Copyright (C) 2003-2011 PEAK System-Technik GmbH
* Copyright (C) 2011-2013 Stephane Grosjean <s.grosjean@peak-system.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#ifndef PEAK_CANFD_USER_H
#define PEAK_CANFD_USER_H
#include <linux/can/dev/peak_canfd.h>
#define PCANFD_ECHO_SKB_DEF -1
/* data structure private to each uCAN interface */
struct peak_canfd_priv {
struct can_priv can; /* socket-can private data */
struct net_device *ndev; /* network device */
int index; /* channel index */
struct can_berr_counter bec; /* rx/tx err counters */
int echo_idx; /* echo skb free slot index */
spinlock_t echo_lock;
int cmd_len;
void *cmd_buffer;
int cmd_maxlen;
int (*pre_cmd)(struct peak_canfd_priv *priv);
int (*write_cmd)(struct peak_canfd_priv *priv);
int (*post_cmd)(struct peak_canfd_priv *priv);
int (*enable_tx_path)(struct peak_canfd_priv *priv);
void *(*alloc_tx_msg)(struct peak_canfd_priv *priv, u16 msg_size,
int *room_left);
int (*write_tx_msg)(struct peak_canfd_priv *priv,
struct pucan_tx_msg *msg);
};
struct net_device *alloc_peak_canfd_dev(int sizeof_priv, int index,
int echo_skb_max);
int peak_canfd_handle_msg(struct peak_canfd_priv *priv,
struct pucan_rx_msg *msg);
int peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv,
struct pucan_rx_msg *rx_msg, int rx_count);
#endif

View File

@ -0,0 +1,842 @@
/*
* Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com>
* Copyright (C) 2012 Stephane Grosjean <s.grosjean@peak-system.com>
*
* Derived from the PCAN project file driver/src/pcan_pci.c:
*
* Copyright (C) 2001-2006 PEAK System-Technik GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the version 2 of the GNU General Public License
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include "peak_canfd_user.h"
MODULE_AUTHOR("Stephane Grosjean <s.grosjean@peak-system.com>");
MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCIe FD family cards");
MODULE_SUPPORTED_DEVICE("PEAK PCAN PCIe FD CAN cards");
MODULE_LICENSE("GPL v2");
#define PCIEFD_DRV_NAME "peak_pciefd"
#define PEAK_PCI_VENDOR_ID 0x001c /* The PCI device and vendor IDs */
#define PEAK_PCIEFD_ID 0x0013 /* for PCIe slot cards */
/* PEAK PCIe board access description */
#define PCIEFD_BAR0_SIZE (64 * 1024)
#define PCIEFD_RX_DMA_SIZE (4 * 1024)
#define PCIEFD_TX_DMA_SIZE (4 * 1024)
#define PCIEFD_TX_PAGE_SIZE (2 * 1024)
/* System Control Registers */
#define PCIEFD_REG_SYS_CTL_SET 0x0000 /* set bits */
#define PCIEFD_REG_SYS_CTL_CLR 0x0004 /* clear bits */
/* Version info registers */
#define PCIEFD_REG_SYS_VER1 0x0040 /* version reg #1 */
#define PCIEFD_REG_SYS_VER2 0x0044 /* version reg #2 */
/* System Control Registers Bits */
#define PCIEFD_SYS_CTL_TS_RST 0x00000001 /* timestamp clock */
#define PCIEFD_SYS_CTL_CLK_EN 0x00000002 /* system clock */
/* CAN-FD channel addresses */
#define PCIEFD_CANX_OFF(c) (((c) + 1) * 0x1000)
#define PCIEFD_ECHO_SKB_MAX PCANFD_ECHO_SKB_DEF
/* CAN-FD channel registers */
#define PCIEFD_REG_CAN_MISC 0x0000 /* Misc. control */
#define PCIEFD_REG_CAN_CLK_SEL 0x0008 /* Clock selector */
#define PCIEFD_REG_CAN_CMD_PORT_L 0x0010 /* 64-bits command port */
#define PCIEFD_REG_CAN_CMD_PORT_H 0x0014
#define PCIEFD_REG_CAN_TX_REQ_ACC 0x0020 /* Tx request accumulator */
#define PCIEFD_REG_CAN_TX_CTL_SET 0x0030 /* Tx control set register */
#define PCIEFD_REG_CAN_TX_CTL_CLR 0x0038 /* Tx control clear register */
#define PCIEFD_REG_CAN_TX_DMA_ADDR_L 0x0040 /* 64-bits addr for Tx DMA */
#define PCIEFD_REG_CAN_TX_DMA_ADDR_H 0x0044
#define PCIEFD_REG_CAN_RX_CTL_SET 0x0050 /* Rx control set register */
#define PCIEFD_REG_CAN_RX_CTL_CLR 0x0058 /* Rx control clear register */
#define PCIEFD_REG_CAN_RX_CTL_WRT 0x0060 /* Rx control write register */
#define PCIEFD_REG_CAN_RX_CTL_ACK 0x0068 /* Rx control ACK register */
#define PCIEFD_REG_CAN_RX_DMA_ADDR_L 0x0070 /* 64-bits addr for Rx DMA */
#define PCIEFD_REG_CAN_RX_DMA_ADDR_H 0x0074
/* CAN-FD channel misc register bits */
#define CANFD_MISC_TS_RST 0x00000001 /* timestamp cnt rst */
/* CAN-FD channel Clock SELector Source & DIVider */
#define CANFD_CLK_SEL_DIV_MASK 0x00000007
#define CANFD_CLK_SEL_DIV_60MHZ 0x00000000 /* SRC=240MHz only */
#define CANFD_CLK_SEL_DIV_40MHZ 0x00000001 /* SRC=240MHz only */
#define CANFD_CLK_SEL_DIV_30MHZ 0x00000002 /* SRC=240MHz only */
#define CANFD_CLK_SEL_DIV_24MHZ 0x00000003 /* SRC=240MHz only */
#define CANFD_CLK_SEL_DIV_20MHZ 0x00000004 /* SRC=240MHz only */
#define CANFD_CLK_SEL_SRC_MASK 0x00000008 /* 0=80MHz, 1=240MHz */
#define CANFD_CLK_SEL_SRC_240MHZ 0x00000008
#define CANFD_CLK_SEL_SRC_80MHZ (~CANFD_CLK_SEL_SRC_240MHZ & \
CANFD_CLK_SEL_SRC_MASK)
#define CANFD_CLK_SEL_20MHZ (CANFD_CLK_SEL_SRC_240MHZ |\
CANFD_CLK_SEL_DIV_20MHZ)
#define CANFD_CLK_SEL_24MHZ (CANFD_CLK_SEL_SRC_240MHZ |\
CANFD_CLK_SEL_DIV_24MHZ)
#define CANFD_CLK_SEL_30MHZ (CANFD_CLK_SEL_SRC_240MHZ |\
CANFD_CLK_SEL_DIV_30MHZ)
#define CANFD_CLK_SEL_40MHZ (CANFD_CLK_SEL_SRC_240MHZ |\
CANFD_CLK_SEL_DIV_40MHZ)
#define CANFD_CLK_SEL_60MHZ (CANFD_CLK_SEL_SRC_240MHZ |\
CANFD_CLK_SEL_DIV_60MHZ)
#define CANFD_CLK_SEL_80MHZ (CANFD_CLK_SEL_SRC_80MHZ)
/* CAN-FD channel Rx/Tx control register bits */
#define CANFD_CTL_UNC_BIT 0x00010000 /* Uncached DMA mem */
#define CANFD_CTL_RST_BIT 0x00020000 /* reset DMA action */
#define CANFD_CTL_IEN_BIT 0x00040000 /* IRQ enable */
/* Rx IRQ Count and Time Limits */
#define CANFD_CTL_IRQ_CL_DEF 16 /* Rx msg max nb per IRQ in Rx DMA */
#define CANFD_CTL_IRQ_TL_DEF 10 /* Time before IRQ if < CL (x100 µs) */
#define CANFD_OPTIONS_SET (CANFD_OPTION_ERROR | CANFD_OPTION_BUSLOAD)
/* Tx anticipation window (link logical address should be aligned on 2K
* boundary)
*/
#define PCIEFD_TX_PAGE_COUNT (PCIEFD_TX_DMA_SIZE / PCIEFD_TX_PAGE_SIZE)
#define CANFD_MSG_LNK_TX 0x1001 /* Tx msgs link */
/* 32-bits IRQ status fields, heading Rx DMA area */
static inline int pciefd_irq_tag(u32 irq_status)
{
return irq_status & 0x0000000f;
}
static inline int pciefd_irq_rx_cnt(u32 irq_status)
{
return (irq_status & 0x000007f0) >> 4;
}
static inline int pciefd_irq_is_lnk(u32 irq_status)
{
return irq_status & 0x00010000;
}
/* Rx record */
struct pciefd_rx_dma {
__le32 irq_status;
__le32 sys_time_low;
__le32 sys_time_high;
struct pucan_rx_msg msg[0];
} __packed __aligned(4);
/* Tx Link record */
struct pciefd_tx_link {
__le16 size;
__le16 type;
__le32 laddr_lo;
__le32 laddr_hi;
} __packed __aligned(4);
/* Tx page descriptor */
struct pciefd_page {
void *vbase; /* page virtual address */
dma_addr_t lbase; /* page logical address */
u32 offset;
u32 size;
};
#define CANFD_IRQ_SET 0x00000001
#define CANFD_TX_PATH_SET 0x00000002
/* CAN-FD channel object */
struct pciefd_board;
struct pciefd_can {
struct peak_canfd_priv ucan; /* must be the first member */
void __iomem *reg_base; /* channel config base addr */
struct pciefd_board *board; /* reverse link */
struct pucan_command pucan_cmd; /* command buffer */
dma_addr_t rx_dma_laddr; /* DMA virtual and logical addr */
void *rx_dma_vaddr; /* for Rx and Tx areas */
dma_addr_t tx_dma_laddr;
void *tx_dma_vaddr;
struct pciefd_page tx_pages[PCIEFD_TX_PAGE_COUNT];
u16 tx_pages_free; /* free Tx pages counter */
u16 tx_page_index; /* current page used for Tx */
spinlock_t tx_lock;
u32 irq_status;
u32 irq_tag; /* next irq tag */
};
/* PEAK-PCIe FD board object */
struct pciefd_board {
void __iomem *reg_base;
struct pci_dev *pci_dev;
int can_count;
spinlock_t cmd_lock; /* 64-bits cmds must be atomic */
struct pciefd_can *can[0]; /* array of network devices */
};
/* supported device ids. */
static const struct pci_device_id peak_pciefd_tbl[] = {
{PEAK_PCI_VENDOR_ID, PEAK_PCIEFD_ID, PCI_ANY_ID, PCI_ANY_ID,},
{0,}
};
MODULE_DEVICE_TABLE(pci, peak_pciefd_tbl);
/* read a 32 bits value from a SYS block register */
static inline u32 pciefd_sys_readreg(const struct pciefd_board *priv, u16 reg)
{
return readl(priv->reg_base + reg);
}
/* write a 32 bits value into a SYS block register */
static inline void pciefd_sys_writereg(const struct pciefd_board *priv,
u32 val, u16 reg)
{
writel(val, priv->reg_base + reg);
}
/* read a 32 bits value from CAN-FD block register */
static inline u32 pciefd_can_readreg(const struct pciefd_can *priv, u16 reg)
{
return readl(priv->reg_base + reg);
}
/* write a 32 bits value into a CAN-FD block register */
static inline void pciefd_can_writereg(const struct pciefd_can *priv,
u32 val, u16 reg)
{
writel(val, priv->reg_base + reg);
}
/* give a channel logical Rx DMA address to the board */
static void pciefd_can_setup_rx_dma(struct pciefd_can *priv)
{
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
const u32 dma_addr_h = (u32)(priv->rx_dma_laddr >> 32);
#else
const u32 dma_addr_h = 0;
#endif
/* (DMA must be reset for Rx) */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET);
/* write the logical address of the Rx DMA area for this channel */
pciefd_can_writereg(priv, (u32)priv->rx_dma_laddr,
PCIEFD_REG_CAN_RX_DMA_ADDR_L);
pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_RX_DMA_ADDR_H);
/* also indicates that Rx DMA is cacheable */
pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_RX_CTL_CLR);
}
/* clear channel logical Rx DMA address from the board */
static void pciefd_can_clear_rx_dma(struct pciefd_can *priv)
{
/* DMA must be reset for Rx */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET);
/* clear the logical address of the Rx DMA area for this channel */
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_L);
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_H);
}
/* give a channel logical Tx DMA address to the board */
static void pciefd_can_setup_tx_dma(struct pciefd_can *priv)
{
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
const u32 dma_addr_h = (u32)(priv->tx_dma_laddr >> 32);
#else
const u32 dma_addr_h = 0;
#endif
/* (DMA must be reset for Tx) */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET);
/* write the logical address of the Tx DMA area for this channel */
pciefd_can_writereg(priv, (u32)priv->tx_dma_laddr,
PCIEFD_REG_CAN_TX_DMA_ADDR_L);
pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_TX_DMA_ADDR_H);
/* also indicates that Tx DMA is cacheable */
pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_TX_CTL_CLR);
}
/* clear channel logical Tx DMA address from the board */
static void pciefd_can_clear_tx_dma(struct pciefd_can *priv)
{
/* DMA must be reset for Tx */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET);
/* clear the logical address of the Tx DMA area for this channel */
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_L);
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_H);
}
static void pciefd_can_ack_rx_dma(struct pciefd_can *priv)
{
/* read value of current IRQ tag and inc it for next one */
priv->irq_tag = le32_to_cpu(*(__le32 *)priv->rx_dma_vaddr);
priv->irq_tag++;
priv->irq_tag &= 0xf;
/* write the next IRQ tag for this CAN */
pciefd_can_writereg(priv, priv->irq_tag, PCIEFD_REG_CAN_RX_CTL_ACK);
}
/* IRQ handler */
static irqreturn_t pciefd_irq_handler(int irq, void *arg)
{
struct pciefd_can *priv = arg;
struct pciefd_rx_dma *rx_dma = priv->rx_dma_vaddr;
/* INTA mode only to sync with PCIe transaction */
if (!pci_dev_msi_enabled(priv->board->pci_dev))
(void)pciefd_sys_readreg(priv->board, PCIEFD_REG_SYS_VER1);
/* read IRQ status from the first 32-bits of the Rx DMA area */
priv->irq_status = le32_to_cpu(rx_dma->irq_status);
/* check if this (shared) IRQ is for this CAN */
if (pciefd_irq_tag(priv->irq_status) != priv->irq_tag)
return IRQ_NONE;
/* handle rx messages (if any) */
peak_canfd_handle_msgs_list(&priv->ucan,
rx_dma->msg,
pciefd_irq_rx_cnt(priv->irq_status));
/* handle tx link interrupt (if any) */
if (pciefd_irq_is_lnk(priv->irq_status)) {
unsigned long flags;
spin_lock_irqsave(&priv->tx_lock, flags);
priv->tx_pages_free++;
spin_unlock_irqrestore(&priv->tx_lock, flags);
/* wake producer up */
netif_wake_queue(priv->ucan.ndev);
}
/* re-enable Rx DMA transfer for this CAN */
pciefd_can_ack_rx_dma(priv);
return IRQ_HANDLED;
}
static int pciefd_enable_tx_path(struct peak_canfd_priv *ucan)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
int i;
/* initialize the Tx pages descriptors */
priv->tx_pages_free = PCIEFD_TX_PAGE_COUNT - 1;
priv->tx_page_index = 0;
priv->tx_pages[0].vbase = priv->tx_dma_vaddr;
priv->tx_pages[0].lbase = priv->tx_dma_laddr;
for (i = 0; i < PCIEFD_TX_PAGE_COUNT; i++) {
priv->tx_pages[i].offset = 0;
priv->tx_pages[i].size = PCIEFD_TX_PAGE_SIZE -
sizeof(struct pciefd_tx_link);
if (i) {
priv->tx_pages[i].vbase =
priv->tx_pages[i - 1].vbase +
PCIEFD_TX_PAGE_SIZE;
priv->tx_pages[i].lbase =
priv->tx_pages[i - 1].lbase +
PCIEFD_TX_PAGE_SIZE;
}
}
/* setup Tx DMA addresses into IP core */
pciefd_can_setup_tx_dma(priv);
/* start (TX_RST=0) Tx Path */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_CLR);
return 0;
}
/* board specific CANFD command pre-processing */
static int pciefd_pre_cmd(struct peak_canfd_priv *ucan)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd);
int err;
/* pre-process command */
switch (cmd) {
case PUCAN_CMD_NORMAL_MODE:
case PUCAN_CMD_LISTEN_ONLY_MODE:
if (ucan->can.state == CAN_STATE_BUS_OFF)
break;
/* going into operational mode: setup IRQ handler */
err = request_irq(priv->board->pci_dev->irq,
pciefd_irq_handler,
IRQF_SHARED,
PCIEFD_DRV_NAME,
priv);
if (err)
return err;
/* setup Rx DMA address */
pciefd_can_setup_rx_dma(priv);
/* setup max count of msgs per IRQ */
pciefd_can_writereg(priv, (CANFD_CTL_IRQ_TL_DEF) << 8 |
CANFD_CTL_IRQ_CL_DEF,
PCIEFD_REG_CAN_RX_CTL_WRT);
/* clear DMA RST for Rx (Rx start) */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT,
PCIEFD_REG_CAN_RX_CTL_CLR);
/* reset timestamps */
pciefd_can_writereg(priv, !CANFD_MISC_TS_RST,
PCIEFD_REG_CAN_MISC);
/* do an initial ACK */
pciefd_can_ack_rx_dma(priv);
/* enable IRQ for this CAN after having set next irq_tag */
pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT,
PCIEFD_REG_CAN_RX_CTL_SET);
/* Tx path will be setup as soon as RX_BARRIER is received */
break;
default:
break;
}
return 0;
}
/* write a command */
static int pciefd_write_cmd(struct peak_canfd_priv *ucan)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
unsigned long flags;
/* 64-bits command is atomic */
spin_lock_irqsave(&priv->board->cmd_lock, flags);
pciefd_can_writereg(priv, *(u32 *)ucan->cmd_buffer,
PCIEFD_REG_CAN_CMD_PORT_L);
pciefd_can_writereg(priv, *(u32 *)(ucan->cmd_buffer + 4),
PCIEFD_REG_CAN_CMD_PORT_H);
spin_unlock_irqrestore(&priv->board->cmd_lock, flags);
return 0;
}
/* board specific CANFD command post-processing */
static int pciefd_post_cmd(struct peak_canfd_priv *ucan)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd);
switch (cmd) {
case PUCAN_CMD_RESET_MODE:
if (ucan->can.state == CAN_STATE_STOPPED)
break;
/* controller now in reset mode: */
/* stop and reset DMA addresses in Tx/Rx engines */
pciefd_can_clear_tx_dma(priv);
pciefd_can_clear_rx_dma(priv);
/* disable IRQ for this CAN */
pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT,
PCIEFD_REG_CAN_RX_CTL_CLR);
free_irq(priv->board->pci_dev->irq, priv);
ucan->can.state = CAN_STATE_STOPPED;
break;
}
return 0;
}
static void *pciefd_alloc_tx_msg(struct peak_canfd_priv *ucan, u16 msg_size,
int *room_left)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
struct pciefd_page *page = priv->tx_pages + priv->tx_page_index;
unsigned long flags;
void *msg;
spin_lock_irqsave(&priv->tx_lock, flags);
if (page->offset + msg_size > page->size) {
struct pciefd_tx_link *lk;
/* not enough space in this page: try another one */
if (!priv->tx_pages_free) {
spin_unlock_irqrestore(&priv->tx_lock, flags);
/* Tx overflow */
return NULL;
}
priv->tx_pages_free--;
/* keep address of the very last free slot of current page */
lk = page->vbase + page->offset;
/* next, move on a new free page */
priv->tx_page_index = (priv->tx_page_index + 1) %
PCIEFD_TX_PAGE_COUNT;
page = priv->tx_pages + priv->tx_page_index;
/* put link record to this new page at the end of prev one */
lk->size = cpu_to_le16(sizeof(*lk));
lk->type = cpu_to_le16(CANFD_MSG_LNK_TX);
lk->laddr_lo = cpu_to_le32(page->lbase);
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
lk->laddr_hi = cpu_to_le32(page->lbase >> 32);
#else
lk->laddr_hi = 0;
#endif
/* next msgs will be put from the begininng of this new page */
page->offset = 0;
}
*room_left = priv->tx_pages_free * page->size;
spin_unlock_irqrestore(&priv->tx_lock, flags);
msg = page->vbase + page->offset;
/* give back room left in the tx ring */
*room_left += page->size - (page->offset + msg_size);
return msg;
}
static int pciefd_write_tx_msg(struct peak_canfd_priv *ucan,
struct pucan_tx_msg *msg)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
struct pciefd_page *page = priv->tx_pages + priv->tx_page_index;
/* this slot is now reserved for writing the frame */
page->offset += le16_to_cpu(msg->size);
/* tell the board a frame has been written in Tx DMA area */
pciefd_can_writereg(priv, 1, PCIEFD_REG_CAN_TX_REQ_ACC);
return 0;
}
/* probe for CAN-FD channel #pciefd_board->can_count */
static int pciefd_can_probe(struct pciefd_board *pciefd)
{
struct net_device *ndev;
struct pciefd_can *priv;
u32 clk;
int err;
/* allocate the candev object with default isize of echo skbs ring */
ndev = alloc_peak_canfd_dev(sizeof(*priv), pciefd->can_count,
PCIEFD_ECHO_SKB_MAX);
if (!ndev) {
dev_err(&pciefd->pci_dev->dev,
"failed to alloc candev object\n");
goto failure;
}
priv = netdev_priv(ndev);
/* fill-in candev private object: */
/* setup PCIe-FD own callbacks */
priv->ucan.pre_cmd = pciefd_pre_cmd;
priv->ucan.write_cmd = pciefd_write_cmd;
priv->ucan.post_cmd = pciefd_post_cmd;
priv->ucan.enable_tx_path = pciefd_enable_tx_path;
priv->ucan.alloc_tx_msg = pciefd_alloc_tx_msg;
priv->ucan.write_tx_msg = pciefd_write_tx_msg;
/* setup PCIe-FD own command buffer */
priv->ucan.cmd_buffer = &priv->pucan_cmd;
priv->ucan.cmd_maxlen = sizeof(priv->pucan_cmd);
priv->board = pciefd;
/* CAN config regs block address */
priv->reg_base = pciefd->reg_base + PCIEFD_CANX_OFF(priv->ucan.index);
/* allocate non-cacheable DMA'able 4KB memory area for Rx */
priv->rx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev,
PCIEFD_RX_DMA_SIZE,
&priv->rx_dma_laddr,
GFP_KERNEL);
if (!priv->rx_dma_vaddr) {
dev_err(&pciefd->pci_dev->dev,
"Rx dmam_alloc_coherent(%u) failure\n",
PCIEFD_RX_DMA_SIZE);
goto err_free_candev;
}
/* allocate non-cacheable DMA'able 4KB memory area for Tx */
priv->tx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev,
PCIEFD_TX_DMA_SIZE,
&priv->tx_dma_laddr,
GFP_KERNEL);
if (!priv->tx_dma_vaddr) {
dev_err(&pciefd->pci_dev->dev,
"Tx dmaim_alloc_coherent(%u) failure\n",
PCIEFD_TX_DMA_SIZE);
goto err_free_candev;
}
/* CAN clock in RST mode */
pciefd_can_writereg(priv, CANFD_MISC_TS_RST, PCIEFD_REG_CAN_MISC);
/* read current clock value */
clk = pciefd_can_readreg(priv, PCIEFD_REG_CAN_CLK_SEL);
switch (clk) {
case CANFD_CLK_SEL_20MHZ:
priv->ucan.can.clock.freq = 20 * 1000 * 1000;
break;
case CANFD_CLK_SEL_24MHZ:
priv->ucan.can.clock.freq = 24 * 1000 * 1000;
break;
case CANFD_CLK_SEL_30MHZ:
priv->ucan.can.clock.freq = 30 * 1000 * 1000;
break;
case CANFD_CLK_SEL_40MHZ:
priv->ucan.can.clock.freq = 40 * 1000 * 1000;
break;
case CANFD_CLK_SEL_60MHZ:
priv->ucan.can.clock.freq = 60 * 1000 * 1000;
break;
default:
pciefd_can_writereg(priv, CANFD_CLK_SEL_80MHZ,
PCIEFD_REG_CAN_CLK_SEL);
/* fallthough */
case CANFD_CLK_SEL_80MHZ:
priv->ucan.can.clock.freq = 80 * 1000 * 1000;
break;
}
ndev->irq = pciefd->pci_dev->irq;
SET_NETDEV_DEV(ndev, &pciefd->pci_dev->dev);
err = register_candev(ndev);
if (err) {
dev_err(&pciefd->pci_dev->dev,
"couldn't register CAN device: %d\n", err);
goto err_free_candev;
}
spin_lock_init(&priv->tx_lock);
/* save the object address in the board structure */
pciefd->can[pciefd->can_count] = priv;
dev_info(&pciefd->pci_dev->dev, "%s at reg_base=0x%p irq=%d\n",
ndev->name, priv->reg_base, pciefd->pci_dev->irq);
return 0;
err_free_candev:
free_candev(ndev);
failure:
return -ENOMEM;
}
/* remove a CAN-FD channel by releasing all of its resources */
static void pciefd_can_remove(struct pciefd_can *priv)
{
/* unregister (close) the can device to go back to RST mode first */
unregister_candev(priv->ucan.ndev);
/* finally, free the candev object */
free_candev(priv->ucan.ndev);
}
/* remove all CAN-FD channels by releasing their own resources */
static void pciefd_can_remove_all(struct pciefd_board *pciefd)
{
while (pciefd->can_count > 0)
pciefd_can_remove(pciefd->can[--pciefd->can_count]);
}
/* probe for the entire device */
static int peak_pciefd_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct pciefd_board *pciefd;
int err, can_count;
u16 sub_sys_id;
u8 hw_ver_major;
u8 hw_ver_minor;
u8 hw_ver_sub;
u32 v2;
err = pci_enable_device(pdev);
if (err)
return err;
err = pci_request_regions(pdev, PCIEFD_DRV_NAME);
if (err)
goto err_disable_pci;
/* the number of channels depends on sub-system id */
err = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sub_sys_id);
if (err)
goto err_release_regions;
dev_dbg(&pdev->dev, "probing device %04x:%04x:%04x\n",
pdev->vendor, pdev->device, sub_sys_id);
if (sub_sys_id >= 0x0012)
can_count = 4;
else if (sub_sys_id >= 0x0010)
can_count = 3;
else if (sub_sys_id >= 0x0004)
can_count = 2;
else
can_count = 1;
/* allocate board structure object */
pciefd = devm_kzalloc(&pdev->dev, sizeof(*pciefd) +
can_count * sizeof(*pciefd->can),
GFP_KERNEL);
if (!pciefd) {
err = -ENOMEM;
goto err_release_regions;
}
/* initialize the board structure */
pciefd->pci_dev = pdev;
spin_lock_init(&pciefd->cmd_lock);
/* save the PCI BAR0 virtual address for further system regs access */
pciefd->reg_base = pci_iomap(pdev, 0, PCIEFD_BAR0_SIZE);
if (!pciefd->reg_base) {
dev_err(&pdev->dev, "failed to map PCI resource #0\n");
err = -ENOMEM;
goto err_release_regions;
}
/* read the firmware version number */
v2 = pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER2);
hw_ver_major = (v2 & 0x0000f000) >> 12;
hw_ver_minor = (v2 & 0x00000f00) >> 8;
hw_ver_sub = (v2 & 0x000000f0) >> 4;
dev_info(&pdev->dev,
"%ux CAN-FD PCAN-PCIe FPGA v%u.%u.%u:\n", can_count,
hw_ver_major, hw_ver_minor, hw_ver_sub);
/* stop system clock */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN,
PCIEFD_REG_SYS_CTL_CLR);
pci_set_master(pdev);
/* create now the corresponding channels objects */
while (pciefd->can_count < can_count) {
err = pciefd_can_probe(pciefd);
if (err)
goto err_free_canfd;
pciefd->can_count++;
}
/* set system timestamps counter in RST mode */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST,
PCIEFD_REG_SYS_CTL_SET);
/* wait a bit (read cycle) */
(void)pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER1);
/* free all clocks */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST,
PCIEFD_REG_SYS_CTL_CLR);
/* start system clock */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN,
PCIEFD_REG_SYS_CTL_SET);
/* remember the board structure address in the device user data */
pci_set_drvdata(pdev, pciefd);
return 0;
err_free_canfd:
pciefd_can_remove_all(pciefd);
pci_iounmap(pdev, pciefd->reg_base);
err_release_regions:
pci_release_regions(pdev);
err_disable_pci:
pci_disable_device(pdev);
return err;
}
/* free the board structure object, as well as its resources: */
static void peak_pciefd_remove(struct pci_dev *pdev)
{
struct pciefd_board *pciefd = pci_get_drvdata(pdev);
/* release CAN-FD channels resources */
pciefd_can_remove_all(pciefd);
pci_iounmap(pdev, pciefd->reg_base);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
static struct pci_driver peak_pciefd_driver = {
.name = PCIEFD_DRV_NAME,
.id_table = peak_pciefd_tbl,
.probe = peak_pciefd_probe,
.remove = peak_pciefd_remove,
};
module_pci_driver(peak_pciefd_driver);

View File

@ -898,9 +898,9 @@ static int ti_hecc_probe(struct platform_device *pdev)
}
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (!priv->base) {
if (IS_ERR(priv->base)) {
dev_err(&pdev->dev, "hecc ioremap failed\n");
return -ENOMEM;
return PTR_ERR(priv->base);
}
/* handle hecc-ram memory */
@ -911,9 +911,9 @@ static int ti_hecc_probe(struct platform_device *pdev)
}
priv->hecc_ram = devm_ioremap_resource(&pdev->dev, res);
if (!priv->hecc_ram) {
if (IS_ERR(priv->hecc_ram)) {
dev_err(&pdev->dev, "hecc-ram ioremap failed\n");
return -ENOMEM;
return PTR_ERR(priv->hecc_ram);
}
/* handle mbx memory */
@ -924,9 +924,9 @@ static int ti_hecc_probe(struct platform_device *pdev)
}
priv->mbx = devm_ioremap_resource(&pdev->dev, res);
if (!priv->mbx) {
if (IS_ERR(priv->mbx)) {
dev_err(&pdev->dev, "mbx ioremap failed\n");
return -ENOMEM;
return PTR_ERR(priv->mbx);
}
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

View File

@ -81,4 +81,10 @@ config CAN_8DEV_USB
This driver supports the USB2CAN interface
from 8 devices (http://www.8devices.com).
config CAN_MCBA_USB
tristate "Microchip CAN BUS Analyzer interface"
---help---
This driver supports the CAN BUS Analyzer interface
from Microchip (http://www.microchip.com/development-tools/).
endmenu

View File

@ -8,3 +8,4 @@ obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o

View File

@ -0,0 +1,904 @@
/* SocketCAN driver for Microchip CAN BUS Analyzer Tool
*
* Copyright (C) 2017 Mobica Limited
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program.
*
* This driver is inspired by the 4.6.2 version of net/can/usb/usb_8dev.c
*/
#include <asm/unaligned.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
#include <linux/can/led.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/usb.h>
/* vendor and product id */
#define MCBA_MODULE_NAME "mcba_usb"
#define MCBA_VENDOR_ID 0x04d8
#define MCBA_PRODUCT_ID 0x0a30
/* driver constants */
#define MCBA_MAX_RX_URBS 20
#define MCBA_MAX_TX_URBS 20
#define MCBA_CTX_FREE MCBA_MAX_TX_URBS
/* RX buffer must be bigger than msg size since at the
* beggining USB messages are stacked.
*/
#define MCBA_USB_RX_BUFF_SIZE 64
#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg))
/* MCBA endpoint numbers */
#define MCBA_USB_EP_IN 1
#define MCBA_USB_EP_OUT 1
/* Microchip command id */
#define MBCA_CMD_RECEIVE_MESSAGE 0xE3
#define MBCA_CMD_I_AM_ALIVE_FROM_CAN 0xF5
#define MBCA_CMD_I_AM_ALIVE_FROM_USB 0xF7
#define MBCA_CMD_CHANGE_BIT_RATE 0xA1
#define MBCA_CMD_TRANSMIT_MESSAGE_EV 0xA3
#define MBCA_CMD_SETUP_TERMINATION_RESISTANCE 0xA8
#define MBCA_CMD_READ_FW_VERSION 0xA9
#define MBCA_CMD_NOTHING_TO_SEND 0xFF
#define MBCA_CMD_TRANSMIT_MESSAGE_RSP 0xE2
#define MCBA_VER_REQ_USB 1
#define MCBA_VER_REQ_CAN 2
#define MCBA_SIDL_EXID_MASK 0x8
#define MCBA_DLC_MASK 0xf
#define MCBA_DLC_RTR_MASK 0x40
#define MCBA_CAN_STATE_WRN_TH 95
#define MCBA_CAN_STATE_ERR_PSV_TH 127
#define MCBA_TERMINATION_DISABLED CAN_TERMINATION_DISABLED
#define MCBA_TERMINATION_ENABLED 120
struct mcba_usb_ctx {
struct mcba_priv *priv;
u32 ndx;
u8 dlc;
bool can;
};
/* Structure to hold all of our device specific stuff */
struct mcba_priv {
struct can_priv can; /* must be the first member */
struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
struct usb_device *udev;
struct net_device *netdev;
struct usb_anchor tx_submitted;
struct usb_anchor rx_submitted;
struct can_berr_counter bec;
bool usb_ka_first_pass;
bool can_ka_first_pass;
bool can_speed_check;
atomic_t free_ctx_cnt;
};
/* CAN frame */
struct __packed mcba_usb_msg_can {
u8 cmd_id;
__be16 eid;
__be16 sid;
u8 dlc;
u8 data[8];
u8 timestamp[4];
u8 checksum;
};
/* command frame */
struct __packed mcba_usb_msg {
u8 cmd_id;
u8 unused[18];
};
struct __packed mcba_usb_msg_ka_usb {
u8 cmd_id;
u8 termination_state;
u8 soft_ver_major;
u8 soft_ver_minor;
u8 unused[15];
};
struct __packed mcba_usb_msg_ka_can {
u8 cmd_id;
u8 tx_err_cnt;
u8 rx_err_cnt;
u8 rx_buff_ovfl;
u8 tx_bus_off;
__be16 can_bitrate;
__le16 rx_lost;
u8 can_stat;
u8 soft_ver_major;
u8 soft_ver_minor;
u8 debug_mode;
u8 test_complete;
u8 test_result;
u8 unused[4];
};
struct __packed mcba_usb_msg_change_bitrate {
u8 cmd_id;
__be16 bitrate;
u8 unused[16];
};
struct __packed mcba_usb_msg_termination {
u8 cmd_id;
u8 termination;
u8 unused[17];
};
struct __packed mcba_usb_msg_fw_ver {
u8 cmd_id;
u8 pic;
u8 unused[17];
};
static const struct usb_device_id mcba_usb_table[] = {
{ USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, mcba_usb_table);
static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED,
MCBA_TERMINATION_ENABLED };
static const u32 mcba_bitrate[] = { 20000, 33333, 50000, 80000, 83333,
100000, 125000, 150000, 175000, 200000,
225000, 250000, 275000, 300000, 500000,
625000, 800000, 1000000 };
static inline void mcba_init_ctx(struct mcba_priv *priv)
{
int i = 0;
for (i = 0; i < MCBA_MAX_TX_URBS; i++) {
priv->tx_context[i].ndx = MCBA_CTX_FREE;
priv->tx_context[i].priv = priv;
}
atomic_set(&priv->free_ctx_cnt, ARRAY_SIZE(priv->tx_context));
}
static inline struct mcba_usb_ctx *mcba_usb_get_free_ctx(struct mcba_priv *priv,
struct can_frame *cf)
{
int i = 0;
struct mcba_usb_ctx *ctx = NULL;
for (i = 0; i < MCBA_MAX_TX_URBS; i++) {
if (priv->tx_context[i].ndx == MCBA_CTX_FREE) {
ctx = &priv->tx_context[i];
ctx->ndx = i;
if (cf) {
ctx->can = true;
ctx->dlc = cf->can_dlc;
} else {
ctx->can = false;
ctx->dlc = 0;
}
atomic_dec(&priv->free_ctx_cnt);
break;
}
}
if (!atomic_read(&priv->free_ctx_cnt))
/* That was the last free ctx. Slow down tx path */
netif_stop_queue(priv->netdev);
return ctx;
}
/* mcba_usb_free_ctx and mcba_usb_get_free_ctx are executed by different
* threads. The order of execution in below function is important.
*/
static inline void mcba_usb_free_ctx(struct mcba_usb_ctx *ctx)
{
/* Increase number of free ctxs before freeing ctx */
atomic_inc(&ctx->priv->free_ctx_cnt);
ctx->ndx = MCBA_CTX_FREE;
/* Wake up the queue once ctx is marked free */
netif_wake_queue(ctx->priv->netdev);
}
static void mcba_usb_write_bulk_callback(struct urb *urb)
{
struct mcba_usb_ctx *ctx = urb->context;
struct net_device *netdev;
WARN_ON(!ctx);
netdev = ctx->priv->netdev;
/* free up our allocated buffer */
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
if (ctx->can) {
if (!netif_device_present(netdev))
return;
netdev->stats.tx_packets++;
netdev->stats.tx_bytes += ctx->dlc;
can_led_event(netdev, CAN_LED_EVENT_TX);
can_get_echo_skb(netdev, ctx->ndx);
}
if (urb->status)
netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status);
/* Release the context */
mcba_usb_free_ctx(ctx);
}
/* Send data to device */
static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv,
struct mcba_usb_msg *usb_msg,
struct mcba_usb_ctx *ctx)
{
struct urb *urb;
u8 *buf;
int err;
/* create a URB, and a buffer for it, and copy the data to the URB */
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb)
return -ENOMEM;
buf = usb_alloc_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, GFP_ATOMIC,
&urb->transfer_dma);
if (!buf) {
err = -ENOMEM;
goto nomembuf;
}
memcpy(buf, usb_msg, MCBA_USB_TX_BUFF_SIZE);
usb_fill_bulk_urb(urb, priv->udev,
usb_sndbulkpipe(priv->udev, MCBA_USB_EP_OUT), buf,
MCBA_USB_TX_BUFF_SIZE, mcba_usb_write_bulk_callback,
ctx);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &priv->tx_submitted);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (unlikely(err))
goto failed;
/* Release our reference to this URB, the USB core will eventually free
* it entirely.
*/
usb_free_urb(urb);
return 0;
failed:
usb_unanchor_urb(urb);
usb_free_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, buf,
urb->transfer_dma);
if (err == -ENODEV)
netif_device_detach(priv->netdev);
else
netdev_warn(priv->netdev, "failed tx_urb %d\n", err);
nomembuf:
usb_free_urb(urb);
return err;
}
/* Send data to device */
static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct mcba_priv *priv = netdev_priv(netdev);
struct can_frame *cf = (struct can_frame *)skb->data;
struct mcba_usb_ctx *ctx = NULL;
struct net_device_stats *stats = &priv->netdev->stats;
u16 sid;
int err;
struct mcba_usb_msg_can usb_msg = {
.cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV
};
if (can_dropped_invalid_skb(netdev, skb))
return NETDEV_TX_OK;
ctx = mcba_usb_get_free_ctx(priv, cf);
if (!ctx)
return NETDEV_TX_BUSY;
can_put_echo_skb(skb, priv->netdev, ctx->ndx);
if (cf->can_id & CAN_EFF_FLAG) {
/* SIDH | SIDL | EIDH | EIDL
* 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0
*/
sid = MCBA_SIDL_EXID_MASK;
/* store 28-18 bits */
sid |= (cf->can_id & 0x1ffc0000) >> 13;
/* store 17-16 bits */
sid |= (cf->can_id & 0x30000) >> 16;
put_unaligned_be16(sid, &usb_msg.sid);
/* store 15-0 bits */
put_unaligned_be16(cf->can_id & 0xffff, &usb_msg.eid);
} else {
/* SIDH | SIDL
* 10 - 3 | 2 1 0 x x x x x
*/
put_unaligned_be16((cf->can_id & CAN_SFF_MASK) << 5,
&usb_msg.sid);
usb_msg.eid = 0;
}
usb_msg.dlc = cf->can_dlc;
memcpy(usb_msg.data, cf->data, usb_msg.dlc);
if (cf->can_id & CAN_RTR_FLAG)
usb_msg.dlc |= MCBA_DLC_RTR_MASK;
err = mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, ctx);
if (err)
goto xmit_failed;
return NETDEV_TX_OK;
xmit_failed:
can_free_echo_skb(priv->netdev, ctx->ndx);
mcba_usb_free_ctx(ctx);
dev_kfree_skb(skb);
stats->tx_dropped++;
return NETDEV_TX_OK;
}
/* Send cmd to device */
static void mcba_usb_xmit_cmd(struct mcba_priv *priv,
struct mcba_usb_msg *usb_msg)
{
struct mcba_usb_ctx *ctx = NULL;
int err;
ctx = mcba_usb_get_free_ctx(priv, NULL);
if (!ctx) {
netdev_err(priv->netdev,
"Lack of free ctx. Sending (%d) cmd aborted",
usb_msg->cmd_id);
return;
}
err = mcba_usb_xmit(priv, usb_msg, ctx);
if (err)
netdev_err(priv->netdev, "Failed to send cmd (%d)",
usb_msg->cmd_id);
}
static void mcba_usb_xmit_change_bitrate(struct mcba_priv *priv, u16 bitrate)
{
struct mcba_usb_msg_change_bitrate usb_msg = {
.cmd_id = MBCA_CMD_CHANGE_BIT_RATE
};
put_unaligned_be16(bitrate, &usb_msg.bitrate);
mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
}
static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic)
{
struct mcba_usb_msg_fw_ver usb_msg = {
.cmd_id = MBCA_CMD_READ_FW_VERSION,
.pic = pic
};
mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
}
static void mcba_usb_process_can(struct mcba_priv *priv,
struct mcba_usb_msg_can *msg)
{
struct can_frame *cf;
struct sk_buff *skb;
struct net_device_stats *stats = &priv->netdev->stats;
u16 sid;
skb = alloc_can_skb(priv->netdev, &cf);
if (!skb)
return;
sid = get_unaligned_be16(&msg->sid);
if (sid & MCBA_SIDL_EXID_MASK) {
/* SIDH | SIDL | EIDH | EIDL
* 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0
*/
cf->can_id = CAN_EFF_FLAG;
/* store 28-18 bits */
cf->can_id |= (sid & 0xffe0) << 13;
/* store 17-16 bits */
cf->can_id |= (sid & 3) << 16;
/* store 15-0 bits */
cf->can_id |= get_unaligned_be16(&msg->eid);
} else {
/* SIDH | SIDL
* 10 - 3 | 2 1 0 x x x x x
*/
cf->can_id = (sid & 0xffe0) >> 5;
}
if (msg->dlc & MCBA_DLC_RTR_MASK)
cf->can_id |= CAN_RTR_FLAG;
cf->can_dlc = get_can_dlc(msg->dlc & MCBA_DLC_MASK);
memcpy(cf->data, msg->data, cf->can_dlc);
stats->rx_packets++;
stats->rx_bytes += cf->can_dlc;
can_led_event(priv->netdev, CAN_LED_EVENT_RX);
netif_rx(skb);
}
static void mcba_usb_process_ka_usb(struct mcba_priv *priv,
struct mcba_usb_msg_ka_usb *msg)
{
if (unlikely(priv->usb_ka_first_pass)) {
netdev_info(priv->netdev, "PIC USB version %hhu.%hhu\n",
msg->soft_ver_major, msg->soft_ver_minor);
priv->usb_ka_first_pass = false;
}
if (msg->termination_state)
priv->can.termination = MCBA_TERMINATION_ENABLED;
else
priv->can.termination = MCBA_TERMINATION_DISABLED;
}
static u32 convert_can2host_bitrate(struct mcba_usb_msg_ka_can *msg)
{
const u32 bitrate = get_unaligned_be16(&msg->can_bitrate);
if ((bitrate == 33) || (bitrate == 83))
return bitrate * 1000 + 333;
else
return bitrate * 1000;
}
static void mcba_usb_process_ka_can(struct mcba_priv *priv,
struct mcba_usb_msg_ka_can *msg)
{
if (unlikely(priv->can_ka_first_pass)) {
netdev_info(priv->netdev, "PIC CAN version %hhu.%hhu\n",
msg->soft_ver_major, msg->soft_ver_minor);
priv->can_ka_first_pass = false;
}
if (unlikely(priv->can_speed_check)) {
const u32 bitrate = convert_can2host_bitrate(msg);
priv->can_speed_check = false;
if (bitrate != priv->can.bittiming.bitrate)
netdev_err(
priv->netdev,
"Wrong bitrate reported by the device (%u). Expected %u",
bitrate, priv->can.bittiming.bitrate);
}
priv->bec.txerr = msg->tx_err_cnt;
priv->bec.rxerr = msg->rx_err_cnt;
if (msg->tx_bus_off)
priv->can.state = CAN_STATE_BUS_OFF;
else if ((priv->bec.txerr > MCBA_CAN_STATE_ERR_PSV_TH) ||
(priv->bec.rxerr > MCBA_CAN_STATE_ERR_PSV_TH))
priv->can.state = CAN_STATE_ERROR_PASSIVE;
else if ((priv->bec.txerr > MCBA_CAN_STATE_WRN_TH) ||
(priv->bec.rxerr > MCBA_CAN_STATE_WRN_TH))
priv->can.state = CAN_STATE_ERROR_WARNING;
}
static void mcba_usb_process_rx(struct mcba_priv *priv,
struct mcba_usb_msg *msg)
{
switch (msg->cmd_id) {
case MBCA_CMD_I_AM_ALIVE_FROM_CAN:
mcba_usb_process_ka_can(priv,
(struct mcba_usb_msg_ka_can *)msg);
break;
case MBCA_CMD_I_AM_ALIVE_FROM_USB:
mcba_usb_process_ka_usb(priv,
(struct mcba_usb_msg_ka_usb *)msg);
break;
case MBCA_CMD_RECEIVE_MESSAGE:
mcba_usb_process_can(priv, (struct mcba_usb_msg_can *)msg);
break;
case MBCA_CMD_NOTHING_TO_SEND:
/* Side effect of communication between PIC_USB and PIC_CAN.
* PIC_CAN is telling us that it has nothing to send
*/
break;
case MBCA_CMD_TRANSMIT_MESSAGE_RSP:
/* Transmission response from the device containing timestamp */
break;
default:
netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)",
msg->cmd_id);
break;
}
}
/* Callback for reading data from device
*
* Check urb status, call read function and resubmit urb read operation.
*/
static void mcba_usb_read_bulk_callback(struct urb *urb)
{
struct mcba_priv *priv = urb->context;
struct net_device *netdev;
int retval;
int pos = 0;
netdev = priv->netdev;
if (!netif_device_present(netdev))
return;
switch (urb->status) {
case 0: /* success */
break;
case -ENOENT:
case -ESHUTDOWN:
return;
default:
netdev_info(netdev, "Rx URB aborted (%d)\n", urb->status);
goto resubmit_urb;
}
while (pos < urb->actual_length) {
struct mcba_usb_msg *msg;
if (pos + sizeof(struct mcba_usb_msg) > urb->actual_length) {
netdev_err(priv->netdev, "format error\n");
break;
}
msg = (struct mcba_usb_msg *)(urb->transfer_buffer + pos);
mcba_usb_process_rx(priv, msg);
pos += sizeof(struct mcba_usb_msg);
}
resubmit_urb:
usb_fill_bulk_urb(urb, priv->udev,
usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_OUT),
urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE,
mcba_usb_read_bulk_callback, priv);
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval == -ENODEV)
netif_device_detach(netdev);
else if (retval)
netdev_err(netdev, "failed resubmitting read bulk urb: %d\n",
retval);
}
/* Start USB device */
static int mcba_usb_start(struct mcba_priv *priv)
{
struct net_device *netdev = priv->netdev;
int err, i;
mcba_init_ctx(priv);
for (i = 0; i < MCBA_MAX_RX_URBS; i++) {
struct urb *urb = NULL;
u8 *buf;
/* create a URB, and a buffer for it */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
err = -ENOMEM;
break;
}
buf = usb_alloc_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE,
GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
netdev_err(netdev, "No memory left for USB buffer\n");
usb_free_urb(urb);
err = -ENOMEM;
break;
}
usb_fill_bulk_urb(urb, priv->udev,
usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_IN),
buf, MCBA_USB_RX_BUFF_SIZE,
mcba_usb_read_bulk_callback, priv);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &priv->rx_submitted);
err = usb_submit_urb(urb, GFP_KERNEL);
if (err) {
usb_unanchor_urb(urb);
usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE,
buf, urb->transfer_dma);
usb_free_urb(urb);
break;
}
/* Drop reference, USB core will take care of freeing it */
usb_free_urb(urb);
}
/* Did we submit any URBs */
if (i == 0) {
netdev_warn(netdev, "couldn't setup read URBs\n");
return err;
}
/* Warn if we've couldn't transmit all the URBs */
if (i < MCBA_MAX_RX_URBS)
netdev_warn(netdev, "rx performance may be slow\n");
mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_USB);
mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_CAN);
return err;
}
/* Open USB device */
static int mcba_usb_open(struct net_device *netdev)
{
struct mcba_priv *priv = netdev_priv(netdev);
int err;
/* common open */
err = open_candev(netdev);
if (err)
return err;
priv->can_speed_check = true;
priv->can.state = CAN_STATE_ERROR_ACTIVE;
can_led_event(netdev, CAN_LED_EVENT_OPEN);
netif_start_queue(netdev);
return 0;
}
static void mcba_urb_unlink(struct mcba_priv *priv)
{
usb_kill_anchored_urbs(&priv->rx_submitted);
usb_kill_anchored_urbs(&priv->tx_submitted);
}
/* Close USB device */
static int mcba_usb_close(struct net_device *netdev)
{
struct mcba_priv *priv = netdev_priv(netdev);
priv->can.state = CAN_STATE_STOPPED;
netif_stop_queue(netdev);
/* Stop polling */
mcba_urb_unlink(priv);
close_candev(netdev);
can_led_event(netdev, CAN_LED_EVENT_STOP);
return 0;
}
/* Set network device mode
*
* Maybe we should leave this function empty, because the device
* set mode variable with open command.
*/
static int mcba_net_set_mode(struct net_device *netdev, enum can_mode mode)
{
return 0;
}
static int mcba_net_get_berr_counter(const struct net_device *netdev,
struct can_berr_counter *bec)
{
struct mcba_priv *priv = netdev_priv(netdev);
bec->txerr = priv->bec.txerr;
bec->rxerr = priv->bec.rxerr;
return 0;
}
static const struct net_device_ops mcba_netdev_ops = {
.ndo_open = mcba_usb_open,
.ndo_stop = mcba_usb_close,
.ndo_start_xmit = mcba_usb_start_xmit,
};
/* Microchip CANBUS has hardcoded bittiming values by default.
* This function sends request via USB to change the speed and align bittiming
* values for presentation purposes only
*/
static int mcba_net_set_bittiming(struct net_device *netdev)
{
struct mcba_priv *priv = netdev_priv(netdev);
const u16 bitrate_kbps = priv->can.bittiming.bitrate / 1000;
mcba_usb_xmit_change_bitrate(priv, bitrate_kbps);
return 0;
}
static int mcba_set_termination(struct net_device *netdev, u16 term)
{
struct mcba_priv *priv = netdev_priv(netdev);
struct mcba_usb_msg_termination usb_msg = {
.cmd_id = MBCA_CMD_SETUP_TERMINATION_RESISTANCE
};
if (term == MCBA_TERMINATION_ENABLED)
usb_msg.termination = 1;
else
usb_msg.termination = 0;
mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
return 0;
}
static int mcba_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct net_device *netdev;
struct mcba_priv *priv;
int err = -ENOMEM;
struct usb_device *usbdev = interface_to_usbdev(intf);
netdev = alloc_candev(sizeof(struct mcba_priv), MCBA_MAX_TX_URBS);
if (!netdev) {
dev_err(&intf->dev, "Couldn't alloc candev\n");
return -ENOMEM;
}
priv = netdev_priv(netdev);
priv->udev = usbdev;
priv->netdev = netdev;
priv->usb_ka_first_pass = true;
priv->can_ka_first_pass = true;
priv->can_speed_check = false;
init_usb_anchor(&priv->rx_submitted);
init_usb_anchor(&priv->tx_submitted);
usb_set_intfdata(intf, priv);
/* Init CAN device */
priv->can.state = CAN_STATE_STOPPED;
priv->can.termination_const = mcba_termination;
priv->can.termination_const_cnt = ARRAY_SIZE(mcba_termination);
priv->can.bitrate_const = mcba_bitrate;
priv->can.bitrate_const_cnt = ARRAY_SIZE(mcba_bitrate);
priv->can.do_set_termination = mcba_set_termination;
priv->can.do_set_mode = mcba_net_set_mode;
priv->can.do_get_berr_counter = mcba_net_get_berr_counter;
priv->can.do_set_bittiming = mcba_net_set_bittiming;
netdev->netdev_ops = &mcba_netdev_ops;
netdev->flags |= IFF_ECHO; /* we support local echo */
SET_NETDEV_DEV(netdev, &intf->dev);
err = register_candev(netdev);
if (err) {
netdev_err(netdev, "couldn't register CAN device: %d\n", err);
goto cleanup_free_candev;
}
devm_can_led_init(netdev);
/* Start USB dev only if we have successfully registered CAN device */
err = mcba_usb_start(priv);
if (err) {
if (err == -ENODEV)
netif_device_detach(priv->netdev);
netdev_warn(netdev, "couldn't start device: %d\n", err);
goto cleanup_unregister_candev;
}
dev_info(&intf->dev, "Microchip CAN BUS analizer connected\n");
return 0;
cleanup_unregister_candev:
unregister_candev(priv->netdev);
cleanup_free_candev:
free_candev(netdev);
return err;
}
/* Called by the usb core when driver is unloaded or device is removed */
static void mcba_usb_disconnect(struct usb_interface *intf)
{
struct mcba_priv *priv = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
netdev_info(priv->netdev, "device disconnected\n");
unregister_candev(priv->netdev);
free_candev(priv->netdev);
mcba_urb_unlink(priv);
}
static struct usb_driver mcba_usb_driver = {
.name = MCBA_MODULE_NAME,
.probe = mcba_usb_probe,
.disconnect = mcba_usb_disconnect,
.id_table = mcba_usb_table,
};
module_usb_driver(mcba_usb_driver);
MODULE_AUTHOR("Remigiusz Kołłątaj <remigiusz.kollataj@mobica.com>");
MODULE_DESCRIPTION("SocketCAN driver for Microchip CAN BUS Analyzer Tool");
MODULE_LICENSE("GPL v2");

View File

@ -19,10 +19,10 @@
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
#include <linux/can/dev/peak_canfd.h>
#include "pcan_usb_core.h"
#include "pcan_usb_pro.h"
#include "pcan_ucan.h"
MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB FD adapter");
MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro FD adapter");
@ -238,7 +238,7 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf)
/* 1st, reset error counters: */
prc = (struct pucan_wr_err_cnt *)pc;
prc->opcode_channel = pucan_cmd_opcode_channel(dev,
prc->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
PUCAN_CMD_WR_ERR_CNT);
/* select both counters */
@ -257,9 +257,10 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf)
puo->opcode_channel =
(dev->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) ?
pucan_cmd_opcode_channel(dev,
pucan_cmd_opcode_channel(dev->ctrl_idx,
PUCAN_CMD_CLR_DIS_OPTION) :
pucan_cmd_opcode_channel(dev, PUCAN_CMD_SET_EN_OPTION);
pucan_cmd_opcode_channel(dev->ctrl_idx,
PUCAN_CMD_SET_EN_OPTION);
puo->options = cpu_to_le16(PUCAN_OPTION_CANDFDISO);
@ -274,7 +275,7 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf)
/* next, go back to operational mode */
cmd = (struct pucan_command *)pc;
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ?
PUCAN_CMD_LISTEN_ONLY_MODE :
PUCAN_CMD_NORMAL_MODE);
@ -296,7 +297,7 @@ static int pcan_usb_fd_set_bus(struct peak_usb_device *dev, u8 onoff)
struct pucan_command *cmd = (struct pucan_command *)pc;
/* build cmd to go back to reset mode */
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
PUCAN_CMD_RESET_MODE);
l = sizeof(struct pucan_command);
}
@ -332,7 +333,7 @@ static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx,
}
for (i = idx; i < n; i++, cmd++) {
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
PUCAN_CMD_FILTER_STD);
cmd->idx = cpu_to_le16(i);
cmd->mask = cpu_to_le32(mask);
@ -352,7 +353,7 @@ static int pcan_usb_fd_set_options(struct peak_usb_device *dev,
{
struct pcan_ufd_options *cmd = pcan_usb_fd_cmd_buffer(dev);
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
(onoff) ? PUCAN_CMD_SET_EN_OPTION :
PUCAN_CMD_CLR_DIS_OPTION);
@ -368,7 +369,7 @@ static int pcan_usb_fd_set_can_led(struct peak_usb_device *dev, u8 led_mode)
{
struct pcan_ufd_led *cmd = pcan_usb_fd_cmd_buffer(dev);
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
PCAN_UFD_CMD_LED_SET);
cmd->mode = led_mode;
@ -382,7 +383,7 @@ static int pcan_usb_fd_set_clock_domain(struct peak_usb_device *dev,
{
struct pcan_ufd_clock *cmd = pcan_usb_fd_cmd_buffer(dev);
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
PCAN_UFD_CMD_CLK_SET);
cmd->mode = clk_mode;
@ -396,7 +397,7 @@ static int pcan_usb_fd_set_bittiming_slow(struct peak_usb_device *dev,
{
struct pucan_timing_slow *cmd = pcan_usb_fd_cmd_buffer(dev);
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
PUCAN_CMD_TIMING_SLOW);
cmd->sjw_t = PUCAN_TSLOW_SJW_T(bt->sjw - 1,
dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES);
@ -417,7 +418,7 @@ static int pcan_usb_fd_set_bittiming_fast(struct peak_usb_device *dev,
{
struct pucan_timing_fast *cmd = pcan_usb_fd_cmd_buffer(dev);
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx,
PUCAN_CMD_TIMING_FAST);
cmd->sjw = PUCAN_TFAST_SJW(bt->sjw - 1);
cmd->tseg2 = PUCAN_TFAST_TSEG2(bt->phase_seg2 - 1);

View File

@ -1,7 +1,7 @@
/*
* vcan.c - Virtual CAN interface
*
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* Copyright (c) 2002-2017 Volkswagen Group Electronic Research
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -50,9 +50,12 @@
#include <linux/slab.h>
#include <net/rtnetlink.h>
#define DRV_NAME "vcan"
MODULE_DESCRIPTION("virtual CAN interface");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
MODULE_ALIAS_RTNL_LINK(DRV_NAME);
/*
@ -164,7 +167,7 @@ static void vcan_setup(struct net_device *dev)
}
static struct rtnl_link_ops vcan_link_ops __read_mostly = {
.kind = "vcan",
.kind = DRV_NAME,
.setup = vcan_setup,
};

316
drivers/net/can/vxcan.c Normal file
View File

@ -0,0 +1,316 @@
/*
* vxcan.c - Virtual CAN Tunnel for cross namespace communication
*
* This code is derived from drivers/net/can/vcan.c for the virtual CAN
* specific parts and from drivers/net/veth.c to implement the netlink API
* for network interface pairs in a common and established way.
*
* Copyright (c) 2017 Oliver Hartkopp <socketcan@hartkopp.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the version 2 of the GNU General Public License
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/skb.h>
#include <linux/can/vxcan.h>
#include <linux/slab.h>
#include <net/rtnetlink.h>
#define DRV_NAME "vxcan"
MODULE_DESCRIPTION("Virtual CAN Tunnel");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Oliver Hartkopp <socketcan@hartkopp.net>");
MODULE_ALIAS_RTNL_LINK(DRV_NAME);
struct vxcan_priv {
struct net_device __rcu *peer;
};
static netdev_tx_t vxcan_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct vxcan_priv *priv = netdev_priv(dev);
struct net_device *peer;
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
struct net_device_stats *peerstats, *srcstats = &dev->stats;
if (can_dropped_invalid_skb(dev, skb))
return NETDEV_TX_OK;
rcu_read_lock();
peer = rcu_dereference(priv->peer);
if (unlikely(!peer)) {
kfree_skb(skb);
dev->stats.tx_dropped++;
goto out_unlock;
}
skb = can_create_echo_skb(skb);
if (!skb)
goto out_unlock;
/* reset CAN GW hop counter */
skb->csum_start = 0;
skb->pkt_type = PACKET_BROADCAST;
skb->dev = peer;
skb->ip_summed = CHECKSUM_UNNECESSARY;
if (netif_rx_ni(skb) == NET_RX_SUCCESS) {
srcstats->tx_packets++;
srcstats->tx_bytes += cfd->len;
peerstats = &peer->stats;
peerstats->rx_packets++;
peerstats->rx_bytes += cfd->len;
}
out_unlock:
rcu_read_unlock();
return NETDEV_TX_OK;
}
static int vxcan_open(struct net_device *dev)
{
struct vxcan_priv *priv = netdev_priv(dev);
struct net_device *peer = rtnl_dereference(priv->peer);
if (!peer)
return -ENOTCONN;
if (peer->flags & IFF_UP) {
netif_carrier_on(dev);
netif_carrier_on(peer);
}
return 0;
}
static int vxcan_close(struct net_device *dev)
{
struct vxcan_priv *priv = netdev_priv(dev);
struct net_device *peer = rtnl_dereference(priv->peer);
netif_carrier_off(dev);
if (peer)
netif_carrier_off(peer);
return 0;
}
static int vxcan_get_iflink(const struct net_device *dev)
{
struct vxcan_priv *priv = netdev_priv(dev);
struct net_device *peer;
int iflink;
rcu_read_lock();
peer = rcu_dereference(priv->peer);
iflink = peer ? peer->ifindex : 0;
rcu_read_unlock();
return iflink;
}
static int vxcan_change_mtu(struct net_device *dev, int new_mtu)
{
/* Do not allow changing the MTU while running */
if (dev->flags & IFF_UP)
return -EBUSY;
if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU)
return -EINVAL;
dev->mtu = new_mtu;
return 0;
}
static const struct net_device_ops vxcan_netdev_ops = {
.ndo_open = vxcan_open,
.ndo_stop = vxcan_close,
.ndo_start_xmit = vxcan_xmit,
.ndo_get_iflink = vxcan_get_iflink,
.ndo_change_mtu = vxcan_change_mtu,
};
static void vxcan_setup(struct net_device *dev)
{
dev->type = ARPHRD_CAN;
dev->mtu = CAN_MTU;
dev->hard_header_len = 0;
dev->addr_len = 0;
dev->tx_queue_len = 0;
dev->flags = (IFF_NOARP|IFF_ECHO);
dev->netdev_ops = &vxcan_netdev_ops;
dev->destructor = free_netdev;
}
/* forward declaration for rtnl_create_link() */
static struct rtnl_link_ops vxcan_link_ops;
static int vxcan_newlink(struct net *net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
struct vxcan_priv *priv;
struct net_device *peer;
struct net *peer_net;
struct nlattr *peer_tb[IFLA_MAX + 1], **tbp = tb;
char ifname[IFNAMSIZ];
unsigned char name_assign_type;
struct ifinfomsg *ifmp = NULL;
int err;
/* register peer device */
if (data && data[VXCAN_INFO_PEER]) {
struct nlattr *nla_peer;
nla_peer = data[VXCAN_INFO_PEER];
ifmp = nla_data(nla_peer);
err = rtnl_nla_parse_ifla(peer_tb,
nla_data(nla_peer) +
sizeof(struct ifinfomsg),
nla_len(nla_peer) -
sizeof(struct ifinfomsg),
NULL);
if (err < 0)
return err;
tbp = peer_tb;
}
if (tbp[IFLA_IFNAME]) {
nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ);
name_assign_type = NET_NAME_USER;
} else {
snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d");
name_assign_type = NET_NAME_ENUM;
}
peer_net = rtnl_link_get_net(net, tbp);
if (IS_ERR(peer_net))
return PTR_ERR(peer_net);
peer = rtnl_create_link(peer_net, ifname, name_assign_type,
&vxcan_link_ops, tbp);
if (IS_ERR(peer)) {
put_net(peer_net);
return PTR_ERR(peer);
}
if (ifmp && dev->ifindex)
peer->ifindex = ifmp->ifi_index;
err = register_netdevice(peer);
put_net(peer_net);
peer_net = NULL;
if (err < 0) {
free_netdev(peer);
return err;
}
netif_carrier_off(peer);
err = rtnl_configure_link(peer, ifmp);
if (err < 0) {
unregister_netdevice(peer);
return err;
}
/* register first device */
if (tb[IFLA_IFNAME])
nla_strlcpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ);
else
snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d");
err = register_netdevice(dev);
if (err < 0) {
unregister_netdevice(peer);
return err;
}
netif_carrier_off(dev);
/* cross link the device pair */
priv = netdev_priv(dev);
rcu_assign_pointer(priv->peer, peer);
priv = netdev_priv(peer);
rcu_assign_pointer(priv->peer, dev);
return 0;
}
static void vxcan_dellink(struct net_device *dev, struct list_head *head)
{
struct vxcan_priv *priv;
struct net_device *peer;
priv = netdev_priv(dev);
peer = rtnl_dereference(priv->peer);
/* Note : dellink() is called from default_device_exit_batch(),
* before a rcu_synchronize() point. The devices are guaranteed
* not being freed before one RCU grace period.
*/
RCU_INIT_POINTER(priv->peer, NULL);
unregister_netdevice_queue(dev, head);
if (peer) {
priv = netdev_priv(peer);
RCU_INIT_POINTER(priv->peer, NULL);
unregister_netdevice_queue(peer, head);
}
}
static const struct nla_policy vxcan_policy[VXCAN_INFO_MAX + 1] = {
[VXCAN_INFO_PEER] = { .len = sizeof(struct ifinfomsg) },
};
static struct net *vxcan_get_link_net(const struct net_device *dev)
{
struct vxcan_priv *priv = netdev_priv(dev);
struct net_device *peer = rtnl_dereference(priv->peer);
return peer ? dev_net(peer) : dev_net(dev);
}
static struct rtnl_link_ops vxcan_link_ops = {
.kind = DRV_NAME,
.priv_size = sizeof(struct vxcan_priv),
.setup = vxcan_setup,
.newlink = vxcan_newlink,
.dellink = vxcan_dellink,
.policy = vxcan_policy,
.maxtype = VXCAN_INFO_MAX,
.get_link_net = vxcan_get_link_net,
};
static __init int vxcan_init(void)
{
pr_info("vxcan: Virtual CAN Tunnel driver\n");
return rtnl_link_register(&vxcan_link_ops);
}
static __exit void vxcan_exit(void)
{
rtnl_link_unregister(&vxcan_link_ops);
}
module_init(vxcan_init);
module_exit(vxcan_exit);

View File

@ -5,7 +5,7 @@
*
* Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
* Urs Thuermann <urs.thuermann@volkswagen.de>
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* Copyright (c) 2002-2017 Volkswagen Group Electronic Research
* All rights reserved.
*
*/
@ -17,7 +17,7 @@
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#define CAN_VERSION "20120528"
#define CAN_VERSION "20170425"
/* increment this number each time you change some user-space interface */
#define CAN_ABI_VERSION "9"

View File

@ -23,11 +23,14 @@
#define PUCAN_CMD_LISTEN_ONLY_MODE 0x003
#define PUCAN_CMD_TIMING_SLOW 0x004
#define PUCAN_CMD_TIMING_FAST 0x005
#define PUCAN_CMD_SET_STD_FILTER 0x006
#define PUCAN_CMD_RESERVED2 0x007
#define PUCAN_CMD_FILTER_STD 0x008
#define PUCAN_CMD_TX_ABORT 0x009
#define PUCAN_CMD_WR_ERR_CNT 0x00a
#define PUCAN_CMD_SET_EN_OPTION 0x00b
#define PUCAN_CMD_CLR_DIS_OPTION 0x00c
#define PUCAN_CMD_RX_BARRIER 0x010
#define PUCAN_CMD_END_OF_COLLECTION 0x3ff
/* uCAN received messages list */
@ -35,6 +38,10 @@
#define PUCAN_MSG_ERROR 0x0002
#define PUCAN_MSG_STATUS 0x0003
#define PUCAN_MSG_BUSLOAD 0x0004
#define PUCAN_MSG_CACHE_CRITICAL 0x0102
/* uCAN transmitted messages */
#define PUCAN_MSG_CAN_TX 0x1000
/* uCAN command common header */
@ -43,6 +50,12 @@ struct __packed pucan_command {
u16 args[3];
};
/* return the opcode from the opcode_channel field of a command */
static inline u16 pucan_cmd_get_opcode(struct pucan_command *c)
{
return le16_to_cpu(c->opcode_channel) & 0x3ff;
}
#define PUCAN_TSLOW_BRP_BITS 10
#define PUCAN_TSLOW_TSGEG1_BITS 8
#define PUCAN_TSLOW_TSGEG2_BITS 7
@ -108,6 +121,27 @@ struct __packed pucan_filter_std {
__le32 mask; /* CAN-ID bitmask in idx range */
};
#define PUCAN_FLTSTD_ROW_IDX_MAX ((1 << PUCAN_FLTSTD_ROW_IDX_BITS) - 1)
/* uCAN SET_STD_FILTER command fields */
struct __packed pucan_std_filter {
__le16 opcode_channel;
u8 unused;
u8 idx;
__le32 mask; /* CAN-ID bitmask in idx range */
};
/* uCAN TX_ABORT commands fields */
#define PUCAN_TX_ABORT_FLUSH 0x0001
struct __packed pucan_tx_abort {
__le16 opcode_channel;
__le16 flags;
u32 unused;
};
/* uCAN WR_ERR_CNT command fields */
#define PUCAN_WRERRCNT_TE 0x4000 /* Tx error cntr write Enable */
#define PUCAN_WRERRCNT_RE 0x8000 /* Rx error cntr write Enable */
@ -184,6 +218,12 @@ struct __packed pucan_error_msg {
u8 rx_err_cnt;
};
static inline int pucan_error_get_channel(const struct pucan_error_msg *msg)
{
return msg->channel_type_d & 0x0f;
}
#define PUCAN_RX_BARRIER 0x10
#define PUCAN_BUS_PASSIVE 0x20
#define PUCAN_BUS_WARNING 0x40
#define PUCAN_BUS_BUSOFF 0x80
@ -197,6 +237,31 @@ struct __packed pucan_status_msg {
u8 unused[3];
};
static inline int pucan_status_get_channel(const struct pucan_status_msg *msg)
{
return msg->channel_p_w_b & 0x0f;
}
static inline int pucan_status_is_rx_barrier(const struct pucan_status_msg *msg)
{
return msg->channel_p_w_b & PUCAN_RX_BARRIER;
}
static inline int pucan_status_is_passive(const struct pucan_status_msg *msg)
{
return msg->channel_p_w_b & PUCAN_BUS_PASSIVE;
}
static inline int pucan_status_is_warning(const struct pucan_status_msg *msg)
{
return msg->channel_p_w_b & PUCAN_BUS_WARNING;
}
static inline int pucan_status_is_busoff(const struct pucan_status_msg *msg)
{
return msg->channel_p_w_b & PUCAN_BUS_BUSOFF;
}
/* uCAN transmitted message format */
#define PUCAN_MSG_CHANNEL_DLC(c, d) (((c) & 0xf) | ((d) << 4))
@ -213,32 +278,31 @@ struct __packed pucan_tx_msg {
};
/* build the cmd opcode_channel field with respect to the correct endianness */
static inline __le16 pucan_cmd_opcode_channel(struct peak_usb_device *dev,
int opcode)
static inline __le16 pucan_cmd_opcode_channel(int index, int opcode)
{
return cpu_to_le16(((dev->ctrl_idx) << 12) | ((opcode) & 0x3ff));
return cpu_to_le16(((index) << 12) | ((opcode) & 0x3ff));
}
/* return the channel number part from any received message channel_dlc field */
static inline int pucan_msg_get_channel(struct pucan_rx_msg *rm)
static inline int pucan_msg_get_channel(const struct pucan_rx_msg *msg)
{
return rm->channel_dlc & 0xf;
return msg->channel_dlc & 0xf;
}
/* return the dlc value from any received message channel_dlc field */
static inline int pucan_msg_get_dlc(struct pucan_rx_msg *rm)
static inline int pucan_msg_get_dlc(const struct pucan_rx_msg *msg)
{
return rm->channel_dlc >> 4;
return msg->channel_dlc >> 4;
}
static inline int pucan_ermsg_get_channel(struct pucan_error_msg *em)
static inline int pucan_ermsg_get_channel(const struct pucan_error_msg *msg)
{
return em->channel_type_d & 0x0f;
return msg->channel_type_d & 0x0f;
}
static inline int pucan_stmsg_get_channel(struct pucan_status_msg *sm)
static inline int pucan_stmsg_get_channel(const struct pucan_status_msg *msg)
{
return sm->channel_p_w_b & 0x0f;
return msg->channel_p_w_b & 0x0f;
}
#endif

View File

@ -8,6 +8,8 @@
#include <linux/spinlock.h>
struct dev_rcv_lists;
struct s_stats;
struct s_pstats;
struct netns_can {
#if IS_ENABLED(CONFIG_PROC_FS)
@ -21,11 +23,18 @@ struct netns_can {
struct proc_dir_entry *pde_rcvlist_sff;
struct proc_dir_entry *pde_rcvlist_eff;
struct proc_dir_entry *pde_rcvlist_err;
struct proc_dir_entry *bcmproc_dir;
#endif
/* receive filters subscribed for 'all' CAN devices */
struct dev_rcv_lists *can_rx_alldev_list;
spinlock_t can_rcvlists_lock;
struct timer_list can_stattimer;/* timer for statistics update */
struct s_stats *can_stats; /* packet statistics */
struct s_pstats *can_pstats; /* receive list statistics */
/* CAN GW per-net gateway jobs */
struct hlist_head cgw_list;
};
#endif /* __NETNS_CAN_H__ */

View File

@ -0,0 +1,12 @@
#ifndef _UAPI_CAN_VXCAN_H
#define _UAPI_CAN_VXCAN_H
enum {
VXCAN_INFO_UNSPEC,
VXCAN_INFO_PEER,
__VXCAN_INFO_MAX
#define VXCAN_INFO_MAX (__VXCAN_INFO_MAX - 1)
};
#endif

View File

@ -2,7 +2,7 @@
* af_can.c - Protocol family CAN core module
* (used by different CAN protocol modules)
*
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* Copyright (c) 2002-2017 Volkswagen Group Electronic Research
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -75,18 +75,12 @@ static int stats_timer __read_mostly = 1;
module_param(stats_timer, int, S_IRUGO);
MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
static int can_net_id;
static struct kmem_cache *rcv_cache __read_mostly;
/* table of registered CAN protocols */
static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
static DEFINE_MUTEX(proto_tab_lock);
struct timer_list can_stattimer; /* timer for statistics update */
struct s_stats can_stats; /* packet statistics */
struct s_pstats can_pstats; /* receive list statistics */
static atomic_t skbcounter = ATOMIC_INIT(0);
/*
@ -223,6 +217,7 @@ int can_send(struct sk_buff *skb, int loop)
{
struct sk_buff *newskb = NULL;
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
struct s_stats *can_stats = dev_net(skb->dev)->can.can_stats;
int err = -EINVAL;
if (skb->len == CAN_MTU) {
@ -311,8 +306,8 @@ int can_send(struct sk_buff *skb, int loop)
netif_rx_ni(newskb);
/* update statistics */
can_stats.tx_frames++;
can_stats.tx_frames_delta++;
can_stats->tx_frames++;
can_stats->tx_frames_delta++;
return 0;
@ -470,6 +465,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
struct receiver *r;
struct hlist_head *rl;
struct dev_rcv_lists *d;
struct s_pstats *can_pstats = net->can.can_pstats;
int err = 0;
/* insert new receiver (dev,canid,mask) -> (func,data) */
@ -501,9 +497,9 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
hlist_add_head_rcu(&r->list, rl);
d->entries++;
can_pstats.rcv_entries++;
if (can_pstats.rcv_entries_max < can_pstats.rcv_entries)
can_pstats.rcv_entries_max = can_pstats.rcv_entries;
can_pstats->rcv_entries++;
if (can_pstats->rcv_entries_max < can_pstats->rcv_entries)
can_pstats->rcv_entries_max = can_pstats->rcv_entries;
} else {
kmem_cache_free(rcv_cache, r);
err = -ENODEV;
@ -545,6 +541,7 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
{
struct receiver *r = NULL;
struct hlist_head *rl;
struct s_pstats *can_pstats = net->can.can_pstats;
struct dev_rcv_lists *d;
if (dev && dev->type != ARPHRD_CAN)
@ -591,8 +588,8 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
hlist_del_rcu(&r->list);
d->entries--;
if (can_pstats.rcv_entries > 0)
can_pstats.rcv_entries--;
if (can_pstats->rcv_entries > 0)
can_pstats->rcv_entries--;
/* remove device structure requested by NETDEV_UNREGISTER */
if (d->remove_on_zero_entries && !d->entries) {
@ -686,11 +683,13 @@ static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
static void can_receive(struct sk_buff *skb, struct net_device *dev)
{
struct dev_rcv_lists *d;
struct net *net = dev_net(dev);
struct s_stats *can_stats = net->can.can_stats;
int matches;
/* update statistics */
can_stats.rx_frames++;
can_stats.rx_frames_delta++;
can_stats->rx_frames++;
can_stats->rx_frames_delta++;
/* create non-zero unique skb identifier together with *skb */
while (!(can_skb_prv(skb)->skbcnt))
@ -699,10 +698,10 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev)
rcu_read_lock();
/* deliver the packet to sockets listening on all devices */
matches = can_rcv_filter(dev_net(dev)->can.can_rx_alldev_list, skb);
matches = can_rcv_filter(net->can.can_rx_alldev_list, skb);
/* find receive list for this device */
d = find_dev_rcv_lists(dev_net(dev), dev);
d = find_dev_rcv_lists(net, dev);
if (d)
matches += can_rcv_filter(d, skb);
@ -712,8 +711,8 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev)
consume_skb(skb);
if (matches > 0) {
can_stats.matches++;
can_stats.matches_delta++;
can_stats->matches++;
can_stats->matches_delta++;
}
}
@ -878,8 +877,20 @@ static int can_pernet_init(struct net *net)
net->can.can_rx_alldev_list =
kzalloc(sizeof(struct dev_rcv_lists), GFP_KERNEL);
if (IS_ENABLED(CONFIG_PROC_FS))
net->can.can_stats = kzalloc(sizeof(struct s_stats), GFP_KERNEL);
net->can.can_pstats = kzalloc(sizeof(struct s_pstats), GFP_KERNEL);
if (IS_ENABLED(CONFIG_PROC_FS)) {
/* the statistics are updated every second (timer triggered) */
if (stats_timer) {
setup_timer(&net->can.can_stattimer, can_stat_update,
(unsigned long)net);
mod_timer(&net->can.can_stattimer,
round_jiffies(jiffies + HZ));
}
net->can.can_stats->jiffies_init = jiffies;
can_init_proc(net);
}
return 0;
}
@ -888,8 +899,11 @@ static void can_pernet_exit(struct net *net)
{
struct net_device *dev;
if (IS_ENABLED(CONFIG_PROC_FS))
if (IS_ENABLED(CONFIG_PROC_FS)) {
can_remove_proc(net);
if (stats_timer)
del_timer_sync(&net->can.can_stattimer);
}
/* remove created dev_rcv_lists from still registered CAN devices */
rcu_read_lock();
@ -903,6 +917,10 @@ static void can_pernet_exit(struct net *net)
}
}
rcu_read_unlock();
kfree(net->can.can_rx_alldev_list);
kfree(net->can.can_stats);
kfree(net->can.can_pstats);
}
/*
@ -933,8 +951,6 @@ static struct notifier_block can_netdev_notifier __read_mostly = {
static struct pernet_operations can_pernet_ops __read_mostly = {
.init = can_pernet_init,
.exit = can_pernet_exit,
.id = &can_net_id,
.size = 0,
};
static __init int can_init(void)
@ -952,14 +968,6 @@ static __init int can_init(void)
if (!rcv_cache)
return -ENOMEM;
if (IS_ENABLED(CONFIG_PROC_FS)) {
if (stats_timer) {
/* the statistics are updated every second (timer triggered) */
setup_timer(&can_stattimer, can_stat_update, 0);
mod_timer(&can_stattimer, round_jiffies(jiffies + HZ));
}
}
register_pernet_subsys(&can_pernet_ops);
/* protocol register */
@ -973,11 +981,6 @@ static __init int can_init(void)
static __exit void can_exit(void)
{
if (IS_ENABLED(CONFIG_PROC_FS)) {
if (stats_timer)
del_timer_sync(&can_stattimer);
}
/* protocol unregister */
dev_remove_pack(&canfd_packet);
dev_remove_pack(&can_packet);

View File

@ -110,18 +110,9 @@ struct s_pstats {
unsigned long rcv_entries_max;
};
/* receive filters subscribed for 'all' CAN devices */
extern struct dev_rcv_lists can_rx_alldev_list;
/* function prototypes for the CAN networklayer procfs (proc.c) */
void can_init_proc(struct net *net);
void can_remove_proc(struct net *net);
void can_stat_update(unsigned long data);
/* structures and variables from af_can.c needed in proc.c for reading */
extern struct timer_list can_stattimer; /* timer for statistics update */
extern struct s_stats can_stats; /* packet statistics */
extern struct s_pstats can_pstats; /* receive list statistics */
extern struct hlist_head can_rx_dev_list; /* rx dispatcher structures */
#endif /* AF_CAN_H */

View File

@ -1,7 +1,7 @@
/*
* bcm.c - Broadcast Manager to filter/send (cyclic) CAN content
*
* Copyright (c) 2002-2016 Volkswagen Group Electronic Research
* Copyright (c) 2002-2017 Volkswagen Group Electronic Research
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -77,7 +77,7 @@
(CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \
(CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
#define CAN_BCM_VERSION "20161123"
#define CAN_BCM_VERSION "20170425"
MODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
MODULE_LICENSE("Dual BSD/GPL");
@ -118,8 +118,6 @@ struct bcm_op {
struct net_device *rx_reg_dev;
};
static struct proc_dir_entry *proc_dir;
struct bcm_sock {
struct sock sk;
int bound;
@ -149,7 +147,7 @@ static inline ktime_t bcm_timeval_to_ktime(struct bcm_timeval tv)
/*
* procfs functions
*/
static char *bcm_proc_getifname(char *result, int ifindex)
static char *bcm_proc_getifname(struct net *net, char *result, int ifindex)
{
struct net_device *dev;
@ -157,7 +155,7 @@ static char *bcm_proc_getifname(char *result, int ifindex)
return "any";
rcu_read_lock();
dev = dev_get_by_index_rcu(&init_net, ifindex);
dev = dev_get_by_index_rcu(net, ifindex);
if (dev)
strcpy(result, dev->name);
else
@ -170,7 +168,8 @@ static char *bcm_proc_getifname(char *result, int ifindex)
static int bcm_proc_show(struct seq_file *m, void *v)
{
char ifname[IFNAMSIZ];
struct sock *sk = (struct sock *)m->private;
struct net *net = m->private;
struct sock *sk = (struct sock *)PDE_DATA(m->file->f_inode);
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
@ -178,7 +177,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
seq_printf(m, " / sk %pK", sk);
seq_printf(m, " / bo %pK", bo);
seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs);
seq_printf(m, " / bound %s", bcm_proc_getifname(ifname, bo->ifindex));
seq_printf(m, " / bound %s", bcm_proc_getifname(net, ifname, bo->ifindex));
seq_printf(m, " <<<\n");
list_for_each_entry(op, &bo->rx_ops, list) {
@ -190,7 +189,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
continue;
seq_printf(m, "rx_op: %03X %-5s ", op->can_id,
bcm_proc_getifname(ifname, op->ifindex));
bcm_proc_getifname(net, ifname, op->ifindex));
if (op->flags & CAN_FD_FRAME)
seq_printf(m, "(%u)", op->nframes);
@ -219,7 +218,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
list_for_each_entry(op, &bo->tx_ops, list) {
seq_printf(m, "tx_op: %03X %s ", op->can_id,
bcm_proc_getifname(ifname, op->ifindex));
bcm_proc_getifname(net, ifname, op->ifindex));
if (op->flags & CAN_FD_FRAME)
seq_printf(m, "(%u) ", op->nframes);
@ -242,7 +241,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
static int bcm_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, bcm_proc_show, PDE_DATA(inode));
return single_open_net(inode, file, bcm_proc_show);
}
static const struct file_operations bcm_proc_fops = {
@ -267,7 +266,7 @@ static void bcm_can_tx(struct bcm_op *op)
if (!op->ifindex)
return;
dev = dev_get_by_index(&init_net, op->ifindex);
dev = dev_get_by_index(sock_net(op->sk), op->ifindex);
if (!dev) {
/* RFC: should this bcm_op remove itself here? */
return;
@ -764,7 +763,7 @@ static void bcm_remove_op(struct bcm_op *op)
static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
{
if (op->rx_reg_dev == dev) {
can_rx_unregister(&init_net, dev, op->can_id,
can_rx_unregister(dev_net(dev), dev, op->can_id,
REGMASK(op->can_id), bcm_rx_handler, op);
/* mark as removed subscription */
@ -800,7 +799,7 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
if (op->rx_reg_dev) {
struct net_device *dev;
dev = dev_get_by_index(&init_net,
dev = dev_get_by_index(sock_net(op->sk),
op->ifindex);
if (dev) {
bcm_rx_unreg(dev, op);
@ -808,7 +807,8 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
}
}
} else
can_rx_unregister(&init_net, NULL, op->can_id,
can_rx_unregister(sock_net(op->sk), NULL,
op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op);
@ -1220,9 +1220,9 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
if (ifindex) {
struct net_device *dev;
dev = dev_get_by_index(&init_net, ifindex);
dev = dev_get_by_index(sock_net(sk), ifindex);
if (dev) {
err = can_rx_register(&init_net, dev,
err = can_rx_register(sock_net(sk), dev,
op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op,
@ -1233,7 +1233,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
}
} else
err = can_rx_register(&init_net, NULL, op->can_id,
err = can_rx_register(sock_net(sk), NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op, "bcm", sk);
if (err) {
@ -1273,7 +1273,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk,
return err;
}
dev = dev_get_by_index(&init_net, ifindex);
dev = dev_get_by_index(sock_net(sk), ifindex);
if (!dev) {
kfree_skb(skb);
return -ENODEV;
@ -1338,7 +1338,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if (ifindex) {
struct net_device *dev;
dev = dev_get_by_index(&init_net, ifindex);
dev = dev_get_by_index(sock_net(sk), ifindex);
if (!dev)
return -ENODEV;
@ -1419,7 +1419,7 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
struct bcm_op *op;
int notify_enodev = 0;
if (!net_eq(dev_net(dev), &init_net))
if (!net_eq(dev_net(dev), sock_net(sk)))
return NOTIFY_DONE;
if (dev->type != ARPHRD_CAN)
@ -1491,6 +1491,7 @@ static int bcm_init(struct sock *sk)
static int bcm_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
struct bcm_sock *bo;
struct bcm_op *op, *next;
@ -1522,14 +1523,14 @@ static int bcm_release(struct socket *sock)
if (op->rx_reg_dev) {
struct net_device *dev;
dev = dev_get_by_index(&init_net, op->ifindex);
dev = dev_get_by_index(net, op->ifindex);
if (dev) {
bcm_rx_unreg(dev, op);
dev_put(dev);
}
}
} else
can_rx_unregister(&init_net, NULL, op->can_id,
can_rx_unregister(net, NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op);
@ -1537,8 +1538,8 @@ static int bcm_release(struct socket *sock)
}
/* remove procfs entry */
if (proc_dir && bo->bcm_proc_read)
remove_proc_entry(bo->procname, proc_dir);
if (net->can.bcmproc_dir && bo->bcm_proc_read)
remove_proc_entry(bo->procname, net->can.bcmproc_dir);
/* remove device reference */
if (bo->bound) {
@ -1561,6 +1562,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
struct sock *sk = sock->sk;
struct bcm_sock *bo = bcm_sk(sk);
struct net *net = sock_net(sk);
int ret = 0;
if (len < sizeof(*addr))
@ -1577,7 +1579,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
if (addr->can_ifindex) {
struct net_device *dev;
dev = dev_get_by_index(&init_net, addr->can_ifindex);
dev = dev_get_by_index(net, addr->can_ifindex);
if (!dev) {
ret = -ENODEV;
goto fail;
@ -1596,11 +1598,11 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
bo->ifindex = 0;
}
if (proc_dir) {
if (net->can.bcmproc_dir) {
/* unique socket address as filename */
sprintf(bo->procname, "%lu", sock_i_ino(sk));
bo->bcm_proc_read = proc_create_data(bo->procname, 0644,
proc_dir,
net->can.bcmproc_dir,
&bcm_proc_fops, sk);
if (!bo->bcm_proc_read) {
ret = -ENOMEM;
@ -1687,6 +1689,31 @@ static const struct can_proto bcm_can_proto = {
.prot = &bcm_proto,
};
static int canbcm_pernet_init(struct net *net)
{
/* create /proc/net/can-bcm directory */
if (IS_ENABLED(CONFIG_PROC_FS)) {
net->can.bcmproc_dir =
proc_net_mkdir(net, "can-bcm", net->proc_net);
}
return 0;
}
static void canbcm_pernet_exit(struct net *net)
{
/* remove /proc/net/can-bcm directory */
if (IS_ENABLED(CONFIG_PROC_FS)) {
if (net->can.bcmproc_dir)
remove_proc_entry("can-bcm", net->proc_net);
}
}
static struct pernet_operations canbcm_pernet_ops __read_mostly = {
.init = canbcm_pernet_init,
.exit = canbcm_pernet_exit,
};
static int __init bcm_module_init(void)
{
int err;
@ -1699,17 +1726,14 @@ static int __init bcm_module_init(void)
return err;
}
/* create /proc/net/can-bcm directory */
proc_dir = proc_mkdir("can-bcm", init_net.proc_net);
register_pernet_subsys(&canbcm_pernet_ops);
return 0;
}
static void __exit bcm_module_exit(void)
{
can_proto_unregister(&bcm_can_proto);
if (proc_dir)
remove_proc_entry("can-bcm", init_net.proc_net);
unregister_pernet_subsys(&canbcm_pernet_ops);
}
module_init(bcm_module_init);

View File

@ -1,7 +1,7 @@
/*
* gw.c - CAN frame Gateway/Router/Bridge with netlink interface
*
* Copyright (c) 2011 Volkswagen Group Electronic Research
* Copyright (c) 2017 Volkswagen Group Electronic Research
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -59,7 +59,7 @@
#include <net/net_namespace.h>
#include <net/sock.h>
#define CAN_GW_VERSION "20130117"
#define CAN_GW_VERSION "20170425"
#define CAN_GW_NAME "can-gw"
MODULE_DESCRIPTION("PF_CAN netlink gateway");
@ -79,9 +79,7 @@ MODULE_PARM_DESC(max_hops,
__stringify(CGW_MAX_HOPS) " hops, "
"default: " __stringify(CGW_DEFAULT_HOPS) ")");
static HLIST_HEAD(cgw_list);
static struct notifier_block notifier;
static struct kmem_cache *cgw_cache __read_mostly;
/* structure that contains the (on-the-fly) CAN frame modifications */
@ -438,16 +436,16 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
gwj->handled_frames++;
}
static inline int cgw_register_filter(struct cgw_job *gwj)
static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)
{
return can_rx_register(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id,
return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv,
gwj, "gw", NULL);
}
static inline void cgw_unregister_filter(struct cgw_job *gwj)
static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj)
{
can_rx_unregister(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id,
can_rx_unregister(net, gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
}
@ -455,9 +453,8 @@ static int cgw_notifier(struct notifier_block *nb,
unsigned long msg, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct net *net = dev_net(dev);
if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
@ -468,11 +465,11 @@ static int cgw_notifier(struct notifier_block *nb,
ASSERT_RTNL();
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
if (gwj->src.dev == dev || gwj->dst.dev == dev) {
hlist_del(&gwj->list);
cgw_unregister_filter(gwj);
cgw_unregister_filter(net, gwj);
kmem_cache_free(cgw_cache, gwj);
}
}
@ -592,12 +589,13 @@ cancel:
/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
struct cgw_job *gwj = NULL;
int idx = 0;
int s_idx = cb->args[0];
rcu_read_lock();
hlist_for_each_entry_rcu(gwj, &cgw_list, list) {
hlist_for_each_entry_rcu(gwj, &net->can.cgw_list, list) {
if (idx < s_idx)
goto cont;
@ -812,6 +810,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct net *net = sock_net(skb->sk);
struct rtcanmsg *r;
struct cgw_job *gwj;
struct cf_mod mod;
@ -842,7 +841,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
ASSERT_RTNL();
/* check for updating an existing job with identical uid */
hlist_for_each_entry(gwj, &cgw_list, list) {
hlist_for_each_entry(gwj, &net->can.cgw_list, list) {
if (gwj->mod.uid != mod.uid)
continue;
@ -880,7 +879,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
err = -ENODEV;
gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx);
gwj->src.dev = __dev_get_by_index(net, gwj->ccgw.src_idx);
if (!gwj->src.dev)
goto out;
@ -888,7 +887,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
if (gwj->src.dev->type != ARPHRD_CAN)
goto out;
gwj->dst.dev = __dev_get_by_index(&init_net, gwj->ccgw.dst_idx);
gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);
if (!gwj->dst.dev)
goto out;
@ -898,9 +897,9 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
ASSERT_RTNL();
err = cgw_register_filter(gwj);
err = cgw_register_filter(net, gwj);
if (!err)
hlist_add_head_rcu(&gwj->list, &cgw_list);
hlist_add_head_rcu(&gwj->list, &net->can.cgw_list);
out:
if (err)
kmem_cache_free(cgw_cache, gwj);
@ -908,16 +907,16 @@ out:
return err;
}
static void cgw_remove_all_jobs(void)
static void cgw_remove_all_jobs(struct net *net)
{
struct cgw_job *gwj = NULL;
struct hlist_node *nx;
ASSERT_RTNL();
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
hlist_del(&gwj->list);
cgw_unregister_filter(gwj);
cgw_unregister_filter(net, gwj);
kmem_cache_free(cgw_cache, gwj);
}
}
@ -925,6 +924,7 @@ static void cgw_remove_all_jobs(void)
static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct net *net = sock_net(skb->sk);
struct cgw_job *gwj = NULL;
struct hlist_node *nx;
struct rtcanmsg *r;
@ -953,7 +953,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
/* two interface indices both set to 0 => remove all entries */
if (!ccgw.src_idx && !ccgw.dst_idx) {
cgw_remove_all_jobs();
cgw_remove_all_jobs(net);
return 0;
}
@ -962,7 +962,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
ASSERT_RTNL();
/* remove only the first matching entry */
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
if (gwj->flags != r->flags)
continue;
@ -985,7 +985,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
continue;
hlist_del(&gwj->list);
cgw_unregister_filter(gwj);
cgw_unregister_filter(net, gwj);
kmem_cache_free(cgw_cache, gwj);
err = 0;
break;
@ -994,6 +994,24 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
return err;
}
static int __net_init cangw_pernet_init(struct net *net)
{
INIT_HLIST_HEAD(&net->can.cgw_list);
return 0;
}
static void __net_exit cangw_pernet_exit(struct net *net)
{
rtnl_lock();
cgw_remove_all_jobs(net);
rtnl_unlock();
}
static struct pernet_operations cangw_pernet_ops = {
.init = cangw_pernet_init,
.exit = cangw_pernet_exit,
};
static __init int cgw_module_init(void)
{
/* sanitize given module parameter */
@ -1002,6 +1020,7 @@ static __init int cgw_module_init(void)
pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n",
max_hops);
register_pernet_subsys(&cangw_pernet_ops);
cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
0, 0, NULL);
@ -1031,10 +1050,7 @@ static __exit void cgw_module_exit(void)
unregister_netdevice_notifier(&notifier);
rtnl_lock();
cgw_remove_all_jobs();
rtnl_unlock();
unregister_pernet_subsys(&cangw_pernet_ops);
rcu_barrier(); /* Wait for completion of call_rcu()'s */
kmem_cache_destroy(cgw_cache);

View File

@ -75,21 +75,23 @@ static const char rx_list_name[][8] = {
* af_can statistics stuff
*/
static void can_init_stats(void)
static void can_init_stats(struct net *net)
{
struct s_stats *can_stats = net->can.can_stats;
struct s_pstats *can_pstats = net->can.can_pstats;
/*
* This memset function is called from a timer context (when
* can_stattimer is active which is the default) OR in a process
* context (reading the proc_fs when can_stattimer is disabled).
*/
memset(&can_stats, 0, sizeof(can_stats));
can_stats.jiffies_init = jiffies;
memset(can_stats, 0, sizeof(struct s_stats));
can_stats->jiffies_init = jiffies;
can_pstats.stats_reset++;
can_pstats->stats_reset++;
if (user_reset) {
user_reset = 0;
can_pstats.user_reset++;
can_pstats->user_reset++;
}
}
@ -115,64 +117,66 @@ static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
void can_stat_update(unsigned long data)
{
struct net *net = (struct net *)data;
struct s_stats *can_stats = net->can.can_stats;
unsigned long j = jiffies; /* snapshot */
/* restart counting in timer context on user request */
if (user_reset)
can_init_stats();
can_init_stats(net);
/* restart counting on jiffies overflow */
if (j < can_stats.jiffies_init)
can_init_stats();
if (j < can_stats->jiffies_init)
can_init_stats(net);
/* prevent overflow in calc_rate() */
if (can_stats.rx_frames > (ULONG_MAX / HZ))
can_init_stats();
if (can_stats->rx_frames > (ULONG_MAX / HZ))
can_init_stats(net);
/* prevent overflow in calc_rate() */
if (can_stats.tx_frames > (ULONG_MAX / HZ))
can_init_stats();
if (can_stats->tx_frames > (ULONG_MAX / HZ))
can_init_stats(net);
/* matches overflow - very improbable */
if (can_stats.matches > (ULONG_MAX / 100))
can_init_stats();
if (can_stats->matches > (ULONG_MAX / 100))
can_init_stats(net);
/* calc total values */
if (can_stats.rx_frames)
can_stats.total_rx_match_ratio = (can_stats.matches * 100) /
can_stats.rx_frames;
if (can_stats->rx_frames)
can_stats->total_rx_match_ratio = (can_stats->matches * 100) /
can_stats->rx_frames;
can_stats.total_tx_rate = calc_rate(can_stats.jiffies_init, j,
can_stats.tx_frames);
can_stats.total_rx_rate = calc_rate(can_stats.jiffies_init, j,
can_stats.rx_frames);
can_stats->total_tx_rate = calc_rate(can_stats->jiffies_init, j,
can_stats->tx_frames);
can_stats->total_rx_rate = calc_rate(can_stats->jiffies_init, j,
can_stats->rx_frames);
/* calc current values */
if (can_stats.rx_frames_delta)
can_stats.current_rx_match_ratio =
(can_stats.matches_delta * 100) /
can_stats.rx_frames_delta;
if (can_stats->rx_frames_delta)
can_stats->current_rx_match_ratio =
(can_stats->matches_delta * 100) /
can_stats->rx_frames_delta;
can_stats.current_tx_rate = calc_rate(0, HZ, can_stats.tx_frames_delta);
can_stats.current_rx_rate = calc_rate(0, HZ, can_stats.rx_frames_delta);
can_stats->current_tx_rate = calc_rate(0, HZ, can_stats->tx_frames_delta);
can_stats->current_rx_rate = calc_rate(0, HZ, can_stats->rx_frames_delta);
/* check / update maximum values */
if (can_stats.max_tx_rate < can_stats.current_tx_rate)
can_stats.max_tx_rate = can_stats.current_tx_rate;
if (can_stats->max_tx_rate < can_stats->current_tx_rate)
can_stats->max_tx_rate = can_stats->current_tx_rate;
if (can_stats.max_rx_rate < can_stats.current_rx_rate)
can_stats.max_rx_rate = can_stats.current_rx_rate;
if (can_stats->max_rx_rate < can_stats->current_rx_rate)
can_stats->max_rx_rate = can_stats->current_rx_rate;
if (can_stats.max_rx_match_ratio < can_stats.current_rx_match_ratio)
can_stats.max_rx_match_ratio = can_stats.current_rx_match_ratio;
if (can_stats->max_rx_match_ratio < can_stats->current_rx_match_ratio)
can_stats->max_rx_match_ratio = can_stats->current_rx_match_ratio;
/* clear values for 'current rate' calculation */
can_stats.tx_frames_delta = 0;
can_stats.rx_frames_delta = 0;
can_stats.matches_delta = 0;
can_stats->tx_frames_delta = 0;
can_stats->rx_frames_delta = 0;
can_stats->matches_delta = 0;
/* restart timer (one second) */
mod_timer(&can_stattimer, round_jiffies(jiffies + HZ));
mod_timer(&net->can.can_stattimer, round_jiffies(jiffies + HZ));
}
/*
@ -206,57 +210,61 @@ static void can_print_recv_banner(struct seq_file *m)
static int can_stats_proc_show(struct seq_file *m, void *v)
{
struct net *net = m->private;
struct s_stats *can_stats = net->can.can_stats;
struct s_pstats *can_pstats = net->can.can_pstats;
seq_putc(m, '\n');
seq_printf(m, " %8ld transmitted frames (TXF)\n", can_stats.tx_frames);
seq_printf(m, " %8ld received frames (RXF)\n", can_stats.rx_frames);
seq_printf(m, " %8ld matched frames (RXMF)\n", can_stats.matches);
seq_printf(m, " %8ld transmitted frames (TXF)\n", can_stats->tx_frames);
seq_printf(m, " %8ld received frames (RXF)\n", can_stats->rx_frames);
seq_printf(m, " %8ld matched frames (RXMF)\n", can_stats->matches);
seq_putc(m, '\n');
if (can_stattimer.function == can_stat_update) {
if (net->can.can_stattimer.function == can_stat_update) {
seq_printf(m, " %8ld %% total match ratio (RXMR)\n",
can_stats.total_rx_match_ratio);
can_stats->total_rx_match_ratio);
seq_printf(m, " %8ld frames/s total tx rate (TXR)\n",
can_stats.total_tx_rate);
can_stats->total_tx_rate);
seq_printf(m, " %8ld frames/s total rx rate (RXR)\n",
can_stats.total_rx_rate);
can_stats->total_rx_rate);
seq_putc(m, '\n');
seq_printf(m, " %8ld %% current match ratio (CRXMR)\n",
can_stats.current_rx_match_ratio);
can_stats->current_rx_match_ratio);
seq_printf(m, " %8ld frames/s current tx rate (CTXR)\n",
can_stats.current_tx_rate);
can_stats->current_tx_rate);
seq_printf(m, " %8ld frames/s current rx rate (CRXR)\n",
can_stats.current_rx_rate);
can_stats->current_rx_rate);
seq_putc(m, '\n');
seq_printf(m, " %8ld %% max match ratio (MRXMR)\n",
can_stats.max_rx_match_ratio);
can_stats->max_rx_match_ratio);
seq_printf(m, " %8ld frames/s max tx rate (MTXR)\n",
can_stats.max_tx_rate);
can_stats->max_tx_rate);
seq_printf(m, " %8ld frames/s max rx rate (MRXR)\n",
can_stats.max_rx_rate);
can_stats->max_rx_rate);
seq_putc(m, '\n');
}
seq_printf(m, " %8ld current receive list entries (CRCV)\n",
can_pstats.rcv_entries);
can_pstats->rcv_entries);
seq_printf(m, " %8ld maximum receive list entries (MRCV)\n",
can_pstats.rcv_entries_max);
can_pstats->rcv_entries_max);
if (can_pstats.stats_reset)
if (can_pstats->stats_reset)
seq_printf(m, "\n %8ld statistic resets (STR)\n",
can_pstats.stats_reset);
can_pstats->stats_reset);
if (can_pstats.user_reset)
if (can_pstats->user_reset)
seq_printf(m, " %8ld user statistic resets (USTR)\n",
can_pstats.user_reset);
can_pstats->user_reset);
seq_putc(m, '\n');
return 0;
@ -264,7 +272,7 @@ static int can_stats_proc_show(struct seq_file *m, void *v)
static int can_stats_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, can_stats_proc_show, NULL);
return single_open_net(inode, file, can_stats_proc_show);
}
static const struct file_operations can_stats_proc_fops = {
@ -277,25 +285,28 @@ static const struct file_operations can_stats_proc_fops = {
static int can_reset_stats_proc_show(struct seq_file *m, void *v)
{
struct net *net = m->private;
struct s_pstats *can_pstats = net->can.can_pstats;
struct s_stats *can_stats = net->can.can_stats;
user_reset = 1;
if (can_stattimer.function == can_stat_update) {
if (net->can.can_stattimer.function == can_stat_update) {
seq_printf(m, "Scheduled statistic reset #%ld.\n",
can_pstats.stats_reset + 1);
can_pstats->stats_reset + 1);
} else {
if (can_stats.jiffies_init != jiffies)
can_init_stats();
if (can_stats->jiffies_init != jiffies)
can_init_stats(net);
seq_printf(m, "Performed statistic reset #%ld.\n",
can_pstats.stats_reset);
can_pstats->stats_reset);
}
return 0;
}
static int can_reset_stats_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, can_reset_stats_proc_show, NULL);
return single_open_net(inode, file, can_reset_stats_proc_show);
}
static const struct file_operations can_reset_stats_proc_fops = {
@ -314,7 +325,7 @@ static int can_version_proc_show(struct seq_file *m, void *v)
static int can_version_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, can_version_proc_show, NULL);
return single_open_net(inode, file, can_version_proc_show);
}
static const struct file_operations can_version_proc_fops = {