提交 45a6f3bc 编写于 作者: D David S. Miller

Merge tag 'linux-can-next-for-4.12-20170425' of...

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: NDavid S. Miller <davem@davemloft.net>
......@@ -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"
......
......@@ -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
......
此差异已折叠。
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.
#
# 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
/*
* 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;
}
/*
* 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
此差异已折叠。
......@@ -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);
......
......@@ -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
......@@ -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
此差异已折叠。
......@@ -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);
......
/*
* 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,
};
......
/*
* 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);
......@@ -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"
......
......@@ -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
......@@ -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__ */
#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
......@@ -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);
......
......@@ -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 */
/*
* 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);
......
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册