Commit 45a6f3bc authored by David S. Miller's avatar 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: default avatarDavid S. Miller <davem@davemloft.net>
parents 3133822f b655f0e9
...@@ -9,6 +9,24 @@ config CAN_VCAN ...@@ -9,6 +9,24 @@ config CAN_VCAN
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called vcan. 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 config CAN_SLCAN
tristate "Serial / USB serial CAN Adaptors (slcan)" tristate "Serial / USB serial CAN Adaptors (slcan)"
depends on TTY depends on TTY
...@@ -142,6 +160,7 @@ source "drivers/net/can/cc770/Kconfig" ...@@ -142,6 +160,7 @@ source "drivers/net/can/cc770/Kconfig"
source "drivers/net/can/ifi_canfd/Kconfig" source "drivers/net/can/ifi_canfd/Kconfig"
source "drivers/net/can/m_can/Kconfig" source "drivers/net/can/m_can/Kconfig"
source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/mscan/Kconfig"
source "drivers/net/can/peak_canfd/Kconfig"
source "drivers/net/can/rcar/Kconfig" source "drivers/net/can/rcar/Kconfig"
source "drivers/net/can/sja1000/Kconfig" source "drivers/net/can/sja1000/Kconfig"
source "drivers/net/can/softing/Kconfig" source "drivers/net/can/softing/Kconfig"
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# #
obj-$(CONFIG_CAN_VCAN) += vcan.o obj-$(CONFIG_CAN_VCAN) += vcan.o
obj-$(CONFIG_CAN_VXCAN) += vxcan.o
obj-$(CONFIG_CAN_SLCAN) += slcan.o obj-$(CONFIG_CAN_SLCAN) += slcan.o
obj-$(CONFIG_CAN_DEV) += can-dev.o obj-$(CONFIG_CAN_DEV) += can-dev.o
...@@ -26,6 +27,7 @@ obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/ ...@@ -26,6 +27,7 @@ obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/
obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_MSCAN) += mscan/
obj-$(CONFIG_CAN_M_CAN) += m_can/ obj-$(CONFIG_CAN_M_CAN) += m_can/
obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/
obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_SJA1000) += sja1000/
obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/iopoll.h>
#include <linux/can/dev.h> #include <linux/can/dev.h>
/* napi related */ /* napi related */
...@@ -37,17 +37,19 @@ enum m_can_reg { ...@@ -37,17 +37,19 @@ enum m_can_reg {
M_CAN_CREL = 0x0, M_CAN_CREL = 0x0,
M_CAN_ENDN = 0x4, M_CAN_ENDN = 0x4,
M_CAN_CUST = 0x8, M_CAN_CUST = 0x8,
M_CAN_FBTP = 0xc, M_CAN_DBTP = 0xc,
M_CAN_TEST = 0x10, M_CAN_TEST = 0x10,
M_CAN_RWD = 0x14, M_CAN_RWD = 0x14,
M_CAN_CCCR = 0x18, M_CAN_CCCR = 0x18,
M_CAN_BTP = 0x1c, M_CAN_NBTP = 0x1c,
M_CAN_TSCC = 0x20, M_CAN_TSCC = 0x20,
M_CAN_TSCV = 0x24, M_CAN_TSCV = 0x24,
M_CAN_TOCC = 0x28, M_CAN_TOCC = 0x28,
M_CAN_TOCV = 0x2c, M_CAN_TOCV = 0x2c,
M_CAN_ECR = 0x40, M_CAN_ECR = 0x40,
M_CAN_PSR = 0x44, M_CAN_PSR = 0x44,
/* TDCR Register only available for version >=3.1.x */
M_CAN_TDCR = 0x48,
M_CAN_IR = 0x50, M_CAN_IR = 0x50,
M_CAN_IE = 0x54, M_CAN_IE = 0x54,
M_CAN_ILS = 0x58, M_CAN_ILS = 0x58,
...@@ -105,21 +107,29 @@ enum m_can_mram_cfg { ...@@ -105,21 +107,29 @@ enum m_can_mram_cfg {
MRAM_CFG_NUM, MRAM_CFG_NUM,
}; };
/* Fast Bit Timing & Prescaler Register (FBTP) */ /* Core Release Register (CREL) */
#define FBTR_FBRP_MASK 0x1f #define CREL_REL_SHIFT 28
#define FBTR_FBRP_SHIFT 16 #define CREL_REL_MASK (0xF << CREL_REL_SHIFT)
#define FBTR_FTSEG1_SHIFT 8 #define CREL_STEP_SHIFT 24
#define FBTR_FTSEG1_MASK (0xf << FBTR_FTSEG1_SHIFT) #define CREL_STEP_MASK (0xF << CREL_STEP_SHIFT)
#define FBTR_FTSEG2_SHIFT 4 #define CREL_SUBSTEP_SHIFT 20
#define FBTR_FTSEG2_MASK (0x7 << FBTR_FTSEG2_SHIFT) #define CREL_SUBSTEP_MASK (0xF << CREL_SUBSTEP_SHIFT)
#define FBTR_FSJW_SHIFT 0
#define FBTR_FSJW_MASK 0x3 /* Data Bit Timing & Prescaler Register (DBTP) */
#define DBTP_TDC BIT(23)
#define DBTP_DBRP_SHIFT 16
#define DBTP_DBRP_MASK (0x1f << DBTP_DBRP_SHIFT)
#define DBTP_DTSEG1_SHIFT 8
#define DBTP_DTSEG1_MASK (0x1f << DBTP_DTSEG1_SHIFT)
#define DBTP_DTSEG2_SHIFT 4
#define DBTP_DTSEG2_MASK (0xf << DBTP_DTSEG2_SHIFT)
#define DBTP_DSJW_SHIFT 0
#define DBTP_DSJW_MASK (0xf << DBTP_DSJW_SHIFT)
/* Test Register (TEST) */ /* Test Register (TEST) */
#define TEST_LBCK BIT(4) #define TEST_LBCK BIT(4)
/* CC Control Register(CCCR) */ /* CC Control Register(CCCR) */
#define CCCR_TEST BIT(7)
#define CCCR_CMR_MASK 0x3 #define CCCR_CMR_MASK 0x3
#define CCCR_CMR_SHIFT 10 #define CCCR_CMR_SHIFT 10
#define CCCR_CMR_CANFD 0x1 #define CCCR_CMR_CANFD 0x1
...@@ -130,21 +140,32 @@ enum m_can_mram_cfg { ...@@ -130,21 +140,32 @@ enum m_can_mram_cfg {
#define CCCR_CME_CAN 0 #define CCCR_CME_CAN 0
#define CCCR_CME_CANFD 0x1 #define CCCR_CME_CANFD 0x1
#define CCCR_CME_CANFD_BRS 0x2 #define CCCR_CME_CANFD_BRS 0x2
#define CCCR_TXP BIT(14)
#define CCCR_TEST BIT(7) #define CCCR_TEST BIT(7)
#define CCCR_MON BIT(5) #define CCCR_MON BIT(5)
#define CCCR_CSR BIT(4)
#define CCCR_CSA BIT(3)
#define CCCR_ASM BIT(2)
#define CCCR_CCE BIT(1) #define CCCR_CCE BIT(1)
#define CCCR_INIT BIT(0) #define CCCR_INIT BIT(0)
#define CCCR_CANFD 0x10 #define CCCR_CANFD 0x10
/* for version >=3.1.x */
/* Bit Timing & Prescaler Register (BTP) */ #define CCCR_EFBI BIT(13)
#define BTR_BRP_MASK 0x3ff #define CCCR_PXHD BIT(12)
#define BTR_BRP_SHIFT 16 #define CCCR_BRSE BIT(9)
#define BTR_TSEG1_SHIFT 8 #define CCCR_FDOE BIT(8)
#define BTR_TSEG1_MASK (0x3f << BTR_TSEG1_SHIFT) /* only for version >=3.2.x */
#define BTR_TSEG2_SHIFT 4 #define CCCR_NISO BIT(15)
#define BTR_TSEG2_MASK (0xf << BTR_TSEG2_SHIFT)
#define BTR_SJW_SHIFT 0 /* Nominal Bit Timing & Prescaler Register (NBTP) */
#define BTR_SJW_MASK 0xf #define NBTP_NSJW_SHIFT 25
#define NBTP_NSJW_MASK (0x7f << NBTP_NSJW_SHIFT)
#define NBTP_NBRP_SHIFT 16
#define NBTP_NBRP_MASK (0x1ff << NBTP_NBRP_SHIFT)
#define NBTP_NTSEG1_SHIFT 8
#define NBTP_NTSEG1_MASK (0xff << NBTP_NTSEG1_SHIFT)
#define NBTP_NTSEG2_SHIFT 0
#define NBTP_NTSEG2_MASK (0x7f << NBTP_NTSEG2_SHIFT)
/* Error Counter Register(ECR) */ /* Error Counter Register(ECR) */
#define ECR_RP BIT(15) #define ECR_RP BIT(15)
...@@ -161,6 +182,13 @@ enum m_can_mram_cfg { ...@@ -161,6 +182,13 @@ enum m_can_mram_cfg {
/* Interrupt Register(IR) */ /* Interrupt Register(IR) */
#define IR_ALL_INT 0xffffffff #define IR_ALL_INT 0xffffffff
/* Renamed bits for versions > 3.1.x */
#define IR_ARA BIT(29)
#define IR_PED BIT(28)
#define IR_PEA BIT(27)
/* Bits for version 3.0.x */
#define IR_STE BIT(31) #define IR_STE BIT(31)
#define IR_FOE BIT(30) #define IR_FOE BIT(30)
#define IR_ACKE BIT(29) #define IR_ACKE BIT(29)
...@@ -194,33 +222,40 @@ enum m_can_mram_cfg { ...@@ -194,33 +222,40 @@ enum m_can_mram_cfg {
#define IR_RF0W BIT(1) #define IR_RF0W BIT(1)
#define IR_RF0N BIT(0) #define IR_RF0N BIT(0)
#define IR_ERR_STATE (IR_BO | IR_EW | IR_EP) #define IR_ERR_STATE (IR_BO | IR_EW | IR_EP)
#define IR_ERR_LEC (IR_STE | IR_FOE | IR_ACKE | IR_BE | IR_CRCE)
#define IR_ERR_BUS (IR_ERR_LEC | IR_WDI | IR_ELO | IR_BEU | \ /* Interrupts for version 3.0.x */
#define IR_ERR_LEC_30X (IR_STE | IR_FOE | IR_ACKE | IR_BE | IR_CRCE)
#define IR_ERR_BUS_30X (IR_ERR_LEC_30X | IR_WDI | IR_ELO | IR_BEU | \
IR_BEC | IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | \ IR_BEC | IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | \
IR_RF1L | IR_RF0L) IR_RF1L | IR_RF0L)
#define IR_ERR_ALL (IR_ERR_STATE | IR_ERR_BUS) #define IR_ERR_ALL_30X (IR_ERR_STATE | IR_ERR_BUS_30X)
/* Interrupts for version >= 3.1.x */
#define IR_ERR_LEC_31X (IR_PED | IR_PEA)
#define IR_ERR_BUS_31X (IR_ERR_LEC_31X | IR_WDI | IR_ELO | IR_BEU | \
IR_BEC | IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | \
IR_RF1L | IR_RF0L)
#define IR_ERR_ALL_31X (IR_ERR_STATE | IR_ERR_BUS_31X)
/* Interrupt Line Select (ILS) */ /* Interrupt Line Select (ILS) */
#define ILS_ALL_INT0 0x0 #define ILS_ALL_INT0 0x0
#define ILS_ALL_INT1 0xFFFFFFFF #define ILS_ALL_INT1 0xFFFFFFFF
/* Interrupt Line Enable (ILE) */ /* Interrupt Line Enable (ILE) */
#define ILE_EINT0 BIT(0)
#define ILE_EINT1 BIT(1) #define ILE_EINT1 BIT(1)
#define ILE_EINT0 BIT(0)
/* Rx FIFO 0/1 Configuration (RXF0C/RXF1C) */ /* Rx FIFO 0/1 Configuration (RXF0C/RXF1C) */
#define RXFC_FWM_OFF 24 #define RXFC_FWM_SHIFT 24
#define RXFC_FWM_MASK 0x7f #define RXFC_FWM_MASK (0x7f < RXFC_FWM_SHIFT)
#define RXFC_FWM_1 (1 << RXFC_FWM_OFF) #define RXFC_FS_SHIFT 16
#define RXFC_FS_OFF 16 #define RXFC_FS_MASK (0x7f << RXFC_FS_SHIFT)
#define RXFC_FS_MASK 0x7f
/* Rx FIFO 0/1 Status (RXF0S/RXF1S) */ /* Rx FIFO 0/1 Status (RXF0S/RXF1S) */
#define RXFS_RFL BIT(25) #define RXFS_RFL BIT(25)
#define RXFS_FF BIT(24) #define RXFS_FF BIT(24)
#define RXFS_FPI_OFF 16 #define RXFS_FPI_SHIFT 16
#define RXFS_FPI_MASK 0x3f0000 #define RXFS_FPI_MASK 0x3f0000
#define RXFS_FGI_OFF 8 #define RXFS_FGI_SHIFT 8
#define RXFS_FGI_MASK 0x3f00 #define RXFS_FGI_MASK 0x3f00
#define RXFS_FFL_MASK 0x7f #define RXFS_FFL_MASK 0x7f
...@@ -229,23 +264,46 @@ enum m_can_mram_cfg { ...@@ -229,23 +264,46 @@ enum m_can_mram_cfg {
#define M_CAN_RXESC_64BYTES 0x777 #define M_CAN_RXESC_64BYTES 0x777
/* Tx Buffer Configuration(TXBC) */ /* Tx Buffer Configuration(TXBC) */
#define TXBC_NDTB_OFF 16 #define TXBC_NDTB_SHIFT 16
#define TXBC_NDTB_MASK 0x3f #define TXBC_NDTB_MASK (0x3f << TXBC_NDTB_SHIFT)
#define TXBC_TFQS_SHIFT 24
#define TXBC_TFQS_MASK (0x3f << TXBC_TFQS_SHIFT)
/* Tx FIFO/Queue Status (TXFQS) */
#define TXFQS_TFQF BIT(21)
#define TXFQS_TFQPI_SHIFT 16
#define TXFQS_TFQPI_MASK (0x1f << TXFQS_TFQPI_SHIFT)
#define TXFQS_TFGI_SHIFT 8
#define TXFQS_TFGI_MASK (0x1f << TXFQS_TFGI_SHIFT)
#define TXFQS_TFFL_SHIFT 0
#define TXFQS_TFFL_MASK (0x3f << TXFQS_TFFL_SHIFT)
/* Tx Buffer Element Size Configuration(TXESC) */ /* Tx Buffer Element Size Configuration(TXESC) */
#define TXESC_TBDS_8BYTES 0x0 #define TXESC_TBDS_8BYTES 0x0
#define TXESC_TBDS_64BYTES 0x7 #define TXESC_TBDS_64BYTES 0x7
/* Tx Event FIFO Con.guration (TXEFC) */ /* Tx Event FIFO Configuration (TXEFC) */
#define TXEFC_EFS_OFF 16 #define TXEFC_EFS_SHIFT 16
#define TXEFC_EFS_MASK 0x3f #define TXEFC_EFS_MASK (0x3f << TXEFC_EFS_SHIFT)
/* Tx Event FIFO Status (TXEFS) */
#define TXEFS_TEFL BIT(25)
#define TXEFS_EFF BIT(24)
#define TXEFS_EFGI_SHIFT 8
#define TXEFS_EFGI_MASK (0x1f << TXEFS_EFGI_SHIFT)
#define TXEFS_EFFL_SHIFT 0
#define TXEFS_EFFL_MASK (0x3f << TXEFS_EFFL_SHIFT)
/* Tx Event FIFO Acknowledge (TXEFA) */
#define TXEFA_EFAI_SHIFT 0
#define TXEFA_EFAI_MASK (0x1f << TXEFA_EFAI_SHIFT)
/* Message RAM Configuration (in bytes) */ /* Message RAM Configuration (in bytes) */
#define SIDF_ELEMENT_SIZE 4 #define SIDF_ELEMENT_SIZE 4
#define XIDF_ELEMENT_SIZE 8 #define XIDF_ELEMENT_SIZE 8
#define RXF0_ELEMENT_SIZE 72 #define RXF0_ELEMENT_SIZE 72
#define RXF1_ELEMENT_SIZE 72 #define RXF1_ELEMENT_SIZE 72
#define RXB_ELEMENT_SIZE 16 #define RXB_ELEMENT_SIZE 72
#define TXE_ELEMENT_SIZE 8 #define TXE_ELEMENT_SIZE 8
#define TXB_ELEMENT_SIZE 72 #define TXB_ELEMENT_SIZE 72
...@@ -261,13 +319,25 @@ enum m_can_mram_cfg { ...@@ -261,13 +319,25 @@ enum m_can_mram_cfg {
#define RX_BUF_RTR BIT(29) #define RX_BUF_RTR BIT(29)
/* R1 */ /* R1 */
#define RX_BUF_ANMF BIT(31) #define RX_BUF_ANMF BIT(31)
#define RX_BUF_EDL BIT(21) #define RX_BUF_FDF BIT(21)
#define RX_BUF_BRS BIT(20) #define RX_BUF_BRS BIT(20)
/* Tx Buffer Element */ /* Tx Buffer Element */
/* R0 */ /* T0 */
#define TX_BUF_ESI BIT(31)
#define TX_BUF_XTD BIT(30) #define TX_BUF_XTD BIT(30)
#define TX_BUF_RTR BIT(29) #define TX_BUF_RTR BIT(29)
/* T1 */
#define TX_BUF_EFC BIT(23)
#define TX_BUF_FDF BIT(21)
#define TX_BUF_BRS BIT(20)
#define TX_BUF_MM_SHIFT 24
#define TX_BUF_MM_MASK (0xff << TX_BUF_MM_SHIFT)
/* Tx event FIFO Element */
/* E1 */
#define TX_EVENT_MM_SHIFT TX_BUF_MM_SHIFT
#define TX_EVENT_MM_MASK (0xff << TX_EVENT_MM_SHIFT)
/* address offset and element number for each FIFO/Buffer in the Message RAM */ /* address offset and element number for each FIFO/Buffer in the Message RAM */
struct mram_cfg { struct mram_cfg {
...@@ -285,6 +355,7 @@ struct m_can_priv { ...@@ -285,6 +355,7 @@ struct m_can_priv {
struct clk *cclk; struct clk *cclk;
void __iomem *base; void __iomem *base;
u32 irqstatus; u32 irqstatus;
int version;
/* message ram configuration */ /* message ram configuration */
void __iomem *mram_base; void __iomem *mram_base;
...@@ -316,6 +387,18 @@ static inline void m_can_fifo_write(const struct m_can_priv *priv, ...@@ -316,6 +387,18 @@ static inline void m_can_fifo_write(const struct m_can_priv *priv,
fpi * TXB_ELEMENT_SIZE + offset); fpi * TXB_ELEMENT_SIZE + offset);
} }
static inline u32 m_can_txe_fifo_read(const struct m_can_priv *priv,
u32 fgi,
u32 offset) {
return readl(priv->mram_base + priv->mcfg[MRAM_TXE].off +
fgi * TXE_ELEMENT_SIZE + offset);
}
static inline bool m_can_tx_fifo_full(const struct m_can_priv *priv)
{
return !!(m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQF);
}
static inline void m_can_config_endisable(const struct m_can_priv *priv, static inline void m_can_config_endisable(const struct m_can_priv *priv,
bool enable) bool enable)
{ {
...@@ -349,7 +432,8 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv, ...@@ -349,7 +432,8 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
static inline void m_can_enable_all_interrupts(const struct m_can_priv *priv) static inline void m_can_enable_all_interrupts(const struct m_can_priv *priv)
{ {
m_can_write(priv, M_CAN_ILE, ILE_EINT0 | ILE_EINT1); /* Only interrupt line 0 is used in this driver */
m_can_write(priv, M_CAN_ILE, ILE_EINT0);
} }
static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv) static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv)
...@@ -367,9 +451,9 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) ...@@ -367,9 +451,9 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
int i; int i;
/* calculate the fifo get index for where to read data */ /* calculate the fifo get index for where to read data */
fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_OFF; fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_SHIFT;
dlc = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC); dlc = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC);
if (dlc & RX_BUF_EDL) if (dlc & RX_BUF_FDF)
skb = alloc_canfd_skb(dev, &cf); skb = alloc_canfd_skb(dev, &cf);
else else
skb = alloc_can_skb(dev, (struct can_frame **)&cf); skb = alloc_can_skb(dev, (struct can_frame **)&cf);
...@@ -378,7 +462,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) ...@@ -378,7 +462,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
return; return;
} }
if (dlc & RX_BUF_EDL) if (dlc & RX_BUF_FDF)
cf->len = can_dlc2len((dlc >> 16) & 0x0F); cf->len = can_dlc2len((dlc >> 16) & 0x0F);
else else
cf->len = get_can_dlc((dlc >> 16) & 0x0F); cf->len = get_can_dlc((dlc >> 16) & 0x0F);
...@@ -394,7 +478,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) ...@@ -394,7 +478,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
netdev_dbg(dev, "ESI Error\n"); netdev_dbg(dev, "ESI Error\n");
} }
if (!(dlc & RX_BUF_EDL) && (id & RX_BUF_RTR)) { if (!(dlc & RX_BUF_FDF) && (id & RX_BUF_RTR)) {
cf->can_id |= CAN_RTR_FLAG; cf->can_id |= CAN_RTR_FLAG;
} else { } else {
if (dlc & RX_BUF_BRS) if (dlc & RX_BUF_BRS)
...@@ -532,7 +616,7 @@ static int __m_can_get_berr_counter(const struct net_device *dev, ...@@ -532,7 +616,7 @@ static int __m_can_get_berr_counter(const struct net_device *dev,
ecr = m_can_read(priv, M_CAN_ECR); ecr = m_can_read(priv, M_CAN_ECR);
bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT; bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT;
bec->txerr = ecr & ECR_TEC_MASK; bec->txerr = (ecr & ECR_TEC_MASK) >> ECR_TEC_SHIFT;
return 0; return 0;
} }
...@@ -723,7 +807,7 @@ static int m_can_poll(struct napi_struct *napi, int quota) ...@@ -723,7 +807,7 @@ static int m_can_poll(struct napi_struct *napi, int quota)
if (irqstatus & IR_ERR_STATE) if (irqstatus & IR_ERR_STATE)
work_done += m_can_handle_state_errors(dev, psr); work_done += m_can_handle_state_errors(dev, psr);
if (irqstatus & IR_ERR_BUS) if (irqstatus & IR_ERR_BUS_30X)
work_done += m_can_handle_bus_errors(dev, irqstatus, psr); work_done += m_can_handle_bus_errors(dev, irqstatus, psr);
if (irqstatus & IR_RF0N) if (irqstatus & IR_RF0N)
...@@ -738,6 +822,44 @@ static int m_can_poll(struct napi_struct *napi, int quota) ...@@ -738,6 +822,44 @@ static int m_can_poll(struct napi_struct *napi, int quota)
return work_done; return work_done;
} }
static void m_can_echo_tx_event(struct net_device *dev)
{
u32 txe_count = 0;
u32 m_can_txefs;
u32 fgi = 0;
int i = 0;
unsigned int msg_mark;
struct m_can_priv *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
/* read tx event fifo status */
m_can_txefs = m_can_read(priv, M_CAN_TXEFS);
/* Get Tx Event fifo element count */
txe_count = (m_can_txefs & TXEFS_EFFL_MASK)
>> TXEFS_EFFL_SHIFT;
/* Get and process all sent elements */
for (i = 0; i < txe_count; i++) {
/* retrieve get index */
fgi = (m_can_read(priv, M_CAN_TXEFS) & TXEFS_EFGI_MASK)
>> TXEFS_EFGI_SHIFT;
/* get message marker */
msg_mark = (m_can_txe_fifo_read(priv, fgi, 4) &
TX_EVENT_MM_MASK) >> TX_EVENT_MM_SHIFT;
/* ack txe element */
m_can_write(priv, M_CAN_TXEFA, (TXEFA_EFAI_MASK &
(fgi << TXEFA_EFAI_SHIFT)));
/* update stats */
stats->tx_bytes += can_get_echo_skb(dev, msg_mark);
stats->tx_packets++;
}
}
static irqreturn_t m_can_isr(int irq, void *dev_id) static irqreturn_t m_can_isr(int irq, void *dev_id)
{ {
struct net_device *dev = (struct net_device *)dev_id; struct net_device *dev = (struct net_device *)dev_id;
...@@ -758,24 +880,35 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) ...@@ -758,24 +880,35 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
* - state change IRQ * - state change IRQ
* - bus error IRQ and bus error reporting * - bus error IRQ and bus error reporting
*/ */
if ((ir & IR_RF0N) || (ir & IR_ERR_ALL)) { if ((ir & IR_RF0N) || (ir & IR_ERR_ALL_30X)) {
priv->irqstatus = ir; priv->irqstatus = ir;
m_can_disable_all_interrupts(priv); m_can_disable_all_interrupts(priv);
napi_schedule(&priv->napi); napi_schedule(&priv->napi);
} }
/* transmission complete interrupt */ if (priv->version == 30) {
if (ir & IR_TC) { if (ir & IR_TC) {
stats->tx_bytes += can_get_echo_skb(dev, 0); /* Transmission Complete Interrupt*/
stats->tx_packets++; stats->tx_bytes += can_get_echo_skb(dev, 0);
can_led_event(dev, CAN_LED_EVENT_TX); stats->tx_packets++;
netif_wake_queue(dev); can_led_event(dev, CAN_LED_EVENT_TX);
netif_wake_queue(dev);
}
} else {
if (ir & IR_TEFN) {
/* New TX FIFO Element arrived */
m_can_echo_tx_event(dev);
can_led_event(dev, CAN_LED_EVENT_TX);
if (netif_queue_stopped(dev) &&
!m_can_tx_fifo_full(priv))
netif_wake_queue(dev);
}
} }
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static const struct can_bittiming_const m_can_bittiming_const = { static const struct can_bittiming_const m_can_bittiming_const_30X = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
.tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */
.tseg1_max = 64, .tseg1_max = 64,
...@@ -787,7 +920,7 @@ static const struct can_bittiming_const m_can_bittiming_const = { ...@@ -787,7 +920,7 @@ static const struct can_bittiming_const m_can_bittiming_const = {
.brp_inc = 1, .brp_inc = 1,
}; };
static const struct can_bittiming_const m_can_data_bittiming_const = { static const struct can_bittiming_const m_can_data_bittiming_const_30X = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
.tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */
.tseg1_max = 16, .tseg1_max = 16,
...@@ -799,6 +932,30 @@ static const struct can_bittiming_const m_can_data_bittiming_const = { ...@@ -799,6 +932,30 @@ static const struct can_bittiming_const m_can_data_bittiming_const = {
.brp_inc = 1, .brp_inc = 1,
}; };
static const struct can_bittiming_const m_can_bittiming_const_31X = {
.name = KBUILD_MODNAME,
.tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */
.tseg1_max = 256,
.tseg2_min = 1, /* Time segment 2 = phase_seg2 */
.tseg2_max = 128,
.sjw_max = 128,
.brp_min = 1,
.brp_max = 512,
.brp_inc = 1,
};
static const struct can_bittiming_const m_can_data_bittiming_const_31X = {
.name = KBUILD_MODNAME,
.tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */
.tseg1_max = 32,
.tseg2_min = 1, /* Time segment 2 = phase_seg2 */
.tseg2_max = 16,
.sjw_max = 16,
.brp_min = 1,
.brp_max = 32,
.brp_inc = 1,
};
static int m_can_set_bittiming(struct net_device *dev) static int m_can_set_bittiming(struct net_device *dev)
{ {
struct m_can_priv *priv = netdev_priv(dev); struct m_can_priv *priv = netdev_priv(dev);
...@@ -811,19 +968,19 @@ static int m_can_set_bittiming(struct net_device *dev) ...@@ -811,19 +968,19 @@ static int m_can_set_bittiming(struct net_device *dev)
sjw = bt->sjw - 1; sjw = bt->sjw - 1;
tseg1 = bt->prop_seg + bt->phase_seg1 - 1; tseg1 = bt->prop_seg + bt->phase_seg1 - 1;
tseg2 = bt->phase_seg2 - 1; tseg2 = bt->phase_seg2 - 1;
reg_btp = (brp << BTR_BRP_SHIFT) | (sjw << BTR_SJW_SHIFT) | reg_btp = (brp << NBTP_NBRP_SHIFT) | (sjw << NBTP_NSJW_SHIFT) |
(tseg1 << BTR_TSEG1_SHIFT) | (tseg2 << BTR_TSEG2_SHIFT); (tseg1 << NBTP_NTSEG1_SHIFT) | (tseg2 << NBTP_NTSEG2_SHIFT);
m_can_write(priv, M_CAN_BTP, reg_btp); m_can_write(priv, M_CAN_NBTP, reg_btp);
if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
brp = dbt->brp - 1; brp = dbt->brp - 1;
sjw = dbt->sjw - 1; sjw = dbt->sjw - 1;
tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
tseg2 = dbt->phase_seg2 - 1; tseg2 = dbt->phase_seg2 - 1;
reg_btp = (brp << FBTR_FBRP_SHIFT) | (sjw << FBTR_FSJW_SHIFT) | reg_btp = (brp << DBTP_DBRP_SHIFT) | (sjw << DBTP_DSJW_SHIFT) |
(tseg1 << FBTR_FTSEG1_SHIFT) | (tseg1 << DBTP_DTSEG1_SHIFT) |
(tseg2 << FBTR_FTSEG2_SHIFT); (tseg2 << DBTP_DTSEG2_SHIFT);
m_can_write(priv, M_CAN_FBTP, reg_btp); m_can_write(priv, M_CAN_DBTP, reg_btp);
} }
return 0; return 0;
...@@ -834,6 +991,7 @@ static int m_can_set_bittiming(struct net_device *dev) ...@@ -834,6 +991,7 @@ static int m_can_set_bittiming(struct net_device *dev)
* - configure rx fifo * - configure rx fifo
* - accept non-matching frame into fifo 0 * - accept non-matching frame into fifo 0
* - configure tx buffer * - configure tx buffer
* - >= v3.1.x: TX FIFO is used
* - configure mode * - configure mode
* - setup bittiming * - setup bittiming
*/ */
...@@ -850,49 +1008,89 @@ static void m_can_chip_config(struct net_device *dev) ...@@ -850,49 +1008,89 @@ static void m_can_chip_config(struct net_device *dev)
/* Accept Non-matching Frames Into FIFO 0 */ /* Accept Non-matching Frames Into FIFO 0 */
m_can_write(priv, M_CAN_GFC, 0x0); m_can_write(priv, M_CAN_GFC, 0x0);
/* only support one Tx Buffer currently */ if (priv->version == 30) {
m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_OFF) | /* only support one Tx Buffer currently */
priv->mcfg[MRAM_TXB].off); m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_SHIFT) |
priv->mcfg[MRAM_TXB].off);
} else {
/* TX FIFO is used for newer IP Core versions */
m_can_write(priv, M_CAN_TXBC,
(priv->mcfg[MRAM_TXB].num << TXBC_TFQS_SHIFT) |
(priv->mcfg[MRAM_TXB].off));
}
/* support 64 bytes payload */ /* support 64 bytes payload */
m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_64BYTES); m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_64BYTES);
m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_OFF) | /* TX Event FIFO */
priv->mcfg[MRAM_TXE].off); if (priv->version == 30) {
m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_SHIFT) |
priv->mcfg[MRAM_TXE].off);
} else {
/* Full TX Event FIFO is used */
m_can_write(priv, M_CAN_TXEFC,
((priv->mcfg[MRAM_TXE].num << TXEFC_EFS_SHIFT)
& TXEFC_EFS_MASK) |
priv->mcfg[MRAM_TXE].off);
}
/* rx fifo configuration, blocking mode, fifo size 1 */ /* rx fifo configuration, blocking mode, fifo size 1 */
m_can_write(priv, M_CAN_RXF0C, m_can_write(priv, M_CAN_RXF0C,
(priv->mcfg[MRAM_RXF0].num << RXFC_FS_OFF) | (priv->mcfg[MRAM_RXF0].num << RXFC_FS_SHIFT) |
RXFC_FWM_1 | priv->mcfg[MRAM_RXF0].off); priv->mcfg[MRAM_RXF0].off);
m_can_write(priv, M_CAN_RXF1C, m_can_write(priv, M_CAN_RXF1C,
(priv->mcfg[MRAM_RXF1].num << RXFC_FS_OFF) | (priv->mcfg[MRAM_RXF1].num << RXFC_FS_SHIFT) |
RXFC_FWM_1 | priv->mcfg[MRAM_RXF1].off); priv->mcfg[MRAM_RXF1].off);
cccr = m_can_read(priv, M_CAN_CCCR); cccr = m_can_read(priv, M_CAN_CCCR);
cccr &= ~(CCCR_TEST | CCCR_MON | (CCCR_CMR_MASK << CCCR_CMR_SHIFT) |
(CCCR_CME_MASK << CCCR_CME_SHIFT));
test = m_can_read(priv, M_CAN_TEST); test = m_can_read(priv, M_CAN_TEST);
test &= ~TEST_LBCK; test &= ~TEST_LBCK;
if (priv->version == 30) {
/* Version 3.0.x */
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) cccr &= ~(CCCR_TEST | CCCR_MON |
cccr |= CCCR_MON; (CCCR_CMR_MASK << CCCR_CMR_SHIFT) |
(CCCR_CME_MASK << CCCR_CME_SHIFT));
if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT;
} else {
/* Version 3.1.x or 3.2.x */
cccr &= ~(CCCR_TEST | CCCR_MON | CCCR_BRSE | CCCR_FDOE);
/* Only 3.2.x has NISO Bit implemented */
if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)
cccr |= CCCR_NISO;
if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
cccr |= (CCCR_BRSE | CCCR_FDOE);
}
/* Loopback Mode */
if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
cccr |= CCCR_TEST; cccr |= CCCR_TEST | CCCR_MON;
test |= TEST_LBCK; test |= TEST_LBCK;
} }
if (priv->can.ctrlmode & CAN_CTRLMODE_FD) /* Enable Monitoring (all versions) */
cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT; if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
cccr |= CCCR_MON;
/* Write config */
m_can_write(priv, M_CAN_CCCR, cccr); m_can_write(priv, M_CAN_CCCR, cccr);
m_can_write(priv, M_CAN_TEST, test); m_can_write(priv, M_CAN_TEST, test);
/* enable interrupts */ /* Enable interrupts */
m_can_write(priv, M_CAN_IR, IR_ALL_INT); m_can_write(priv, M_CAN_IR, IR_ALL_INT);
if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
m_can_write(priv, M_CAN_IE, IR_ALL_INT & ~IR_ERR_LEC); if (priv->version == 30)
m_can_write(priv, M_CAN_IE, IR_ALL_INT &
~(IR_ERR_LEC_30X));
else
m_can_write(priv, M_CAN_IE, IR_ALL_INT &
~(IR_ERR_LEC_31X));
else else
m_can_write(priv, M_CAN_IE, IR_ALL_INT); m_can_write(priv, M_CAN_IE, IR_ALL_INT);
...@@ -936,33 +1134,140 @@ static void free_m_can_dev(struct net_device *dev) ...@@ -936,33 +1134,140 @@ static void free_m_can_dev(struct net_device *dev)
free_candev(dev); free_candev(dev);
} }
static struct net_device *alloc_m_can_dev(void) /* Checks core release number of M_CAN
* returns 0 if an unsupported device is detected
* else it returns the release and step coded as:
* return value = 10 * <release> + 1 * <step>
*/
static int m_can_check_core_release(void __iomem *m_can_base)
{
u32 crel_reg;
u8 rel;
u8 step;
int res;
struct m_can_priv temp_priv = {
.base = m_can_base
};
/* Read Core Release Version and split into version number
* Example: Version 3.2.1 => rel = 3; step = 2; substep = 1;
*/
crel_reg = m_can_read(&temp_priv, M_CAN_CREL);
rel = (u8)((crel_reg & CREL_REL_MASK) >> CREL_REL_SHIFT);
step = (u8)((crel_reg & CREL_STEP_MASK) >> CREL_STEP_SHIFT);
if (rel == 3) {
/* M_CAN v3.x.y: create return value */
res = 30 + step;
} else {
/* Unsupported M_CAN version */
res = 0;
}
return res;
}
/* Selectable Non ISO support only in version 3.2.x
* This function checks if the bit is writable.
*/
static bool m_can_niso_supported(const struct m_can_priv *priv)
{
u32 cccr_reg, cccr_poll;
int niso_timeout;
m_can_config_endisable(priv, true);
cccr_reg = m_can_read(priv, M_CAN_CCCR);
cccr_reg |= CCCR_NISO;
m_can_write(priv, M_CAN_CCCR, cccr_reg);
niso_timeout = readl_poll_timeout((priv->base + M_CAN_CCCR), cccr_poll,
(cccr_poll == cccr_reg), 0, 10);
/* Clear NISO */
cccr_reg &= ~(CCCR_NISO);
m_can_write(priv, M_CAN_CCCR, cccr_reg);
m_can_config_endisable(priv, false);
/* return false if time out (-ETIMEDOUT), else return true */
return !niso_timeout;
}
static struct net_device *alloc_m_can_dev(struct platform_device *pdev,
void __iomem *addr, u32 tx_fifo_size)
{ {
struct net_device *dev; struct net_device *dev;
struct m_can_priv *priv; struct m_can_priv *priv;
int m_can_version;
unsigned int echo_buffer_count;
m_can_version = m_can_check_core_release(addr);
/* return if unsupported version */
if (!m_can_version) {
dev = NULL;
goto return_dev;
}
dev = alloc_candev(sizeof(*priv), 1); /* If version < 3.1.x, then only one echo buffer is used */
if (!dev) echo_buffer_count = ((m_can_version == 30)
return NULL; ? 1U
: (unsigned int)tx_fifo_size);
dev = alloc_candev(sizeof(*priv), echo_buffer_count);
if (!dev) {
dev = NULL;
goto return_dev;
}
priv = netdev_priv(dev); priv = netdev_priv(dev);
netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT); netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT);
/* Shared properties of all M_CAN versions */
priv->version = m_can_version;
priv->dev = dev; priv->dev = dev;
priv->can.bittiming_const = &m_can_bittiming_const; priv->base = addr;
priv->can.data_bittiming_const = &m_can_data_bittiming_const;
priv->can.do_set_mode = m_can_set_mode; priv->can.do_set_mode = m_can_set_mode;
priv->can.do_get_berr_counter = m_can_get_berr_counter; priv->can.do_get_berr_counter = m_can_get_berr_counter;
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.1 */ /* Set M_CAN supported operations */
can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
/* CAN_CTRLMODE_FD_NON_ISO can not be changed with M_CAN IP v3.0.1 */
priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_LISTENONLY |
CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_BERR_REPORTING |
CAN_CTRLMODE_FD; CAN_CTRLMODE_FD;
/* Set properties depending on M_CAN version */
switch (priv->version) {
case 30:
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.x */
can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
priv->can.bittiming_const = &m_can_bittiming_const_30X;
priv->can.data_bittiming_const =
&m_can_data_bittiming_const_30X;
break;
case 31:
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.1.x */
can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
priv->can.bittiming_const = &m_can_bittiming_const_31X;
priv->can.data_bittiming_const =
&m_can_data_bittiming_const_31X;
break;
case 32:
priv->can.bittiming_const = &m_can_bittiming_const_31X;
priv->can.data_bittiming_const =
&m_can_data_bittiming_const_31X;
priv->can.ctrlmode_supported |= (m_can_niso_supported(priv)
? CAN_CTRLMODE_FD_NON_ISO
: 0);
break;
default:
/* Unsupported device: free candev */
free_m_can_dev(dev);
dev_err(&pdev->dev, "Unsupported version number: %2d",
priv->version);
dev = NULL;
break;
}
return_dev:
return dev; return dev;
} }
...@@ -1040,19 +1345,34 @@ static int m_can_close(struct net_device *dev) ...@@ -1040,19 +1345,34 @@ static int m_can_close(struct net_device *dev)
return 0; return 0;
} }
static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx)
{
struct m_can_priv *priv = netdev_priv(dev);
/*get wrap around for loopback skb index */
unsigned int wrap = priv->can.echo_skb_max;
int next_idx;
/* calculate next index */
next_idx = (++putidx >= wrap ? 0 : putidx);
/* check if occupied */
return !!priv->can.echo_skb[next_idx];
}
static netdev_tx_t m_can_start_xmit(struct sk_buff *skb, static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
struct net_device *dev) struct net_device *dev)
{ {
struct m_can_priv *priv = netdev_priv(dev); struct m_can_priv *priv = netdev_priv(dev);
struct canfd_frame *cf = (struct canfd_frame *)skb->data; struct canfd_frame *cf = (struct canfd_frame *)skb->data;
u32 id, cccr; u32 id, cccr, fdflags;
int i; int i;
int putidx;
if (can_dropped_invalid_skb(dev, skb)) if (can_dropped_invalid_skb(dev, skb))
return NETDEV_TX_OK; return NETDEV_TX_OK;
netif_stop_queue(dev); /* Generate ID field for TX buffer Element */
/* Common to all supported M_CAN versions */
if (cf->can_id & CAN_EFF_FLAG) { if (cf->can_id & CAN_EFF_FLAG) {
id = cf->can_id & CAN_EFF_MASK; id = cf->can_id & CAN_EFF_MASK;
id |= TX_BUF_XTD; id |= TX_BUF_XTD;
...@@ -1063,33 +1383,93 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb, ...@@ -1063,33 +1383,93 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
if (cf->can_id & CAN_RTR_FLAG) if (cf->can_id & CAN_RTR_FLAG)
id |= TX_BUF_RTR; id |= TX_BUF_RTR;
/* message ram configuration */ if (priv->version == 30) {
m_can_fifo_write(priv, 0, M_CAN_FIFO_ID, id); netif_stop_queue(dev);
m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, can_len2dlc(cf->len) << 16);
for (i = 0; i < cf->len; i += 4) /* message ram configuration */
m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(i / 4), m_can_fifo_write(priv, 0, M_CAN_FIFO_ID, id);
*(u32 *)(cf->data + i)); m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC,
can_len2dlc(cf->len) << 16);
can_put_echo_skb(skb, dev, 0); for (i = 0; i < cf->len; i += 4)
m_can_fifo_write(priv, 0,
M_CAN_FIFO_DATA(i / 4),
*(u32 *)(cf->data + i));
can_put_echo_skb(skb, dev, 0);
if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
cccr = m_can_read(priv, M_CAN_CCCR);
cccr &= ~(CCCR_CMR_MASK << CCCR_CMR_SHIFT);
if (can_is_canfd_skb(skb)) {
if (cf->flags & CANFD_BRS)
cccr |= CCCR_CMR_CANFD_BRS <<
CCCR_CMR_SHIFT;
else
cccr |= CCCR_CMR_CANFD <<
CCCR_CMR_SHIFT;
} else {
cccr |= CCCR_CMR_CAN << CCCR_CMR_SHIFT;
}
m_can_write(priv, M_CAN_CCCR, cccr);
}
m_can_write(priv, M_CAN_TXBTIE, 0x1);
m_can_write(priv, M_CAN_TXBAR, 0x1);
/* End of xmit function for version 3.0.x */
} else {
/* Transmit routine for version >= v3.1.x */
/* Check if FIFO full */
if (m_can_tx_fifo_full(priv)) {
/* This shouldn't happen */
netif_stop_queue(dev);
netdev_warn(dev,
"TX queue active although FIFO is full.");
return NETDEV_TX_BUSY;
}
if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { /* get put index for frame */
cccr = m_can_read(priv, M_CAN_CCCR); putidx = ((m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQPI_MASK)
cccr &= ~(CCCR_CMR_MASK << CCCR_CMR_SHIFT); >> TXFQS_TFQPI_SHIFT);
/* Write ID Field to FIFO Element */
m_can_fifo_write(priv, putidx, M_CAN_FIFO_ID, id);
/* get CAN FD configuration of frame */
fdflags = 0;
if (can_is_canfd_skb(skb)) { if (can_is_canfd_skb(skb)) {
fdflags |= TX_BUF_FDF;
if (cf->flags & CANFD_BRS) if (cf->flags & CANFD_BRS)
cccr |= CCCR_CMR_CANFD_BRS << CCCR_CMR_SHIFT; fdflags |= TX_BUF_BRS;
else
cccr |= CCCR_CMR_CANFD << CCCR_CMR_SHIFT;
} else {
cccr |= CCCR_CMR_CAN << CCCR_CMR_SHIFT;
} }
m_can_write(priv, M_CAN_CCCR, cccr);
}
/* enable first TX buffer to start transfer */ /* Construct DLC Field. Also contains CAN-FD configuration
m_can_write(priv, M_CAN_TXBTIE, 0x1); * use put index of fifo as message marker
m_can_write(priv, M_CAN_TXBAR, 0x1); * it is used in TX interrupt for
* sending the correct echo frame
*/
m_can_fifo_write(priv, putidx, M_CAN_FIFO_DLC,
((putidx << TX_BUF_MM_SHIFT) &
TX_BUF_MM_MASK) |
(can_len2dlc(cf->len) << 16) |
fdflags | TX_BUF_EFC);
for (i = 0; i < cf->len; i += 4)
m_can_fifo_write(priv, putidx, M_CAN_FIFO_DATA(i / 4),
*(u32 *)(cf->data + i));
/* Push loopback echo.
* Will be looped back on TX interrupt based on message marker
*/
can_put_echo_skb(skb, dev, putidx);
/* Enable TX FIFO element to start transfer */
m_can_write(priv, M_CAN_TXBAR, (1 << putidx));
/* stop network queue if fifo full */
if (m_can_tx_fifo_full(priv) ||
m_can_next_echo_skb_occupied(dev, putidx))
netif_stop_queue(dev);
}
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
...@@ -1109,55 +1489,37 @@ static int register_m_can_dev(struct net_device *dev) ...@@ -1109,55 +1489,37 @@ static int register_m_can_dev(struct net_device *dev)
return register_candev(dev); return register_candev(dev);
} }
static int m_can_of_parse_mram(struct platform_device *pdev, static void m_can_of_parse_mram(struct m_can_priv *priv,
struct m_can_priv *priv) const u32 *mram_config_vals)
{ {
struct device_node *np = pdev->dev.of_node; int i, start, end;
struct resource *res;
void __iomem *addr;
u32 out_val[MRAM_CFG_LEN];
int i, start, end, ret;
/* message ram could be shared */ priv->mcfg[MRAM_SIDF].off = mram_config_vals[0];
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); priv->mcfg[MRAM_SIDF].num = mram_config_vals[1];
if (!res)
return -ENODEV;
addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!addr)
return -ENOMEM;
/* get message ram configuration */
ret = of_property_read_u32_array(np, "bosch,mram-cfg",
out_val, sizeof(out_val) / 4);
if (ret) {
dev_err(&pdev->dev, "can not get message ram configuration\n");
return -ENODEV;
}
priv->mram_base = addr;
priv->mcfg[MRAM_SIDF].off = out_val[0];
priv->mcfg[MRAM_SIDF].num = out_val[1];
priv->mcfg[MRAM_XIDF].off = priv->mcfg[MRAM_SIDF].off + priv->mcfg[MRAM_XIDF].off = priv->mcfg[MRAM_SIDF].off +
priv->mcfg[MRAM_SIDF].num * SIDF_ELEMENT_SIZE; priv->mcfg[MRAM_SIDF].num * SIDF_ELEMENT_SIZE;
priv->mcfg[MRAM_XIDF].num = out_val[2]; priv->mcfg[MRAM_XIDF].num = mram_config_vals[2];
priv->mcfg[MRAM_RXF0].off = priv->mcfg[MRAM_XIDF].off + priv->mcfg[MRAM_RXF0].off = priv->mcfg[MRAM_XIDF].off +
priv->mcfg[MRAM_XIDF].num * XIDF_ELEMENT_SIZE; priv->mcfg[MRAM_XIDF].num * XIDF_ELEMENT_SIZE;
priv->mcfg[MRAM_RXF0].num = out_val[3] & RXFC_FS_MASK; priv->mcfg[MRAM_RXF0].num = mram_config_vals[3] &
(RXFC_FS_MASK >> RXFC_FS_SHIFT);
priv->mcfg[MRAM_RXF1].off = priv->mcfg[MRAM_RXF0].off + priv->mcfg[MRAM_RXF1].off = priv->mcfg[MRAM_RXF0].off +
priv->mcfg[MRAM_RXF0].num * RXF0_ELEMENT_SIZE; priv->mcfg[MRAM_RXF0].num * RXF0_ELEMENT_SIZE;
priv->mcfg[MRAM_RXF1].num = out_val[4] & RXFC_FS_MASK; priv->mcfg[MRAM_RXF1].num = mram_config_vals[4] &
(RXFC_FS_MASK >> RXFC_FS_SHIFT);
priv->mcfg[MRAM_RXB].off = priv->mcfg[MRAM_RXF1].off + priv->mcfg[MRAM_RXB].off = priv->mcfg[MRAM_RXF1].off +
priv->mcfg[MRAM_RXF1].num * RXF1_ELEMENT_SIZE; priv->mcfg[MRAM_RXF1].num * RXF1_ELEMENT_SIZE;
priv->mcfg[MRAM_RXB].num = out_val[5]; priv->mcfg[MRAM_RXB].num = mram_config_vals[5];
priv->mcfg[MRAM_TXE].off = priv->mcfg[MRAM_RXB].off + priv->mcfg[MRAM_TXE].off = priv->mcfg[MRAM_RXB].off +
priv->mcfg[MRAM_RXB].num * RXB_ELEMENT_SIZE; priv->mcfg[MRAM_RXB].num * RXB_ELEMENT_SIZE;
priv->mcfg[MRAM_TXE].num = out_val[6]; priv->mcfg[MRAM_TXE].num = mram_config_vals[6];
priv->mcfg[MRAM_TXB].off = priv->mcfg[MRAM_TXE].off + priv->mcfg[MRAM_TXB].off = priv->mcfg[MRAM_TXE].off +
priv->mcfg[MRAM_TXE].num * TXE_ELEMENT_SIZE; priv->mcfg[MRAM_TXE].num * TXE_ELEMENT_SIZE;
priv->mcfg[MRAM_TXB].num = out_val[7] & TXBC_NDTB_MASK; priv->mcfg[MRAM_TXB].num = mram_config_vals[7] &
(TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT);
dev_dbg(&pdev->dev, "mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n", dev_dbg(priv->device,
"mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
priv->mram_base, priv->mram_base,
priv->mcfg[MRAM_SIDF].off, priv->mcfg[MRAM_SIDF].num, priv->mcfg[MRAM_SIDF].off, priv->mcfg[MRAM_SIDF].num,
priv->mcfg[MRAM_XIDF].off, priv->mcfg[MRAM_XIDF].num, priv->mcfg[MRAM_XIDF].off, priv->mcfg[MRAM_XIDF].num,
...@@ -1176,7 +1538,6 @@ static int m_can_of_parse_mram(struct platform_device *pdev, ...@@ -1176,7 +1538,6 @@ static int m_can_of_parse_mram(struct platform_device *pdev,
for (i = start; i < end; i += 4) for (i = start; i < end; i += 4)
writel(0x0, priv->mram_base + i); writel(0x0, priv->mram_base + i);
return 0;
} }
static int m_can_plat_probe(struct platform_device *pdev) static int m_can_plat_probe(struct platform_device *pdev)
...@@ -1185,38 +1546,86 @@ static int m_can_plat_probe(struct platform_device *pdev) ...@@ -1185,38 +1546,86 @@ static int m_can_plat_probe(struct platform_device *pdev)
struct m_can_priv *priv; struct m_can_priv *priv;
struct resource *res; struct resource *res;
void __iomem *addr; void __iomem *addr;
void __iomem *mram_addr;
struct clk *hclk, *cclk; struct clk *hclk, *cclk;
int irq, ret; int irq, ret;
struct device_node *np;
u32 mram_config_vals[MRAM_CFG_LEN];
u32 tx_fifo_size;
np = pdev->dev.of_node;
hclk = devm_clk_get(&pdev->dev, "hclk"); hclk = devm_clk_get(&pdev->dev, "hclk");
cclk = devm_clk_get(&pdev->dev, "cclk"); cclk = devm_clk_get(&pdev->dev, "cclk");
if (IS_ERR(hclk) || IS_ERR(cclk)) { if (IS_ERR(hclk) || IS_ERR(cclk)) {
dev_err(&pdev->dev, "no clock find\n"); dev_err(&pdev->dev, "no clock found\n");
return -ENODEV; ret = -ENODEV;
goto failed_ret;
} }
/* Enable clocks. Necessary to read Core Release in order to determine
* M_CAN version
*/
ret = clk_prepare_enable(hclk);
if (ret)
goto disable_hclk_ret;
ret = clk_prepare_enable(cclk);
if (ret)
goto disable_cclk_ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can"); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
addr = devm_ioremap_resource(&pdev->dev, res); addr = devm_ioremap_resource(&pdev->dev, res);
irq = platform_get_irq_byname(pdev, "int0"); irq = platform_get_irq_byname(pdev, "int0");
if (IS_ERR(addr) || irq < 0)
return -EINVAL;
/* allocate the m_can device */ if (IS_ERR(addr) || irq < 0) {
dev = alloc_m_can_dev(); ret = -EINVAL;
if (!dev) goto disable_cclk_ret;
return -ENOMEM; }
/* message ram could be shared */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
if (!res) {
ret = -ENODEV;
goto disable_cclk_ret;
}
mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!mram_addr) {
ret = -ENOMEM;
goto disable_cclk_ret;
}
/* get message ram configuration */
ret = of_property_read_u32_array(np, "bosch,mram-cfg",
mram_config_vals,
sizeof(mram_config_vals) / 4);
if (ret) {
dev_err(&pdev->dev, "Could not get Message RAM configuration.");
goto disable_cclk_ret;
}
/* Get TX FIFO size
* Defines the total amount of echo buffers for loopback
*/
tx_fifo_size = mram_config_vals[7];
/* allocate the m_can device */
dev = alloc_m_can_dev(pdev, addr, tx_fifo_size);
if (!dev) {
ret = -ENOMEM;
goto disable_cclk_ret;
}
priv = netdev_priv(dev); priv = netdev_priv(dev);
dev->irq = irq; dev->irq = irq;
priv->base = addr;
priv->device = &pdev->dev; priv->device = &pdev->dev;
priv->hclk = hclk; priv->hclk = hclk;
priv->cclk = cclk; priv->cclk = cclk;
priv->can.clock.freq = clk_get_rate(cclk); priv->can.clock.freq = clk_get_rate(cclk);
priv->mram_base = mram_addr;
ret = m_can_of_parse_mram(pdev, priv); m_can_of_parse_mram(priv, mram_config_vals);
if (ret)
goto failed_free_dev;
platform_set_drvdata(pdev, dev); platform_set_drvdata(pdev, dev);
SET_NETDEV_DEV(dev, &pdev->dev); SET_NETDEV_DEV(dev, &pdev->dev);
...@@ -1230,13 +1639,22 @@ static int m_can_plat_probe(struct platform_device *pdev) ...@@ -1230,13 +1639,22 @@ static int m_can_plat_probe(struct platform_device *pdev)
devm_can_led_init(dev); devm_can_led_init(dev);
dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n",
KBUILD_MODNAME, priv->base, dev->irq); KBUILD_MODNAME, dev->irq, priv->version);
return 0; /* Probe finished
* Stop clocks. They will be reactivated once the M_CAN device is opened
*/
goto disable_cclk_ret;
failed_free_dev: failed_free_dev:
free_m_can_dev(dev); free_m_can_dev(dev);
disable_cclk_ret:
clk_disable_unprepare(cclk);
disable_hclk_ret:
clk_disable_unprepare(hclk);
failed_ret:
return ret; return ret;
} }
......
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
/*
* Copyright (C) 2007, 2011 Wolfgang Grandegger <wg@grandegger.com>
* Copyright (C) 2012 Stephane Grosjean <s.grosjean@peak-system.com>
*
* Derived from the PCAN project file driver/src/pcan_pci.c:
*
* Copyright (C) 2001-2006 PEAK System-Technik GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the version 2 of the GNU General Public License
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include "peak_canfd_user.h"
MODULE_AUTHOR("Stephane Grosjean <s.grosjean@peak-system.com>");
MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCIe FD family cards");
MODULE_SUPPORTED_DEVICE("PEAK PCAN PCIe FD CAN cards");
MODULE_LICENSE("GPL v2");
#define PCIEFD_DRV_NAME "peak_pciefd"
#define PEAK_PCI_VENDOR_ID 0x001c /* The PCI device and vendor IDs */
#define PEAK_PCIEFD_ID 0x0013 /* for PCIe slot cards */
/* PEAK PCIe board access description */
#define PCIEFD_BAR0_SIZE (64 * 1024)
#define PCIEFD_RX_DMA_SIZE (4 * 1024)
#define PCIEFD_TX_DMA_SIZE (4 * 1024)
#define PCIEFD_TX_PAGE_SIZE (2 * 1024)
/* System Control Registers */
#define PCIEFD_REG_SYS_CTL_SET 0x0000 /* set bits */
#define PCIEFD_REG_SYS_CTL_CLR 0x0004 /* clear bits */
/* Version info registers */
#define PCIEFD_REG_SYS_VER1 0x0040 /* version reg #1 */
#define PCIEFD_REG_SYS_VER2 0x0044 /* version reg #2 */
/* System Control Registers Bits */
#define PCIEFD_SYS_CTL_TS_RST 0x00000001 /* timestamp clock */
#define PCIEFD_SYS_CTL_CLK_EN 0x00000002 /* system clock */
/* CAN-FD channel addresses */
#define PCIEFD_CANX_OFF(c) (((c) + 1) * 0x1000)
#define PCIEFD_ECHO_SKB_MAX PCANFD_ECHO_SKB_DEF
/* CAN-FD channel registers */
#define PCIEFD_REG_CAN_MISC 0x0000 /* Misc. control */
#define PCIEFD_REG_CAN_CLK_SEL 0x0008 /* Clock selector */
#define PCIEFD_REG_CAN_CMD_PORT_L 0x0010 /* 64-bits command port */
#define PCIEFD_REG_CAN_CMD_PORT_H 0x0014
#define PCIEFD_REG_CAN_TX_REQ_ACC 0x0020 /* Tx request accumulator */
#define PCIEFD_REG_CAN_TX_CTL_SET 0x0030 /* Tx control set register */
#define PCIEFD_REG_CAN_TX_CTL_CLR 0x0038 /* Tx control clear register */
#define PCIEFD_REG_CAN_TX_DMA_ADDR_L 0x0040 /* 64-bits addr for Tx DMA */
#define PCIEFD_REG_CAN_TX_DMA_ADDR_H 0x0044
#define PCIEFD_REG_CAN_RX_CTL_SET 0x0050 /* Rx control set register */
#define PCIEFD_REG_CAN_RX_CTL_CLR 0x0058 /* Rx control clear register */
#define PCIEFD_REG_CAN_RX_CTL_WRT 0x0060 /* Rx control write register */
#define PCIEFD_REG_CAN_RX_CTL_ACK 0x0068 /* Rx control ACK register */
#define PCIEFD_REG_CAN_RX_DMA_ADDR_L 0x0070 /* 64-bits addr for Rx DMA */
#define PCIEFD_REG_CAN_RX_DMA_ADDR_H 0x0074
/* CAN-FD channel misc register bits */
#define CANFD_MISC_TS_RST 0x00000001 /* timestamp cnt rst */
/* CAN-FD channel Clock SELector Source & DIVider */
#define CANFD_CLK_SEL_DIV_MASK 0x00000007
#define CANFD_CLK_SEL_DIV_60MHZ 0x00000000 /* SRC=240MHz only */
#define CANFD_CLK_SEL_DIV_40MHZ 0x00000001 /* SRC=240MHz only */
#define CANFD_CLK_SEL_DIV_30MHZ 0x00000002 /* SRC=240MHz only */
#define CANFD_CLK_SEL_DIV_24MHZ 0x00000003 /* SRC=240MHz only */
#define CANFD_CLK_SEL_DIV_20MHZ 0x00000004 /* SRC=240MHz only */
#define CANFD_CLK_SEL_SRC_MASK 0x00000008 /* 0=80MHz, 1=240MHz */
#define CANFD_CLK_SEL_SRC_240MHZ 0x00000008
#define CANFD_CLK_SEL_SRC_80MHZ (~CANFD_CLK_SEL_SRC_240MHZ & \
CANFD_CLK_SEL_SRC_MASK)
#define CANFD_CLK_SEL_20MHZ (CANFD_CLK_SEL_SRC_240MHZ |\
CANFD_CLK_SEL_DIV_20MHZ)
#define CANFD_CLK_SEL_24MHZ (CANFD_CLK_SEL_SRC_240MHZ |\
CANFD_CLK_SEL_DIV_24MHZ)
#define CANFD_CLK_SEL_30MHZ (CANFD_CLK_SEL_SRC_240MHZ |\
CANFD_CLK_SEL_DIV_30MHZ)
#define CANFD_CLK_SEL_40MHZ (CANFD_CLK_SEL_SRC_240MHZ |\
CANFD_CLK_SEL_DIV_40MHZ)
#define CANFD_CLK_SEL_60MHZ (CANFD_CLK_SEL_SRC_240MHZ |\
CANFD_CLK_SEL_DIV_60MHZ)
#define CANFD_CLK_SEL_80MHZ (CANFD_CLK_SEL_SRC_80MHZ)
/* CAN-FD channel Rx/Tx control register bits */
#define CANFD_CTL_UNC_BIT 0x00010000 /* Uncached DMA mem */
#define CANFD_CTL_RST_BIT 0x00020000 /* reset DMA action */
#define CANFD_CTL_IEN_BIT 0x00040000 /* IRQ enable */
/* Rx IRQ Count and Time Limits */
#define CANFD_CTL_IRQ_CL_DEF 16 /* Rx msg max nb per IRQ in Rx DMA */
#define CANFD_CTL_IRQ_TL_DEF 10 /* Time before IRQ if < CL (x100 µs) */
#define CANFD_OPTIONS_SET (CANFD_OPTION_ERROR | CANFD_OPTION_BUSLOAD)
/* Tx anticipation window (link logical address should be aligned on 2K
* boundary)
*/
#define PCIEFD_TX_PAGE_COUNT (PCIEFD_TX_DMA_SIZE / PCIEFD_TX_PAGE_SIZE)
#define CANFD_MSG_LNK_TX 0x1001 /* Tx msgs link */
/* 32-bits IRQ status fields, heading Rx DMA area */
static inline int pciefd_irq_tag(u32 irq_status)
{
return irq_status & 0x0000000f;
}
static inline int pciefd_irq_rx_cnt(u32 irq_status)
{
return (irq_status & 0x000007f0) >> 4;
}
static inline int pciefd_irq_is_lnk(u32 irq_status)
{
return irq_status & 0x00010000;
}
/* Rx record */
struct pciefd_rx_dma {
__le32 irq_status;
__le32 sys_time_low;
__le32 sys_time_high;
struct pucan_rx_msg msg[0];
} __packed __aligned(4);
/* Tx Link record */
struct pciefd_tx_link {
__le16 size;
__le16 type;
__le32 laddr_lo;
__le32 laddr_hi;
} __packed __aligned(4);
/* Tx page descriptor */
struct pciefd_page {
void *vbase; /* page virtual address */
dma_addr_t lbase; /* page logical address */
u32 offset;
u32 size;
};
#define CANFD_IRQ_SET 0x00000001
#define CANFD_TX_PATH_SET 0x00000002
/* CAN-FD channel object */
struct pciefd_board;
struct pciefd_can {
struct peak_canfd_priv ucan; /* must be the first member */
void __iomem *reg_base; /* channel config base addr */
struct pciefd_board *board; /* reverse link */
struct pucan_command pucan_cmd; /* command buffer */
dma_addr_t rx_dma_laddr; /* DMA virtual and logical addr */
void *rx_dma_vaddr; /* for Rx and Tx areas */
dma_addr_t tx_dma_laddr;
void *tx_dma_vaddr;
struct pciefd_page tx_pages[PCIEFD_TX_PAGE_COUNT];
u16 tx_pages_free; /* free Tx pages counter */
u16 tx_page_index; /* current page used for Tx */
spinlock_t tx_lock;
u32 irq_status;
u32 irq_tag; /* next irq tag */
};
/* PEAK-PCIe FD board object */
struct pciefd_board {
void __iomem *reg_base;
struct pci_dev *pci_dev;
int can_count;
spinlock_t cmd_lock; /* 64-bits cmds must be atomic */
struct pciefd_can *can[0]; /* array of network devices */
};
/* supported device ids. */
static const struct pci_device_id peak_pciefd_tbl[] = {
{PEAK_PCI_VENDOR_ID, PEAK_PCIEFD_ID, PCI_ANY_ID, PCI_ANY_ID,},
{0,}
};
MODULE_DEVICE_TABLE(pci, peak_pciefd_tbl);
/* read a 32 bits value from a SYS block register */
static inline u32 pciefd_sys_readreg(const struct pciefd_board *priv, u16 reg)
{
return readl(priv->reg_base + reg);
}
/* write a 32 bits value into a SYS block register */
static inline void pciefd_sys_writereg(const struct pciefd_board *priv,
u32 val, u16 reg)
{
writel(val, priv->reg_base + reg);
}
/* read a 32 bits value from CAN-FD block register */
static inline u32 pciefd_can_readreg(const struct pciefd_can *priv, u16 reg)
{
return readl(priv->reg_base + reg);
}
/* write a 32 bits value into a CAN-FD block register */
static inline void pciefd_can_writereg(const struct pciefd_can *priv,
u32 val, u16 reg)
{
writel(val, priv->reg_base + reg);
}
/* give a channel logical Rx DMA address to the board */
static void pciefd_can_setup_rx_dma(struct pciefd_can *priv)
{
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
const u32 dma_addr_h = (u32)(priv->rx_dma_laddr >> 32);
#else
const u32 dma_addr_h = 0;
#endif
/* (DMA must be reset for Rx) */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET);
/* write the logical address of the Rx DMA area for this channel */
pciefd_can_writereg(priv, (u32)priv->rx_dma_laddr,
PCIEFD_REG_CAN_RX_DMA_ADDR_L);
pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_RX_DMA_ADDR_H);
/* also indicates that Rx DMA is cacheable */
pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_RX_CTL_CLR);
}
/* clear channel logical Rx DMA address from the board */
static void pciefd_can_clear_rx_dma(struct pciefd_can *priv)
{
/* DMA must be reset for Rx */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET);
/* clear the logical address of the Rx DMA area for this channel */
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_L);
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_H);
}
/* give a channel logical Tx DMA address to the board */
static void pciefd_can_setup_tx_dma(struct pciefd_can *priv)
{
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
const u32 dma_addr_h = (u32)(priv->tx_dma_laddr >> 32);
#else
const u32 dma_addr_h = 0;
#endif
/* (DMA must be reset for Tx) */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET);
/* write the logical address of the Tx DMA area for this channel */
pciefd_can_writereg(priv, (u32)priv->tx_dma_laddr,
PCIEFD_REG_CAN_TX_DMA_ADDR_L);
pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_TX_DMA_ADDR_H);
/* also indicates that Tx DMA is cacheable */
pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_TX_CTL_CLR);
}
/* clear channel logical Tx DMA address from the board */
static void pciefd_can_clear_tx_dma(struct pciefd_can *priv)
{
/* DMA must be reset for Tx */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET);
/* clear the logical address of the Tx DMA area for this channel */
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_L);
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_H);
}
static void pciefd_can_ack_rx_dma(struct pciefd_can *priv)
{
/* read value of current IRQ tag and inc it for next one */
priv->irq_tag = le32_to_cpu(*(__le32 *)priv->rx_dma_vaddr);
priv->irq_tag++;
priv->irq_tag &= 0xf;
/* write the next IRQ tag for this CAN */
pciefd_can_writereg(priv, priv->irq_tag, PCIEFD_REG_CAN_RX_CTL_ACK);
}
/* IRQ handler */
static irqreturn_t pciefd_irq_handler(int irq, void *arg)
{
struct pciefd_can *priv = arg;
struct pciefd_rx_dma *rx_dma = priv->rx_dma_vaddr;
/* INTA mode only to sync with PCIe transaction */
if (!pci_dev_msi_enabled(priv->board->pci_dev))
(void)pciefd_sys_readreg(priv->board, PCIEFD_REG_SYS_VER1);
/* read IRQ status from the first 32-bits of the Rx DMA area */
priv->irq_status = le32_to_cpu(rx_dma->irq_status);
/* check if this (shared) IRQ is for this CAN */
if (pciefd_irq_tag(priv->irq_status) != priv->irq_tag)
return IRQ_NONE;
/* handle rx messages (if any) */
peak_canfd_handle_msgs_list(&priv->ucan,
rx_dma->msg,
pciefd_irq_rx_cnt(priv->irq_status));
/* handle tx link interrupt (if any) */
if (pciefd_irq_is_lnk(priv->irq_status)) {
unsigned long flags;
spin_lock_irqsave(&priv->tx_lock, flags);
priv->tx_pages_free++;
spin_unlock_irqrestore(&priv->tx_lock, flags);
/* wake producer up */
netif_wake_queue(priv->ucan.ndev);
}
/* re-enable Rx DMA transfer for this CAN */
pciefd_can_ack_rx_dma(priv);
return IRQ_HANDLED;
}
static int pciefd_enable_tx_path(struct peak_canfd_priv *ucan)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
int i;
/* initialize the Tx pages descriptors */
priv->tx_pages_free = PCIEFD_TX_PAGE_COUNT - 1;
priv->tx_page_index = 0;
priv->tx_pages[0].vbase = priv->tx_dma_vaddr;
priv->tx_pages[0].lbase = priv->tx_dma_laddr;
for (i = 0; i < PCIEFD_TX_PAGE_COUNT; i++) {
priv->tx_pages[i].offset = 0;
priv->tx_pages[i].size = PCIEFD_TX_PAGE_SIZE -
sizeof(struct pciefd_tx_link);
if (i) {
priv->tx_pages[i].vbase =
priv->tx_pages[i - 1].vbase +
PCIEFD_TX_PAGE_SIZE;
priv->tx_pages[i].lbase =
priv->tx_pages[i - 1].lbase +
PCIEFD_TX_PAGE_SIZE;
}
}
/* setup Tx DMA addresses into IP core */
pciefd_can_setup_tx_dma(priv);
/* start (TX_RST=0) Tx Path */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_CLR);
return 0;
}
/* board specific CANFD command pre-processing */
static int pciefd_pre_cmd(struct peak_canfd_priv *ucan)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd);
int err;
/* pre-process command */
switch (cmd) {
case PUCAN_CMD_NORMAL_MODE:
case PUCAN_CMD_LISTEN_ONLY_MODE:
if (ucan->can.state == CAN_STATE_BUS_OFF)
break;
/* going into operational mode: setup IRQ handler */
err = request_irq(priv->board->pci_dev->irq,
pciefd_irq_handler,
IRQF_SHARED,
PCIEFD_DRV_NAME,
priv);
if (err)
return err;
/* setup Rx DMA address */
pciefd_can_setup_rx_dma(priv);
/* setup max count of msgs per IRQ */
pciefd_can_writereg(priv, (CANFD_CTL_IRQ_TL_DEF) << 8 |
CANFD_CTL_IRQ_CL_DEF,
PCIEFD_REG_CAN_RX_CTL_WRT);
/* clear DMA RST for Rx (Rx start) */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT,
PCIEFD_REG_CAN_RX_CTL_CLR);
/* reset timestamps */
pciefd_can_writereg(priv, !CANFD_MISC_TS_RST,
PCIEFD_REG_CAN_MISC);
/* do an initial ACK */
pciefd_can_ack_rx_dma(priv);
/* enable IRQ for this CAN after having set next irq_tag */
pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT,
PCIEFD_REG_CAN_RX_CTL_SET);
/* Tx path will be setup as soon as RX_BARRIER is received */
break;
default:
break;
}
return 0;
}
/* write a command */
static int pciefd_write_cmd(struct peak_canfd_priv *ucan)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
unsigned long flags;
/* 64-bits command is atomic */
spin_lock_irqsave(&priv->board->cmd_lock, flags);
pciefd_can_writereg(priv, *(u32 *)ucan->cmd_buffer,
PCIEFD_REG_CAN_CMD_PORT_L);
pciefd_can_writereg(priv, *(u32 *)(ucan->cmd_buffer + 4),
PCIEFD_REG_CAN_CMD_PORT_H);
spin_unlock_irqrestore(&priv->board->cmd_lock, flags);
return 0;
}
/* board specific CANFD command post-processing */
static int pciefd_post_cmd(struct peak_canfd_priv *ucan)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd);
switch (cmd) {
case PUCAN_CMD_RESET_MODE:
if (ucan->can.state == CAN_STATE_STOPPED)
break;
/* controller now in reset mode: */
/* stop and reset DMA addresses in Tx/Rx engines */
pciefd_can_clear_tx_dma(priv);
pciefd_can_clear_rx_dma(priv);
/* disable IRQ for this CAN */
pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT,
PCIEFD_REG_CAN_RX_CTL_CLR);
free_irq(priv->board->pci_dev->irq, priv);
ucan->can.state = CAN_STATE_STOPPED;
break;
}
return 0;
}
static void *pciefd_alloc_tx_msg(struct peak_canfd_priv *ucan, u16 msg_size,
int *room_left)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
struct pciefd_page *page = priv->tx_pages + priv->tx_page_index;
unsigned long flags;
void *msg;
spin_lock_irqsave(&priv->tx_lock, flags);
if (page->offset + msg_size > page->size) {
struct pciefd_tx_link *lk;
/* not enough space in this page: try another one */
if (!priv->tx_pages_free) {
spin_unlock_irqrestore(&priv->tx_lock, flags);
/* Tx overflow */
return NULL;
}
priv->tx_pages_free--;
/* keep address of the very last free slot of current page */
lk = page->vbase + page->offset;
/* next, move on a new free page */
priv->tx_page_index = (priv->tx_page_index + 1) %
PCIEFD_TX_PAGE_COUNT;
page = priv->tx_pages + priv->tx_page_index;
/* put link record to this new page at the end of prev one */
lk->size = cpu_to_le16(sizeof(*lk));
lk->type = cpu_to_le16(CANFD_MSG_LNK_TX);
lk->laddr_lo = cpu_to_le32(page->lbase);
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
lk->laddr_hi = cpu_to_le32(page->lbase >> 32);
#else
lk->laddr_hi = 0;
#endif
/* next msgs will be put from the begininng of this new page */
page->offset = 0;
}
*room_left = priv->tx_pages_free * page->size;
spin_unlock_irqrestore(&priv->tx_lock, flags);
msg = page->vbase + page->offset;
/* give back room left in the tx ring */
*room_left += page->size - (page->offset + msg_size);
return msg;
}
static int pciefd_write_tx_msg(struct peak_canfd_priv *ucan,
struct pucan_tx_msg *msg)
{
struct pciefd_can *priv = (struct pciefd_can *)ucan;
struct pciefd_page *page = priv->tx_pages + priv->tx_page_index;
/* this slot is now reserved for writing the frame */
page->offset += le16_to_cpu(msg->size);
/* tell the board a frame has been written in Tx DMA area */
pciefd_can_writereg(priv, 1, PCIEFD_REG_CAN_TX_REQ_ACC);
return 0;
}
/* probe for CAN-FD channel #pciefd_board->can_count */
static int pciefd_can_probe(struct pciefd_board *pciefd)
{
struct net_device *ndev;
struct pciefd_can *priv;
u32 clk;
int err;
/* allocate the candev object with default isize of echo skbs ring */
ndev = alloc_peak_canfd_dev(sizeof(*priv), pciefd->can_count,
PCIEFD_ECHO_SKB_MAX);
if (!ndev) {
dev_err(&pciefd->pci_dev->dev,
"failed to alloc candev object\n");
goto failure;
}
priv = netdev_priv(ndev);
/* fill-in candev private object: */
/* setup PCIe-FD own callbacks */
priv->ucan.pre_cmd = pciefd_pre_cmd;
priv->ucan.write_cmd = pciefd_write_cmd;
priv->ucan.post_cmd = pciefd_post_cmd;
priv->ucan.enable_tx_path = pciefd_enable_tx_path;
priv->ucan.alloc_tx_msg = pciefd_alloc_tx_msg;
priv->ucan.write_tx_msg = pciefd_write_tx_msg;
/* setup PCIe-FD own command buffer */
priv->ucan.cmd_buffer = &priv->pucan_cmd;
priv->ucan.cmd_maxlen = sizeof(priv->pucan_cmd);
priv->board = pciefd;
/* CAN config regs block address */
priv->reg_base = pciefd->reg_base + PCIEFD_CANX_OFF(priv->ucan.index);
/* allocate non-cacheable DMA'able 4KB memory area for Rx */
priv->rx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev,
PCIEFD_RX_DMA_SIZE,
&priv->rx_dma_laddr,
GFP_KERNEL);
if (!priv->rx_dma_vaddr) {
dev_err(&pciefd->pci_dev->dev,
"Rx dmam_alloc_coherent(%u) failure\n",
PCIEFD_RX_DMA_SIZE);
goto err_free_candev;
}
/* allocate non-cacheable DMA'able 4KB memory area for Tx */
priv->tx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev,
PCIEFD_TX_DMA_SIZE,
&priv->tx_dma_laddr,
GFP_KERNEL);
if (!priv->tx_dma_vaddr) {
dev_err(&pciefd->pci_dev->dev,
"Tx dmaim_alloc_coherent(%u) failure\n",
PCIEFD_TX_DMA_SIZE);
goto err_free_candev;
}
/* CAN clock in RST mode */
pciefd_can_writereg(priv, CANFD_MISC_TS_RST, PCIEFD_REG_CAN_MISC);
/* read current clock value */
clk = pciefd_can_readreg(priv, PCIEFD_REG_CAN_CLK_SEL);
switch (clk) {
case CANFD_CLK_SEL_20MHZ:
priv->ucan.can.clock.freq = 20 * 1000 * 1000;
break;
case CANFD_CLK_SEL_24MHZ:
priv->ucan.can.clock.freq = 24 * 1000 * 1000;
break;
case CANFD_CLK_SEL_30MHZ:
priv->ucan.can.clock.freq = 30 * 1000 * 1000;
break;
case CANFD_CLK_SEL_40MHZ:
priv->ucan.can.clock.freq = 40 * 1000 * 1000;
break;
case CANFD_CLK_SEL_60MHZ:
priv->ucan.can.clock.freq = 60 * 1000 * 1000;
break;
default:
pciefd_can_writereg(priv, CANFD_CLK_SEL_80MHZ,
PCIEFD_REG_CAN_CLK_SEL);
/* fallthough */
case CANFD_CLK_SEL_80MHZ:
priv->ucan.can.clock.freq = 80 * 1000 * 1000;
break;
}
ndev->irq = pciefd->pci_dev->irq;
SET_NETDEV_DEV(ndev, &pciefd->pci_dev->dev);
err = register_candev(ndev);
if (err) {
dev_err(&pciefd->pci_dev->dev,
"couldn't register CAN device: %d\n", err);
goto err_free_candev;
}
spin_lock_init(&priv->tx_lock);
/* save the object address in the board structure */
pciefd->can[pciefd->can_count] = priv;
dev_info(&pciefd->pci_dev->dev, "%s at reg_base=0x%p irq=%d\n",
ndev->name, priv->reg_base, pciefd->pci_dev->irq);
return 0;
err_free_candev:
free_candev(ndev);
failure:
return -ENOMEM;
}
/* remove a CAN-FD channel by releasing all of its resources */
static void pciefd_can_remove(struct pciefd_can *priv)
{
/* unregister (close) the can device to go back to RST mode first */
unregister_candev(priv->ucan.ndev);
/* finally, free the candev object */
free_candev(priv->ucan.ndev);
}
/* remove all CAN-FD channels by releasing their own resources */
static void pciefd_can_remove_all(struct pciefd_board *pciefd)
{
while (pciefd->can_count > 0)
pciefd_can_remove(pciefd->can[--pciefd->can_count]);
}
/* probe for the entire device */
static int peak_pciefd_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct pciefd_board *pciefd;
int err, can_count;
u16 sub_sys_id;
u8 hw_ver_major;
u8 hw_ver_minor;
u8 hw_ver_sub;
u32 v2;
err = pci_enable_device(pdev);
if (err)
return err;
err = pci_request_regions(pdev, PCIEFD_DRV_NAME);
if (err)
goto err_disable_pci;
/* the number of channels depends on sub-system id */
err = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sub_sys_id);
if (err)
goto err_release_regions;
dev_dbg(&pdev->dev, "probing device %04x:%04x:%04x\n",
pdev->vendor, pdev->device, sub_sys_id);
if (sub_sys_id >= 0x0012)
can_count = 4;
else if (sub_sys_id >= 0x0010)
can_count = 3;
else if (sub_sys_id >= 0x0004)
can_count = 2;
else
can_count = 1;
/* allocate board structure object */
pciefd = devm_kzalloc(&pdev->dev, sizeof(*pciefd) +
can_count * sizeof(*pciefd->can),
GFP_KERNEL);
if (!pciefd) {
err = -ENOMEM;
goto err_release_regions;
}
/* initialize the board structure */
pciefd->pci_dev = pdev;
spin_lock_init(&pciefd->cmd_lock);
/* save the PCI BAR0 virtual address for further system regs access */
pciefd->reg_base = pci_iomap(pdev, 0, PCIEFD_BAR0_SIZE);
if (!pciefd->reg_base) {
dev_err(&pdev->dev, "failed to map PCI resource #0\n");
err = -ENOMEM;
goto err_release_regions;
}
/* read the firmware version number */
v2 = pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER2);
hw_ver_major = (v2 & 0x0000f000) >> 12;
hw_ver_minor = (v2 & 0x00000f00) >> 8;
hw_ver_sub = (v2 & 0x000000f0) >> 4;
dev_info(&pdev->dev,
"%ux CAN-FD PCAN-PCIe FPGA v%u.%u.%u:\n", can_count,
hw_ver_major, hw_ver_minor, hw_ver_sub);
/* stop system clock */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN,
PCIEFD_REG_SYS_CTL_CLR);
pci_set_master(pdev);
/* create now the corresponding channels objects */
while (pciefd->can_count < can_count) {
err = pciefd_can_probe(pciefd);
if (err)
goto err_free_canfd;
pciefd->can_count++;
}
/* set system timestamps counter in RST mode */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST,
PCIEFD_REG_SYS_CTL_SET);
/* wait a bit (read cycle) */
(void)pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER1);
/* free all clocks */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST,
PCIEFD_REG_SYS_CTL_CLR);
/* start system clock */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN,
PCIEFD_REG_SYS_CTL_SET);
/* remember the board structure address in the device user data */
pci_set_drvdata(pdev, pciefd);
return 0;
err_free_canfd:
pciefd_can_remove_all(pciefd);
pci_iounmap(pdev, pciefd->reg_base);
err_release_regions:
pci_release_regions(pdev);
err_disable_pci:
pci_disable_device(pdev);
return err;
}
/* free the board structure object, as well as its resources: */
static void peak_pciefd_remove(struct pci_dev *pdev)
{
struct pciefd_board *pciefd = pci_get_drvdata(pdev);
/* release CAN-FD channels resources */
pciefd_can_remove_all(pciefd);
pci_iounmap(pdev, pciefd->reg_base);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
static struct pci_driver peak_pciefd_driver = {
.name = PCIEFD_DRV_NAME,
.id_table = peak_pciefd_tbl,
.probe = peak_pciefd_probe,
.remove = peak_pciefd_remove,
};
module_pci_driver(peak_pciefd_driver);
...@@ -898,9 +898,9 @@ static int ti_hecc_probe(struct platform_device *pdev) ...@@ -898,9 +898,9 @@ static int ti_hecc_probe(struct platform_device *pdev)
} }
priv->base = devm_ioremap_resource(&pdev->dev, res); priv->base = devm_ioremap_resource(&pdev->dev, res);
if (!priv->base) { if (IS_ERR(priv->base)) {
dev_err(&pdev->dev, "hecc ioremap failed\n"); dev_err(&pdev->dev, "hecc ioremap failed\n");
return -ENOMEM; return PTR_ERR(priv->base);
} }
/* handle hecc-ram memory */ /* handle hecc-ram memory */
...@@ -911,9 +911,9 @@ static int ti_hecc_probe(struct platform_device *pdev) ...@@ -911,9 +911,9 @@ static int ti_hecc_probe(struct platform_device *pdev)
} }
priv->hecc_ram = devm_ioremap_resource(&pdev->dev, res); 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"); dev_err(&pdev->dev, "hecc-ram ioremap failed\n");
return -ENOMEM; return PTR_ERR(priv->hecc_ram);
} }
/* handle mbx memory */ /* handle mbx memory */
...@@ -924,9 +924,9 @@ static int ti_hecc_probe(struct platform_device *pdev) ...@@ -924,9 +924,9 @@ static int ti_hecc_probe(struct platform_device *pdev)
} }
priv->mbx = devm_ioremap_resource(&pdev->dev, res); priv->mbx = devm_ioremap_resource(&pdev->dev, res);
if (!priv->mbx) { if (IS_ERR(priv->mbx)) {
dev_err(&pdev->dev, "mbx ioremap failed\n"); dev_err(&pdev->dev, "mbx ioremap failed\n");
return -ENOMEM; return PTR_ERR(priv->mbx);
} }
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
......
...@@ -81,4 +81,10 @@ config CAN_8DEV_USB ...@@ -81,4 +81,10 @@ config CAN_8DEV_USB
This driver supports the USB2CAN interface This driver supports the USB2CAN interface
from 8 devices (http://www.8devices.com). 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 endmenu
...@@ -8,3 +8,4 @@ obj-$(CONFIG_CAN_GS_USB) += gs_usb.o ...@@ -8,3 +8,4 @@ obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o
/* SocketCAN driver for Microchip CAN BUS Analyzer Tool
*
* Copyright (C) 2017 Mobica Limited
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program.
*
* This driver is inspired by the 4.6.2 version of net/can/usb/usb_8dev.c
*/
#include <asm/unaligned.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
#include <linux/can/led.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/usb.h>
/* vendor and product id */
#define MCBA_MODULE_NAME "mcba_usb"
#define MCBA_VENDOR_ID 0x04d8
#define MCBA_PRODUCT_ID 0x0a30
/* driver constants */
#define MCBA_MAX_RX_URBS 20
#define MCBA_MAX_TX_URBS 20
#define MCBA_CTX_FREE MCBA_MAX_TX_URBS
/* RX buffer must be bigger than msg size since at the
* beggining USB messages are stacked.
*/
#define MCBA_USB_RX_BUFF_SIZE 64
#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg))
/* MCBA endpoint numbers */
#define MCBA_USB_EP_IN 1
#define MCBA_USB_EP_OUT 1
/* Microchip command id */
#define MBCA_CMD_RECEIVE_MESSAGE 0xE3
#define MBCA_CMD_I_AM_ALIVE_FROM_CAN 0xF5
#define MBCA_CMD_I_AM_ALIVE_FROM_USB 0xF7
#define MBCA_CMD_CHANGE_BIT_RATE 0xA1
#define MBCA_CMD_TRANSMIT_MESSAGE_EV 0xA3
#define MBCA_CMD_SETUP_TERMINATION_RESISTANCE 0xA8
#define MBCA_CMD_READ_FW_VERSION 0xA9
#define MBCA_CMD_NOTHING_TO_SEND 0xFF
#define MBCA_CMD_TRANSMIT_MESSAGE_RSP 0xE2
#define MCBA_VER_REQ_USB 1
#define MCBA_VER_REQ_CAN 2
#define MCBA_SIDL_EXID_MASK 0x8
#define MCBA_DLC_MASK 0xf
#define MCBA_DLC_RTR_MASK 0x40
#define MCBA_CAN_STATE_WRN_TH 95
#define MCBA_CAN_STATE_ERR_PSV_TH 127
#define MCBA_TERMINATION_DISABLED CAN_TERMINATION_DISABLED
#define MCBA_TERMINATION_ENABLED 120
struct mcba_usb_ctx {
struct mcba_priv *priv;
u32 ndx;
u8 dlc;
bool can;
};
/* Structure to hold all of our device specific stuff */
struct mcba_priv {
struct can_priv can; /* must be the first member */
struct sk_buff *echo_skb[MCBA_MAX_TX_URBS];
struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS];
struct usb_device *udev;
struct net_device *netdev;
struct usb_anchor tx_submitted;
struct usb_anchor rx_submitted;
struct can_berr_counter bec;
bool usb_ka_first_pass;
bool can_ka_first_pass;
bool can_speed_check;
atomic_t free_ctx_cnt;
};
/* CAN frame */
struct __packed mcba_usb_msg_can {
u8 cmd_id;
__be16 eid;
__be16 sid;
u8 dlc;
u8 data[8];
u8 timestamp[4];
u8 checksum;
};
/* command frame */
struct __packed mcba_usb_msg {
u8 cmd_id;
u8 unused[18];
};
struct __packed mcba_usb_msg_ka_usb {
u8 cmd_id;
u8 termination_state;
u8 soft_ver_major;
u8 soft_ver_minor;
u8 unused[15];
};
struct __packed mcba_usb_msg_ka_can {
u8 cmd_id;
u8 tx_err_cnt;
u8 rx_err_cnt;
u8 rx_buff_ovfl;
u8 tx_bus_off;
__be16 can_bitrate;
__le16 rx_lost;
u8 can_stat;
u8 soft_ver_major;
u8 soft_ver_minor;
u8 debug_mode;
u8 test_complete;
u8 test_result;
u8 unused[4];
};
struct __packed mcba_usb_msg_change_bitrate {
u8 cmd_id;
__be16 bitrate;
u8 unused[16];
};
struct __packed mcba_usb_msg_termination {
u8 cmd_id;
u8 termination;
u8 unused[17];
};
struct __packed mcba_usb_msg_fw_ver {
u8 cmd_id;
u8 pic;
u8 unused[17];
};
static const struct usb_device_id mcba_usb_table[] = {
{ USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, mcba_usb_table);
static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED,
MCBA_TERMINATION_ENABLED };
static const u32 mcba_bitrate[] = { 20000, 33333, 50000, 80000, 83333,
100000, 125000, 150000, 175000, 200000,
225000, 250000, 275000, 300000, 500000,
625000, 800000, 1000000 };
static inline void mcba_init_ctx(struct mcba_priv *priv)
{
int i = 0;
for (i = 0; i < MCBA_MAX_TX_URBS; i++) {
priv->tx_context[i].ndx = MCBA_CTX_FREE;
priv->tx_context[i].priv = priv;
}
atomic_set(&priv->free_ctx_cnt, ARRAY_SIZE(priv->tx_context));
}
static inline struct mcba_usb_ctx *mcba_usb_get_free_ctx(struct mcba_priv *priv,
struct can_frame *cf)
{
int i = 0;
struct mcba_usb_ctx *ctx = NULL;
for (i = 0; i < MCBA_MAX_TX_URBS; i++) {
if (priv->tx_context[i].ndx == MCBA_CTX_FREE) {
ctx = &priv->tx_context[i];
ctx->ndx = i;
if (cf) {
ctx->can = true;
ctx->dlc = cf->can_dlc;
} else {
ctx->can = false;
ctx->dlc = 0;
}
atomic_dec(&priv->free_ctx_cnt);
break;
}
}
if (!atomic_read(&priv->free_ctx_cnt))
/* That was the last free ctx. Slow down tx path */
netif_stop_queue(priv->netdev);
return ctx;
}
/* mcba_usb_free_ctx and mcba_usb_get_free_ctx are executed by different
* threads. The order of execution in below function is important.
*/
static inline void mcba_usb_free_ctx(struct mcba_usb_ctx *ctx)
{
/* Increase number of free ctxs before freeing ctx */
atomic_inc(&ctx->priv->free_ctx_cnt);
ctx->ndx = MCBA_CTX_FREE;
/* Wake up the queue once ctx is marked free */
netif_wake_queue(ctx->priv->netdev);
}
static void mcba_usb_write_bulk_callback(struct urb *urb)
{
struct mcba_usb_ctx *ctx = urb->context;
struct net_device *netdev;
WARN_ON(!ctx);
netdev = ctx->priv->netdev;
/* free up our allocated buffer */
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
if (ctx->can) {
if (!netif_device_present(netdev))
return;
netdev->stats.tx_packets++;
netdev->stats.tx_bytes += ctx->dlc;
can_led_event(netdev, CAN_LED_EVENT_TX);
can_get_echo_skb(netdev, ctx->ndx);
}
if (urb->status)
netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status);
/* Release the context */
mcba_usb_free_ctx(ctx);
}
/* Send data to device */
static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv,
struct mcba_usb_msg *usb_msg,
struct mcba_usb_ctx *ctx)
{
struct urb *urb;
u8 *buf;
int err;
/* create a URB, and a buffer for it, and copy the data to the URB */
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb)
return -ENOMEM;
buf = usb_alloc_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, GFP_ATOMIC,
&urb->transfer_dma);
if (!buf) {
err = -ENOMEM;
goto nomembuf;
}
memcpy(buf, usb_msg, MCBA_USB_TX_BUFF_SIZE);
usb_fill_bulk_urb(urb, priv->udev,
usb_sndbulkpipe(priv->udev, MCBA_USB_EP_OUT), buf,
MCBA_USB_TX_BUFF_SIZE, mcba_usb_write_bulk_callback,
ctx);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &priv->tx_submitted);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (unlikely(err))
goto failed;
/* Release our reference to this URB, the USB core will eventually free
* it entirely.
*/
usb_free_urb(urb);
return 0;
failed:
usb_unanchor_urb(urb);
usb_free_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, buf,
urb->transfer_dma);
if (err == -ENODEV)
netif_device_detach(priv->netdev);
else
netdev_warn(priv->netdev, "failed tx_urb %d\n", err);
nomembuf:
usb_free_urb(urb);
return err;
}
/* Send data to device */
static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct mcba_priv *priv = netdev_priv(netdev);
struct can_frame *cf = (struct can_frame *)skb->data;
struct mcba_usb_ctx *ctx = NULL;
struct net_device_stats *stats = &priv->netdev->stats;
u16 sid;
int err;
struct mcba_usb_msg_can usb_msg = {
.cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV
};
if (can_dropped_invalid_skb(netdev, skb))
return NETDEV_TX_OK;
ctx = mcba_usb_get_free_ctx(priv, cf);
if (!ctx)
return NETDEV_TX_BUSY;
can_put_echo_skb(skb, priv->netdev, ctx->ndx);
if (cf->can_id & CAN_EFF_FLAG) {
/* SIDH | SIDL | EIDH | EIDL
* 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0
*/
sid = MCBA_SIDL_EXID_MASK;
/* store 28-18 bits */
sid |= (cf->can_id & 0x1ffc0000) >> 13;
/* store 17-16 bits */
sid |= (cf->can_id & 0x30000) >> 16;
put_unaligned_be16(sid, &usb_msg.sid);
/* store 15-0 bits */
put_unaligned_be16(cf->can_id & 0xffff, &usb_msg.eid);
} else {
/* SIDH | SIDL
* 10 - 3 | 2 1 0 x x x x x
*/
put_unaligned_be16((cf->can_id & CAN_SFF_MASK) << 5,
&usb_msg.sid);
usb_msg.eid = 0;
}
usb_msg.dlc = cf->can_dlc;
memcpy(usb_msg.data, cf->data, usb_msg.dlc);
if (cf->can_id & CAN_RTR_FLAG)
usb_msg.dlc |= MCBA_DLC_RTR_MASK;
err = mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, ctx);
if (err)
goto xmit_failed;
return NETDEV_TX_OK;
xmit_failed:
can_free_echo_skb(priv->netdev, ctx->ndx);
mcba_usb_free_ctx(ctx);
dev_kfree_skb(skb);
stats->tx_dropped++;
return NETDEV_TX_OK;
}
/* Send cmd to device */
static void mcba_usb_xmit_cmd(struct mcba_priv *priv,
struct mcba_usb_msg *usb_msg)
{
struct mcba_usb_ctx *ctx = NULL;
int err;
ctx = mcba_usb_get_free_ctx(priv, NULL);
if (!ctx) {
netdev_err(priv->netdev,
"Lack of free ctx. Sending (%d) cmd aborted",
usb_msg->cmd_id);
return;
}
err = mcba_usb_xmit(priv, usb_msg, ctx);
if (err)
netdev_err(priv->netdev, "Failed to send cmd (%d)",
usb_msg->cmd_id);
}
static void mcba_usb_xmit_change_bitrate(struct mcba_priv *priv, u16 bitrate)
{
struct mcba_usb_msg_change_bitrate usb_msg = {
.cmd_id = MBCA_CMD_CHANGE_BIT_RATE
};
put_unaligned_be16(bitrate, &usb_msg.bitrate);
mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
}
static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic)
{
struct mcba_usb_msg_fw_ver usb_msg = {
.cmd_id = MBCA_CMD_READ_FW_VERSION,
.pic = pic
};
mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
}
static void mcba_usb_process_can(struct mcba_priv *priv,
struct mcba_usb_msg_can *msg)
{
struct can_frame *cf;
struct sk_buff *skb;
struct net_device_stats *stats = &priv->netdev->stats;
u16 sid;
skb = alloc_can_skb(priv->netdev, &cf);
if (!skb)
return;
sid = get_unaligned_be16(&msg->sid);
if (sid & MCBA_SIDL_EXID_MASK) {
/* SIDH | SIDL | EIDH | EIDL
* 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0
*/
cf->can_id = CAN_EFF_FLAG;
/* store 28-18 bits */
cf->can_id |= (sid & 0xffe0) << 13;
/* store 17-16 bits */
cf->can_id |= (sid & 3) << 16;
/* store 15-0 bits */
cf->can_id |= get_unaligned_be16(&msg->eid);
} else {
/* SIDH | SIDL
* 10 - 3 | 2 1 0 x x x x x
*/
cf->can_id = (sid & 0xffe0) >> 5;
}
if (msg->dlc & MCBA_DLC_RTR_MASK)
cf->can_id |= CAN_RTR_FLAG;
cf->can_dlc = get_can_dlc(msg->dlc & MCBA_DLC_MASK);
memcpy(cf->data, msg->data, cf->can_dlc);
stats->rx_packets++;
stats->rx_bytes += cf->can_dlc;
can_led_event(priv->netdev, CAN_LED_EVENT_RX);
netif_rx(skb);
}
static void mcba_usb_process_ka_usb(struct mcba_priv *priv,
struct mcba_usb_msg_ka_usb *msg)
{
if (unlikely(priv->usb_ka_first_pass)) {
netdev_info(priv->netdev, "PIC USB version %hhu.%hhu\n",
msg->soft_ver_major, msg->soft_ver_minor);
priv->usb_ka_first_pass = false;
}
if (msg->termination_state)
priv->can.termination = MCBA_TERMINATION_ENABLED;
else
priv->can.termination = MCBA_TERMINATION_DISABLED;
}
static u32 convert_can2host_bitrate(struct mcba_usb_msg_ka_can *msg)
{
const u32 bitrate = get_unaligned_be16(&msg->can_bitrate);
if ((bitrate == 33) || (bitrate == 83))
return bitrate * 1000 + 333;
else
return bitrate * 1000;
}
static void mcba_usb_process_ka_can(struct mcba_priv *priv,
struct mcba_usb_msg_ka_can *msg)
{
if (unlikely(priv->can_ka_first_pass)) {
netdev_info(priv->netdev, "PIC CAN version %hhu.%hhu\n",
msg->soft_ver_major, msg->soft_ver_minor);
priv->can_ka_first_pass = false;
}
if (unlikely(priv->can_speed_check)) {
const u32 bitrate = convert_can2host_bitrate(msg);
priv->can_speed_check = false;
if (bitrate != priv->can.bittiming.bitrate)
netdev_err(
priv->netdev,
"Wrong bitrate reported by the device (%u). Expected %u",
bitrate, priv->can.bittiming.bitrate);
}
priv->bec.txerr = msg->tx_err_cnt;
priv->bec.rxerr = msg->rx_err_cnt;
if (msg->tx_bus_off)
priv->can.state = CAN_STATE_BUS_OFF;
else if ((priv->bec.txerr > MCBA_CAN_STATE_ERR_PSV_TH) ||
(priv->bec.rxerr > MCBA_CAN_STATE_ERR_PSV_TH))
priv->can.state = CAN_STATE_ERROR_PASSIVE;
else if ((priv->bec.txerr > MCBA_CAN_STATE_WRN_TH) ||
(priv->bec.rxerr > MCBA_CAN_STATE_WRN_TH))
priv->can.state = CAN_STATE_ERROR_WARNING;
}
static void mcba_usb_process_rx(struct mcba_priv *priv,
struct mcba_usb_msg *msg)
{
switch (msg->cmd_id) {
case MBCA_CMD_I_AM_ALIVE_FROM_CAN:
mcba_usb_process_ka_can(priv,
(struct mcba_usb_msg_ka_can *)msg);
break;
case MBCA_CMD_I_AM_ALIVE_FROM_USB:
mcba_usb_process_ka_usb(priv,
(struct mcba_usb_msg_ka_usb *)msg);
break;
case MBCA_CMD_RECEIVE_MESSAGE:
mcba_usb_process_can(priv, (struct mcba_usb_msg_can *)msg);
break;
case MBCA_CMD_NOTHING_TO_SEND:
/* Side effect of communication between PIC_USB and PIC_CAN.
* PIC_CAN is telling us that it has nothing to send
*/
break;
case MBCA_CMD_TRANSMIT_MESSAGE_RSP:
/* Transmission response from the device containing timestamp */
break;
default:
netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)",
msg->cmd_id);
break;
}
}
/* Callback for reading data from device
*
* Check urb status, call read function and resubmit urb read operation.
*/
static void mcba_usb_read_bulk_callback(struct urb *urb)
{
struct mcba_priv *priv = urb->context;
struct net_device *netdev;
int retval;
int pos = 0;
netdev = priv->netdev;
if (!netif_device_present(netdev))
return;
switch (urb->status) {
case 0: /* success */
break;
case -ENOENT:
case -ESHUTDOWN:
return;
default:
netdev_info(netdev, "Rx URB aborted (%d)\n", urb->status);
goto resubmit_urb;
}
while (pos < urb->actual_length) {
struct mcba_usb_msg *msg;
if (pos + sizeof(struct mcba_usb_msg) > urb->actual_length) {
netdev_err(priv->netdev, "format error\n");
break;
}
msg = (struct mcba_usb_msg *)(urb->transfer_buffer + pos);
mcba_usb_process_rx(priv, msg);
pos += sizeof(struct mcba_usb_msg);
}
resubmit_urb:
usb_fill_bulk_urb(urb, priv->udev,
usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_OUT),
urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE,
mcba_usb_read_bulk_callback, priv);
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval == -ENODEV)
netif_device_detach(netdev);
else if (retval)
netdev_err(netdev, "failed resubmitting read bulk urb: %d\n",
retval);
}
/* Start USB device */
static int mcba_usb_start(struct mcba_priv *priv)
{
struct net_device *netdev = priv->netdev;
int err, i;
mcba_init_ctx(priv);
for (i = 0; i < MCBA_MAX_RX_URBS; i++) {
struct urb *urb = NULL;
u8 *buf;
/* create a URB, and a buffer for it */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
err = -ENOMEM;
break;
}
buf = usb_alloc_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE,
GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
netdev_err(netdev, "No memory left for USB buffer\n");
usb_free_urb(urb);
err = -ENOMEM;
break;
}
usb_fill_bulk_urb(urb, priv->udev,
usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_IN),
buf, MCBA_USB_RX_BUFF_SIZE,
mcba_usb_read_bulk_callback, priv);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &priv->rx_submitted);
err = usb_submit_urb(urb, GFP_KERNEL);
if (err) {
usb_unanchor_urb(urb);
usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE,
buf, urb->transfer_dma);
usb_free_urb(urb);
break;
}
/* Drop reference, USB core will take care of freeing it */
usb_free_urb(urb);
}
/* Did we submit any URBs */
if (i == 0) {
netdev_warn(netdev, "couldn't setup read URBs\n");
return err;
}
/* Warn if we've couldn't transmit all the URBs */
if (i < MCBA_MAX_RX_URBS)
netdev_warn(netdev, "rx performance may be slow\n");
mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_USB);
mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_CAN);
return err;
}
/* Open USB device */
static int mcba_usb_open(struct net_device *netdev)
{
struct mcba_priv *priv = netdev_priv(netdev);
int err;
/* common open */
err = open_candev(netdev);
if (err)
return err;
priv->can_speed_check = true;
priv->can.state = CAN_STATE_ERROR_ACTIVE;
can_led_event(netdev, CAN_LED_EVENT_OPEN);
netif_start_queue(netdev);
return 0;
}
static void mcba_urb_unlink(struct mcba_priv *priv)
{
usb_kill_anchored_urbs(&priv->rx_submitted);
usb_kill_anchored_urbs(&priv->tx_submitted);
}
/* Close USB device */
static int mcba_usb_close(struct net_device *netdev)
{
struct mcba_priv *priv = netdev_priv(netdev);
priv->can.state = CAN_STATE_STOPPED;
netif_stop_queue(netdev);
/* Stop polling */
mcba_urb_unlink(priv);
close_candev(netdev);
can_led_event(netdev, CAN_LED_EVENT_STOP);
return 0;
}
/* Set network device mode
*
* Maybe we should leave this function empty, because the device
* set mode variable with open command.
*/
static int mcba_net_set_mode(struct net_device *netdev, enum can_mode mode)
{
return 0;
}
static int mcba_net_get_berr_counter(const struct net_device *netdev,
struct can_berr_counter *bec)
{
struct mcba_priv *priv = netdev_priv(netdev);
bec->txerr = priv->bec.txerr;
bec->rxerr = priv->bec.rxerr;
return 0;
}
static const struct net_device_ops mcba_netdev_ops = {
.ndo_open = mcba_usb_open,
.ndo_stop = mcba_usb_close,
.ndo_start_xmit = mcba_usb_start_xmit,
};
/* Microchip CANBUS has hardcoded bittiming values by default.
* This function sends request via USB to change the speed and align bittiming
* values for presentation purposes only
*/
static int mcba_net_set_bittiming(struct net_device *netdev)
{
struct mcba_priv *priv = netdev_priv(netdev);
const u16 bitrate_kbps = priv->can.bittiming.bitrate / 1000;
mcba_usb_xmit_change_bitrate(priv, bitrate_kbps);
return 0;
}
static int mcba_set_termination(struct net_device *netdev, u16 term)
{
struct mcba_priv *priv = netdev_priv(netdev);
struct mcba_usb_msg_termination usb_msg = {
.cmd_id = MBCA_CMD_SETUP_TERMINATION_RESISTANCE
};
if (term == MCBA_TERMINATION_ENABLED)
usb_msg.termination = 1;
else
usb_msg.termination = 0;
mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg);
return 0;
}
static int mcba_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct net_device *netdev;
struct mcba_priv *priv;
int err = -ENOMEM;
struct usb_device *usbdev = interface_to_usbdev(intf);
netdev = alloc_candev(sizeof(struct mcba_priv), MCBA_MAX_TX_URBS);
if (!netdev) {
dev_err(&intf->dev, "Couldn't alloc candev\n");
return -ENOMEM;
}
priv = netdev_priv(netdev);
priv->udev = usbdev;
priv->netdev = netdev;
priv->usb_ka_first_pass = true;
priv->can_ka_first_pass = true;
priv->can_speed_check = false;
init_usb_anchor(&priv->rx_submitted);
init_usb_anchor(&priv->tx_submitted);
usb_set_intfdata(intf, priv);
/* Init CAN device */
priv->can.state = CAN_STATE_STOPPED;
priv->can.termination_const = mcba_termination;
priv->can.termination_const_cnt = ARRAY_SIZE(mcba_termination);
priv->can.bitrate_const = mcba_bitrate;
priv->can.bitrate_const_cnt = ARRAY_SIZE(mcba_bitrate);
priv->can.do_set_termination = mcba_set_termination;
priv->can.do_set_mode = mcba_net_set_mode;
priv->can.do_get_berr_counter = mcba_net_get_berr_counter;
priv->can.do_set_bittiming = mcba_net_set_bittiming;
netdev->netdev_ops = &mcba_netdev_ops;
netdev->flags |= IFF_ECHO; /* we support local echo */
SET_NETDEV_DEV(netdev, &intf->dev);
err = register_candev(netdev);
if (err) {
netdev_err(netdev, "couldn't register CAN device: %d\n", err);
goto cleanup_free_candev;
}
devm_can_led_init(netdev);
/* Start USB dev only if we have successfully registered CAN device */
err = mcba_usb_start(priv);
if (err) {
if (err == -ENODEV)
netif_device_detach(priv->netdev);
netdev_warn(netdev, "couldn't start device: %d\n", err);
goto cleanup_unregister_candev;
}
dev_info(&intf->dev, "Microchip CAN BUS analizer connected\n");
return 0;
cleanup_unregister_candev:
unregister_candev(priv->netdev);
cleanup_free_candev:
free_candev(netdev);
return err;
}
/* Called by the usb core when driver is unloaded or device is removed */
static void mcba_usb_disconnect(struct usb_interface *intf)
{
struct mcba_priv *priv = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
netdev_info(priv->netdev, "device disconnected\n");
unregister_candev(priv->netdev);
free_candev(priv->netdev);
mcba_urb_unlink(priv);
}
static struct usb_driver mcba_usb_driver = {
.name = MCBA_MODULE_NAME,
.probe = mcba_usb_probe,
.disconnect = mcba_usb_disconnect,
.id_table = mcba_usb_table,
};
module_usb_driver(mcba_usb_driver);
MODULE_AUTHOR("Remigiusz Kołłątaj <remigiusz.kollataj@mobica.com>");
MODULE_DESCRIPTION("SocketCAN driver for Microchip CAN BUS Analyzer Tool");
MODULE_LICENSE("GPL v2");
...@@ -19,10 +19,10 @@ ...@@ -19,10 +19,10 @@
#include <linux/can.h> #include <linux/can.h>
#include <linux/can/dev.h> #include <linux/can/dev.h>
#include <linux/can/error.h> #include <linux/can/error.h>
#include <linux/can/dev/peak_canfd.h>
#include "pcan_usb_core.h" #include "pcan_usb_core.h"
#include "pcan_usb_pro.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 FD adapter");
MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro 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) ...@@ -238,7 +238,7 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf)
/* 1st, reset error counters: */ /* 1st, reset error counters: */
prc = (struct pucan_wr_err_cnt *)pc; 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); PUCAN_CMD_WR_ERR_CNT);
/* select both counters */ /* select both counters */
...@@ -257,9 +257,10 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) ...@@ -257,9 +257,10 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf)
puo->opcode_channel = puo->opcode_channel =
(dev->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) ? (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_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); 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) ...@@ -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 */ /* next, go back to operational mode */
cmd = (struct pucan_command *)pc; 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) ? (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ?
PUCAN_CMD_LISTEN_ONLY_MODE : PUCAN_CMD_LISTEN_ONLY_MODE :
PUCAN_CMD_NORMAL_MODE); PUCAN_CMD_NORMAL_MODE);
...@@ -296,7 +297,7 @@ static int pcan_usb_fd_set_bus(struct peak_usb_device *dev, u8 onoff) ...@@ -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; struct pucan_command *cmd = (struct pucan_command *)pc;
/* build cmd to go back to reset mode */ /* 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); PUCAN_CMD_RESET_MODE);
l = sizeof(struct pucan_command); l = sizeof(struct pucan_command);
} }
...@@ -332,7 +333,7 @@ static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx, ...@@ -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++) { 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); PUCAN_CMD_FILTER_STD);
cmd->idx = cpu_to_le16(i); cmd->idx = cpu_to_le16(i);
cmd->mask = cpu_to_le32(mask); cmd->mask = cpu_to_le32(mask);
...@@ -352,7 +353,7 @@ static int pcan_usb_fd_set_options(struct peak_usb_device *dev, ...@@ -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); 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 : (onoff) ? PUCAN_CMD_SET_EN_OPTION :
PUCAN_CMD_CLR_DIS_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) ...@@ -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); 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); PCAN_UFD_CMD_LED_SET);
cmd->mode = led_mode; cmd->mode = led_mode;
...@@ -382,7 +383,7 @@ static int pcan_usb_fd_set_clock_domain(struct peak_usb_device *dev, ...@@ -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); 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); PCAN_UFD_CMD_CLK_SET);
cmd->mode = clk_mode; cmd->mode = clk_mode;
...@@ -396,7 +397,7 @@ static int pcan_usb_fd_set_bittiming_slow(struct peak_usb_device *dev, ...@@ -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); 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); PUCAN_CMD_TIMING_SLOW);
cmd->sjw_t = PUCAN_TSLOW_SJW_T(bt->sjw - 1, cmd->sjw_t = PUCAN_TSLOW_SJW_T(bt->sjw - 1,
dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES); dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES);
...@@ -417,7 +418,7 @@ static int pcan_usb_fd_set_bittiming_fast(struct peak_usb_device *dev, ...@@ -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); 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); PUCAN_CMD_TIMING_FAST);
cmd->sjw = PUCAN_TFAST_SJW(bt->sjw - 1); cmd->sjw = PUCAN_TFAST_SJW(bt->sjw - 1);
cmd->tseg2 = PUCAN_TFAST_TSEG2(bt->phase_seg2 - 1); cmd->tseg2 = PUCAN_TFAST_TSEG2(bt->phase_seg2 - 1);
......
/* /*
* vcan.c - Virtual CAN interface * vcan.c - Virtual CAN interface
* *
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research * Copyright (c) 2002-2017 Volkswagen Group Electronic Research
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
...@@ -50,9 +50,12 @@ ...@@ -50,9 +50,12 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <net/rtnetlink.h> #include <net/rtnetlink.h>
#define DRV_NAME "vcan"
MODULE_DESCRIPTION("virtual CAN interface"); MODULE_DESCRIPTION("virtual CAN interface");
MODULE_LICENSE("Dual BSD/GPL"); MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>"); 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) ...@@ -164,7 +167,7 @@ static void vcan_setup(struct net_device *dev)
} }
static struct rtnl_link_ops vcan_link_ops __read_mostly = { static struct rtnl_link_ops vcan_link_ops __read_mostly = {
.kind = "vcan", .kind = DRV_NAME,
.setup = vcan_setup, .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 @@ ...@@ -5,7 +5,7 @@
* *
* Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de> * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
* Urs Thuermann <urs.thuermann@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. * All rights reserved.
* *
*/ */
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#define CAN_VERSION "20120528" #define CAN_VERSION "20170425"
/* increment this number each time you change some user-space interface */ /* increment this number each time you change some user-space interface */
#define CAN_ABI_VERSION "9" #define CAN_ABI_VERSION "9"
......
...@@ -23,11 +23,14 @@ ...@@ -23,11 +23,14 @@
#define PUCAN_CMD_LISTEN_ONLY_MODE 0x003 #define PUCAN_CMD_LISTEN_ONLY_MODE 0x003
#define PUCAN_CMD_TIMING_SLOW 0x004 #define PUCAN_CMD_TIMING_SLOW 0x004
#define PUCAN_CMD_TIMING_FAST 0x005 #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_FILTER_STD 0x008
#define PUCAN_CMD_TX_ABORT 0x009 #define PUCAN_CMD_TX_ABORT 0x009
#define PUCAN_CMD_WR_ERR_CNT 0x00a #define PUCAN_CMD_WR_ERR_CNT 0x00a
#define PUCAN_CMD_SET_EN_OPTION 0x00b #define PUCAN_CMD_SET_EN_OPTION 0x00b
#define PUCAN_CMD_CLR_DIS_OPTION 0x00c #define PUCAN_CMD_CLR_DIS_OPTION 0x00c
#define PUCAN_CMD_RX_BARRIER 0x010
#define PUCAN_CMD_END_OF_COLLECTION 0x3ff #define PUCAN_CMD_END_OF_COLLECTION 0x3ff
/* uCAN received messages list */ /* uCAN received messages list */
...@@ -35,6 +38,10 @@ ...@@ -35,6 +38,10 @@
#define PUCAN_MSG_ERROR 0x0002 #define PUCAN_MSG_ERROR 0x0002
#define PUCAN_MSG_STATUS 0x0003 #define PUCAN_MSG_STATUS 0x0003
#define PUCAN_MSG_BUSLOAD 0x0004 #define PUCAN_MSG_BUSLOAD 0x0004
#define PUCAN_MSG_CACHE_CRITICAL 0x0102
/* uCAN transmitted messages */
#define PUCAN_MSG_CAN_TX 0x1000 #define PUCAN_MSG_CAN_TX 0x1000
/* uCAN command common header */ /* uCAN command common header */
...@@ -43,6 +50,12 @@ struct __packed pucan_command { ...@@ -43,6 +50,12 @@ struct __packed pucan_command {
u16 args[3]; 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_BRP_BITS 10
#define PUCAN_TSLOW_TSGEG1_BITS 8 #define PUCAN_TSLOW_TSGEG1_BITS 8
#define PUCAN_TSLOW_TSGEG2_BITS 7 #define PUCAN_TSLOW_TSGEG2_BITS 7
...@@ -108,6 +121,27 @@ struct __packed pucan_filter_std { ...@@ -108,6 +121,27 @@ struct __packed pucan_filter_std {
__le32 mask; /* CAN-ID bitmask in idx range */ __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 */ /* uCAN WR_ERR_CNT command fields */
#define PUCAN_WRERRCNT_TE 0x4000 /* Tx error cntr write Enable */ #define PUCAN_WRERRCNT_TE 0x4000 /* Tx error cntr write Enable */
#define PUCAN_WRERRCNT_RE 0x8000 /* Rx error cntr write Enable */ #define PUCAN_WRERRCNT_RE 0x8000 /* Rx error cntr write Enable */
...@@ -184,6 +218,12 @@ struct __packed pucan_error_msg { ...@@ -184,6 +218,12 @@ struct __packed pucan_error_msg {
u8 rx_err_cnt; 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_PASSIVE 0x20
#define PUCAN_BUS_WARNING 0x40 #define PUCAN_BUS_WARNING 0x40
#define PUCAN_BUS_BUSOFF 0x80 #define PUCAN_BUS_BUSOFF 0x80
...@@ -197,6 +237,31 @@ struct __packed pucan_status_msg { ...@@ -197,6 +237,31 @@ struct __packed pucan_status_msg {
u8 unused[3]; 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 */ /* uCAN transmitted message format */
#define PUCAN_MSG_CHANNEL_DLC(c, d) (((c) & 0xf) | ((d) << 4)) #define PUCAN_MSG_CHANNEL_DLC(c, d) (((c) & 0xf) | ((d) << 4))
...@@ -213,32 +278,31 @@ struct __packed pucan_tx_msg { ...@@ -213,32 +278,31 @@ struct __packed pucan_tx_msg {
}; };
/* build the cmd opcode_channel field with respect to the correct endianness */ /* build the cmd opcode_channel field with respect to the correct endianness */
static inline __le16 pucan_cmd_opcode_channel(struct peak_usb_device *dev, static inline __le16 pucan_cmd_opcode_channel(int index, int opcode)
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 */ /* 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 */ /* 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 #endif
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
struct dev_rcv_lists; struct dev_rcv_lists;
struct s_stats;
struct s_pstats;
struct netns_can { struct netns_can {
#if IS_ENABLED(CONFIG_PROC_FS) #if IS_ENABLED(CONFIG_PROC_FS)
...@@ -21,11 +23,18 @@ struct netns_can { ...@@ -21,11 +23,18 @@ struct netns_can {
struct proc_dir_entry *pde_rcvlist_sff; struct proc_dir_entry *pde_rcvlist_sff;
struct proc_dir_entry *pde_rcvlist_eff; struct proc_dir_entry *pde_rcvlist_eff;
struct proc_dir_entry *pde_rcvlist_err; struct proc_dir_entry *pde_rcvlist_err;
struct proc_dir_entry *bcmproc_dir;
#endif #endif
/* receive filters subscribed for 'all' CAN devices */ /* receive filters subscribed for 'all' CAN devices */
struct dev_rcv_lists *can_rx_alldev_list; struct dev_rcv_lists *can_rx_alldev_list;
spinlock_t can_rcvlists_lock; 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__ */ #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 @@ ...@@ -2,7 +2,7 @@
* af_can.c - Protocol family CAN core module * af_can.c - Protocol family CAN core module
* (used by different CAN protocol modules) * (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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
...@@ -75,18 +75,12 @@ static int stats_timer __read_mostly = 1; ...@@ -75,18 +75,12 @@ static int stats_timer __read_mostly = 1;
module_param(stats_timer, int, S_IRUGO); module_param(stats_timer, int, S_IRUGO);
MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)"); MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
static int can_net_id;
static struct kmem_cache *rcv_cache __read_mostly; static struct kmem_cache *rcv_cache __read_mostly;
/* table of registered CAN protocols */ /* table of registered CAN protocols */
static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly; static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
static DEFINE_MUTEX(proto_tab_lock); 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); static atomic_t skbcounter = ATOMIC_INIT(0);
/* /*
...@@ -223,6 +217,7 @@ int can_send(struct sk_buff *skb, int loop) ...@@ -223,6 +217,7 @@ int can_send(struct sk_buff *skb, int loop)
{ {
struct sk_buff *newskb = NULL; struct sk_buff *newskb = NULL;
struct canfd_frame *cfd = (struct canfd_frame *)skb->data; struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
struct s_stats *can_stats = dev_net(skb->dev)->can.can_stats;
int err = -EINVAL; int err = -EINVAL;
if (skb->len == CAN_MTU) { if (skb->len == CAN_MTU) {
...@@ -311,8 +306,8 @@ int can_send(struct sk_buff *skb, int loop) ...@@ -311,8 +306,8 @@ int can_send(struct sk_buff *skb, int loop)
netif_rx_ni(newskb); netif_rx_ni(newskb);
/* update statistics */ /* update statistics */
can_stats.tx_frames++; can_stats->tx_frames++;
can_stats.tx_frames_delta++; can_stats->tx_frames_delta++;
return 0; return 0;
...@@ -470,6 +465,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id, ...@@ -470,6 +465,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id,
struct receiver *r; struct receiver *r;
struct hlist_head *rl; struct hlist_head *rl;
struct dev_rcv_lists *d; struct dev_rcv_lists *d;
struct s_pstats *can_pstats = net->can.can_pstats;
int err = 0; int err = 0;
/* insert new receiver (dev,canid,mask) -> (func,data) */ /* 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, ...@@ -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); hlist_add_head_rcu(&r->list, rl);
d->entries++; d->entries++;
can_pstats.rcv_entries++; can_pstats->rcv_entries++;
if (can_pstats.rcv_entries_max < 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_max = can_pstats->rcv_entries;
} else { } else {
kmem_cache_free(rcv_cache, r); kmem_cache_free(rcv_cache, r);
err = -ENODEV; err = -ENODEV;
...@@ -545,6 +541,7 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id, ...@@ -545,6 +541,7 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
{ {
struct receiver *r = NULL; struct receiver *r = NULL;
struct hlist_head *rl; struct hlist_head *rl;
struct s_pstats *can_pstats = net->can.can_pstats;
struct dev_rcv_lists *d; struct dev_rcv_lists *d;
if (dev && dev->type != ARPHRD_CAN) 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, ...@@ -591,8 +588,8 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id,
hlist_del_rcu(&r->list); hlist_del_rcu(&r->list);
d->entries--; d->entries--;
if (can_pstats.rcv_entries > 0) if (can_pstats->rcv_entries > 0)
can_pstats.rcv_entries--; can_pstats->rcv_entries--;
/* remove device structure requested by NETDEV_UNREGISTER */ /* remove device structure requested by NETDEV_UNREGISTER */
if (d->remove_on_zero_entries && !d->entries) { 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) ...@@ -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) static void can_receive(struct sk_buff *skb, struct net_device *dev)
{ {
struct dev_rcv_lists *d; struct dev_rcv_lists *d;
struct net *net = dev_net(dev);
struct s_stats *can_stats = net->can.can_stats;
int matches; int matches;
/* update statistics */ /* update statistics */
can_stats.rx_frames++; can_stats->rx_frames++;
can_stats.rx_frames_delta++; can_stats->rx_frames_delta++;
/* create non-zero unique skb identifier together with *skb */ /* create non-zero unique skb identifier together with *skb */
while (!(can_skb_prv(skb)->skbcnt)) while (!(can_skb_prv(skb)->skbcnt))
...@@ -699,10 +698,10 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev) ...@@ -699,10 +698,10 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev)
rcu_read_lock(); rcu_read_lock();
/* deliver the packet to sockets listening on all devices */ /* 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 */ /* find receive list for this device */
d = find_dev_rcv_lists(dev_net(dev), dev); d = find_dev_rcv_lists(net, dev);
if (d) if (d)
matches += can_rcv_filter(d, skb); matches += can_rcv_filter(d, skb);
...@@ -712,8 +711,8 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev) ...@@ -712,8 +711,8 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev)
consume_skb(skb); consume_skb(skb);
if (matches > 0) { if (matches > 0) {
can_stats.matches++; can_stats->matches++;
can_stats.matches_delta++; can_stats->matches_delta++;
} }
} }
...@@ -878,8 +877,20 @@ static int can_pernet_init(struct net *net) ...@@ -878,8 +877,20 @@ static int can_pernet_init(struct net *net)
net->can.can_rx_alldev_list = net->can.can_rx_alldev_list =
kzalloc(sizeof(struct dev_rcv_lists), GFP_KERNEL); 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); can_init_proc(net);
}
return 0; return 0;
} }
...@@ -888,8 +899,11 @@ static void can_pernet_exit(struct net *net) ...@@ -888,8 +899,11 @@ static void can_pernet_exit(struct net *net)
{ {
struct net_device *dev; struct net_device *dev;
if (IS_ENABLED(CONFIG_PROC_FS)) if (IS_ENABLED(CONFIG_PROC_FS)) {
can_remove_proc(net); can_remove_proc(net);
if (stats_timer)
del_timer_sync(&net->can.can_stattimer);
}
/* remove created dev_rcv_lists from still registered CAN devices */ /* remove created dev_rcv_lists from still registered CAN devices */
rcu_read_lock(); rcu_read_lock();
...@@ -903,6 +917,10 @@ static void can_pernet_exit(struct net *net) ...@@ -903,6 +917,10 @@ static void can_pernet_exit(struct net *net)
} }
} }
rcu_read_unlock(); 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 = { ...@@ -933,8 +951,6 @@ static struct notifier_block can_netdev_notifier __read_mostly = {
static struct pernet_operations can_pernet_ops __read_mostly = { static struct pernet_operations can_pernet_ops __read_mostly = {
.init = can_pernet_init, .init = can_pernet_init,
.exit = can_pernet_exit, .exit = can_pernet_exit,
.id = &can_net_id,
.size = 0,
}; };
static __init int can_init(void) static __init int can_init(void)
...@@ -952,14 +968,6 @@ static __init int can_init(void) ...@@ -952,14 +968,6 @@ static __init int can_init(void)
if (!rcv_cache) if (!rcv_cache)
return -ENOMEM; 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); register_pernet_subsys(&can_pernet_ops);
/* protocol register */ /* protocol register */
...@@ -973,11 +981,6 @@ static __init int can_init(void) ...@@ -973,11 +981,6 @@ static __init int can_init(void)
static __exit void can_exit(void) static __exit void can_exit(void)
{ {
if (IS_ENABLED(CONFIG_PROC_FS)) {
if (stats_timer)
del_timer_sync(&can_stattimer);
}
/* protocol unregister */ /* protocol unregister */
dev_remove_pack(&canfd_packet); dev_remove_pack(&canfd_packet);
dev_remove_pack(&can_packet); dev_remove_pack(&can_packet);
......
...@@ -110,18 +110,9 @@ struct s_pstats { ...@@ -110,18 +110,9 @@ struct s_pstats {
unsigned long rcv_entries_max; 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) */ /* function prototypes for the CAN networklayer procfs (proc.c) */
void can_init_proc(struct net *net); void can_init_proc(struct net *net);
void can_remove_proc(struct net *net); void can_remove_proc(struct net *net);
void can_stat_update(unsigned long data); 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 */ #endif /* AF_CAN_H */
/* /*
* bcm.c - Broadcast Manager to filter/send (cyclic) CAN content * 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
(CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \ (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \
(CAN_SFF_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_DESCRIPTION("PF_CAN broadcast manager protocol");
MODULE_LICENSE("Dual BSD/GPL"); MODULE_LICENSE("Dual BSD/GPL");
...@@ -118,8 +118,6 @@ struct bcm_op { ...@@ -118,8 +118,6 @@ struct bcm_op {
struct net_device *rx_reg_dev; struct net_device *rx_reg_dev;
}; };
static struct proc_dir_entry *proc_dir;
struct bcm_sock { struct bcm_sock {
struct sock sk; struct sock sk;
int bound; int bound;
...@@ -149,7 +147,7 @@ static inline ktime_t bcm_timeval_to_ktime(struct bcm_timeval tv) ...@@ -149,7 +147,7 @@ static inline ktime_t bcm_timeval_to_ktime(struct bcm_timeval tv)
/* /*
* procfs functions * 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; struct net_device *dev;
...@@ -157,7 +155,7 @@ static char *bcm_proc_getifname(char *result, int ifindex) ...@@ -157,7 +155,7 @@ static char *bcm_proc_getifname(char *result, int ifindex)
return "any"; return "any";
rcu_read_lock(); rcu_read_lock();
dev = dev_get_by_index_rcu(&init_net, ifindex); dev = dev_get_by_index_rcu(net, ifindex);
if (dev) if (dev)
strcpy(result, dev->name); strcpy(result, dev->name);
else else
...@@ -170,7 +168,8 @@ static char *bcm_proc_getifname(char *result, int ifindex) ...@@ -170,7 +168,8 @@ static char *bcm_proc_getifname(char *result, int ifindex)
static int bcm_proc_show(struct seq_file *m, void *v) static int bcm_proc_show(struct seq_file *m, void *v)
{ {
char ifname[IFNAMSIZ]; 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_sock *bo = bcm_sk(sk);
struct bcm_op *op; struct bcm_op *op;
...@@ -178,7 +177,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) ...@@ -178,7 +177,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
seq_printf(m, " / sk %pK", sk); seq_printf(m, " / sk %pK", sk);
seq_printf(m, " / bo %pK", bo); seq_printf(m, " / bo %pK", bo);
seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs); 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"); seq_printf(m, " <<<\n");
list_for_each_entry(op, &bo->rx_ops, list) { list_for_each_entry(op, &bo->rx_ops, list) {
...@@ -190,7 +189,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) ...@@ -190,7 +189,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
continue; continue;
seq_printf(m, "rx_op: %03X %-5s ", op->can_id, 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) if (op->flags & CAN_FD_FRAME)
seq_printf(m, "(%u)", op->nframes); seq_printf(m, "(%u)", op->nframes);
...@@ -219,7 +218,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) ...@@ -219,7 +218,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
list_for_each_entry(op, &bo->tx_ops, list) { list_for_each_entry(op, &bo->tx_ops, list) {
seq_printf(m, "tx_op: %03X %s ", op->can_id, 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) if (op->flags & CAN_FD_FRAME)
seq_printf(m, "(%u) ", op->nframes); seq_printf(m, "(%u) ", op->nframes);
...@@ -242,7 +241,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) ...@@ -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) 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 = { static const struct file_operations bcm_proc_fops = {
...@@ -267,7 +266,7 @@ static void bcm_can_tx(struct bcm_op *op) ...@@ -267,7 +266,7 @@ static void bcm_can_tx(struct bcm_op *op)
if (!op->ifindex) if (!op->ifindex)
return; return;
dev = dev_get_by_index(&init_net, op->ifindex); dev = dev_get_by_index(sock_net(op->sk), op->ifindex);
if (!dev) { if (!dev) {
/* RFC: should this bcm_op remove itself here? */ /* RFC: should this bcm_op remove itself here? */
return; return;
...@@ -764,7 +763,7 @@ static void bcm_remove_op(struct bcm_op *op) ...@@ -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) static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
{ {
if (op->rx_reg_dev == dev) { 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); REGMASK(op->can_id), bcm_rx_handler, op);
/* mark as removed subscription */ /* mark as removed subscription */
...@@ -800,7 +799,7 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, ...@@ -800,7 +799,7 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
if (op->rx_reg_dev) { if (op->rx_reg_dev) {
struct net_device *dev; struct net_device *dev;
dev = dev_get_by_index(&init_net, dev = dev_get_by_index(sock_net(op->sk),
op->ifindex); op->ifindex);
if (dev) { if (dev) {
bcm_rx_unreg(dev, op); bcm_rx_unreg(dev, op);
...@@ -808,7 +807,8 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, ...@@ -808,7 +807,8 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
} }
} }
} else } 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), REGMASK(op->can_id),
bcm_rx_handler, op); bcm_rx_handler, op);
...@@ -1220,9 +1220,9 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, ...@@ -1220,9 +1220,9 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
if (ifindex) { if (ifindex) {
struct net_device *dev; struct net_device *dev;
dev = dev_get_by_index(&init_net, ifindex); dev = dev_get_by_index(sock_net(sk), ifindex);
if (dev) { if (dev) {
err = can_rx_register(&init_net, dev, err = can_rx_register(sock_net(sk), dev,
op->can_id, op->can_id,
REGMASK(op->can_id), REGMASK(op->can_id),
bcm_rx_handler, op, bcm_rx_handler, op,
...@@ -1233,7 +1233,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, ...@@ -1233,7 +1233,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
} }
} else } 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), REGMASK(op->can_id),
bcm_rx_handler, op, "bcm", sk); bcm_rx_handler, op, "bcm", sk);
if (err) { if (err) {
...@@ -1273,7 +1273,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, ...@@ -1273,7 +1273,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk,
return err; return err;
} }
dev = dev_get_by_index(&init_net, ifindex); dev = dev_get_by_index(sock_net(sk), ifindex);
if (!dev) { if (!dev) {
kfree_skb(skb); kfree_skb(skb);
return -ENODEV; return -ENODEV;
...@@ -1338,7 +1338,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) ...@@ -1338,7 +1338,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if (ifindex) { if (ifindex) {
struct net_device *dev; struct net_device *dev;
dev = dev_get_by_index(&init_net, ifindex); dev = dev_get_by_index(sock_net(sk), ifindex);
if (!dev) if (!dev)
return -ENODEV; return -ENODEV;
...@@ -1419,7 +1419,7 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg, ...@@ -1419,7 +1419,7 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
struct bcm_op *op; struct bcm_op *op;
int notify_enodev = 0; int notify_enodev = 0;
if (!net_eq(dev_net(dev), &init_net)) if (!net_eq(dev_net(dev), sock_net(sk)))
return NOTIFY_DONE; return NOTIFY_DONE;
if (dev->type != ARPHRD_CAN) if (dev->type != ARPHRD_CAN)
...@@ -1491,6 +1491,7 @@ static int bcm_init(struct sock *sk) ...@@ -1491,6 +1491,7 @@ static int bcm_init(struct sock *sk)
static int bcm_release(struct socket *sock) static int bcm_release(struct socket *sock)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
struct bcm_sock *bo; struct bcm_sock *bo;
struct bcm_op *op, *next; struct bcm_op *op, *next;
...@@ -1522,14 +1523,14 @@ static int bcm_release(struct socket *sock) ...@@ -1522,14 +1523,14 @@ static int bcm_release(struct socket *sock)
if (op->rx_reg_dev) { if (op->rx_reg_dev) {
struct net_device *dev; struct net_device *dev;
dev = dev_get_by_index(&init_net, op->ifindex); dev = dev_get_by_index(net, op->ifindex);
if (dev) { if (dev) {
bcm_rx_unreg(dev, op); bcm_rx_unreg(dev, op);
dev_put(dev); dev_put(dev);
} }
} }
} else } else
can_rx_unregister(&init_net, NULL, op->can_id, can_rx_unregister(net, NULL, op->can_id,
REGMASK(op->can_id), REGMASK(op->can_id),
bcm_rx_handler, op); bcm_rx_handler, op);
...@@ -1537,8 +1538,8 @@ static int bcm_release(struct socket *sock) ...@@ -1537,8 +1538,8 @@ static int bcm_release(struct socket *sock)
} }
/* remove procfs entry */ /* remove procfs entry */
if (proc_dir && bo->bcm_proc_read) if (net->can.bcmproc_dir && bo->bcm_proc_read)
remove_proc_entry(bo->procname, proc_dir); remove_proc_entry(bo->procname, net->can.bcmproc_dir);
/* remove device reference */ /* remove device reference */
if (bo->bound) { if (bo->bound) {
...@@ -1561,6 +1562,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, ...@@ -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 sockaddr_can *addr = (struct sockaddr_can *)uaddr;
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct bcm_sock *bo = bcm_sk(sk); struct bcm_sock *bo = bcm_sk(sk);
struct net *net = sock_net(sk);
int ret = 0; int ret = 0;
if (len < sizeof(*addr)) if (len < sizeof(*addr))
...@@ -1577,7 +1579,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, ...@@ -1577,7 +1579,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
if (addr->can_ifindex) { if (addr->can_ifindex) {
struct net_device *dev; 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) { if (!dev) {
ret = -ENODEV; ret = -ENODEV;
goto fail; goto fail;
...@@ -1596,11 +1598,11 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, ...@@ -1596,11 +1598,11 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
bo->ifindex = 0; bo->ifindex = 0;
} }
if (proc_dir) { if (net->can.bcmproc_dir) {
/* unique socket address as filename */ /* unique socket address as filename */
sprintf(bo->procname, "%lu", sock_i_ino(sk)); sprintf(bo->procname, "%lu", sock_i_ino(sk));
bo->bcm_proc_read = proc_create_data(bo->procname, 0644, bo->bcm_proc_read = proc_create_data(bo->procname, 0644,
proc_dir, net->can.bcmproc_dir,
&bcm_proc_fops, sk); &bcm_proc_fops, sk);
if (!bo->bcm_proc_read) { if (!bo->bcm_proc_read) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -1687,6 +1689,31 @@ static const struct can_proto bcm_can_proto = { ...@@ -1687,6 +1689,31 @@ static const struct can_proto bcm_can_proto = {
.prot = &bcm_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) static int __init bcm_module_init(void)
{ {
int err; int err;
...@@ -1699,17 +1726,14 @@ static int __init bcm_module_init(void) ...@@ -1699,17 +1726,14 @@ static int __init bcm_module_init(void)
return err; return err;
} }
/* create /proc/net/can-bcm directory */ register_pernet_subsys(&canbcm_pernet_ops);
proc_dir = proc_mkdir("can-bcm", init_net.proc_net);
return 0; return 0;
} }
static void __exit bcm_module_exit(void) static void __exit bcm_module_exit(void)
{ {
can_proto_unregister(&bcm_can_proto); can_proto_unregister(&bcm_can_proto);
unregister_pernet_subsys(&canbcm_pernet_ops);
if (proc_dir)
remove_proc_entry("can-bcm", init_net.proc_net);
} }
module_init(bcm_module_init); module_init(bcm_module_init);
......
/* /*
* gw.c - CAN frame Gateway/Router/Bridge with netlink interface * gw.c - CAN frame Gateway/Router/Bridge with netlink interface
* *
* Copyright (c) 2011 Volkswagen Group Electronic Research * Copyright (c) 2017 Volkswagen Group Electronic Research
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/sock.h> #include <net/sock.h>
#define CAN_GW_VERSION "20130117" #define CAN_GW_VERSION "20170425"
#define CAN_GW_NAME "can-gw" #define CAN_GW_NAME "can-gw"
MODULE_DESCRIPTION("PF_CAN netlink gateway"); MODULE_DESCRIPTION("PF_CAN netlink gateway");
...@@ -79,9 +79,7 @@ MODULE_PARM_DESC(max_hops, ...@@ -79,9 +79,7 @@ MODULE_PARM_DESC(max_hops,
__stringify(CGW_MAX_HOPS) " hops, " __stringify(CGW_MAX_HOPS) " hops, "
"default: " __stringify(CGW_DEFAULT_HOPS) ")"); "default: " __stringify(CGW_DEFAULT_HOPS) ")");
static HLIST_HEAD(cgw_list);
static struct notifier_block notifier; static struct notifier_block notifier;
static struct kmem_cache *cgw_cache __read_mostly; static struct kmem_cache *cgw_cache __read_mostly;
/* structure that contains the (on-the-fly) CAN frame modifications */ /* structure that contains the (on-the-fly) CAN frame modifications */
...@@ -438,16 +436,16 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) ...@@ -438,16 +436,16 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
gwj->handled_frames++; gwj->handled_frames++;
} }
static inline int cgw_register_filter(struct cgw_job *gwj) static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)
{ {
return can_rx_register(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id, return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj->ccgw.filter.can_mask, can_can_gw_rcv,
gwj, "gw", NULL); gwj, "gw", NULL);
} }
static inline void cgw_unregister_filter(struct cgw_job *gwj) static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj)
{ {
can_rx_unregister(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id, can_rx_unregister(net, gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj); gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
} }
...@@ -455,9 +453,8 @@ static int cgw_notifier(struct notifier_block *nb, ...@@ -455,9 +453,8 @@ static int cgw_notifier(struct notifier_block *nb,
unsigned long msg, void *ptr) unsigned long msg, void *ptr)
{ {
struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct net *net = dev_net(dev);
if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
if (dev->type != ARPHRD_CAN) if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -468,11 +465,11 @@ static int cgw_notifier(struct notifier_block *nb, ...@@ -468,11 +465,11 @@ static int cgw_notifier(struct notifier_block *nb,
ASSERT_RTNL(); ASSERT_RTNL();
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) { hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
if (gwj->src.dev == dev || gwj->dst.dev == dev) { if (gwj->src.dev == dev || gwj->dst.dev == dev) {
hlist_del(&gwj->list); hlist_del(&gwj->list);
cgw_unregister_filter(gwj); cgw_unregister_filter(net, gwj);
kmem_cache_free(cgw_cache, gwj); kmem_cache_free(cgw_cache, gwj);
} }
} }
...@@ -592,12 +589,13 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type, ...@@ -592,12 +589,13 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */ /* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb) static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
{ {
struct net *net = sock_net(skb->sk);
struct cgw_job *gwj = NULL; struct cgw_job *gwj = NULL;
int idx = 0; int idx = 0;
int s_idx = cb->args[0]; int s_idx = cb->args[0];
rcu_read_lock(); rcu_read_lock();
hlist_for_each_entry_rcu(gwj, &cgw_list, list) { hlist_for_each_entry_rcu(gwj, &net->can.cgw_list, list) {
if (idx < s_idx) if (idx < s_idx)
goto cont; goto cont;
...@@ -812,6 +810,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, ...@@ -812,6 +810,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk);
struct rtcanmsg *r; struct rtcanmsg *r;
struct cgw_job *gwj; struct cgw_job *gwj;
struct cf_mod mod; struct cf_mod mod;
...@@ -842,7 +841,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -842,7 +841,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
ASSERT_RTNL(); ASSERT_RTNL();
/* check for updating an existing job with identical uid */ /* check for updating an existing job with identical uid */
hlist_for_each_entry(gwj, &cgw_list, list) { hlist_for_each_entry(gwj, &net->can.cgw_list, list) {
if (gwj->mod.uid != mod.uid) if (gwj->mod.uid != mod.uid)
continue; continue;
...@@ -880,7 +879,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -880,7 +879,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
err = -ENODEV; err = -ENODEV;
gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx); gwj->src.dev = __dev_get_by_index(net, gwj->ccgw.src_idx);
if (!gwj->src.dev) if (!gwj->src.dev)
goto out; goto out;
...@@ -888,7 +887,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -888,7 +887,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
if (gwj->src.dev->type != ARPHRD_CAN) if (gwj->src.dev->type != ARPHRD_CAN)
goto out; goto out;
gwj->dst.dev = __dev_get_by_index(&init_net, gwj->ccgw.dst_idx); gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);
if (!gwj->dst.dev) if (!gwj->dst.dev)
goto out; goto out;
...@@ -898,9 +897,9 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -898,9 +897,9 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
ASSERT_RTNL(); ASSERT_RTNL();
err = cgw_register_filter(gwj); err = cgw_register_filter(net, gwj);
if (!err) if (!err)
hlist_add_head_rcu(&gwj->list, &cgw_list); hlist_add_head_rcu(&gwj->list, &net->can.cgw_list);
out: out:
if (err) if (err)
kmem_cache_free(cgw_cache, gwj); kmem_cache_free(cgw_cache, gwj);
...@@ -908,16 +907,16 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -908,16 +907,16 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
return err; return err;
} }
static void cgw_remove_all_jobs(void) static void cgw_remove_all_jobs(struct net *net)
{ {
struct cgw_job *gwj = NULL; struct cgw_job *gwj = NULL;
struct hlist_node *nx; struct hlist_node *nx;
ASSERT_RTNL(); ASSERT_RTNL();
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) { hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
hlist_del(&gwj->list); hlist_del(&gwj->list);
cgw_unregister_filter(gwj); cgw_unregister_filter(net, gwj);
kmem_cache_free(cgw_cache, gwj); kmem_cache_free(cgw_cache, gwj);
} }
} }
...@@ -925,6 +924,7 @@ static void cgw_remove_all_jobs(void) ...@@ -925,6 +924,7 @@ static void cgw_remove_all_jobs(void)
static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk);
struct cgw_job *gwj = NULL; struct cgw_job *gwj = NULL;
struct hlist_node *nx; struct hlist_node *nx;
struct rtcanmsg *r; struct rtcanmsg *r;
...@@ -953,7 +953,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -953,7 +953,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
/* two interface indices both set to 0 => remove all entries */ /* two interface indices both set to 0 => remove all entries */
if (!ccgw.src_idx && !ccgw.dst_idx) { if (!ccgw.src_idx && !ccgw.dst_idx) {
cgw_remove_all_jobs(); cgw_remove_all_jobs(net);
return 0; return 0;
} }
...@@ -962,7 +962,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -962,7 +962,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
ASSERT_RTNL(); ASSERT_RTNL();
/* remove only the first matching entry */ /* remove only the first matching entry */
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) { hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
if (gwj->flags != r->flags) if (gwj->flags != r->flags)
continue; continue;
...@@ -985,7 +985,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -985,7 +985,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
continue; continue;
hlist_del(&gwj->list); hlist_del(&gwj->list);
cgw_unregister_filter(gwj); cgw_unregister_filter(net, gwj);
kmem_cache_free(cgw_cache, gwj); kmem_cache_free(cgw_cache, gwj);
err = 0; err = 0;
break; break;
...@@ -994,6 +994,24 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -994,6 +994,24 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
return err; return err;
} }
static int __net_init cangw_pernet_init(struct net *net)
{
INIT_HLIST_HEAD(&net->can.cgw_list);
return 0;
}
static void __net_exit cangw_pernet_exit(struct net *net)
{
rtnl_lock();
cgw_remove_all_jobs(net);
rtnl_unlock();
}
static struct pernet_operations cangw_pernet_ops = {
.init = cangw_pernet_init,
.exit = cangw_pernet_exit,
};
static __init int cgw_module_init(void) static __init int cgw_module_init(void)
{ {
/* sanitize given module parameter */ /* sanitize given module parameter */
...@@ -1002,6 +1020,7 @@ static __init int cgw_module_init(void) ...@@ -1002,6 +1020,7 @@ static __init int cgw_module_init(void)
pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n", pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n",
max_hops); max_hops);
register_pernet_subsys(&cangw_pernet_ops);
cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job), cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
0, 0, NULL); 0, 0, NULL);
...@@ -1031,10 +1050,7 @@ static __exit void cgw_module_exit(void) ...@@ -1031,10 +1050,7 @@ static __exit void cgw_module_exit(void)
unregister_netdevice_notifier(&notifier); unregister_netdevice_notifier(&notifier);
rtnl_lock(); unregister_pernet_subsys(&cangw_pernet_ops);
cgw_remove_all_jobs();
rtnl_unlock();
rcu_barrier(); /* Wait for completion of call_rcu()'s */ rcu_barrier(); /* Wait for completion of call_rcu()'s */
kmem_cache_destroy(cgw_cache); kmem_cache_destroy(cgw_cache);
......
...@@ -75,21 +75,23 @@ static const char rx_list_name[][8] = { ...@@ -75,21 +75,23 @@ static const char rx_list_name[][8] = {
* af_can statistics stuff * af_can statistics stuff
*/ */
static void can_init_stats(void) static void can_init_stats(struct net *net)
{ {
struct s_stats *can_stats = net->can.can_stats;
struct s_pstats *can_pstats = net->can.can_pstats;
/* /*
* This memset function is called from a timer context (when * This memset function is called from a timer context (when
* can_stattimer is active which is the default) OR in a process * can_stattimer is active which is the default) OR in a process
* context (reading the proc_fs when can_stattimer is disabled). * context (reading the proc_fs when can_stattimer is disabled).
*/ */
memset(&can_stats, 0, sizeof(can_stats)); memset(can_stats, 0, sizeof(struct s_stats));
can_stats.jiffies_init = jiffies; can_stats->jiffies_init = jiffies;
can_pstats.stats_reset++; can_pstats->stats_reset++;
if (user_reset) { if (user_reset) {
user_reset = 0; user_reset = 0;
can_pstats.user_reset++; can_pstats->user_reset++;
} }
} }
...@@ -115,64 +117,66 @@ static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif, ...@@ -115,64 +117,66 @@ static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
void can_stat_update(unsigned long data) void can_stat_update(unsigned long data)
{ {
struct net *net = (struct net *)data;
struct s_stats *can_stats = net->can.can_stats;
unsigned long j = jiffies; /* snapshot */ unsigned long j = jiffies; /* snapshot */
/* restart counting in timer context on user request */ /* restart counting in timer context on user request */
if (user_reset) if (user_reset)
can_init_stats(); can_init_stats(net);
/* restart counting on jiffies overflow */ /* restart counting on jiffies overflow */
if (j < can_stats.jiffies_init) if (j < can_stats->jiffies_init)
can_init_stats(); can_init_stats(net);
/* prevent overflow in calc_rate() */ /* prevent overflow in calc_rate() */
if (can_stats.rx_frames > (ULONG_MAX / HZ)) if (can_stats->rx_frames > (ULONG_MAX / HZ))
can_init_stats(); can_init_stats(net);
/* prevent overflow in calc_rate() */ /* prevent overflow in calc_rate() */
if (can_stats.tx_frames > (ULONG_MAX / HZ)) if (can_stats->tx_frames > (ULONG_MAX / HZ))
can_init_stats(); can_init_stats(net);
/* matches overflow - very improbable */ /* matches overflow - very improbable */
if (can_stats.matches > (ULONG_MAX / 100)) if (can_stats->matches > (ULONG_MAX / 100))
can_init_stats(); can_init_stats(net);
/* calc total values */ /* calc total values */
if (can_stats.rx_frames) if (can_stats->rx_frames)
can_stats.total_rx_match_ratio = (can_stats.matches * 100) / can_stats->total_rx_match_ratio = (can_stats->matches * 100) /
can_stats.rx_frames; can_stats->rx_frames;
can_stats.total_tx_rate = calc_rate(can_stats.jiffies_init, j, can_stats->total_tx_rate = calc_rate(can_stats->jiffies_init, j,
can_stats.tx_frames); can_stats->tx_frames);
can_stats.total_rx_rate = calc_rate(can_stats.jiffies_init, j, can_stats->total_rx_rate = calc_rate(can_stats->jiffies_init, j,
can_stats.rx_frames); can_stats->rx_frames);
/* calc current values */ /* calc current values */
if (can_stats.rx_frames_delta) if (can_stats->rx_frames_delta)
can_stats.current_rx_match_ratio = can_stats->current_rx_match_ratio =
(can_stats.matches_delta * 100) / (can_stats->matches_delta * 100) /
can_stats.rx_frames_delta; can_stats->rx_frames_delta;
can_stats.current_tx_rate = calc_rate(0, HZ, can_stats.tx_frames_delta); can_stats->current_tx_rate = calc_rate(0, HZ, can_stats->tx_frames_delta);
can_stats.current_rx_rate = calc_rate(0, HZ, can_stats.rx_frames_delta); can_stats->current_rx_rate = calc_rate(0, HZ, can_stats->rx_frames_delta);
/* check / update maximum values */ /* check / update maximum values */
if (can_stats.max_tx_rate < can_stats.current_tx_rate) if (can_stats->max_tx_rate < can_stats->current_tx_rate)
can_stats.max_tx_rate = can_stats.current_tx_rate; can_stats->max_tx_rate = can_stats->current_tx_rate;
if (can_stats.max_rx_rate < can_stats.current_rx_rate) if (can_stats->max_rx_rate < can_stats->current_rx_rate)
can_stats.max_rx_rate = can_stats.current_rx_rate; can_stats->max_rx_rate = can_stats->current_rx_rate;
if (can_stats.max_rx_match_ratio < can_stats.current_rx_match_ratio) if (can_stats->max_rx_match_ratio < can_stats->current_rx_match_ratio)
can_stats.max_rx_match_ratio = can_stats.current_rx_match_ratio; can_stats->max_rx_match_ratio = can_stats->current_rx_match_ratio;
/* clear values for 'current rate' calculation */ /* clear values for 'current rate' calculation */
can_stats.tx_frames_delta = 0; can_stats->tx_frames_delta = 0;
can_stats.rx_frames_delta = 0; can_stats->rx_frames_delta = 0;
can_stats.matches_delta = 0; can_stats->matches_delta = 0;
/* restart timer (one second) */ /* restart timer (one second) */
mod_timer(&can_stattimer, round_jiffies(jiffies + HZ)); mod_timer(&net->can.can_stattimer, round_jiffies(jiffies + HZ));
} }
/* /*
...@@ -206,57 +210,61 @@ static void can_print_recv_banner(struct seq_file *m) ...@@ -206,57 +210,61 @@ static void can_print_recv_banner(struct seq_file *m)
static int can_stats_proc_show(struct seq_file *m, void *v) static int can_stats_proc_show(struct seq_file *m, void *v)
{ {
struct net *net = m->private;
struct s_stats *can_stats = net->can.can_stats;
struct s_pstats *can_pstats = net->can.can_pstats;
seq_putc(m, '\n'); seq_putc(m, '\n');
seq_printf(m, " %8ld transmitted frames (TXF)\n", can_stats.tx_frames); seq_printf(m, " %8ld transmitted frames (TXF)\n", can_stats->tx_frames);
seq_printf(m, " %8ld received frames (RXF)\n", can_stats.rx_frames); seq_printf(m, " %8ld received frames (RXF)\n", can_stats->rx_frames);
seq_printf(m, " %8ld matched frames (RXMF)\n", can_stats.matches); seq_printf(m, " %8ld matched frames (RXMF)\n", can_stats->matches);
seq_putc(m, '\n'); seq_putc(m, '\n');
if (can_stattimer.function == can_stat_update) { if (net->can.can_stattimer.function == can_stat_update) {
seq_printf(m, " %8ld %% total match ratio (RXMR)\n", seq_printf(m, " %8ld %% total match ratio (RXMR)\n",
can_stats.total_rx_match_ratio); can_stats->total_rx_match_ratio);
seq_printf(m, " %8ld frames/s total tx rate (TXR)\n", seq_printf(m, " %8ld frames/s total tx rate (TXR)\n",
can_stats.total_tx_rate); can_stats->total_tx_rate);
seq_printf(m, " %8ld frames/s total rx rate (RXR)\n", seq_printf(m, " %8ld frames/s total rx rate (RXR)\n",
can_stats.total_rx_rate); can_stats->total_rx_rate);
seq_putc(m, '\n'); seq_putc(m, '\n');
seq_printf(m, " %8ld %% current match ratio (CRXMR)\n", seq_printf(m, " %8ld %% current match ratio (CRXMR)\n",
can_stats.current_rx_match_ratio); can_stats->current_rx_match_ratio);
seq_printf(m, " %8ld frames/s current tx rate (CTXR)\n", seq_printf(m, " %8ld frames/s current tx rate (CTXR)\n",
can_stats.current_tx_rate); can_stats->current_tx_rate);
seq_printf(m, " %8ld frames/s current rx rate (CRXR)\n", seq_printf(m, " %8ld frames/s current rx rate (CRXR)\n",
can_stats.current_rx_rate); can_stats->current_rx_rate);
seq_putc(m, '\n'); seq_putc(m, '\n');
seq_printf(m, " %8ld %% max match ratio (MRXMR)\n", seq_printf(m, " %8ld %% max match ratio (MRXMR)\n",
can_stats.max_rx_match_ratio); can_stats->max_rx_match_ratio);
seq_printf(m, " %8ld frames/s max tx rate (MTXR)\n", seq_printf(m, " %8ld frames/s max tx rate (MTXR)\n",
can_stats.max_tx_rate); can_stats->max_tx_rate);
seq_printf(m, " %8ld frames/s max rx rate (MRXR)\n", seq_printf(m, " %8ld frames/s max rx rate (MRXR)\n",
can_stats.max_rx_rate); can_stats->max_rx_rate);
seq_putc(m, '\n'); seq_putc(m, '\n');
} }
seq_printf(m, " %8ld current receive list entries (CRCV)\n", seq_printf(m, " %8ld current receive list entries (CRCV)\n",
can_pstats.rcv_entries); can_pstats->rcv_entries);
seq_printf(m, " %8ld maximum receive list entries (MRCV)\n", seq_printf(m, " %8ld maximum receive list entries (MRCV)\n",
can_pstats.rcv_entries_max); can_pstats->rcv_entries_max);
if (can_pstats.stats_reset) if (can_pstats->stats_reset)
seq_printf(m, "\n %8ld statistic resets (STR)\n", seq_printf(m, "\n %8ld statistic resets (STR)\n",
can_pstats.stats_reset); can_pstats->stats_reset);
if (can_pstats.user_reset) if (can_pstats->user_reset)
seq_printf(m, " %8ld user statistic resets (USTR)\n", seq_printf(m, " %8ld user statistic resets (USTR)\n",
can_pstats.user_reset); can_pstats->user_reset);
seq_putc(m, '\n'); seq_putc(m, '\n');
return 0; return 0;
...@@ -264,7 +272,7 @@ static int can_stats_proc_show(struct seq_file *m, void *v) ...@@ -264,7 +272,7 @@ static int can_stats_proc_show(struct seq_file *m, void *v)
static int can_stats_proc_open(struct inode *inode, struct file *file) static int can_stats_proc_open(struct inode *inode, struct file *file)
{ {
return single_open(file, can_stats_proc_show, NULL); return single_open_net(inode, file, can_stats_proc_show);
} }
static const struct file_operations can_stats_proc_fops = { static const struct file_operations can_stats_proc_fops = {
...@@ -277,25 +285,28 @@ static const struct file_operations can_stats_proc_fops = { ...@@ -277,25 +285,28 @@ static const struct file_operations can_stats_proc_fops = {
static int can_reset_stats_proc_show(struct seq_file *m, void *v) static int can_reset_stats_proc_show(struct seq_file *m, void *v)
{ {
struct net *net = m->private;
struct s_pstats *can_pstats = net->can.can_pstats;
struct s_stats *can_stats = net->can.can_stats;
user_reset = 1; user_reset = 1;
if (can_stattimer.function == can_stat_update) { if (net->can.can_stattimer.function == can_stat_update) {
seq_printf(m, "Scheduled statistic reset #%ld.\n", seq_printf(m, "Scheduled statistic reset #%ld.\n",
can_pstats.stats_reset + 1); can_pstats->stats_reset + 1);
} else { } else {
if (can_stats.jiffies_init != jiffies) if (can_stats->jiffies_init != jiffies)
can_init_stats(); can_init_stats(net);
seq_printf(m, "Performed statistic reset #%ld.\n", seq_printf(m, "Performed statistic reset #%ld.\n",
can_pstats.stats_reset); can_pstats->stats_reset);
} }
return 0; return 0;
} }
static int can_reset_stats_proc_open(struct inode *inode, struct file *file) static int can_reset_stats_proc_open(struct inode *inode, struct file *file)
{ {
return single_open(file, can_reset_stats_proc_show, NULL); return single_open_net(inode, file, can_reset_stats_proc_show);
} }
static const struct file_operations can_reset_stats_proc_fops = { static const struct file_operations can_reset_stats_proc_fops = {
...@@ -314,7 +325,7 @@ static int can_version_proc_show(struct seq_file *m, void *v) ...@@ -314,7 +325,7 @@ static int can_version_proc_show(struct seq_file *m, void *v)
static int can_version_proc_open(struct inode *inode, struct file *file) static int can_version_proc_open(struct inode *inode, struct file *file)
{ {
return single_open(file, can_version_proc_show, NULL); return single_open_net(inode, file, can_version_proc_show);
} }
static const struct file_operations can_version_proc_fops = { static const struct file_operations can_version_proc_fops = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment