You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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:
@@ -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
|
||||
|
||||
+586
-168
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
@@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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
|
||||
+40
-37
@@ -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);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user