diff --git a/arch/arm/src/stm32/Kconfig b/arch/arm/src/stm32/Kconfig index 7d52339bb2..567cc9a8b6 100644 --- a/arch/arm/src/stm32/Kconfig +++ b/arch/arm/src/stm32/Kconfig @@ -10836,6 +10836,20 @@ config STM32_USB_ITRMP menu "CAN driver configuration" depends on STM32_CAN +choice + prompt "CAN character driver or SocketCAN support" + default STM32_CAN_CHARDRIVER + +config STM32_CAN_CHARDRIVER + bool "STM32 CAN character driver support" + select ARCH_HAVE_CAN_ERRORS + +config STM32_CAN_SOCKET + bool "STM32 CAN SocketCAN support" + select NET_CAN_HAVE_ERRORS + +endchoice # CAN character driver or SocketCAN support + config STM32_CAN1_BAUD int "CAN1 BAUD" default 250000 diff --git a/arch/arm/src/stm32/Make.defs b/arch/arm/src/stm32/Make.defs index 4de2988847..14e91e1dea 100644 --- a/arch/arm/src/stm32/Make.defs +++ b/arch/arm/src/stm32/Make.defs @@ -241,8 +241,13 @@ CHIP_CSRCS += stm32_hall3ph.c endif ifeq ($(CONFIG_STM32_CAN),y) +ifeq ($(CONFIG_STM32_CAN_CHARDRIVER),y) CHIP_CSRCS += stm32_can.c endif +ifeq ($(CONFIG_STM32_CAN_SOCKET),y) +CHIP_CSRCS += stm32_can_sock.c +endif +endif ifeq ($(CONFIG_STM32_IWDG),y) CHIP_CSRCS += stm32_iwdg.c diff --git a/arch/arm/src/stm32/stm32_can.h b/arch/arm/src/stm32/stm32_can.h index 7beefda91d..37ef72daf2 100644 --- a/arch/arm/src/stm32/stm32_can.h +++ b/arch/arm/src/stm32/stm32_can.h @@ -48,9 +48,6 @@ # undef CONFIG_STM32_CAN1 #endif -#if defined(CONFIG_CAN) && \ - (defined(CONFIG_STM32_CAN1) || defined(CONFIG_STM32_CAN2)) - /* CAN BAUD */ #if defined(CONFIG_STM32_CAN1) && !defined(CONFIG_STM32_CAN1_BAUD) @@ -107,11 +104,13 @@ extern "C" * Public Function Prototypes ****************************************************************************/ +#ifdef CONFIG_STM32_CAN_CHARDRIVER + /**************************************************************************** * Name: stm32_caninitialize * * Description: - * Initialize the selected CAN port + * Initialize the selected CAN port as character device * * Input Parameters: * Port number (for hardware that has multiple CAN interfaces) @@ -123,6 +122,26 @@ extern "C" struct can_dev_s; FAR struct can_dev_s *stm32_caninitialize(int port); +#endif + +#ifdef CONFIG_STM32_CAN_SOCKET + +/**************************************************************************** + * Name: stm32_cansockinitialize + * + * Description: + * Initialize the selected CAN port as SocketCAN interface + * + * Input Parameters: + * Port number (for hardware that has multiple CAN interfaces) + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +int stm32_cansockinitialize(int port); +#endif #undef EXTERN #if defined(__cplusplus) @@ -130,5 +149,4 @@ FAR struct can_dev_s *stm32_caninitialize(int port); #endif #endif /* __ASSEMBLY__ */ -#endif /* CONFIG_CAN && (CONFIG_STM32_CAN1 || CONFIG_STM32_CAN2) */ #endif /* __ARCH_ARM_SRC_STM32_STM32_CAN_H */ diff --git a/arch/arm/src/stm32/stm32_can_sock.c b/arch/arm/src/stm32/stm32_can_sock.c new file mode 100644 index 0000000000..c04505ab4f --- /dev/null +++ b/arch/arm/src/stm32/stm32_can_sock.c @@ -0,0 +1,2465 @@ +/**************************************************************************** + * arch/arm/src/stm32/stm32_can_sock.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "arm_internal.h" +#include "arm_arch.h" + +#include "chip.h" +#include "stm32.h" +#include "stm32_rcc.h" +#include "stm32_can.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Delays *******************************************************************/ + +/* Time out for INAK bit */ + +#define INAK_TIMEOUT 65535 + +/* Bit timing ***************************************************************/ + +#define CAN_BIT_QUANTA (CONFIG_STM32_CAN_TSEG1 + CONFIG_STM32_CAN_TSEG2 + 1) + +#ifndef CONFIG_DEBUG_CAN_INFO +# undef CONFIG_STM32_CAN_REGDEBUG +#endif + +/* Pool configuration *******************************************************/ + +#define POOL_SIZE (1) + +/* Work queue support is required. */ + +#if !defined(CONFIG_SCHED_WORKQUEUE) +# error Work queue support is required +#endif + +/* The low priority work queue is preferred. If it is not enabled, LPWORK + * will be the same as HPWORK. + * + * NOTE: However, the network should NEVER run on the high priority work + * queue! That queue is intended only to service short back end interrupt + * processing that never suspends. Suspending the high priority work queue + * may bring the system to its knees! + */ + +#define CANWORK LPWORK + +/* CAN error interrupts */ + +#ifdef CONFIG_NET_CAN_ERRORS +# define STM32_CAN_ERRINT (CAN_IER_LECIE | CAN_IER_ERRIE | \ + CAN_IER_BOFIE | CAN_IER_EPVIE | \ + CAN_IER_EWGIE) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct stm32_can_s +{ + uint8_t port; /* CAN port number (1 or 2) */ + uint8_t canrx[2]; /* CAN RX FIFO 0/1 IRQ number */ + uint8_t cantx; /* CAN TX IRQ number */ +#ifdef CONFIG_NET_CAN_ERRORS + uint8_t cansce; /* CAN SCE IRQ number */ +#endif + uint8_t filter; /* Filter number */ + uint32_t base; /* Base address of the CAN control registers */ + uint32_t fbase; /* Base address of the CAN filter registers */ + uint32_t baud; /* Configured baud */ + + bool bifup; /* true:ifup false:ifdown */ + struct net_driver_s dev; /* Interface understood by the network */ + + struct work_s irqwork; /* For deferring interrupt work to the wq */ + struct work_s pollwork; /* For deferring poll work to the work wq */ + + /* A pointers to the list of TX/RX descriptors */ + + FAR struct can_frame *txdesc; + FAR struct can_frame *rxdesc; + + /* TX/RX pool */ + + uint8_t tx_pool[sizeof(struct can_frame)*POOL_SIZE]; + uint8_t rx_pool[sizeof(struct can_frame)*POOL_SIZE]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* CAN Register access */ + +static uint32_t stm32can_getreg(FAR struct stm32_can_s *priv, + int offset); +static uint32_t stm32can_getfreg(FAR struct stm32_can_s *priv, + int offset); +static void stm32can_putreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value); +static void stm32can_putfreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value); +#ifdef CONFIG_STM32_CAN_REGDEBUG +static void stm32can_dumpctrlregs(FAR struct stm32_can_s *priv, + FAR const char *msg); +static void stm32can_dumpmbregs(FAR struct stm32_can_s *priv, + FAR const char *msg); +static void stm32can_dumpfiltregs(FAR struct stm32_can_s *priv, + FAR const char *msg); +#else +# define stm32can_dumpctrlregs(priv,msg) +# define stm32can_dumpmbregs(priv,msg) +# define stm32can_dumpfiltregs(priv,msg) +#endif + +/* CAN interrupt enable functions */ + +static void stm32can_rx0int(FAR struct stm32_can_s *priv, bool enable); +static void stm32can_rx1int(FAR struct stm32_can_s *priv, bool enable); +static void stm32can_txint(FAR struct stm32_can_s *priv, bool enable); +#ifdef CONFIG_NET_CAN_ERRORS +static void stm32can_errint(FAR struct stm32_can_s *priv, bool enable); +#endif + +/* Common TX logic */ + +static int stm32can_transmit(FAR struct stm32_can_s *priv); +static bool stm32can_txready(FAR struct stm32_can_s *priv); +static int stm32can_txpoll(struct net_driver_s *dev); + +/* CAN RX interrupt handling */ + +static int stm32can_rxinterrupt_work(FAR struct stm32_can_s *priv, + int rxmb); + +static void stm32can_rx0interrupt_work(FAR void *arg); +static void stm32can_rx1interrupt_work(FAR void *arg); +static int stm32can_rxinterrupt(FAR struct stm32_can_s *priv, int rxmb); + +static int stm32can_rx0interrupt(int irq, FAR void *context, FAR void *arg); +static int stm32can_rx1interrupt(int irq, FAR void *context, FAR void *arg); + +/* CAN TX interrupt handling */ + +static int stm32can_txinterrupt(int irq, FAR void *context, FAR void *arg); +static void stm32can_txdone_work(FAR void *arg); +static void stm32can_txdone(FAR struct stm32_can_s *priv); + +#ifdef CONFIG_NET_CAN_ERRORS +/* CAN errors interrupt handling */ + +static void stm32can_sceinterrupt_work(FAR void *arg); +static int stm32can_sceinterrupt(int irq, FAR void *context, FAR void *arg); +#endif + +/* Initialization */ + +static int stm32can_setup(FAR struct stm32_can_s *priv); +static void stm32can_shutdown(FAR struct stm32_can_s *priv); +static void stm32can_reset(FAR struct stm32_can_s *priv); +static int stm32can_enterinitmode(FAR struct stm32_can_s *priv); +static int stm32can_exitinitmode(FAR struct stm32_can_s *priv); +static int stm32can_bittiming(FAR struct stm32_can_s *priv); +static int stm32can_cellinit(FAR struct stm32_can_s *priv); +static int stm32can_filterinit(FAR struct stm32_can_s *priv); + +/* TX mailbox status */ + +static bool stm32can_txmb0empty(uint32_t tsr_regval); +static bool stm32can_txmb1empty(uint32_t tsr_regval); +static bool stm32can_txmb2empty(uint32_t tsr_regval); + +/* NuttX callback functions */ + +static int stm32can_ifup(struct net_driver_s *dev); +static int stm32can_ifdown(struct net_driver_s *dev); + +static void stm32can_txavail_work(FAR void *arg); +static int stm32can_txavail(struct net_driver_s *dev); + +#ifdef CONFIG_NETDEV_IOCTL +static int stm32can_netdev_ioctl(struct net_driver_s *dev, int cmd, + unsigned long arg); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_STM32_CAN1 + +static struct stm32_can_s g_can1priv = +{ + .port = 1, + .canrx = + { + STM32_IRQ_CAN1RX0, + STM32_IRQ_CAN1RX1, + }, + .cantx = STM32_IRQ_CAN1TX, +#ifdef CONFIG_NET_CAN_ERRORS + .cansce = STM32_IRQ_CAN1SCE, +#endif + .filter = 0, + .base = STM32_CAN1_BASE, + .fbase = STM32_CAN1_BASE, + .baud = CONFIG_STM32_CAN1_BAUD, +}; + +#endif + +#ifdef CONFIG_STM32_CAN2 + +static struct stm32_can_s g_can2priv = +{ + .port = 2, + .canrx = + { + STM32_IRQ_CAN2RX0, + STM32_IRQ_CAN2RX1, + }, + .cantx = STM32_IRQ_CAN2TX, +#ifdef CONFIG_NET_CAN_ERRORS + .cansce = STM32_IRQ_CAN2SCE, +#endif + .filter = CAN_NFILTERS / 2, + .base = STM32_CAN2_BASE, + .fbase = STM32_CAN1_BASE, + .baud = CONFIG_STM32_CAN2_BAUD, +}; + +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32can_getreg + * Name: stm32can_getfreg + * + * Description: + * Read the value of a CAN register or filter block register. + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_CAN_REGDEBUG +static uint32_t stm32can_vgetreg(uint32_t addr) +{ + static uint32_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + + /* Read the value from the register */ + + uint32_t val = getreg32(addr); + + /* Is this the same value that we read from the same register last time? + * Are we polling the register? If so, suppress some of the output. + */ + + if (addr == prevaddr && val == preval) + { + if (count == 0xffffffff || ++count > 3) + { + if (count == 4) + { + ninfo("...\n"); + } + + return val; + } + } + + /* No this is a new address or value */ + + else + { + /* Did we print "..." for the previous value? */ + + if (count > 3) + { + /* Yes.. then show how many times the value repeated */ + + ninfo("[repeats %" PRIu32 " more times]\n", count - 3); + } + + /* Save the new address, value, and count */ + + prevaddr = addr; + preval = val; + count = 1; + } + + /* Show the register value read */ + + ninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val); + return val; +} + +static uint32_t stm32can_getreg(FAR struct stm32_can_s *priv, int offset) +{ + return stm32can_vgetreg(priv->base + offset); +} + +static uint32_t stm32can_getfreg(FAR struct stm32_can_s *priv, int offset) +{ + return stm32can_vgetreg(priv->fbase + offset); +} + +#else +static uint32_t stm32can_getreg(FAR struct stm32_can_s *priv, int offset) +{ + return getreg32(priv->base + offset); +} + +static uint32_t stm32can_getfreg(FAR struct stm32_can_s *priv, int offset) +{ + return getreg32(priv->fbase + offset); +} + +#endif + +/**************************************************************************** + * Name: stm32can_putreg + * Name: stm32can_putfreg + * + * Description: + * Set the value of a CAN register or filter block register. + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_CAN_REGDEBUG +static void stm32can_vputreg(uint32_t addr, uint32_t value) +{ + /* Show the register value being written */ + + ninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val); + + /* Write the value */ + + putreg32(value, addr); +} + +static void stm32can_putreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value) +{ + stm32can_vputreg(priv->base + offset, value); +} + +static void stm32can_putfreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value) +{ + stm32can_vputreg(priv->fbase + offset, value); +} + +#else +static void stm32can_putreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +static void stm32can_putfreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value) +{ + putreg32(value, priv->fbase + offset); +} +#endif + +/**************************************************************************** + * Name: stm32can_dumpctrlregs + * + * Description: + * Dump the contents of all CAN control registers + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * msg - message + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_CAN_REGDEBUG +static void stm32can_dumpctrlregs(FAR struct stm32_can_s *priv, + FAR const char *msg) +{ + if (msg) + { + ninfo("Control Registers: %s\n", msg); + } + else + { + ninfo("Control Registers:\n"); + } + + /* CAN control and status registers */ + + ninfo(" MCR: %08" PRIx32 " MSR: %08" PRIx32 " TSR: %08" PRIx32 "\n", + getreg32(priv->base + STM32_CAN_MCR_OFFSET), + getreg32(priv->base + STM32_CAN_MSR_OFFSET), + getreg32(priv->base + STM32_CAN_TSR_OFFSET)); + + ninfo(" RF0R: %08" PRIx32 " RF1R: %08" PRIx32 "\n", + getreg32(priv->base + STM32_CAN_RF0R_OFFSET), + getreg32(priv->base + STM32_CAN_RF1R_OFFSET)); + + ninfo(" IER: %08" PRIx32 " ESR: %08" PRIx32 " BTR: %08" PRIx32 "\n", + getreg32(priv->base + STM32_CAN_IER_OFFSET), + getreg32(priv->base + STM32_CAN_ESR_OFFSET), + getreg32(priv->base + STM32_CAN_BTR_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: stm32can_dumpmbregs + * + * Description: + * Dump the contents of all CAN mailbox registers + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * msg - message + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_CAN_REGDEBUG +static void stm32can_dumpmbregs(FAR struct stm32_can_s *priv, + FAR const char *msg) +{ + if (msg) + { + ninfo("Mailbox Registers: %s\n", msg); + } + else + { + ninfo("Mailbox Registers:\n"); + } + + /* CAN mailbox registers (3 TX and 2 RX) */ + + ninfo(" TI0R: %08" PRIx32 " TDT0R: %08" PRIx32 " TDL0R: %08" + PRIx32 " TDH0R: %08" PRIx32 "\n", + getreg32(priv->base + STM32_CAN_TI0R_OFFSET), + getreg32(priv->base + STM32_CAN_TDT0R_OFFSET), + getreg32(priv->base + STM32_CAN_TDL0R_OFFSET), + getreg32(priv->base + STM32_CAN_TDH0R_OFFSET)); + + ninfo(" TI1R: %08" PRIx32 " TDT1R: %08" PRIx32 " TDL1R: %08" + PRIx32 " TDH1R: %08" PRIx32 "\n", + getreg32(priv->base + STM32_CAN_TI1R_OFFSET), + getreg32(priv->base + STM32_CAN_TDT1R_OFFSET), + getreg32(priv->base + STM32_CAN_TDL1R_OFFSET), + getreg32(priv->base + STM32_CAN_TDH1R_OFFSET)); + + ninfo(" TI2R: %08" PRIx32 " TDT2R: %08" PRIx32 " TDL2R: %08" + PRIx32 " TDH2R: %08" PRIx32 "\n", + getreg32(priv->base + STM32_CAN_TI2R_OFFSET), + getreg32(priv->base + STM32_CAN_TDT2R_OFFSET), + getreg32(priv->base + STM32_CAN_TDL2R_OFFSET), + getreg32(priv->base + STM32_CAN_TDH2R_OFFSET)); + + ninfo(" RI0R: %08" PRIx32 " RDT0R: %08" PRIx32 " RDL0R: %08" + PRIx32 " RDH0R: %08" PRIx32 "\n", + getreg32(priv->base + STM32_CAN_RI0R_OFFSET), + getreg32(priv->base + STM32_CAN_RDT0R_OFFSET), + getreg32(priv->base + STM32_CAN_RDL0R_OFFSET), + getreg32(priv->base + STM32_CAN_RDH0R_OFFSET)); + + ninfo(" RI1R: %08" PRIx32 " RDT1R: %08" PRIx32 " RDL1R: %08" + PRIx32 " RDH1R: %08" PRIx32 "\n", + getreg32(priv->base + STM32_CAN_RI1R_OFFSET), + getreg32(priv->base + STM32_CAN_RDT1R_OFFSET), + getreg32(priv->base + STM32_CAN_RDL1R_OFFSET), + getreg32(priv->base + STM32_CAN_RDH1R_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: stm32can_dumpfiltregs + * + * Description: + * Dump the contents of all CAN filter registers + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * msg - message + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_CAN_REGDEBUG +static void stm32can_dumpfiltregs(FAR struct stm32_can_s *priv, + FAR const char *msg) +{ + int i; + + if (msg) + { + ninfo("Filter Registers: %s\n", msg); + } + else + { + ninfo("Filter Registers:\n"); + } + + ninfo(" FMR: %08" PRIx32 " FM1R: %08" PRIx32 " FS1R: %08" + PRIx32 " FFA1R: %08" PRIx32 " FA1R: %08" PRIx32 "\n", + getreg32(priv->base + STM32_CAN_FMR_OFFSET), + getreg32(priv->base + STM32_CAN_FM1R_OFFSET), + getreg32(priv->base + STM32_CAN_FS1R_OFFSET), + getreg32(priv->base + STM32_CAN_FFA1R_OFFSET), + getreg32(priv->base + STM32_CAN_FA1R_OFFSET)); + + for (i = 0; i < CAN_NFILTERS; i++) + { + ninfo(" F%dR1: %08" PRIx32 " F%dR2: %08" PRIx32 "\n", + i, getreg32(priv->base + STM32_CAN_FIR_OFFSET(i, 1)), + i, getreg32(priv->base + STM32_CAN_FIR_OFFSET(i, 2))); + } +} +#endif + +/**************************************************************************** + * Name: stm32can_rx0int + * + * Description: + * Call to enable or disable RX0 interrupts. + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32can_rx0int(FAR struct stm32_can_s *priv, bool enable) +{ + uint32_t regval = 0; + + ninfo("CAN%" PRIu8 "RX0 enable: %d\n", priv->port, enable); + + /* Enable/disable the FIFO 0 message pending interrupt */ + + regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET); + if (enable) + { + regval |= CAN_IER_FMPIE0; + } + else + { + regval &= ~CAN_IER_FMPIE0; + } + + stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval); +} + +/**************************************************************************** + * Name: stm32can_rx1int + * + * Description: + * Call to enable or disable RX1 interrupts. + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32can_rx1int(FAR struct stm32_can_s *priv, bool enable) +{ + uint32_t regval = 0; + + ninfo("CAN%" PRIu8 "RX1 enable: %d\n", priv->port, enable); + + /* Enable/disable the FIFO 1 message pending interrupt */ + + regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET); + if (enable) + { + regval |= CAN_IER_FMPIE1; + } + else + { + regval &= ~CAN_IER_FMPIE1; + } + + stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval); +} + +/**************************************************************************** + * Name: stm32can_txint + * + * Description: + * Call to enable or disable TX interrupts. + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32can_txint(FAR struct stm32_can_s *priv, bool enable) +{ + uint32_t regval = 0; + + ninfo("CAN%" PRIu8 " txint enable: %d\n", priv->port, enable); + + /* Enable/disable the transmit mailbox interrupt */ + + regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET); + if (enable) + { + regval |= CAN_IER_TMEIE; + } + else + { + regval &= ~CAN_IER_TMEIE; + } + + stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval); +} + +#ifdef CONFIG_NET_CAN_ERRORS +/**************************************************************************** + * Name: stm32can_txint + * + * Description: + * Call to enable or disable CAN SCE interrupts. + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32can_errint(FAR struct stm32_can_s *priv, bool enable) +{ + uint32_t regval = 0; + + /* Enable/disable the transmit mailbox interrupt */ + + regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET); + if (enable) + { + regval |= STM32_CAN_ERRINT; + } + else + { + regval &= ~STM32_CAN_ERRINT; + } + + stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval); +} +#endif + +/**************************************************************************** + * Function: stm32can_ifup + * + * Description: + * NuttX Callback: Bring up the Ethernet interface when an IP address is + * provided + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int stm32can_ifup(struct net_driver_s *dev) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)dev->d_private; + + /* Setup CAN */ + + stm32can_setup(priv); + + /* Enable interrupts */ + + stm32can_rx0int(priv, true); + stm32can_rx1int(priv, true); + stm32can_txint(priv, true); +#ifdef CONFIG_NET_CAN_ERRORS + stm32can_errint(priv, true); +#endif + + /* Enable the interrupts at the NVIC */ + + up_enable_irq(priv->canrx[0]); + up_enable_irq(priv->canrx[1]); + up_enable_irq(priv->cantx); +#ifdef CONFIG_NET_CAN_ERRORS + up_enable_irq(priv->cansce); +#endif + + priv->bifup = true; + + priv->txdesc = (struct can_frame *)priv->tx_pool; + priv->rxdesc = (struct can_frame *)priv->rx_pool; + + priv->dev.d_buf = (uint8_t *)priv->txdesc; + + return OK; +} + +/**************************************************************************** + * Function: stm32can_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int stm32can_ifdown(struct net_driver_s *dev) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)dev->d_private; + + /* Disable CAN interrupts */ + + stm32can_shutdown(priv); + + /* Reset CAN */ + + stm32can_reset(priv); + + return OK; +} + +/**************************************************************************** + * Name: stm32can_txready + * + * Description: + * Return true if the CAN hardware can accept another TX message. + * + ****************************************************************************/ + +static bool stm32can_txready(FAR struct stm32_can_s *priv) +{ + uint32_t regval; + + /* Return true if any mailbox is available */ + + regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET); + ninfo("CAN%" PRIu8 " TSR: %08" PRIx32 "\n", priv->port, regval); + + return (stm32can_txmb0empty(regval) || stm32can_txmb1empty(regval) || + stm32can_txmb2empty(regval)); +} + +/**************************************************************************** + * Name: stm32can_transmit + * + * Description: + * Start hardware transmission. Called either from the txdone interrupt + * handling or from watchdog based polling. + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + ****************************************************************************/ + +static int stm32can_transmit(FAR struct stm32_can_s *priv) +{ + struct can_frame *frame = (struct can_frame *)priv->dev.d_buf; + FAR uint8_t *ptr; + uint32_t regval; + uint32_t tmp; + int dlc; + int txmb; + + ninfo("CAN%" PRIu8 " ID: %" PRIu32 " DLC: %" PRIu8 "\n", + priv->port, (uint32_t)frame->can_id, frame->can_dlc); + + /* Select one empty transmit mailbox */ + + regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET); + if (stm32can_txmb0empty(regval)) + { + txmb = 0; + } + else if (stm32can_txmb1empty(regval)) + { + txmb = 1; + } + else if (stm32can_txmb2empty(regval)) + { + txmb = 2; + } + else + { + canerr("ERROR: No available mailbox\n"); + return -EBUSY; + } + + /* Clear TXRQ, RTR, IDE, EXID, and STID fields */ + + regval = stm32can_getreg(priv, STM32_CAN_TIR_OFFSET(txmb)); + regval &= ~(CAN_TIR_TXRQ | CAN_TIR_RTR | CAN_TIR_IDE | + CAN_TIR_EXID_MASK | CAN_TIR_STID_MASK); + stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval); + + /* Set up the ID, standard 11-bit or extended 29-bit. */ + +#ifdef CONFIG_CAN_EXTID + regval &= ~CAN_TIR_EXID_MASK; + if (frame->can_id & CAN_EFF_FLAG) + { + DEBUGASSERT(frame->can_id < (1 << 29)); + regval |= (frame->can_id << CAN_TIR_EXID_SHIFT) | CAN_TIR_IDE; + } + else + { + DEBUGASSERT(frame->can_id < (1 << 11)); + regval |= frame->can_id << CAN_TIR_STID_SHIFT; + } + +#else + regval |= (((uint32_t) frame->can_id << CAN_TIR_STID_SHIFT) & + CAN_TIR_STID_MASK); + +#endif + +#ifdef CONFIG_CAN_USE_RTR + regval |= ((frame->can_id & CAN_RTR_FLAG) ? CAN_TIR_RTR : 0); +#endif + + stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval); + + /* Set up the DLC */ + + dlc = frame->can_dlc; + regval = stm32can_getreg(priv, STM32_CAN_TDTR_OFFSET(txmb)); + regval &= ~(CAN_TDTR_DLC_MASK | CAN_TDTR_TGT); + regval |= (uint32_t)dlc << CAN_TDTR_DLC_SHIFT; + stm32can_putreg(priv, STM32_CAN_TDTR_OFFSET(txmb), regval); + + /* Set up the data fields */ + + ptr = frame->data; + regval = 0; + + if (dlc > 0) + { + tmp = (uint32_t)*ptr++; + regval = tmp << CAN_TDLR_DATA0_SHIFT; + + if (dlc > 1) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDLR_DATA1_SHIFT; + + if (dlc > 2) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDLR_DATA2_SHIFT; + + if (dlc > 3) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDLR_DATA3_SHIFT; + } + } + } + } + + stm32can_putreg(priv, STM32_CAN_TDLR_OFFSET(txmb), regval); + + regval = 0; + if (dlc > 4) + { + tmp = (uint32_t)*ptr++; + regval = tmp << CAN_TDHR_DATA4_SHIFT; + + if (dlc > 5) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDHR_DATA5_SHIFT; + + if (dlc > 6) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDHR_DATA6_SHIFT; + + if (dlc > 7) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDHR_DATA7_SHIFT; + } + } + } + } + + stm32can_putreg(priv, STM32_CAN_TDHR_OFFSET(txmb), regval); + + /* Enable the transmit mailbox empty interrupt (may already be enabled) */ + + regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET); + regval |= CAN_IER_TMEIE; + stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval); + + /* Request transmission */ + + regval = stm32can_getreg(priv, STM32_CAN_TIR_OFFSET(txmb)); + regval |= CAN_TIR_TXRQ; /* Transmit Mailbox Request */ + stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval); + + stm32can_dumpmbregs(priv, "After send"); + return OK; +} + +/**************************************************************************** + * Function: stm32can_txpoll + * + * Description: + * The transmitter is available, check if the network has any outgoing + * packets ready to send. This is a callback from devif_poll(). + * devif_poll() may be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send timesout and the interface is reset + * 3. During normal TX polling + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + ****************************************************************************/ + +static int stm32can_txpoll(struct net_driver_s *dev) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)dev->d_private; + + /* If the polling resulted in data that should be sent out on the network, + * the field d_len is set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + if (!devif_loopback(&priv->dev)) + { + stm32can_txdone(priv); + + /* Send the packet */ + + stm32can_transmit(priv); + + /* Check if there is room in the device to hold another packet. If + * not, return a non-zero value to terminate the poll. + */ + + if (stm32can_txready(priv) == false) + { + return -EBUSY; + } + } + } + + /* If zero is returned, the polling will continue until all connections + * have been examined. + */ + + return 0; +} + +/**************************************************************************** + * Function: stm32can_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Input Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + ****************************************************************************/ + +static void stm32can_txavail_work(FAR void *arg) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)arg; + + /* Ignore the notification if the interface is not yet up */ + + net_lock(); + if (priv->bifup) + { + /* Check if there is room in the hardware to hold another outgoing + * packet. + */ + + if (stm32can_txready(priv)) + { + /* No, there is space for another transfer. Poll the network for + * new XMIT data. + */ + + devif_timer(&priv->dev, 0, stm32can_txpoll); + } + } + + net_unlock(); +} + +/**************************************************************************** + * Function: stm32can_txavail + * + * Description: + * Driver callback invoked when new TX data is available. This is a + * stimulus perform an out-of-cycle poll and, thereby, reduce the TX + * latency. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called in normal user mode + * + ****************************************************************************/ + +static int stm32can_txavail(struct net_driver_s *dev) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)dev->d_private; + + /* Is our single work structure available? It may not be if there are + * pending interrupt actions and we will have to ignore the Tx + * availability action. + */ + + if (work_available(&priv->pollwork)) + { + /* Schedule to serialize the poll on the worker thread. */ + + stm32can_txavail_work(priv); + } + + return OK; +} + +/**************************************************************************** + * Function: stm32can_ioctl + * + * Description: + * PHY ioctl command handler + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * cmd - ioctl command + * arg - Argument accompanying the command + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_IOCTL +static int stm32can_netdev_ioctl(struct net_driver_s *dev, int cmd, + unsigned long arg) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)dev->d_private; + int ret = OK; + + switch (cmd) + { + /* TODO */ + + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#endif /* CONFIG_NETDEV_IOCTL */ + +/**************************************************************************** + * Name: stm32can_rxinterrupt_work + * + * Description: + * CAN RX FIFO 0/1 interrupt handler + * + * Input Parameters: + * irq - The IRQ number of the interrupt. + * context - The register state save array at the time of the interrupt. + * rxmb - The RX mailbox number. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_rxinterrupt_work(FAR struct stm32_can_s *priv, int rxmb) +{ + FAR struct can_frame *frame = (struct can_frame *)priv->rxdesc; + uint32_t regval; + int ret = OK; + + DEBUGASSERT(priv != NULL); + + if (rxmb == 0) + { + stm32can_dumpmbregs(priv, "RX0 interrupt"); + } + else + { + stm32can_dumpmbregs(priv, "RX1 interrupt"); + } + + /* Get the CAN identifier. */ + + regval = stm32can_getreg(priv, STM32_CAN_RIR_OFFSET(rxmb)); + +#ifdef CONFIG_CAN_EXTID + if ((regval & CAN_RIR_IDE) != 0) + { + frame->can_id = (regval & CAN_RIR_EXID_MASK) >> CAN_RIR_EXID_SHIFT; + frame->can_id &= ~CAN_EFF_FLAG; + } + else + { + frame->can_id = (regval & CAN_RIR_STID_MASK) >> CAN_RIR_STID_SHIFT; + frame->can_id |= CAN_EFF_FLAG; + } +#else + if ((regval & CAN_RIR_IDE) != 0) + { + nerr("ERROR: Received message with extended identifier. Dropped\n"); + ret = -ENOSYS; + goto errout; + } + + frame->can_id = (regval & CAN_RIR_STID_MASK) >> CAN_RIR_STID_SHIFT; +#endif + + /* Extract the RTR bit */ + + if ((regval & CAN_RIR_RTR) != 0) + { + frame->can_id |= CAN_RTR_FLAG; + } + + /* Get the DLC */ + + regval = stm32can_getreg(priv, STM32_CAN_RDTR_OFFSET(rxmb)); + frame->can_dlc = (regval & CAN_RDTR_DLC_MASK) >> CAN_RDTR_DLC_SHIFT; + + /* Save the message data */ + + regval = stm32can_getreg(priv, STM32_CAN_RDLR_OFFSET(rxmb)); + frame->data[0] = (regval & CAN_RDLR_DATA0_MASK) >> CAN_RDLR_DATA0_SHIFT; + frame->data[1] = (regval & CAN_RDLR_DATA1_MASK) >> CAN_RDLR_DATA1_SHIFT; + frame->data[2] = (regval & CAN_RDLR_DATA2_MASK) >> CAN_RDLR_DATA2_SHIFT; + frame->data[3] = (regval & CAN_RDLR_DATA3_MASK) >> CAN_RDLR_DATA3_SHIFT; + + regval = stm32can_getreg(priv, STM32_CAN_RDHR_OFFSET(rxmb)); + frame->data[4] = (regval & CAN_RDHR_DATA4_MASK) >> CAN_RDHR_DATA4_SHIFT; + frame->data[5] = (regval & CAN_RDHR_DATA5_MASK) >> CAN_RDHR_DATA5_SHIFT; + frame->data[6] = (regval & CAN_RDHR_DATA6_MASK) >> CAN_RDHR_DATA6_SHIFT; + frame->data[7] = (regval & CAN_RDHR_DATA7_MASK) >> CAN_RDHR_DATA7_SHIFT; + + /* Copy the buffer pointer to priv->dev.. Set amount of data + * in priv->dev.d_len + */ + + priv->dev.d_len = sizeof(struct can_frame); + priv->dev.d_buf = (uint8_t *)frame; + + /* Send to socket interface */ + + NETDEV_RXPACKETS(&priv->dev); + + can_input(&priv->dev); + + /* Point the packet buffer back to the next Tx buffer that will be + * used during the next write. If the write queue is full, then + * this will point at an active buffer, which must not be written + * to. This is OK because devif_poll won't be called unless the + * queue is not full. + */ + + priv->dev.d_buf = (uint8_t *)priv->txdesc; + + /* Release the FIFO */ + +#ifndef CONFIG_CAN_EXTID +errout: +#endif + regval = stm32can_getreg(priv, STM32_CAN_RFR_OFFSET(rxmb)); + regval |= CAN_RFR_RFOM; + stm32can_putreg(priv, STM32_CAN_RFR_OFFSET(rxmb), regval); + + /* Re-enable CAN RX interrupts */ + + if (rxmb == 0) + { + stm32can_rx0int(priv, true); + } + else if (rxmb == 1) + { + stm32can_rx1int(priv, true); + } + else + { + DEBUGASSERT(0); + } + + return ret; +} + +/**************************************************************************** + * Name: stm32can_rx0interrupt_work + ****************************************************************************/ + +static void stm32can_rx0interrupt_work(FAR void *arg) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)arg; + stm32can_rxinterrupt_work(priv, 0); +} + +/**************************************************************************** + * Name: stm32can_rx1interrupt_work + ****************************************************************************/ + +static void stm32can_rx1interrupt_work(FAR void *arg) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)arg; + stm32can_rxinterrupt_work(priv, 1); +} + +/**************************************************************************** + * Name: stm32can_rxinterrupt + * + * Description: + * CAN RX FIFO common interrupt handler + * + ****************************************************************************/ + +static int stm32can_rxinterrupt(FAR struct stm32_can_s *priv, int rxmb) +{ + uint32_t regval = 0; + int npending = 0; + + /* Verify that a message is pending in the FIFO */ + + regval = stm32can_getreg(priv, STM32_CAN_RFR_OFFSET(rxmb)); + npending = (regval & CAN_RFR_FMP_MASK) >> CAN_RFR_FMP_SHIFT; + if (npending < 1) + { + nwarn("WARNING: No messages pending\n"); + return OK; + } + + /* Disable further CAN RX interrupts and schedule to perform the + * interrupt processing on the worker thread + */ + + if (rxmb == 0) + { + stm32can_rx0int(priv, false); + work_queue(CANWORK, &priv->irqwork, + stm32can_rx0interrupt_work, priv, 0); + } + else if (rxmb == 1) + { + stm32can_rx1int(priv, false); + work_queue(CANWORK, &priv->irqwork, + stm32can_rx1interrupt_work, priv, 0); + } + else + { + DEBUGASSERT(0); + } + + return OK; +} + +/**************************************************************************** + * Name: stm32can_rx0interrupt + * + * Description: + * CAN RX FIFO 0 interrupt handler + * + * Input Parameters: + * irq - The IRQ number of the interrupt. + * context - The register state save array at the time of the interrupt. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_rx0interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)arg; + return stm32can_rxinterrupt(priv, 0); +} + +/**************************************************************************** + * Name: stm32can_rx1interrupt + * + * Description: + * CAN RX FIFO 1 interrupt handler + * + * Input Parameters: + * irq - The IRQ number of the interrupt. + * context - The register state save array at the time of the interrupt. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_rx1interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)arg; + return stm32can_rxinterrupt(priv, 1); +} + +/**************************************************************************** + * Name: stm32can_txinterrupt + * + * Description: + * CAN TX mailbox complete interrupt handler + * + * Input Parameters: + * irq - The IRQ number of the interrupt. + * context - The register state save array at the time of the interrupt. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_txinterrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)arg; + uint32_t regval; + + DEBUGASSERT(priv != NULL); + + /* Get the transmit status */ + + regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET); + + /* Check for RQCP0: Request completed mailbox 0 */ + + if ((regval & CAN_TSR_RQCP0) != 0) + { + /* Writing '1' to RCP0 clears RCP0 and all the status bits (TXOK0, + * ALST0 and TERR0) for Mailbox 0. + */ + + stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP0); + + /* Tell the upper half that the transfer is finished. */ + + /* Disable further TX CAN interrupts. here can be no race + * condition here. + */ + + stm32can_txint(priv, false); + work_queue(CANWORK, &priv->irqwork, stm32can_txdone_work, priv, 0); + } + + /* Check for RQCP1: Request completed mailbox 1 */ + + if ((regval & CAN_TSR_RQCP1) != 0) + { + /* Writing '1' to RCP1 clears RCP1 and all the status bits (TXOK1, + * ALST1 and TERR1) for Mailbox 1. + */ + + stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP1); + + /* Tell the upper half that the transfer is finished. */ + + /* Disable further TX CAN interrupts. here can be no race + * condition here. + */ + + stm32can_txint(priv, false); + work_queue(CANWORK, &priv->irqwork, stm32can_txdone_work, priv, 0); + } + + /* Check for RQCP2: Request completed mailbox 2 */ + + if ((regval & CAN_TSR_RQCP2) != 0) + { + /* Writing '1' to RCP2 clears RCP2 and all the status bits (TXOK2, + * ALST2 and TERR2) for Mailbox 2. + */ + + stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP2); + + /* Disable further TX CAN interrupts. here can be no race + * condition here. + */ + + stm32can_txint(priv, false); + work_queue(CANWORK, &priv->irqwork, stm32can_txdone_work, priv, 0); + } + + return OK; +} + +/**************************************************************************** + * Name: stm32can_txdone_work + ****************************************************************************/ + +static void stm32can_txdone_work(FAR void *arg) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)arg; + + stm32can_txdone(priv); + + /* There should be space for a new TX in any event. Poll the network for + * new XMIT data + */ + + net_lock(); + devif_timer(&priv->dev, 0, stm32can_txpoll); + net_unlock(); +} + +/**************************************************************************** + * Name: stm32can_txdone + ****************************************************************************/ + +static void stm32can_txdone(FAR struct stm32_can_s *priv) +{ + stm32can_txint(priv, true); + + NETDEV_TXDONE(&priv->dev); +} + +#ifdef CONFIG_NET_CAN_ERRORS + +/**************************************************************************** + * Name: stm32can_sceinterrupt_work + * + * Description: + * CAN status change interrupt work + * + * Input Parameters: + * arg - reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32can_sceinterrupt_work(FAR void *arg) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)arg; + struct can_frame *frame = (struct can_frame *)priv->rxdesc; + uint32_t regval = 0; + uint16_t errbits = 0; + uint8_t data[CAN_ERR_DLC]; + + DEBUGASSERT(priv != NULL); + + /* Check Error Interrupt flag */ + + regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET); + if (regval & CAN_MSR_ERRI) + { + /* Encode error bits */ + + errbits = 0; + memset(data, 0, sizeof(data)); + + /* Get Error statur register */ + + regval = stm32can_getreg(priv, STM32_CAN_ESR_OFFSET); + + if (regval & CAN_ESR_EWGF) + { + /* Error warning flag */ + + data[1] |= (CAN_ERR_CTRL_RX_WARNING | CAN_ERR_CTRL_TX_WARNING); + errbits |= CAN_ERR_CTRL; + } + + if (regval & CAN_ESR_EPVF) + { + /* Error passive flag */ + + data[1] |= (CAN_ERR_CTRL_RX_PASSIVE | CAN_ERR_CTRL_TX_PASSIVE); + errbits |= CAN_ERR_CTRL; + } + + if (regval & CAN_ESR_BOFF) + { + /* Bus-off flag */ + + errbits |= CAN_ERR_BUSOFF; + } + + /* Last error code */ + + if (regval & CAN_ESR_LEC_MASK) + { + if (regval & CAN_ESR_STUFFERROR) + { + /* Stuff Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_STUFF; + } + else if (regval & CAN_ESR_FORMERROR) + { + /* Format Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_FORM; + } + else if (regval & CAN_ESR_ACKERROR) + { + /* Acknowledge Error */ + + errbits |= CAN_ERR_ACK; + } + else if (regval & CAN_ESR_BRECERROR) + { + /* Bit recessive Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_BIT1; + } + else if (regval & CAN_ESR_BDOMERROR) + { + /* Bit dominant Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_BIT0; + } + else if (regval & CAN_ESR_CRCERRPR) + { + /* Receive CRC Error */ + + errbits |= CAN_ERR_PROT; + data[3] |= CAN_ERR_PROT_LOC_CRCSEQ; + } + } + + /* Get transmit status register */ + + regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET); + + if (regval & CAN_TSR_ALST0 || regval & CAN_TSR_ALST1 || + regval & CAN_TSR_ALST2) + { + /* Lost arbitration Error */ + + errbits |= CAN_ERR_LOSTARB; + } + + /* Clear TSR register */ + + stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, regval); + + /* Clear ERRI flag */ + + stm32can_putreg(priv, STM32_CAN_MSR_OFFSET, CAN_MSR_ERRI); + } + + /* Report a CAN error */ + + if (errbits != 0) + { + canerr("ERROR: errbits = %08" PRIx16 "\n", errbits); + + /* Copy frame */ + + frame->can_id = errbits; + frame->can_dlc = CAN_ERR_DLC; + + memcpy(frame->data, data, CAN_ERR_DLC); + + /* Copy the buffer pointer to priv->dev.. Set amount of data + * in priv->dev.d_len + */ + + priv->dev.d_len = sizeof(struct can_frame); + priv->dev.d_buf = (uint8_t *)frame; + + /* Send to socket interface */ + + NETDEV_ERRORS(&priv->dev); + + can_input(&priv->dev); + + /* Point the packet buffer back to the next Tx buffer that will be + * used during the next write. If the write queue is full, then + * this will point at an active buffer, which must not be written + * to. This is OK because devif_poll won't be called unless the + * queue is not full. + */ + + priv->dev.d_buf = (uint8_t *)priv->txdesc; + } + + /* Re-enable CAN SCE interrupts */ + + stm32can_errint(priv, true); +} + +/**************************************************************************** + * Name: stm32can_sceinterrupt + * + * Description: + * CAN status change interrupt handler + * + * Input Parameters: + * irq - The IRQ number of the interrupt. + * context - The register state save array at the time of the interrupt. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_sceinterrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct stm32_can_s *priv = (FAR struct stm32_can_s *)arg; + + /* Disable further CAN SCE interrupts and schedule to perform the + * interrupt processing on the worker thread + */ + + stm32can_errint(priv, false); + work_queue(CANWORK, &priv->irqwork, + stm32can_sceinterrupt_work, priv, 0); + + return OK; +} +#endif + +/**************************************************************************** + * Name: stm32can_bittiming + * + * Description: + * Set the CAN bit timing register (BTR) based on the configured BAUD. + * + * "The bit timing logic monitors the serial bus-line and performs sampling + * and adjustment of the sample point by synchronizing on the start-bit edge + * and resynchronizing on the following edges. + * + * "Its operation may be explained simply by splitting nominal bit time into + * three segments as follows: + * + * 1. "Synchronization segment (SYNC_SEG): a bit change is expected to occur + * within this time segment. It has a fixed length of one time quantum + * (1 x tCAN). + * 2. "Bit segment 1 (BS1): defines the location of the sample point. It + * includes the PROP_SEG and PHASE_SEG1 of the CAN standard. Its duration + * is programmable between 1 and 16 time quanta but may be automatically + * lengthened to compensate for positive phase drifts due to differences + * in the frequency of the various nodes of the network. + * 3. "Bit segment 2 (BS2): defines the location of the transmit point. It + * represents the PHASE_SEG2 of the CAN standard. Its duration is + * programmable between 1 and 8 time quanta but may also be automatically + * shortened to compensate for negative phase drifts." + * + * Pictorially: + * + * |<----------------- NOMINAL BIT TIME ----------------->| + * |<- SYNC_SEG ->|<------ BS1 ------>|<------ BS2 ------>| + * |<---- Tq ---->|<----- Tbs1 ------>|<----- Tbs2 ------>| + * + * Where + * Tbs1 is the duration of the BS1 segment + * Tbs2 is the duration of the BS2 segment + * Tq is the "Time Quantum" + * + * Relationships: + * + * baud = 1 / bit_time + * bit_time = Tq + Tbs1 + Tbs2 + * Tbs1 = Tq * ts1 + * Tbs2 = Tq * ts2 + * Tq = brp * Tpclk1 + * baud = Fpclk1 / (brp * (1 + ts1 + ts2)) + * + * Where: + * Tpclk1 is the period of the APB1 clock (PCLK1). + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_bittiming(FAR struct stm32_can_s *priv) +{ + uint32_t tmp; + uint32_t brp; + uint32_t ts1; + uint32_t ts2; + + ninfo("CAN%" PRIu8 " PCLK1: %lu baud: %" PRIu32 "\n", + priv->port, (unsigned long) STM32_PCLK1_FREQUENCY, priv->baud); + + /* Try to get CAN_BIT_QUANTA quanta in one bit_time. + * + * bit_time = Tq*(ts1 + ts2 + 1) + * nquanta = bit_time / Tq + * nquanta = (ts1 + ts2 + 1) + * + * bit_time = brp * Tpclk1 * (ts1 + ts2 + 1) + * nquanta = bit_time / brp / Tpclk1 + * = PCLK1 / baud / brp + * brp = PCLK1 / baud / nquanta; + * + * Example: + * PCLK1 = 42,000,000 baud = 1,000,000 nquanta = 14 : brp = 3 + * PCLK1 = 42,000,000 baud = 700,000 nquanta = 14 : brp = 4 + */ + + tmp = STM32_PCLK1_FREQUENCY / priv->baud; + if (tmp < CAN_BIT_QUANTA) + { + /* At the smallest brp value (1), there are already too few bit times + * (PCLCK1 / baud) to meet our goal. brp must be one and we need + * make some reasonable guesses about ts1 and ts2. + */ + + brp = 1; + + /* In this case, we have to guess a good value for ts1 and ts2 */ + + ts1 = (tmp - 1) >> 1; + ts2 = tmp - ts1 - 1; + if (ts1 == ts2 && ts1 > 1 && ts2 < CAN_BTR_TSEG2_MAX) + { + ts1--; + ts2++; + } + } + + /* Otherwise, nquanta is CAN_BIT_QUANTA, ts1 is CONFIG_STM32_CAN_TSEG1, + * ts2 is CONFIG_STM32_CAN_TSEG2 and we calculate brp to achieve + * CAN_BIT_QUANTA quanta in the bit time + */ + + else + { + ts1 = CONFIG_STM32_CAN_TSEG1; + ts2 = CONFIG_STM32_CAN_TSEG2; + brp = (tmp + (CAN_BIT_QUANTA / 2)) / CAN_BIT_QUANTA; + DEBUGASSERT(brp >= 1 && brp <= CAN_BTR_BRP_MAX); + } + + ninfo("TS1: %" PRIu32 " TS2: %" PRIu32 " BRP: %" PRIu32 "\n", + ts1, ts2, brp); + + /* Configure bit timing. This also does the following, less obvious + * things. Unless loopback mode is enabled, it: + * + * - Disables silent mode. + * - Disables loopback mode. + * + * NOTE that for the time being, SJW is set to 1 just because I don't + * know any better. + */ + + tmp = ((brp - 1) << CAN_BTR_BRP_SHIFT) | ((ts1 - 1) << CAN_BTR_TS1_SHIFT) | + ((ts2 - 1) << CAN_BTR_TS2_SHIFT) | ((1 - 1) << CAN_BTR_SJW_SHIFT); +#ifdef CONFIG_CAN_LOOPBACK + /* tmp |= (CAN_BTR_LBKM | CAN_BTR_SILM); */ + + tmp |= CAN_BTR_LBKM; +#endif + + stm32can_putreg(priv, STM32_CAN_BTR_OFFSET, tmp); + return OK; +} + +/**************************************************************************** + * Name: stm32can_setup + ****************************************************************************/ + +static int stm32can_setup(FAR struct stm32_can_s *priv) +{ + int ret; + +#ifdef CONFIG_NET_CAN_ERRORS + ninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8 + " TX irq: %" PRIu8 " SCE irq: %" PRIu8 "\n", + priv->port, priv->canrx[0], priv->canrx[1], priv->cantx, + priv->cansce); +#else + ninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8 + " TX irq: %" PRIu8 "\n", + priv->port, priv->canrx[0], priv->canrx[1], priv->cantx); +#endif + + /* CAN cell initialization */ + + ret = stm32can_cellinit(priv); + if (ret < 0) + { + nerr("ERROR: CAN%" PRId8 " cell initialization failed: %d\n", + priv->port, ret); + return ret; + } + + stm32can_dumpctrlregs(priv, "After cell initialization"); + stm32can_dumpmbregs(priv, NULL); + + /* CAN filter initialization */ + + ret = stm32can_filterinit(priv); + if (ret < 0) + { + nerr("ERROR: CAN%" PRIu8 " filter initialization failed: %d\n", + priv->port, ret); + return ret; + } + + stm32can_dumpfiltregs(priv, "After filter initialization"); + + /* Attach the CAN RX FIFO 0/1 interrupts and TX interrupts. + * The others are not used. + */ + + ret = irq_attach(priv->canrx[0], stm32can_rx0interrupt, priv); + if (ret < 0) + { + nerr("ERROR: Failed to attach CAN%" PRIu8 " RX0 IRQ (%" PRIu8 ")", + priv->port, priv->canrx[0]); + return ret; + } + + ret = irq_attach(priv->canrx[1], stm32can_rx1interrupt, priv); + if (ret < 0) + { + nerr("ERROR: Failed to attach CAN%" PRIu8 " RX1 IRQ (%" PRIu8 ")", + priv->port, priv->canrx[1]); + return ret; + } + + ret = irq_attach(priv->cantx, stm32can_txinterrupt, priv); + if (ret < 0) + { + nerr("ERROR: Failed to attach CAN%" PRIu8 " TX IRQ (%" PRIu8 ")", + priv->port, priv->cantx); + return ret; + } + +#ifdef CONFIG_NET_CAN_ERRORS + ret = irq_attach(priv->cansce, stm32can_sceinterrupt, priv); + if (ret < 0) + { + nerr("ERROR: Failed to attach CAN%" PRIu8 " SCE IRQ (%" PRIu8 ")", + priv->port, priv->cansce); + return ret; + } +#endif + + return OK; +} + +/**************************************************************************** + * Name: stm32can_shutdown + ****************************************************************************/ + +static void stm32can_shutdown(FAR struct stm32_can_s *priv) +{ + ninfo("CAN%" PRIu8 "\n", priv->port); + + /* Disable the RX FIFO 0/1 and TX interrupts */ + + up_disable_irq(priv->canrx[0]); + up_disable_irq(priv->canrx[1]); + up_disable_irq(priv->cantx); +#ifdef CONFIG_NET_CAN_ERRORS + up_disable_irq(priv->cansce); +#endif + + /* Detach the RX FIFO 0/1 and TX interrupts */ + + irq_detach(priv->canrx[0]); + irq_detach(priv->canrx[1]); + irq_detach(priv->cantx); +#ifdef CONFIG_NET_CAN_ERRORS + irq_detach(priv->cansce); +#endif +} + +/**************************************************************************** + * Name: stm32can_reset + * + * Description: + * Put the CAN device in the non-operational, reset state + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void stm32can_reset(FAR struct stm32_can_s *priv) +{ + uint32_t regval; + uint32_t regbit = 0; + irqstate_t flags; + + ninfo("CAN%" PRIu8 "\n", priv->port); + + /* Get the bits in the AHB1RSTR register needed to reset this CAN device */ + +#ifdef CONFIG_STM32_CAN1 + if (priv->port == 1) + { + regbit = RCC_APB1RSTR_CAN1RST; + } + else +#endif +#ifdef CONFIG_STM32_CAN2 + if (priv->port == 2) + { + regbit = RCC_APB1RSTR_CAN2RST; + } + else +#endif + { + nerr("ERROR: Unsupported port %d\n", priv->port); + return; + } + + /* Disable interrupts momentarily to stop any ongoing CAN event processing + * and to prevent any concurrent access to the AHB1RSTR register. + */ + + flags = enter_critical_section(); + + /* Reset the CAN */ + + regval = getreg32(STM32_RCC_APB1RSTR); + regval |= regbit; + putreg32(regval, STM32_RCC_APB1RSTR); + + regval &= ~regbit; + putreg32(regval, STM32_RCC_APB1RSTR); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: stm32can_enterinitmode + * + * Description: + * Put the CAN cell in Initialization mode. This only disconnects the CAN + * peripheral, no registers are changed. The initialization mode is + * required to change the baud rate. + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32can_enterinitmode(FAR struct stm32_can_s *priv) +{ + uint32_t regval; + volatile uint32_t timeout; + + ninfo("CAN%" PRIu8 "\n", priv->port); + + /* Enter initialization mode */ + + regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET); + regval |= CAN_MCR_INRQ; + stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval); + + /* Wait until initialization mode is acknowledged */ + + for (timeout = INAK_TIMEOUT; timeout > 0; timeout--) + { + regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET); + if ((regval & CAN_MSR_INAK) != 0) + { + /* We are in initialization mode */ + + break; + } + } + + /* Check for a timeout */ + + if (timeout < 1) + { + nerr("ERROR: Timed out waiting to enter initialization mode\n"); + return -ETIMEDOUT; + } + + return OK; +} + +/**************************************************************************** + * Name: stm32can_exitinitmode + * + * Description: + * Put the CAN cell out of the Initialization mode (to Normal mode) + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32can_exitinitmode(FAR struct stm32_can_s *priv) +{ + uint32_t regval; + volatile uint32_t timeout; + + /* Exit Initialization mode, enter Normal mode */ + + regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET); + regval &= ~CAN_MCR_INRQ; + stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval); + + /* Wait until the initialization mode exit is acknowledged */ + + for (timeout = INAK_TIMEOUT; timeout > 0; timeout--) + { + regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET); + if ((regval & CAN_MSR_INAK) == 0) + { + /* We are out of initialization mode */ + + break; + } + } + + /* Check for a timeout */ + + if (timeout < 1) + { + nerr("ERROR: Timed out waiting to exit initialization mode: %08" + PRIx32 "\n", regval); + return -ETIMEDOUT; + } + + return OK; +} + +/**************************************************************************** + * Name: stm32can_cellinit + * + * Description: + * CAN cell initialization + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32can_cellinit(FAR struct stm32_can_s *priv) +{ + uint32_t regval; + int ret; + + ninfo("CAN%" PRIu8 "\n", priv->port); + + /* Exit from sleep mode */ + + regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET); + regval &= ~CAN_MCR_SLEEP; + stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval); + + ret = stm32can_enterinitmode(priv); + if (ret != 0) + { + return ret; + } + + /* Disable the following modes: + * + * - Time triggered communication mode + * - Automatic bus-off management + * - Automatic wake-up mode + * - No automatic retransmission + * - Receive FIFO locked mode + * + * Enable: + * + * - Transmit FIFO priority + */ + + regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET); + regval &= ~(CAN_MCR_RFLM | CAN_MCR_NART | CAN_MCR_AWUM | + CAN_MCR_ABOM | CAN_MCR_TTCM); + regval |= CAN_MCR_TXFP; + stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval); + + /* Configure bit timing. */ + + ret = stm32can_bittiming(priv); + if (ret < 0) + { + nerr("ERROR: Failed to set bit timing: %d\n", ret); + return ret; + } + + return stm32can_exitinitmode(priv); +} + +/**************************************************************************** + * Name: stm32can_filterinit + * + * Description: + * CAN filter initialization. CAN filters are not currently used by this + * driver. The CAN filters can be configured in a different way: + * + * 1. As a match of specific IDs in a list (IdList mode), or as + * 2. And ID and a mask (IdMask mode). + * + * Filters can also be configured as: + * + * 3. 16- or 32-bit. The advantage of 16-bit filters is that you get + * more filters; The advantage of 32-bit filters is that you get + * finer control of the filtering. + * + * One filter is set up for each CAN. The filter resources are shared + * between the two CAN modules: CAN1 uses only filter 0 (but reserves + * 0 through CAN_NFILTERS/2-1); CAN2 uses only filter CAN_NFILTERS/2 + * (but reserves CAN_NFILTERS/2 through CAN_NFILTERS-1). + * + * 32-bit IdMask mode is configured. However, both the ID and the MASK + * are set to zero thus suppressing all filtering because anything masked + * with zero matches zero. + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32can_filterinit(FAR struct stm32_can_s *priv) +{ + uint32_t regval; + uint32_t bitmask; + + ninfo("CAN%" PRIu8 " filter: %" PRIu8 "\n", priv->port, priv->filter); + + /* Get the bitmask associated with the filter used by this CAN block */ + + bitmask = (uint32_t)1 << priv->filter; + + /* Enter filter initialization mode */ + + regval = stm32can_getfreg(priv, STM32_CAN_FMR_OFFSET); + regval |= CAN_FMR_FINIT; + stm32can_putfreg(priv, STM32_CAN_FMR_OFFSET, regval); + + /* Assign half the filters to CAN1, half to CAN2 */ + +#if defined(CONFIG_STM32_CONNECTIVITYLINE) || \ + defined(CONFIG_STM32_STM32F20XX) || \ + defined(CONFIG_STM32_STM32F4XXX) + regval = stm32can_getfreg(priv, STM32_CAN_FMR_OFFSET); + regval &= CAN_FMR_CAN2SB_MASK; + regval |= (CAN_NFILTERS / 2) << CAN_FMR_CAN2SB_SHIFT; + stm32can_putfreg(priv, STM32_CAN_FMR_OFFSET, regval); +#endif + + /* Disable the filter */ + + regval = stm32can_getfreg(priv, STM32_CAN_FA1R_OFFSET); + regval &= ~bitmask; + stm32can_putfreg(priv, STM32_CAN_FA1R_OFFSET, regval); + + /* Select the 32-bit scale for the filter */ + + regval = stm32can_getfreg(priv, STM32_CAN_FS1R_OFFSET); + regval |= bitmask; + stm32can_putfreg(priv, STM32_CAN_FS1R_OFFSET, regval); + + /* There are 14 or 28 filter banks (depending) on the device. + * Each filter bank is composed of two 32-bit registers, CAN_FiR: + */ + + stm32can_putfreg(priv, STM32_CAN_FIR_OFFSET(priv->filter, 1), 0); + stm32can_putfreg(priv, STM32_CAN_FIR_OFFSET(priv->filter, 2), 0); + + /* Set Id/Mask mode for the filter */ + + regval = stm32can_getfreg(priv, STM32_CAN_FM1R_OFFSET); + regval &= ~bitmask; + stm32can_putfreg(priv, STM32_CAN_FM1R_OFFSET, regval); + + /* Assign FIFO 0 for the filter */ + + regval = stm32can_getfreg(priv, STM32_CAN_FFA1R_OFFSET); + regval &= ~bitmask; + stm32can_putfreg(priv, STM32_CAN_FFA1R_OFFSET, regval); + + /* Enable the filter */ + + regval = stm32can_getfreg(priv, STM32_CAN_FA1R_OFFSET); + regval |= bitmask; + stm32can_putfreg(priv, STM32_CAN_FA1R_OFFSET, regval); + + /* Exit filter initialization mode */ + + regval = stm32can_getfreg(priv, STM32_CAN_FMR_OFFSET); + regval &= ~CAN_FMR_FINIT; + stm32can_putfreg(priv, STM32_CAN_FMR_OFFSET, regval); + return OK; +} + +/**************************************************************************** + * Name: stm32can_txmb0empty + * + * Input Parameters: + * tsr_regval - value of CAN transmit status register + * + * Returned Value: + * Returns true if mailbox 0 is empty and can be used for sending. + * + ****************************************************************************/ + +static bool stm32can_txmb0empty(uint32_t tsr_regval) +{ + return (tsr_regval & CAN_TSR_TME0) != 0 && + (tsr_regval & CAN_TSR_RQCP0) == 0; +} + +/**************************************************************************** + * Name: stm32can_txmb1empty + * + * Input Parameters: + * tsr_regval - value of CAN transmit status register + * + * Returned Value: + * Returns true if mailbox 1 is empty and can be used for sending. + * + ****************************************************************************/ + +static bool stm32can_txmb1empty(uint32_t tsr_regval) +{ + return (tsr_regval & CAN_TSR_TME1) != 0 && + (tsr_regval & CAN_TSR_RQCP1) == 0; +} + +/**************************************************************************** + * Name: stm32can_txmb2empty + * + * Input Parameters: + * tsr_regval - value of CAN transmit status register + * + * Returned Value: + * Returns true if mailbox 2 is empty and can be used for sending. + * + ****************************************************************************/ + +static bool stm32can_txmb2empty(uint32_t tsr_regval) +{ + return (tsr_regval & CAN_TSR_TME2) != 0 && + (tsr_regval & CAN_TSR_RQCP2) == 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_cansockinitialize + * + * Description: + * Initialize the selected CAN port as CAN socket interface + * + * Input Parameters: + * Port number (for hardware that has multiple CAN interfaces) + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +int stm32_cansockinitialize(int port) +{ + struct stm32_can_s *priv = NULL; + int ret = OK; + + ninfo("CAN%" PRIu8 "\n", port); + + /* NOTE: Peripherical clocking for CAN1 and/or CAN2 was already provided + * by stm32_clockconfig() early in the reset sequence. + */ + +#ifdef CONFIG_STM32_CAN1 + if (port == 1) + { + /* Select the CAN1 device structure */ + + priv = &g_can1priv; + + /* Configure CAN1 pins. The ambiguous settings in the stm32*_pinmap.h + * file must have been disambiguated in the board.h file. + */ + + stm32_configgpio(GPIO_CAN1_RX); + stm32_configgpio(GPIO_CAN1_TX); + } + else +#endif +#ifdef CONFIG_STM32_CAN2 + if (port == 2) + { + /* Select the CAN2 device structure */ + + priv = &g_can2priv; + + /* Configure CAN2 pins. The ambiguous settings in the stm32*_pinmap.h + * file must have been disambiguated in the board.h file. + */ + + stm32_configgpio(GPIO_CAN2_RX); + stm32_configgpio(GPIO_CAN2_TX); + } + else +#endif + { + nerr("ERROR: Unsupported port %d\n", port); + ret = -EINVAL; + goto errout; + } + + /* Initialize the driver structure */ + + priv->dev.d_ifup = stm32can_ifup; + priv->dev.d_ifdown = stm32can_ifdown; + priv->dev.d_txavail = stm32can_txavail; +#ifdef CONFIG_NETDEV_IOCTL + priv->dev.d_ioctl = stm32can_netdev_ioctl; +#endif + priv->dev.d_private = priv; + + /* Put the interface in the down state. This usually amounts to resetting + * the device and/or calling stm32can_ifdown(). + */ + + ninfo("callbacks done\n"); + + stm32can_ifdown(&priv->dev); + + /* Register the device with the OS so that socket IOCTLs can be performed */ + + ret = netdev_register(&priv->dev, NET_LL_CAN); + +errout: + return ret; +}