6.1 general settings
6.2 local loopback of sent frames
6.3 CAN controller hardware filters
- 6.4 currently supported CAN hardware
- 6.5 todo
+ 6.4 The virtual CAN driver (vcan)
+ 6.5 currently supported CAN hardware
+ 6.6 todo
7 Credits
The Linux network devices (by default) just can handle the
transmission and reception of media dependent frames. Due to the
- arbritration on the CAN bus the transmission of a low prio CAN-ID
+ arbitration on the CAN bus the transmission of a low prio CAN-ID
may be delayed by the reception of a high prio CAN frame. To
reflect the correct* traffic on the node the loopback of the sent
data has to be performed right after a successful transmission. If
- stats_timer: To calculate the Socket CAN core statistics
(e.g. current/maximum frames per second) this 1 second timer is
invoked at can.ko module start time by default. This timer can be
- disabled by using stattimer=0 on the module comandline.
+ disabled by using stattimer=0 on the module commandline.
- debug: (removed since SocketCAN SVN r546)
@133MHz with four SJA1000 CAN controllers from 2002 under heavy bus
load without any problems ...
- 6.4 currently supported CAN hardware (September 2007)
+ 6.4 The virtual CAN driver (vcan)
+
+ Similar to the network loopback devices, vcan offers a virtual local
+ CAN interface. A full qualified address on CAN consists of
+
+ - a unique CAN Identifier (CAN ID)
+ - the CAN bus this CAN ID is transmitted on (e.g. can0)
+
+ so in common use cases more than one virtual CAN interface is needed.
+
+ The virtual CAN interfaces allow the transmission and reception of CAN
+ frames without real CAN controller hardware. Virtual CAN network
+ devices are usually named 'vcanX', like vcan0 vcan1 vcan2 ...
+ When compiled as a module the virtual CAN driver module is called vcan.ko
+
+ Since Linux Kernel version 2.6.24 the vcan driver supports the Kernel
+ netlink interface to create vcan network devices. The creation and
+ removal of vcan network devices can be managed with the ip(8) tool:
+
+ - Create a virtual CAN network interface:
+ ip link add type vcan
+
+ - Create a virtual CAN network interface with a specific name 'vcan42':
+ ip link add dev vcan42 type vcan
+
+ - Remove a (virtual CAN) network interface 'vcan42':
+ ip link del vcan42
+
+ The tool 'vcan' from the SocketCAN SVN repository on BerliOS is obsolete.
+
+ Virtual CAN network device creation in older Kernels:
+ In Linux Kernel versions < 2.6.24 the vcan driver creates 4 vcan
+ netdevices at module load time by default. This value can be changed
+ with the module parameter 'numdev'. E.g. 'modprobe vcan numdev=8'
+
+ 6.5 currently supported CAN hardware
On the project website http://developer.berlios.de/projects/socketcan
there are different drivers available:
Please check the Mailing Lists on the berlios OSS project website.
- 6.5 todo (September 2007)
+ 6.6 todo
The configuration interface for CAN network drivers is still an open
issue that has not been finalized in the socketcan project. Also the
config CAN_VCAN
tristate "Virtual Local CAN Interface (vcan)"
- depends on CAN
+ depends on CAN
default N
- ---help---
+ ---help---
Similar to the network loopback devices, vcan offers a
virtual local CAN interface.
This driver can also be built as a module. If so, the module
will be called vcan.
+config CAN_SLCAN
+ tristate "Serial / USB serial CAN Adaptors (slcan)"
+ depends on CAN && EXPERIMENTAL
+ default N
+ ---help---
+ CAN driver for several 'low cost' CAN interfaces that are attached
+ via serial lines or via USB-to-serial adapters using the LAWICEL
+ ASCII protocol. The driver implements the tty linediscipline N_SLCAN.
+
+ This driver can also be built as a module. If so, the module
+ will be called slcan.
+
+config CAN_OLD_DRIVERS
+ tristate "Prompt for old CAN drivers (e.g. no sysfs support)"
+ depends on CAN
+ default N
+ ---help---
+ The old drivers do not support sysfs nor proper platform device
+ support. Some of the old drivers might only be configured by
+ module commandline options.
+
+if CAN_OLD_DRIVERS
+source "drivers/net/can/old/Kconfig"
+endif
+
+config CAN_DEV
+ tristate "Prompt for platform CAN drivers with sysfs support"
+ depends on CAN
+ default Y
+ ---help---
+ Enables the common framework for platform CAN drivers with sysfs
+ support. This is the standard library for CAN drivers.
+ If unsure, say Y.
+
+config CAN_SJA1000
+ depends on CAN_DEV
+ tristate "Philips SJA1000"
+ ---help---
+ The SJA1000 is one of the top CAN controllers out there. As it
+ has a multiplexed interface it fits directly to 8051
+ microcontrollers or into the PC I/O port space. The SJA1000
+ is a full CAN controller, with shadow registers for RX and TX.
+ It can send and receive any kinds of CAN frames (SFF/EFF/RTR)
+ with a single (simple) filter setup.
+
+ This driver will use the new device interface.
+
+config CAN_SJA1000_PLATFORM
+ depends on CAN_SJA1000
+ tristate "generic Platform Bus based SJA1000 driver"
+ ---help---
+ This driver adds support for the SJA1000 chips connected to
+ the "platform bus" (Linux abstraction for directly to the
+ processor attached devices). Which can be found on various
+ boards from Phytec (http://www.phytec.de) like the PCM027,
+ PCM038.
+
+config CAN_EMS_PCI
+ tristate "EMS CPC-PCI and CPC-PCIe Card"
+ depends on PCI && CAN_SJA1000
+ help
+
+ This driver is for the one or two channel CPC-PCI and CPC-PCIe
+ cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
+
+config CAN_EMS_PCMCIA
+ tristate "EMS CPC-CARD Card"
+ depends on PCMCIA && CAN_SJA1000
+ help
+
+ This driver is for the one or two channel CPC-CARD cards from
+ EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
+
+config CAN_IXXAT_PCI
+ tristate "IXXAT PCI Card"
+ depends on PCI && CAN_SJA1000
+ help
+
+ This driver is for the IXXAT PC-I 04/PCI card (1 or 2 channel)
+ from the IXXAT Automation GmbH (http://www.ixxat.de).
+
+config CAN_PEAK_PCI
+ tristate "PEAK PCAN PCI Card"
+ depends on PCI && CAN_SJA1000
+ help
+
+ This driver is for the PCAN PCI, the PC-PCI CAN plug-in card (1 or
+ 2 channel) from PEAK Systems (http://www.peak-system.com).
+
+config CAN_PIPCAN
+ depends on CAN_SJA1000
+ tristate "MPL PIPCAN CAN module driver (SJA1000)"
+ help
+ This driver adds support for the PIPCAN module used on some SBC boards
+ from MPL AG (http://www.mpl.ch).
+
+config CAN_KVASER_PCI
+ tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards"
+ depends on PCI && CAN_SJA1000
+ help
+
+ This driver is for the the PCIcanx and PCIcan cards (1, 2 or
+ 4 channel) from Kvaser (http://www.kvaser.com).
+
+config CAN_SOFTING
+ tristate "Softing Gmbh CAN generic support"
+ depends on CAN_DEV
+ ---help---
+ generic softing CAN cards
+
+config CAN_SOFTING_CS
+ tristate "Softing CAN pcmcia cards"
+ depends on CAN_SOFTING && PCMCIA
+
+config CAN_MSCAN
+ depends on CAN_DEV && (PPC || M68K || M68KNOMMU)
+ tristate "Support for a Freescale MSCAN based chips"
+ ---help---
+ The Motorola Scalable Controller Area Network (MSCAN) definition
+ is based on the MSCAN12 definition which is the specific
+ implementation of the Motorola Scalable CAN concept targeted for
+ the Motorola MC68HC12 Microcontroller Family.
+
+config CAN_MPC52XX
+ tristate "Freescale MPC5200 onboard CAN controller"
+ depends on CAN_MSCAN && (PPC_MPC52xx || PPC_52xx)
+ default LITE5200
+ ---help---
+ If you say yes here you get support for Freescale MPC5200
+ onboard dualCAN controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called mpc52xx_can.
+
+config CAN_MCP251X
+ tristate "Microchip MCP251x SPI CAN controllers"
+ depends on CAN_DEV && SPI
+ ---help---
+ Driver for the Microchip MCP251x SPI CAN controllers.
+
config CAN_DEBUG_DEVICES
bool "CAN devices debugging messages"
depends on CAN
-#
-# Makefile for the Linux Controller Area Network drivers.
-#
-
obj-$(CONFIG_CAN_VCAN) += vcan.o
+obj-$(CONFIG_CAN_SLCAN) += slcan.o
+
+obj-$(CONFIG_CAN_DEV) += can-dev.o
+can-dev-y := dev.o sysfs.o
+
+obj-$(CONFIG_CAN_SJA1000) += sja1000/
+obj-$(CONFIG_CAN_SOFTING) += softing/
+obj-$(CONFIG_CAN_MSCAN) += mscan/
+obj-$(CONFIG_CAN_SJA1000_OLD) += old/sja1000/
+obj-$(CONFIG_CAN_I82527_OLD) += old/i82527/
+obj-$(CONFIG_CAN_MSCAN_OLD) += old/mscan/
+obj-$(CONFIG_CAN_CCAN_OLD) += old/ccan/
+obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
--- /dev/null
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
+ * Copyright (C) 2006 Andrey Volkov, Varma Electronics
+ * Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+#include <net/rtnetlink.h>
+#endif
+
+#include "sysfs.h"
+
+#define MOD_DESC "CAN netdevice library"
+
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+
+#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
+
+/*
+ * Bit-timing calculation derived from:
+ *
+ * Code based on LinCAN sources and H8S2638 project
+ * Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz
+ * Copyright 2005 Stanislav Marek
+ * email: pisa@cmp.felk.cvut.cz
+ */
+static int can_update_spt(const struct can_bittiming_const *btc,
+ int sampl_pt, int tseg, int *tseg1, int *tseg2)
+{
+ *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000;
+ if (*tseg2 < btc->tseg2_min)
+ *tseg2 = btc->tseg2_min;
+ if (*tseg2 > btc->tseg2_max)
+ *tseg2 = btc->tseg2_max;
+ *tseg1 = tseg - *tseg2;
+ if (*tseg1 > btc->tseg1_max) {
+ *tseg1 = btc->tseg1_max;
+ *tseg2 = tseg - *tseg1;
+ }
+ return 1000 * (tseg + 1 - *tseg2) / (tseg + 1);
+}
+
+static int can_calc_bittiming(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ const struct can_bittiming_const *btc = priv->bittiming_const;
+ long rate, best_rate = 0;
+ long best_error = 1000000000, error = 0;
+ int best_tseg = 0, best_brp = 0, brp = 0;
+ int tsegall, tseg = 0, tseg1 = 0, tseg2 = 0;
+ int spt_error = 1000, spt = 0, sampl_pt;
+ uint64_t v64;
+
+ if (!priv->bittiming_const)
+ return -ENOTSUPP;
+
+ /* Use CIA recommended sample points */
+ if (bt->sample_point) {
+ sampl_pt = bt->sample_point;
+ } else {
+ if (bt->bitrate > 800000)
+ sampl_pt = 750;
+ else if (bt->bitrate > 500000)
+ sampl_pt = 800;
+ else
+ sampl_pt = 875;
+ }
+
+ /* tseg even = round down, odd = round up */
+ for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
+ tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) {
+ tsegall = 1 + tseg / 2;
+ /* Compute all possible tseg choices (tseg=tseg1+tseg2) */
+ brp = bt->clock / (tsegall * bt->bitrate) + tseg % 2;
+ /* chose brp step which is possible in system */
+ brp = (brp / btc->brp_inc) * btc->brp_inc;
+ if ((brp < btc->brp_min) || (brp > btc->brp_max))
+ continue;
+ rate = bt->clock / (brp * tsegall);
+ error = bt->bitrate - rate;
+ /* tseg brp biterror */
+ if (error < 0)
+ error = -error;
+ if (error > best_error)
+ continue;
+ best_error = error;
+ if (error == 0) {
+ spt = can_update_spt(btc, sampl_pt, tseg / 2,
+ &tseg1, &tseg2);
+ error = sampl_pt - spt;
+ if (error < 0)
+ error = -error;
+ if (error > spt_error)
+ continue;
+ spt_error = error;
+ }
+ best_tseg = tseg / 2;
+ best_brp = brp;
+ best_rate = rate;
+ if (error == 0)
+ break;
+ }
+
+ if (best_error) {
+ /* Error in one-tenth of a percent */
+ error = (best_error * 1000) / bt->bitrate;
+ if (error > CAN_CALC_MAX_ERROR) {
+ dev_err(ND2D(dev), "bitrate error %ld.%ld%% too high\n",
+ error / 10, error % 10);
+ return -EDOM;
+ } else {
+ dev_warn(ND2D(dev), "bitrate error %ld.%ld%%\n",
+ error / 10, error % 10);
+ }
+ }
+
+ spt = can_update_spt(btc, sampl_pt, best_tseg, &tseg1, &tseg2);
+
+ v64 = (u64)best_brp * 1000000000UL;
+ do_div(v64, bt->clock);
+ bt->tq = (u32)v64;
+ bt->prop_seg = tseg1 / 2;
+ bt->phase_seg1 = tseg1 - bt->prop_seg;
+ bt->phase_seg2 = tseg2;
+ bt->sjw = 1;
+ bt->brp = best_brp;
+
+ return 0;
+}
+
+int can_sample_point(struct can_bittiming *bt)
+{
+ return ((bt->prop_seg + bt->phase_seg1 + 1) * 1000) /
+ (bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1);
+}
+
+int can_fixup_bittiming(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ const struct can_bittiming_const *btc = priv->bittiming_const;
+ int tseg1, alltseg;
+ u32 bitrate;
+ u64 brp64;
+
+ if (!priv->bittiming_const)
+ return -ENOTSUPP;
+
+ tseg1 = bt->prop_seg + bt->phase_seg1;
+ if (bt->sjw > btc->sjw_max ||
+ tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max ||
+ bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max)
+ return -EINVAL;
+
+ brp64 = (u64)bt->clock * (u64)bt->tq;
+ if (btc->brp_inc > 1)
+ do_div(brp64, btc->brp_inc);
+ brp64 += 500000000UL - 1;
+ do_div(brp64, 1000000000UL); /* the practicable BRP */
+ if (btc->brp_inc > 1)
+ brp64 *= btc->brp_inc;
+ bt->brp = (u32)brp64;
+
+ if (bt->brp < btc->brp_min || bt->brp > btc->brp_max)
+ return -EINVAL;
+
+ alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1;
+ bitrate = bt->clock / (bt->brp * alltseg);
+ bt->bitrate = bitrate;
+
+ return 0;
+}
+
+int can_set_bittiming(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ int err;
+
+ /* Check if bit-timing parameters have been pre-defined */
+ if (!priv->bittiming.tq && !priv->bittiming.bitrate)
+ return -EINVAL;
+
+ /* Check if the CAN device has bit-timing parameters */
+ if (priv->bittiming_const) {
+
+ /* Check if bit-timing parameters have already been set */
+ if (priv->bittiming.tq && priv->bittiming.bitrate)
+ return 0;
+
+ /* Non-expert mode? Check if the bitrate has been pre-defined */
+ if (!priv->bittiming.tq)
+ /* Determine bit-timing parameters */
+ err = can_calc_bittiming(dev);
+ else
+ /* Check bit-timing params and calculate proper brp */
+ err = can_fixup_bittiming(dev);
+ if (err)
+ return err;
+ }
+
+ if (priv->do_set_bittiming) {
+ /* Finally, set the bit-timing registers */
+ err = priv->do_set_bittiming(dev);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(can_set_bittiming);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+struct net_device_stats *can_get_stats(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ return &priv->net_stats;
+}
+EXPORT_SYMBOL(can_get_stats);
+#endif
+
+static void can_setup(struct net_device *dev)
+{
+ dev->type = ARPHRD_CAN;
+ dev->mtu = sizeof(struct can_frame);
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ /* New-style flags. */
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_NO_CSUM;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ dev->get_stats = can_get_stats;
+#endif
+}
+
+/*
+ * Function alloc_candev
+ * Allocates and sets up an CAN device
+ */
+struct net_device *alloc_candev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct can_priv *priv;
+
+ dev = alloc_netdev(sizeof_priv, "can%d", can_setup);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+
+ priv->state = CAN_STATE_STOPPED;
+ spin_lock_init(&priv->irq_lock);
+
+ init_timer(&priv->timer);
+ priv->timer.expires = 0;
+
+ return dev;
+}
+EXPORT_SYMBOL(alloc_candev);
+
+void free_candev(struct net_device *dev)
+{
+ free_netdev(dev);
+}
+EXPORT_SYMBOL(free_candev);
+
+/*
+ * Local echo of CAN messages
+ *
+ * CAN network devices *should* support a local echo functionality
+ * (see Documentation/networking/can.txt). To test the handling of CAN
+ * interfaces that do not support the local echo both driver types are
+ * implemented. In the case that the driver does not support the echo
+ * the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core
+ * to perform the echo as a fallback solution.
+ */
+
+void can_flush_echo_skb(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+#ifdef FIXME
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+#endif
+ int i;
+
+ for (i = 0; i < CAN_ECHO_SKB_MAX; i++) {
+ if (priv->echo_skb[i]) {
+ kfree_skb(priv->echo_skb[i]);
+ priv->echo_skb[i] = NULL;
+#ifdef FIXME
+ stats->tx_dropped++;
+ stats->tx_aborted_errors++;
+#endif
+ }
+ }
+}
+
+void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set flag whether this packet has to be looped back */
+ if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK) {
+ kfree_skb(skb);
+ return;
+ }
+
+ if (!priv->echo_skb[idx]) {
+ struct sock *srcsk = skb->sk;
+
+ if (atomic_read(&skb->users) != 1) {
+ struct sk_buff *old_skb = skb;
+
+ skb = skb_clone(old_skb, GFP_ATOMIC);
+ kfree_skb(old_skb);
+ if (!skb)
+ return;
+ } else
+ skb_orphan(skb);
+
+ skb->sk = srcsk;
+
+ /* make settings for echo to reduce code in irq context */
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->dev = dev;
+
+ /* save this skb for tx interrupt echo handling */
+ priv->echo_skb[idx] = skb;
+ } else {
+ /* locking problem with netif_stop_queue() ?? */
+ printk(KERN_ERR "%s: %s: BUG! echo_skb is occupied!\n",
+ dev->name, __func__);
+ kfree_skb(skb);
+ }
+}
+EXPORT_SYMBOL(can_put_echo_skb);
+
+void can_get_echo_skb(struct net_device *dev, int idx)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if ((dev->flags & IFF_ECHO) && priv->echo_skb[idx]) {
+ netif_rx(priv->echo_skb[idx]);
+ priv->echo_skb[idx] = NULL;
+ }
+}
+EXPORT_SYMBOL(can_get_echo_skb);
+
+/*
+ * CAN bus-off handling
+ * FIXME: we need some synchronization
+ */
+int can_restart_now(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ struct sk_buff *skb;
+ struct can_frame *cf;
+ int err;
+
+ if (netif_carrier_ok(dev))
+ netif_carrier_off(dev);
+
+ /* Cancel restart in progress */
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0; /* mark inactive timer */
+ }
+
+ can_flush_echo_skb(dev);
+
+ err = priv->do_set_mode(dev, CAN_MODE_START);
+ if (err)
+ return err;
+
+ netif_carrier_on(dev);
+
+ priv->can_stats.restarts++;
+
+ /* send restart message upstream */
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return -ENOMEM;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ return 0;
+}
+
+static void can_restart_after(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct can_priv *priv = netdev_priv(dev);
+
+ priv->timer.expires = 0; /* mark inactive timer */
+ can_restart_now(dev);
+}
+
+void can_bus_off(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ netif_carrier_off(dev);
+
+ if (priv->restart_ms > 0 && !priv->timer.expires) {
+
+ priv->timer.function = can_restart_after;
+ priv->timer.data = (unsigned long)dev;
+ priv->timer.expires =
+ jiffies + (priv->restart_ms * HZ) / 1000;
+ add_timer(&priv->timer);
+ }
+}
+EXPORT_SYMBOL(can_bus_off);
+
+void can_close_cleanup(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0;
+ }
+
+ can_flush_echo_skb(dev);
+}
+EXPORT_SYMBOL(can_close_cleanup);
+
+static int can_netdev_notifier_call(struct notifier_block *nb,
+ unsigned long state,
+ void *ndev)
+{
+ struct net_device *dev = ndev;
+
+ if (dev->type != ARPHRD_CAN)
+ return 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ /* omit virtual CAN software network devices */
+ if (dev->rtnl_link_ops) {
+ const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
+ if (!strcmp(ops->kind, "vcan"))
+ return 0;
+ }
+#else
+ /* software CAN devices like 'vcan' do not have private data */
+ if (!dev->priv)
+ return 0;
+#endif
+
+ switch (state) {
+ case NETDEV_REGISTER:
+#ifdef CONFIG_SYSFS
+ can_create_sysfs(dev);
+#endif
+ break;
+ case NETDEV_UNREGISTER:
+#ifdef CONFIG_SYSFS
+ can_remove_sysfs(dev);
+#endif
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block can_netdev_notifier = {
+ .notifier_call = can_netdev_notifier_call,
+};
+
+static __init int can_dev_init(void)
+{
+ printk(KERN_INFO MOD_DESC "\n");
+
+ return register_netdevice_notifier(&can_netdev_notifier);
+}
+module_init(can_dev_init);
+
+static __exit void can_dev_exit(void)
+{
+ unregister_netdevice_notifier(&can_netdev_notifier);
+}
+module_exit(can_dev_exit);
--- /dev/null
+/*
+ *
+ * CAN bus driver for Microchip 251x CAN Controller with SPI Interface
+ *
+ * MCP2510 support and bug fixes by Christian Pellegrin
+ * <chripell@evolware.org>
+ *
+ * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved.
+ * Written under contract by:
+ * Chris Elston, Katalix Systems, Ltd.
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ *
+ * Based on CAN bus driver for the CCAN controller written by
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ * Copyright 2007
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ *
+ * Your platform definition file should specify something like:
+ *
+ * static struct mcp251x_platform_data mcp251x_info = {
+ * .oscillator_frequency = 8000000,
+ * .board_specific_setup = &mcp251x_setup,
+ * .model = CAN_MCP251X_MCP2510,
+ * .power_enable = mcp251x_power_enable,
+ * .transceiver_enable = NULL,
+ * };
+ *
+ * static struct spi_board_info spi_board_info[] = {
+ * {
+ * .modalias = "mcp251x",
+ * .platform_data = &mcp251x_info,
+ * .irq = IRQ_EINT13,
+ * .max_speed_hz = 2*1000*1000,
+ * .chip_select = 2,
+ * },
+ * };
+ *
+ * Please see mcp251x.h for a description of the fields in
+ * struct mcp251x_platform_data.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/spi/spi.h>
+#include <linux/can/dev.h>
+#include <linux/can/core.h>
+#include <linux/if_arp.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/can/platform/mcp251x.h>
+
+/* SPI interface instruction set */
+#define INSTRUCTION_WRITE 0x02
+#define INSTRUCTION_READ 0x03
+#define INSTRUCTION_BIT_MODIFY 0x05
+#define INSTRUCTION_LOAD_TXB(n) (0x40 + 2 * (n))
+#define INSTRUCTION_READ_RXB(n) (((n) == 0) ? 0x90 : 0x94)
+#define INSTRUCTION_RESET 0xC0
+
+/* MPC251x registers */
+#define CANSTAT 0x0e
+#define CANCTRL 0x0f
+# define CANCTRL_REQOP_MASK 0xe0
+# define CANCTRL_REQOP_CONF 0x80
+# define CANCTRL_REQOP_LISTEN_ONLY 0x60
+# define CANCTRL_REQOP_LOOPBACK 0x40
+# define CANCTRL_REQOP_SLEEP 0x20
+# define CANCTRL_REQOP_NORMAL 0x00
+# define CANCTRL_OSM 0x08
+# define CANCTRL_ABAT 0x10
+#define TEC 0x1c
+#define REC 0x1d
+#define CNF1 0x2a
+#define CNF2 0x29
+# define CNF2_BTLMODE 0x80
+#define CNF3 0x28
+# define CNF3_SOF 0x08
+# define CNF3_WAKFIL 0x04
+# define CNF3_PHSEG2_MASK 0x07
+#define CANINTE 0x2b
+# define CANINTE_MERRE 0x80
+# define CANINTE_WAKIE 0x40
+# define CANINTE_ERRIE 0x20
+# define CANINTE_TX2IE 0x10
+# define CANINTE_TX1IE 0x08
+# define CANINTE_TX0IE 0x04
+# define CANINTE_RX1IE 0x02
+# define CANINTE_RX0IE 0x01
+#define CANINTF 0x2c
+# define CANINTF_MERRF 0x80
+# define CANINTF_WAKIF 0x40
+# define CANINTF_ERRIF 0x20
+# define CANINTF_TX2IF 0x10
+# define CANINTF_TX1IF 0x08
+# define CANINTF_TX0IF 0x04
+# define CANINTF_RX1IF 0x02
+# define CANINTF_RX0IF 0x01
+#define EFLG 0x2d
+# define EFLG_EWARN 0x01
+# define EFLG_RXWAR 0x02
+# define EFLG_TXWAR 0x04
+# define EFLG_RXEP 0x08
+# define EFLG_TXEP 0x10
+# define EFLG_TXBO 0x20
+# define EFLG_RX0OVR 0x40
+# define EFLG_RX1OVR 0x80
+#define TXBCTRL(n) ((n * 0x10) + 0x30)
+# define TXBCTRL_ABTF 0x40
+# define TXBCTRL_MLOA 0x20
+# define TXBCTRL_TXERR 0x10
+# define TXBCTRL_TXREQ 0x08
+#define RXBCTRL(n) ((n * 0x10) + 0x60)
+# define RXBCTRL_BUKT 0x04
+# define RXBCTRL_RXM0 0x20
+# define RXBCTRL_RXM1 0x40
+
+/* Buffer size required for the largest SPI transfer (i.e., reading a
+ * frame). */
+#define CAN_FRAME_MAX_DATA_LEN 8
+#define SPI_TRANSFER_BUF_LEN (2*(6 + CAN_FRAME_MAX_DATA_LEN))
+#define CAN_FRAME_MAX_BITS 128
+
+#define DEVICE_NAME "mcp251x"
+
+static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
+module_param(mcp251x_enable_dma, int, S_IRUGO);
+MODULE_PARM_DESC(mcp251x_enable_dma, "Enable SPI DMA. Default: 0 (Off)");
+
+static struct can_bittiming_const mcp251x_bittiming_const = {
+ .tseg1_min = 3,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+struct mcp251x_priv {
+ struct can_priv can;
+ struct net_device *net;
+ struct spi_device *spi;
+
+ struct mutex spi_lock; /* SPI buffer lock */
+ u8 *spi_tx_buf;
+ u8 *spi_rx_buf;
+ dma_addr_t spi_tx_dma;
+ dma_addr_t spi_rx_dma;
+
+ struct sk_buff *tx_skb;
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+ struct work_struct irq_work;
+ struct completion awake;
+ int wake;
+ int force_quit;
+ int after_suspend;
+#define AFTER_SUSPEND_UP 1
+#define AFTER_SUSPEND_DOWN 2
+#define AFTER_SUSPEND_POWER 4
+ int restart_tx;
+};
+
+static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 3,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ u8 val = 0;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_READ;
+ priv->spi_tx_buf[1] = reg;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__, ret);
+ else
+ val = priv->spi_rx_buf[2];
+
+ mutex_unlock(&priv->spi_lock);
+
+ dev_dbg(&spi->dev, "%s: read %02x = %02x\n", __func__, reg, val);
+ return val;
+}
+
+static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 3,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_WRITE;
+ priv->spi_tx_buf[1] = reg;
+ priv->spi_tx_buf[2] = val;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed\n", __func__);
+}
+
+static void mcp251x_write_bits(struct spi_device *spi, u8 reg,
+ u8 mask, uint8_t val)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 4,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY;
+ priv->spi_tx_buf[1] = reg;
+ priv->spi_tx_buf[2] = mask;
+ priv->spi_tx_buf[3] = val;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed\n", __func__);
+}
+
+static int mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
+ int tx_buf_idx)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ u32 sid, eid, exide, rtr;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ exide = (frame->can_id & CAN_EFF_FLAG) ? 1 : 0; /* Extended ID Enable */
+ if (exide)
+ sid = (frame->can_id & CAN_EFF_MASK) >> 18;
+ else
+ sid = frame->can_id & CAN_SFF_MASK; /* Standard ID */
+ eid = frame->can_id & CAN_EFF_MASK; /* Extended ID */
+ rtr = (frame->can_id & CAN_RTR_FLAG) ? 1 : 0; /* Remote transmission */
+
+ if (pdata->model == CAN_MCP251X_MCP2510) {
+ int i;
+
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 1, sid >> 3);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 2,
+ ((sid & 7) << 5) | (exide << 3) |
+ ((eid >> 16) & 3));
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 3,
+ (eid >> 8) & 0xff);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 4, eid & 0xff);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 5,
+ (rtr << 6) | frame->can_dlc);
+
+ for (i = 0; i < frame->can_dlc ; i++) {
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 6 + i,
+ frame->data[i]);
+ }
+ } else {
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .cs_change = 0,
+ .len = 6 + CAN_FRAME_MAX_DATA_LEN,
+ };
+ struct spi_message m;
+ int ret;
+ u8 *tx_buf = priv->spi_tx_buf;
+
+ mutex_lock(&priv->spi_lock);
+
+ tx_buf[0] = INSTRUCTION_LOAD_TXB(tx_buf_idx);
+ tx_buf[1] = sid >> 3;
+ tx_buf[2] = ((sid & 7) << 5) | (exide << 3) |
+ ((eid >> 16) & 3);
+ tx_buf[3] = (eid >> 8) & 0xff;
+ tx_buf[4] = eid & 0xff;
+ tx_buf[5] = (rtr << 6) | frame->can_dlc;
+
+ memcpy(tx_buf + 6, frame->data, frame->can_dlc);
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0) {
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__,
+ ret);
+ return -1;
+ }
+ }
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ);
+ return 0;
+}
+
+static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ dev_dbg(&spi->dev, "%s: out of memory for Rx'd frame\n",
+ __func__);
+ priv->net->stats.rx_dropped++;
+ return;
+ }
+ skb->dev = priv->net;
+ frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+ if (pdata->model == CAN_MCP251X_MCP2510) {
+ int i;
+ u8 rx_buf[6];
+
+ rx_buf[1] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 1);
+ rx_buf[2] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 2);
+ rx_buf[3] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 3);
+ rx_buf[4] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 4);
+ rx_buf[5] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 5);
+
+ if ((rx_buf[2] >> 3) & 0x1) {
+ /* Extended ID format */
+ frame->can_id = CAN_EFF_FLAG;
+ frame->can_id |= ((rx_buf[2] & 3) << 16) |
+ (rx_buf[3] << 8) | rx_buf[4] |
+ (((rx_buf[1] << 3) | (rx_buf[2] >> 5)) << 18);
+ } else {
+ /* Standard ID format */
+ frame->can_id = (rx_buf[1] << 3) | (rx_buf[2] >> 5);
+ }
+
+ if ((rx_buf[5] >> 6) & 0x1) {
+ /* Remote transmission request */
+ frame->can_id |= CAN_RTR_FLAG;
+ }
+
+ /* Data length */
+ frame->can_dlc = rx_buf[5] & 0x0f;
+ if (frame->can_dlc > 8) {
+ dev_warn(&spi->dev, "invalid frame recevied\n");
+ priv->net->stats.rx_errors++;
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ for (i = 0; i < frame->can_dlc; i++) {
+ frame->data[i] = mcp251x_read_reg(spi,
+ RXBCTRL(buf_idx) +
+ 6 + i);
+ }
+ } else {
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .cs_change = 0,
+ .len = 14, /* RX buffer: RXBnCTRL to RXBnD7 */
+ };
+ struct spi_message m;
+ int ret;
+ u8 *tx_buf = priv->spi_tx_buf;
+ u8 *rx_buf = priv->spi_rx_buf;
+
+ mutex_lock(&priv->spi_lock);
+
+ tx_buf[0] = INSTRUCTION_READ_RXB(buf_idx);
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ if (ret < 0) {
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n",
+ __func__, ret);
+ priv->net->stats.rx_errors++;
+ mutex_unlock(&priv->spi_lock);
+ return;
+ }
+
+ if ((rx_buf[2] >> 3) & 0x1) {
+ /* Extended ID format */
+ frame->can_id = CAN_EFF_FLAG;
+ frame->can_id |= ((rx_buf[2] & 3) << 16) |
+ (rx_buf[3] << 8) | rx_buf[4] |
+ (((rx_buf[1] << 3) | (rx_buf[2] >> 5)) << 18);
+ } else {
+ /* Standard ID format */
+ frame->can_id = (rx_buf[1] << 3) | (rx_buf[2] >> 5);
+ }
+
+ if ((rx_buf[5] >> 6) & 0x1) {
+ /* Remote transmission request */
+ frame->can_id |= CAN_RTR_FLAG;
+ }
+
+ /* Data length */
+ frame->can_dlc = rx_buf[5] & 0x0f;
+ if (frame->can_dlc > 8) {
+ dev_warn(&spi->dev, "invalid frame recevied\n");
+ priv->net->stats.rx_errors++;
+ dev_kfree_skb(skb);
+ mutex_unlock(&priv->spi_lock);
+ return;
+ }
+
+ memcpy(frame->data, rx_buf + 6, CAN_FRAME_MAX_DATA_LEN);
+
+ mutex_unlock(&priv->spi_lock);
+ }
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += frame->can_dlc;
+
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+}
+
+static void mcp251x_hw_sleep(struct spi_device *spi)
+{
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_SLEEP);
+}
+
+static void mcp251x_hw_wakeup(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ priv->wake = 1;
+
+ /* Can only wake up by generating a wake-up interrupt. */
+ mcp251x_write_bits(spi, CANINTE, CANINTE_WAKIE, CANINTE_WAKIE);
+ mcp251x_write_bits(spi, CANINTF, CANINTF_WAKIF, CANINTF_WAKIF);
+
+ /* Wait until the device is awake */
+ if (!wait_for_completion_timeout(&priv->awake, HZ))
+ dev_err(&spi->dev, "MCP251x didn't wake-up\n");
+}
+
+static int mcp251x_hard_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (priv->tx_skb) {
+ dev_warn(&spi->dev, "hard_xmit called with not null tx_skb\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ if (skb->len != sizeof(struct can_frame)) {
+ dev_dbg(&spi->dev, "dropping packet - bad length\n");
+ dev_kfree_skb(skb);
+ net->stats.tx_dropped++;
+ return 0;
+ }
+
+ netif_stop_queue(net);
+ priv->tx_skb = skb;
+ net->trans_start = jiffies;
+ queue_work(priv->wq, &priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static int mcp251x_do_set_mode(struct net_device *net, enum can_mode mode)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ dev_dbg(&spi->dev, "%s (unimplemented)\n", __func__);
+
+ switch (mode) {
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static void mcp251x_set_normal_mode(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ unsigned long timeout;
+
+ /* Enable interrupts */
+ mcp251x_write_reg(spi, CANINTE,
+ CANINTE_ERRIE | CANINTE_TX2IE | CANINTE_TX1IE |
+ CANINTE_TX0IE | CANINTE_RX1IE | CANINTE_RX0IE);
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+ /* Put device into loopback mode */
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_LOOPBACK);
+ } else {
+ /* Put device into normal mode */
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL);
+
+ /* Wait for the device to enter normal mode */
+ timeout = jiffies + HZ;
+ while (mcp251x_read_reg(spi, CANSTAT) & 0xE0) {
+ udelay(10);
+ if (time_after(jiffies, timeout)) {
+ dev_err(&spi->dev, "MCP251x didn't"
+ " enter in normal mode\n");
+ break;
+ }
+ }
+ }
+}
+
+static int mcp251x_do_set_bittiming(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct spi_device *spi = priv->spi;
+ u8 state;
+
+ dev_dbg(&spi->dev, "%s: BRP = %d, PropSeg = %d, PS1 = %d,"
+ " PS2 = %d, SJW = %d\n", __func__, bt->brp,
+ bt->prop_seg, bt->phase_seg1, bt->phase_seg2,
+ bt->sjw);
+
+ /* Store original mode and set mode to config */
+ state = mcp251x_read_reg(spi, CANCTRL);
+ state = mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK;
+ mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK,
+ CANCTRL_REQOP_CONF);
+
+ mcp251x_write_reg(spi, CNF1, ((bt->sjw - 1) << 6) | (bt->brp - 1));
+ mcp251x_write_reg(spi, CNF2, CNF2_BTLMODE |
+ ((bt->phase_seg1 - 1) << 3) |
+ (bt->prop_seg - 1));
+ mcp251x_write_bits(spi, CNF3, CNF3_PHSEG2_MASK,
+ (bt->phase_seg2 - 1));
+
+ /* Restore original state */
+ mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK, state);
+
+ return 0;
+}
+
+static void mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
+ struct spi_device *spi)
+{
+ int ret;
+
+ /* Set initial baudrate. Make sure that registers are updated
+ always by explicitly calling mcp251x_do_set_bittiming */
+ ret = can_set_bittiming(net);
+ if (ret)
+ dev_err(&spi->dev, "unable to set initial baudrate!\n");
+ else
+ mcp251x_do_set_bittiming(net);
+
+ /* Enable RX0->RX1 buffer roll over and disable filters */
+ mcp251x_write_bits(spi, RXBCTRL(0),
+ RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1,
+ RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1);
+ mcp251x_write_bits(spi, RXBCTRL(1),
+ RXBCTRL_RXM0 | RXBCTRL_RXM1,
+ RXBCTRL_RXM0 | RXBCTRL_RXM1);
+
+ dev_dbg(&spi->dev, "%s RXBCTL 0 and 1: %02x %02x\n", __func__,
+ mcp251x_read_reg(spi, RXBCTRL(0)),
+ mcp251x_read_reg(spi, RXBCTRL(1)));
+}
+
+static void mcp251x_hw_reset(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_RESET;
+
+ ret = spi_write(spi, priv->spi_tx_buf, 1);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__, ret);
+ /* wait for reset to finish */
+ mdelay(10);
+}
+
+static int mcp251x_hw_probe(struct spi_device *spi)
+{
+ int st1, st2;
+
+ mcp251x_hw_reset(spi);
+
+ st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE;
+ st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17;
+
+ dev_dbg(&spi->dev, "%s: 0x%02x - 0x%02x\n", __func__,
+ st1, st2);
+
+ /* check for power up default values */
+ return (st1 == 0x80 && st2 == 0x07) ? 1 : 0;
+}
+
+static int mcp251x_open(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(1);
+
+ priv->force_quit = 0;
+ priv->tx_skb = NULL;
+ enable_irq(spi->irq);
+ mcp251x_hw_wakeup(spi);
+ mcp251x_hw_reset(spi);
+ mcp251x_setup(net, priv, spi);
+ mcp251x_set_normal_mode(spi);
+ netif_wake_queue(net);
+
+ return 0;
+}
+
+static int mcp251x_stop(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ /* Disable and clear pending interrupts */
+ mcp251x_write_reg(spi, CANINTE, 0x00);
+ mcp251x_write_reg(spi, CANINTF, 0x00);
+
+ priv->force_quit = 1;
+ disable_irq(spi->irq);
+ flush_workqueue(priv->wq);
+
+ mcp251x_write_reg(spi, TXBCTRL(0), 0);
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+
+ mcp251x_hw_sleep(spi);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+
+ return 0;
+}
+
+static int mcp251x_do_get_state(struct net_device *net, enum can_state *state)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ u8 eflag;
+
+ eflag = mcp251x_read_reg(spi, EFLG);
+
+ if (eflag & EFLG_TXBO)
+ *state = CAN_STATE_BUS_OFF;
+ else if (eflag & (EFLG_RXEP | EFLG_TXEP))
+ *state = CAN_STATE_BUS_PASSIVE;
+ else if (eflag & EFLG_EWARN)
+ *state = CAN_STATE_BUS_WARNING;
+ else
+ *state = CAN_STATE_ACTIVE;
+
+ return 0;
+}
+
+static void mcp251x_tx_work_handler(struct work_struct *ws)
+{
+ struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv,
+ tx_work);
+ struct spi_device *spi = priv->spi;
+ struct can_frame *frame;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (priv->tx_skb) {
+ frame = (struct can_frame *)priv->tx_skb->data;
+ if (frame->can_dlc > CAN_FRAME_MAX_DATA_LEN)
+ frame->can_dlc = CAN_FRAME_MAX_DATA_LEN;
+ mcp251x_hw_tx(spi, frame, 0);
+ }
+}
+
+static void mcp251x_irq_work_handler(struct work_struct *ws)
+{
+ struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv,
+ irq_work);
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+ u8 intf;
+ u8 txbnctrl;
+ /* the next limitation is needed so we give some time to the
+ * tx workqueue */
+#define MAX_LOOPS 10
+ int loops;
+
+ if (priv->after_suspend) {
+ /* Wait whilst the device wakes up */
+ mdelay(10);
+ mcp251x_hw_reset(spi);
+ mcp251x_setup(net, priv, spi);
+ if (priv->after_suspend & AFTER_SUSPEND_UP) {
+ netif_device_attach(net);
+ /* clear since we lost tx buffer */
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ netif_wake_queue(net);
+ }
+ mcp251x_set_normal_mode(spi);
+ } else
+ mcp251x_hw_sleep(spi);
+ priv->after_suspend = 0;
+ return;
+ }
+
+ loops = 0;
+ while (!priv->force_quit && !freezing(current) && loops < MAX_LOOPS) {
+ if (priv->restart_tx) {
+ priv->restart_tx = 0;
+ dev_warn(&spi->dev,
+ "timeout in txing a packet, restarting\n");
+ mcp251x_write_reg(spi, TXBCTRL(0), 0);
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+
+ if (priv->wake) {
+ /* Wait whilst the device wakes up */
+ mdelay(10);
+ priv->wake = 0;
+ }
+
+ intf = mcp251x_read_reg(spi, CANINTF);
+ if (intf == 0x00)
+ break;
+ mcp251x_write_bits(spi, CANINTF, intf, 0x00);
+
+ dev_dbg(&spi->dev, "interrupt:%s%s%s%s%s%s%s%s\n",
+ (intf & CANINTF_MERRF) ? " MERR" : "",
+ (intf & CANINTF_WAKIF) ? " WAK" : "",
+ (intf & CANINTF_ERRIF) ? " ERR" : "",
+ (intf & CANINTF_TX2IF) ? " TX2" : "",
+ (intf & CANINTF_TX1IF) ? " TX1" : "",
+ (intf & CANINTF_TX0IF) ? " TX0" : "",
+ (intf & CANINTF_RX1IF) ? " RX1" : "",
+ (intf & CANINTF_RX0IF) ? " RX0" : "");
+
+ if (intf & CANINTF_WAKIF)
+ complete(&priv->awake);
+
+ if (intf & CANINTF_MERRF) {
+ /* if there are no pending Tx buffers, restart queue */
+ txbnctrl = mcp251x_read_reg(spi, TXBCTRL(0));
+ if (!(txbnctrl & TXBCTRL_TXREQ)) {
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+ }
+
+ if (intf & CANINTF_ERRIF) {
+ struct sk_buff *skb;
+ struct can_frame *frame = NULL;
+ u8 eflag = mcp251x_read_reg(spi, EFLG);
+
+ dev_dbg(&spi->dev, "EFLG = 0x%02x\n", eflag);
+
+ /* Create error frame */
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb) {
+ frame = (struct can_frame *)
+ skb_put(skb, sizeof(struct can_frame));
+ frame->can_id = CAN_ERR_FLAG;
+ frame->can_dlc = CAN_ERR_DLC;
+
+ skb->dev = net;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ /* Set error frame flags based on bus state */
+ if (eflag & EFLG_TXBO) {
+ frame->can_id |= CAN_ERR_BUSOFF;
+ } else if (eflag & EFLG_TXEP) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_PASSIVE;
+ } else if (eflag & EFLG_RXEP) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_PASSIVE;
+ } else if (eflag & EFLG_TXWAR) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING;
+ } else if (eflag & EFLG_RXWAR) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_WARNING;
+ }
+ }
+
+ if (eflag & (EFLG_RX0OVR | EFLG_RX1OVR)) {
+ if (eflag & EFLG_RX0OVR)
+ net->stats.rx_over_errors++;
+ if (eflag & EFLG_RX1OVR)
+ net->stats.rx_over_errors++;
+ if (frame) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] =
+ CAN_ERR_CRTL_RX_OVERFLOW;
+ }
+ }
+ mcp251x_write_reg(spi, EFLG, 0x00);
+
+ if (skb)
+ netif_rx(skb);
+ }
+
+ if (intf & (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF)) {
+ if (priv->tx_skb) {
+ net->stats.tx_packets++;
+ net->stats.tx_bytes +=
+ ((struct can_frame *)
+ (priv->tx_skb->data))->can_dlc;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+
+ if (intf & CANINTF_RX0IF)
+ mcp251x_hw_rx(spi, 0);
+
+ if (intf & CANINTF_RX1IF)
+ mcp251x_hw_rx(spi, 1);
+
+ loops++;
+ }
+
+ mcp251x_read_reg(spi, CANSTAT);
+
+ dev_dbg(&spi->dev, "interrupt ended\n");
+}
+
+static irqreturn_t mcp251x_can_isr(int irq, void *dev_id)
+{
+ struct net_device *net = (struct net_device *)dev_id;
+ struct mcp251x_priv *priv = netdev_priv(net);
+
+ dev_dbg(&priv->spi->dev, "%s: irq\n", __func__);
+ /* Schedule bottom half */
+ if (!work_pending(&priv->irq_work))
+ queue_work(priv->wq, &priv->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static void mcp251x_tx_timeout(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+
+ priv->restart_tx = 1;
+ queue_work(priv->wq, &priv->irq_work);
+}
+
+static struct net_device *alloc_mcp251x_netdev(int sizeof_priv)
+{
+ struct net_device *net;
+ struct mcp251x_priv *priv;
+
+ net = alloc_candev(sizeof_priv);
+ if (!net)
+ return NULL;
+
+ priv = netdev_priv(net);
+
+ net->open = mcp251x_open;
+ net->stop = mcp251x_stop;
+ net->hard_start_xmit = mcp251x_hard_start_xmit;
+ net->tx_timeout = mcp251x_tx_timeout;
+ net->watchdog_timeo = HZ;
+
+ priv->can.bittiming_const = &mcp251x_bittiming_const;
+ priv->can.do_get_state = mcp251x_do_get_state;
+ priv->can.do_set_mode = mcp251x_do_set_mode;
+
+ priv->net = net;
+
+ return net;
+}
+
+static int __devinit mcp251x_can_probe(struct spi_device *spi)
+{
+ struct net_device *net;
+ struct mcp251x_priv *priv;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ int ret = -ENODEV;
+
+ if (!pdata) {
+ /* Platform data is required for osc freq */
+ goto error_out;
+ }
+
+ /* Allocate can/net device */
+ net = alloc_mcp251x_netdev(sizeof(struct mcp251x_priv));
+ if (!net) {
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+
+ priv = netdev_priv(net);
+ dev_set_drvdata(&spi->dev, priv);
+
+ priv->spi = spi;
+ mutex_init(&priv->spi_lock);
+
+ priv->can.bittiming.clock = pdata->oscillator_frequency / 2;
+
+ /* If requested, allocate DMA buffers */
+ if (mcp251x_enable_dma) {
+ spi->dev.coherent_dma_mask = DMA_32BIT_MASK;
+
+ /* Minimum coherent DMA allocation is PAGE_SIZE, so allocate
+ that much and share it between Tx and Rx DMA buffers. */
+ priv->spi_tx_buf = dma_alloc_coherent(&spi->dev,
+ PAGE_SIZE, &priv->spi_tx_dma, GFP_DMA);
+
+ if (priv->spi_tx_buf) {
+ priv->spi_rx_buf = (u8 *)(priv->spi_tx_buf +
+ (PAGE_SIZE / 2));
+ priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma +
+ (PAGE_SIZE / 2));
+ } else {
+ /* Fall back to non-DMA */
+ mcp251x_enable_dma = 0;
+ }
+ }
+
+ /* Allocate non-DMA buffers */
+ if (!mcp251x_enable_dma) {
+ priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_tx_buf;
+ }
+ priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_rx_buf;
+ }
+ }
+
+ if (pdata->power_enable)
+ pdata->power_enable(1);
+
+ /* Call out to platform specific setup */
+ if (pdata->board_specific_setup)
+ pdata->board_specific_setup(spi);
+
+ SET_NETDEV_DEV(net, &spi->dev);
+
+ priv->wq = create_freezeable_workqueue("mcp251x_wq");
+
+ INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler);
+ INIT_WORK(&priv->irq_work, mcp251x_irq_work_handler);
+
+ init_completion(&priv->awake);
+
+ /* Configure the SPI bus */
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ /* Register IRQ */
+ if (request_irq(spi->irq, mcp251x_can_isr,
+ IRQF_TRIGGER_FALLING, DEVICE_NAME, net) < 0) {
+ dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
+ goto error_irq;
+ }
+ disable_irq(spi->irq);
+
+ if (!mcp251x_hw_probe(spi)) {
+ dev_info(&spi->dev, "Probe failed\n");
+ goto error_probe;
+ }
+ mcp251x_hw_sleep(spi);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+
+ ret = register_netdev(net);
+ if (ret >= 0) {
+ dev_info(&spi->dev, "probed\n");
+ return ret;
+ }
+error_probe:
+ free_irq(spi->irq, net);
+error_irq:
+ if (!mcp251x_enable_dma)
+ kfree(priv->spi_rx_buf);
+error_rx_buf:
+ if (!mcp251x_enable_dma)
+ kfree(priv->spi_tx_buf);
+error_tx_buf:
+ free_candev(net);
+ if (mcp251x_enable_dma) {
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+ }
+error_alloc:
+ dev_err(&spi->dev, "probe failed\n");
+error_out:
+ return ret;
+}
+
+static int __devexit mcp251x_can_remove(struct spi_device *spi)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net = priv->net;
+
+ free_irq(spi->irq, net);
+ priv->force_quit = 1;
+ flush_workqueue(priv->wq);
+ destroy_workqueue(priv->wq);
+
+ if (mcp251x_enable_dma) {
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+ } else {
+ kfree(priv->spi_tx_buf);
+ kfree(priv->spi_rx_buf);
+ }
+
+ unregister_netdev(net);
+ free_candev(net);
+
+ if (pdata->power_enable)
+ pdata->power_enable(0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net = priv->net;
+
+ if (netif_running(net)) {
+ netif_device_detach(net);
+
+ mcp251x_hw_sleep(spi);
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+ priv->after_suspend = AFTER_SUSPEND_UP;
+ } else
+ priv->after_suspend = AFTER_SUSPEND_DOWN;
+
+ if (pdata->power_enable) {
+ pdata->power_enable(0);
+ priv->after_suspend |= AFTER_SUSPEND_POWER;
+ }
+
+ return 0;
+}
+
+static int mcp251x_can_resume(struct spi_device *spi)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ if (priv->after_suspend & AFTER_SUSPEND_POWER) {
+ pdata->power_enable(1);
+ queue_work(priv->wq, &priv->irq_work);
+ } else {
+ if (priv->after_suspend & AFTER_SUSPEND_UP) {
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(1);
+ queue_work(priv->wq, &priv->irq_work);
+ } else
+ priv->after_suspend = 0;
+ }
+ return 0;
+}
+#else
+#define mcp251x_can_suspend NULL
+#define mcp251x_can_resume NULL
+#endif
+
+static struct spi_driver mcp251x_can_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+
+ .probe = mcp251x_can_probe,
+ .remove = __devexit_p(mcp251x_can_remove),
+ .suspend = mcp251x_can_suspend,
+ .resume = mcp251x_can_resume,
+};
+
+static int __init mcp251x_can_init(void)
+{
+ return spi_register_driver(&mcp251x_can_driver);
+}
+
+static void __exit mcp251x_can_exit(void)
+{
+ spi_unregister_driver(&mcp251x_can_driver);
+}
+
+module_init(mcp251x_can_init);
+module_exit(mcp251x_can_exit);
+
+MODULE_AUTHOR("Chris Elston <celston@katalix.com>, "
+ "Christian Pellegrin <chripell@evolware.org>");
+MODULE_DESCRIPTION("Microchip 251x CAN driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+obj-$(CONFIG_CAN_MPC52XX) += mscan-mpc52xx.o
+
+mscan-mpc52xx-objs := mscan.o mpc52xx_can.o
--- /dev/null
+/*
+ * DESCRIPTION:
+ * CAN bus driver for the Freescale MPC52xx embedded CPU.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2004-2005, Varma Electronics Oy
+ *
+ * LICENCE:
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * HISTORY:
+ * 2005-02-03 created
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+#include <asm/mpc52xx.h>
+
+#include "mscan.h"
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+
+RCSID("$Id$");
+
+#define PDEV_MAX 2
+
+struct platform_device *pdev[PDEV_MAX];
+
+static int __devinit mpc52xx_can_probe(struct platform_device *pdev)
+{
+ struct resource *mem;
+ struct net_device *dev;
+ struct mscan_platform_data *pdata = pdev->dev.platform_data;
+ struct can_priv *priv;
+ u32 mem_size;
+ int ret = -ENODEV;
+
+ if (!pdata)
+ return ret;
+
+ dev = alloc_mscandev();
+ if (!dev)
+ return -ENOMEM;
+ priv = netdev_priv(dev);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->irq = platform_get_irq(pdev, 0);
+ if (!mem || !dev->irq)
+ goto req_error;
+
+ mem_size = mem->end - mem->start + 1;
+ if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
+ dev_err(&pdev->dev, "resource unavailable\n");
+ goto req_error;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
+
+ if (!dev->base_addr) {
+ dev_err(&pdev->dev, "failed to map can port\n");
+ ret = -ENOMEM;
+ goto fail_map;
+ }
+
+ priv->bittiming.clock = pdata->clock_frq;
+
+ platform_set_drvdata(pdev, dev);
+
+ ret = register_mscandev(dev, pdata->clock_src);
+ if (ret >= 0) {
+ dev_info(&pdev->dev, "probe for port 0x%lX done (irq=%d)\n",
+ dev->base_addr, dev->irq);
+ return ret;
+ }
+
+ iounmap((unsigned long *)dev->base_addr);
+
+fail_map:
+ release_mem_region(mem->start, mem_size);
+
+req_error:
+ free_candev(dev);
+ dev_err(&pdev->dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit mpc52xx_can_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_mscandev(dev);
+
+ iounmap((volatile void __iomem *)dev->base_addr);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+ free_candev(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static struct mscan_regs saved_regs;
+static int mpc52xx_can_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ _memcpy_fromio(&saved_regs, regs, sizeof(*regs));
+
+ return 0;
+}
+
+static int mpc52xx_can_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ regs->canctl0 |= MSCAN_INITRQ;
+ while ((regs->canctl1 & MSCAN_INITAK) == 0)
+ udelay(10);
+
+ regs->canctl1 = saved_regs.canctl1;
+ regs->canbtr0 = saved_regs.canbtr0;
+ regs->canbtr1 = saved_regs.canbtr1;
+ regs->canidac = saved_regs.canidac;
+
+ /* restore masks, buffers etc. */
+ _memcpy_toio(®s->canidar1_0, (void *)&saved_regs.canidar1_0,
+ sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0));
+
+ regs->canctl0 &= ~MSCAN_INITRQ;
+ regs->cantbsel = saved_regs.cantbsel;
+ regs->canrier = saved_regs.canrier;
+ regs->cantier = saved_regs.cantier;
+ regs->canctl0 = saved_regs.canctl0;
+
+ return 0;
+}
+#endif
+
+static struct platform_driver mpc52xx_can_driver = {
+ .driver = {
+ .name = "mpc52xx-mscan",
+ },
+ .probe = mpc52xx_can_probe,
+ .remove = __devexit_p(mpc52xx_can_remove),
+#ifdef CONFIG_PM
+ .suspend = mpc52xx_can_suspend,
+ .resume = mpc52xx_can_resume,
+#endif
+};
+
+#ifdef CONFIG_PPC_MERGE
+static int __init mpc52xx_of_to_pdev(void)
+{
+ struct device_node *np = NULL;
+ unsigned int i;
+ int err = -ENODEV;
+
+ for (i = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ (np = of_find_compatible_node(np, "mscan", "mpc5200-mscan"));
+#else
+ (np = of_find_compatible_node(np, NULL, "fsl,mpc5200-mscan"));
+#endif
+ i++) {
+ struct resource r[2] = { };
+ struct mscan_platform_data pdata;
+
+ if (i >= PDEV_MAX) {
+ printk(KERN_WARNING "%s: increase PDEV_MAX for more "
+ "than %i devices\n", __func__, PDEV_MAX);
+ break;
+ }
+
+ err = of_address_to_resource(np, 0, &r[0]);
+ if (err)
+ break;
+
+ of_irq_to_resource(np, 0, &r[1]);
+
+ pdev[i] =
+ platform_device_register_simple("mpc52xx-mscan", i, r, 2);
+ if (IS_ERR(pdev[i])) {
+ err = PTR_ERR(pdev[i]);
+ break;
+ }
+
+ pdata.clock_src = MSCAN_CLKSRC_BUS;
+ pdata.clock_frq = mpc52xx_find_ipb_freq(np);
+ err = platform_device_add_data(pdev[i], &pdata, sizeof(pdata));
+ if (err)
+ break;
+ }
+ return err;
+}
+#endif
+
+int __init mpc52xx_can_init(void)
+{
+#ifdef CONFIG_PPC_MERGE
+ int err = mpc52xx_of_to_pdev();
+
+ if (err) {
+ printk(KERN_ERR "%s init failed with err=%d\n",
+ mpc52xx_can_driver.driver.name, err);
+ return err;
+ }
+#endif
+ return platform_driver_register(&mpc52xx_can_driver);
+}
+
+void __exit mpc52xx_can_exit(void)
+{
+ int i;
+ platform_driver_unregister(&mpc52xx_can_driver);
+ for (i = 0; i < PDEV_MAX; i++)
+ platform_device_unregister(pdev[i]);
+ printk(KERN_INFO "%s unloaded\n", mpc52xx_can_driver.driver.name);
+}
+
+module_init(mpc52xx_can_init);
+module_exit(mpc52xx_can_exit);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_DESCRIPTION("Freescale MPC5200 CAN driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * mscan.c
+ *
+ * DESCRIPTION:
+ * CAN bus driver for the alone generic (as possible as) MSCAN controller.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2005-2006, Varma Electronics Oy
+ *
+ * LICENCE:
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/list.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+
+#include "mscan.h"
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
+
+#define MSCAN_NORMAL_MODE 0
+#define MSCAN_SLEEP_MODE MSCAN_SLPRQ
+#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ)
+#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ)
+#define MSCAN_SET_MODE_RETRIES 255
+
+
+#define BTR0_BRP_MASK 0x3f
+#define BTR0_SJW_SHIFT 6
+#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT)
+
+#define BTR1_TSEG1_MASK 0xf
+#define BTR1_TSEG2_SHIFT 4
+#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT)
+#define BTR1_SAM_SHIFT 7
+
+#define BTR0_SET_BRP(brp) (((brp) - 1) & BTR0_BRP_MASK)
+#define BTR0_SET_SJW(sjw) ((((sjw) - 1) << BTR0_SJW_SHIFT) & \
+ BTR0_SJW_MASK)
+
+#define BTR1_SET_TSEG1(tseg1) (((tseg1) - 1) & BTR1_TSEG1_MASK)
+#define BTR1_SET_TSEG2(tseg2) ((((tseg2) - 1) << BTR1_TSEG2_SHIFT) & \
+ BTR1_TSEG2_MASK)
+#define BTR1_SET_SAM(sam) (((sam) & 1) << BTR1_SAM_SHIFT)
+
+static struct can_bittiming_const mscan_bittiming_const = {
+ .tseg1_min = 4,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+struct mscan_state {
+ u8 mode;
+ u8 canrier;
+ u8 cantier;
+};
+
+#define TX_QUEUE_SIZE 3
+
+struct tx_queue_entry {
+ struct list_head list;
+ u8 mask;
+ u8 id;
+};
+
+struct mscan_priv {
+ struct can_priv can;
+ long open_time;
+ volatile unsigned long flags;
+ u8 shadow_statflg;
+ u8 shadow_canrier;
+ u8 cur_pri;
+ u8 tx_active;
+
+ struct list_head tx_head;
+ struct tx_queue_entry tx_queue[TX_QUEUE_SIZE];
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ struct napi_struct napi;
+ struct net_device *dev;
+#endif
+};
+
+#define F_RX_PROGRESS 0
+#define F_TX_PROGRESS 1
+#define F_TX_WAIT_ALL 2
+
+static enum can_state state_map[] = {
+ CAN_STATE_ACTIVE,
+ CAN_STATE_BUS_WARNING,
+ CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_BUS_OFF
+};
+
+static int mscan_set_mode(struct net_device *dev, u8 mode)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ int ret = 0;
+ int i;
+ u8 canctl1;
+
+ if (mode != MSCAN_NORMAL_MODE) {
+
+ if (priv->tx_active) {
+ /* Abort transfers before going to sleep */
+ out_8(®s->cantier, 0);
+ out_8(®s->cantarq, priv->tx_active);
+ out_8(®s->cantier, priv->tx_active);
+ }
+
+ canctl1 = in_8(®s->canctl1);
+ if ((mode & MSCAN_SLPRQ) && (canctl1 & MSCAN_SLPAK) == 0) {
+ out_8(®s->canctl0,
+ in_8(®s->canctl0) | MSCAN_SLPRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(®s->canctl1) & MSCAN_SLPAK)
+ break;
+ udelay(100);
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+ if (!ret)
+ priv->can.state = CAN_STATE_SLEEPING;
+
+ if (!ret && (mode & MSCAN_INITRQ)
+ && (canctl1 & MSCAN_INITAK) == 0) {
+ out_8(®s->canctl0,
+ in_8(®s->canctl0) | MSCAN_INITRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(®s->canctl1) & MSCAN_INITAK)
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+ if (!ret)
+ priv->can.state = CAN_STATE_STOPPED;
+
+ if (!ret && (mode & MSCAN_CSWAI))
+ out_8(®s->canctl0,
+ in_8(®s->canctl0) | MSCAN_CSWAI);
+
+ } else {
+ canctl1 = in_8(®s->canctl1);
+ if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) {
+ out_8(®s->canctl0, in_8(®s->canctl0) &
+ ~(MSCAN_SLPRQ | MSCAN_INITRQ));
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ canctl1 = in_8(®s->canctl1);
+ if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK)))
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ else
+ priv->can.state = CAN_STATE_ACTIVE;
+ }
+ }
+ return ret;
+}
+
+static int mscan_start(struct net_device *dev)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ u8 canrflg;
+ int err;
+
+ out_8(®s->canrier, 0);
+
+ INIT_LIST_HEAD(&priv->tx_head);
+ priv->cur_pri = 0;
+ priv->tx_active = 0;
+ priv->shadow_canrier = 0;
+ priv->flags = 0;
+
+ err = mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+ if (err)
+ return err;
+
+ canrflg = in_8(®s->canrflg);
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ priv->can.state = state_map[max(MSCAN_STATE_RX(canrflg),
+ MSCAN_STATE_TX(canrflg))];
+ out_8(®s->cantier, 0);
+
+ /* Enable receive interrupts. */
+ out_8(®s->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE |
+ MSCAN_RSTATE1 | MSCAN_RSTATE0 | MSCAN_TSTATE1 | MSCAN_TSTATE0);
+
+ return 0;
+}
+
+static int mscan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_frame *frame = (struct can_frame *)skb->data;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ int i, rtr, buf_id;
+ u32 can_id;
+
+ if (frame->can_dlc > 8)
+ return -EINVAL;
+
+ dev_dbg(ND2D(dev), "%s\n", __func__);
+ out_8(®s->cantier, 0);
+
+ i = ~priv->tx_active & MSCAN_TXE;
+ buf_id = ffs(i) - 1;
+ switch (hweight8(i)) {
+ case 0:
+ netif_stop_queue(dev);
+ dev_err(ND2D(dev), "BUG! Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ case 1:
+ /* if buf_id < 3, then current frame will be send out of order,
+ since buffer with lower id have higher priority (hell..) */
+ if (buf_id < 3)
+ priv->cur_pri++;
+ if (priv->cur_pri == 0xff)
+ set_bit(F_TX_WAIT_ALL, &priv->flags);
+ netif_stop_queue(dev);
+ case 2:
+ set_bit(F_TX_PROGRESS, &priv->flags);
+ }
+ out_8(®s->cantbsel, i);
+
+ rtr = frame->can_id & CAN_RTR_FLAG;
+
+ if (frame->can_id & CAN_EFF_FLAG) {
+ dev_dbg(ND2D(dev), "sending extended frame\n");
+
+ can_id = (frame->can_id & CAN_EFF_MASK) << 1;
+ if (rtr)
+ can_id |= 1;
+ out_be16(®s->tx.idr3_2, can_id);
+
+ can_id >>= 16;
+ can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) | (3 << 3);
+ } else {
+ dev_dbg(ND2D(dev), "sending standard frame\n");
+ can_id = (frame->can_id & CAN_SFF_MASK) << 5;
+ if (rtr)
+ can_id |= 1 << 4;
+ }
+ out_be16(®s->tx.idr1_0, can_id);
+
+ if (!rtr) {
+ volatile void __iomem *data = ®s->tx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ /*Its safe to write into dsr[dlc+1] */
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ out_be16(data, *payload++);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ out_8(®s->tx.dlr, frame->can_dlc);
+ out_8(®s->tx.tbpr, priv->cur_pri);
+
+ /* Start transmission. */
+ out_8(®s->cantflg, 1 << buf_id);
+
+ if (!test_bit(F_TX_PROGRESS, &priv->flags))
+ dev->trans_start = jiffies;
+
+ list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head);
+
+ can_put_echo_skb(skb, dev, buf_id);
+
+ /* Enable interrupt. */
+ priv->tx_active |= 1 << buf_id;
+ out_8(®s->cantier, priv->tx_active);
+
+ return NETDEV_TX_OK;
+}
+
+static inline int check_set_state(struct net_device *dev, u8 canrflg)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ enum can_state state;
+ int ret = 0;
+
+ if (!(canrflg & MSCAN_CSCIF) || priv->can.state > CAN_STATE_BUS_OFF)
+ return 0;
+
+ state = state_map[max(MSCAN_STATE_RX(canrflg),
+ MSCAN_STATE_TX(canrflg))];
+ if (priv->can.state < state)
+ ret = 1;
+ if (state == CAN_STATE_BUS_OFF)
+ can_bus_off(dev);
+ priv->can.state = state;
+ return ret;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+static int mscan_rx_poll(struct napi_struct *napi, int quota)
+#else
+static int mscan_rx_poll(struct net_device *dev, int *budget)
+#endif
+{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi);
+ struct net_device *dev = priv->dev;
+#else
+ struct mscan_priv *priv = netdev_priv(dev);
+ int quota = min(dev->quota, *budget);
+#endif
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ int npackets = 0;
+ int ret = 1;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u32 can_id;
+ u8 canrflg;
+ int i;
+
+ while (npackets < quota && ((canrflg = in_8(®s->canrflg)) &
+ (MSCAN_RXF | MSCAN_ERR_IF))) {
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_notice(ND2D(dev), "packet dropped\n");
+ stats->rx_dropped++;
+ out_8(®s->canrflg, canrflg);
+ continue;
+ }
+
+ frame = (struct can_frame *)skb_put(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+
+ if (canrflg & MSCAN_RXF) {
+ can_id = in_be16(®s->rx.idr1_0);
+ if (can_id & (1 << 3)) {
+ frame->can_id = CAN_EFF_FLAG;
+ can_id = ((can_id << 16) |
+ in_be16(®s->rx.idr3_2));
+ can_id = ((can_id & 0xffe00000) |
+ ((can_id & 0x7ffff) << 2)) >> 2;
+ } else {
+ can_id >>= 4;
+ frame->can_id = 0;
+ }
+
+ frame->can_id |= can_id >> 1;
+ if (can_id & 1)
+ frame->can_id |= CAN_RTR_FLAG;
+ frame->can_dlc = in_8(®s->rx.dlr) & 0xf;
+
+ if (!(frame->can_id & CAN_RTR_FLAG)) {
+ volatile void __iomem *data = ®s->rx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ *payload++ = in_be16(data);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ dev_dbg(ND2D(dev),
+ "received pkt: id: %u dlc: %u data: ",
+ frame->can_id, frame->can_dlc);
+#ifdef DEBUG
+ for (i = 0;
+ i < frame->can_dlc && !(frame->can_id &
+ CAN_RTR_FLAG); i++)
+ printk(KERN_DEBUG "%2x ", frame->data[i]);
+ printk(KERN_DEBUG "\n");
+#endif
+
+ out_8(®s->canrflg, MSCAN_RXF);
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+ } else if (canrflg & MSCAN_ERR_IF) {
+ frame->can_id = CAN_ERR_FLAG;
+
+ if (canrflg & MSCAN_OVRIF) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ stats->rx_over_errors++;
+ } else
+ frame->data[1] = 0;
+
+ if (check_set_state(dev, canrflg)) {
+ frame->can_id |= CAN_ERR_CRTL;
+ switch (priv->can.state) {
+ case CAN_STATE_BUS_WARNING:
+ if ((priv->shadow_statflg &
+ MSCAN_RSTAT_MSK) <
+ (canrflg & MSCAN_RSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_WARNING;
+
+ if ((priv->shadow_statflg &
+ MSCAN_TSTAT_MSK) <
+ (canrflg & MSCAN_TSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING;
+ break;
+ case CAN_STATE_BUS_PASSIVE:
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_PASSIVE;
+ break;
+ case CAN_STATE_BUS_OFF:
+ frame->can_id |= CAN_ERR_BUSOFF;
+ frame->can_id &= ~CAN_ERR_CRTL;
+ break;
+ default:
+ break;
+ }
+ }
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ frame->can_dlc = CAN_ERR_DLC;
+ out_8(®s->canrflg, MSCAN_ERR_IF);
+ }
+
+ npackets++;
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_receive_skb(skb);
+ }
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
+ *budget -= npackets;
+ dev->quota -= npackets;
+#endif
+
+ if (!(in_8(®s->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ netif_rx_complete(dev, &priv->napi);
+#else
+ netif_rx_complete(dev);
+#endif
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+#if 0
+ out_8(®s->canrier,
+ in_8(®s->canrier) | MSCAN_ERR_IF | MSCAN_RXFIE);
+#else
+ if (priv->can.state < CAN_STATE_BUS_OFF)
+ out_8(®s->canrier, priv->shadow_canrier);
+#endif
+ ret = 0;
+ }
+ return ret;
+}
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static irqreturn_t mscan_isr(int irq, void *dev_id, struct pt_regs *r)
+#else
+static irqreturn_t mscan_isr(int irq, void *dev_id)
+#endif
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ u8 cantier, cantflg, canrflg;
+ irqreturn_t ret = IRQ_NONE;
+
+ cantier = in_8(®s->cantier) & MSCAN_TXE;
+ cantflg = in_8(®s->cantflg) & cantier;
+
+ if (cantier && cantflg) {
+
+ struct list_head *tmp, *pos;
+
+ list_for_each_safe(pos, tmp, &priv->tx_head) {
+ struct tx_queue_entry *entry =
+ list_entry(pos, struct tx_queue_entry, list);
+ u8 mask = entry->mask;
+
+ if (!(cantflg & mask))
+ continue;
+
+ out_8(®s->cantbsel, mask);
+ stats->tx_bytes += in_8(®s->tx.dlr);
+ stats->tx_packets++;
+ can_get_echo_skb(dev, entry->id);
+ priv->tx_active &= ~mask;
+ list_del(pos);
+ }
+
+ if (list_empty(&priv->tx_head)) {
+ clear_bit(F_TX_WAIT_ALL, &priv->flags);
+ clear_bit(F_TX_PROGRESS, &priv->flags);
+ priv->cur_pri = 0;
+ } else
+ dev->trans_start = jiffies;
+
+ if (!test_bit(F_TX_WAIT_ALL, &priv->flags))
+ netif_wake_queue(dev);
+
+ out_8(®s->cantier, priv->tx_active);
+ ret = IRQ_HANDLED;
+ }
+
+ canrflg = in_8(®s->canrflg);
+ if ((canrflg & ~MSCAN_STAT_MSK) &&
+ !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) {
+#if 0
+ printk(KERN_DEBUG "%s: canrflg=%#02x canrier=%#02x\n",
+ dev->name, canrflg, in_8(®s->canrier));
+#endif
+#if 0
+ if (check_set_state(dev, canrflg)) {
+ out_8(®s->canrflg, MSCAN_CSCIF);
+ ret = IRQ_HANDLED;
+ }
+#endif
+ if (canrflg & ~MSCAN_STAT_MSK) {
+ priv->shadow_canrier = in_8(®s->canrier);
+ out_8(®s->canrier, 0);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ netif_rx_schedule(dev, &priv->napi);
+#else
+ netif_rx_schedule(dev);
+#endif
+ ret = IRQ_HANDLED;
+ } else
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ }
+ return ret;
+}
+
+static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode)
+{
+
+ struct mscan_priv *priv = netdev_priv(dev);
+ int ret = 0;
+
+ if (!priv->open_time)
+ return -EINVAL;
+
+ switch (mode) {
+ case CAN_MODE_SLEEP:
+ case CAN_MODE_STOP:
+ netif_stop_queue(dev);
+ mscan_set_mode(dev,
+ (mode ==
+ CAN_MODE_STOP) ? MSCAN_INIT_MODE :
+ MSCAN_SLEEP_MODE);
+ break;
+ case CAN_MODE_START:
+ if (priv->can.state <= CAN_STATE_BUS_OFF)
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ ret = mscan_start(dev);
+ if (ret)
+ break;
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+static int mscan_do_set_bittiming(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ u8 btr0, btr1;
+
+ btr0 = BTR0_SET_BRP(bt->brp) | BTR0_SET_SJW(bt->sjw);
+ btr1 = (BTR1_SET_TSEG1(bt->prop_seg + bt->phase_seg1) |
+ BTR1_SET_TSEG2(bt->phase_seg2) |
+ BTR1_SET_SAM(priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES));
+
+ dev_info(ND2D(dev), "BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
+
+ out_8(®s->canbtr0, btr0);
+ out_8(®s->canbtr1, btr1);
+
+ return 0;
+}
+
+static int mscan_open(struct net_device *dev)
+{
+ int ret;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ /* determine and set bittime */
+ ret = can_set_bittiming(dev);
+ if (ret)
+ return ret;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ napi_enable(&priv->napi);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ ret = request_irq(dev->irq, mscan_isr, SA_SHIRQ, dev->name, dev);
+#else
+ ret = request_irq(dev->irq, mscan_isr, IRQF_SHARED, dev->name, dev);
+#endif
+
+ if (ret < 0) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ napi_disable(&priv->napi);
+#endif
+ printk(KERN_ERR "%s - failed to attach interrupt\n",
+ dev->name);
+ return ret;
+ }
+
+ priv->open_time = jiffies;
+
+ out_8(®s->canctl1, in_8(®s->canctl1) & ~MSCAN_LISTEN);
+
+ ret = mscan_start(dev);
+ if (ret)
+ return ret;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int mscan_close(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ napi_disable(&priv->napi);
+#endif
+
+ out_8(®s->cantier, 0);
+ out_8(®s->canrier, 0);
+ free_irq(dev->irq, dev);
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ can_close_cleanup(dev);
+ netif_stop_queue(dev);
+ priv->open_time = 0;
+
+ return 0;
+}
+
+int register_mscandev(struct net_device *dev, int clock_src)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ u8 ctl1;
+
+ ctl1 = in_8(®s->canctl1);
+ if (clock_src)
+ ctl1 |= MSCAN_CLKSRC;
+ else
+ ctl1 &= ~MSCAN_CLKSRC;
+
+ ctl1 |= MSCAN_CANE;
+ out_8(®s->canctl1, ctl1);
+ udelay(100);
+
+ /* acceptance mask/acceptance code (accept everything) */
+ out_be16(®s->canidar1_0, 0);
+ out_be16(®s->canidar3_2, 0);
+ out_be16(®s->canidar5_4, 0);
+ out_be16(®s->canidar7_6, 0);
+
+ out_be16(®s->canidmr1_0, 0xffff);
+ out_be16(®s->canidmr3_2, 0xffff);
+ out_be16(®s->canidmr5_4, 0xffff);
+ out_be16(®s->canidmr7_6, 0xffff);
+ /* Two 32 bit Acceptance Filters */
+ out_8(®s->canidac, MSCAN_AF_32BIT);
+
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+
+ return register_netdev(dev);
+}
+EXPORT_SYMBOL(register_mscandev);
+
+void unregister_mscandev(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ out_8(®s->canctl1, in_8(®s->canctl1) & ~MSCAN_CANE);
+ unregister_netdev(dev);
+}
+EXPORT_SYMBOL(unregister_mscandev);
+
+struct net_device *alloc_mscandev(void)
+{
+ struct net_device *dev;
+ struct mscan_priv *priv;
+ int i;
+
+ dev = alloc_candev(sizeof(struct mscan_priv));
+ if (!dev)
+ return NULL;
+ priv = netdev_priv(dev);
+
+ dev->open = mscan_open;
+ dev->stop = mscan_close;
+ dev->hard_start_xmit = mscan_hard_start_xmit;
+
+ dev->flags |= IFF_ECHO; /* we support local echo */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ priv->dev = dev;
+ netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8);
+#else
+ dev->poll = mscan_rx_poll;
+ dev->weight = 8;
+#endif
+
+ priv->can.bittiming_const = &mscan_bittiming_const;
+ priv->can.do_set_bittiming = mscan_do_set_bittiming;
+ priv->can.do_set_mode = mscan_do_set_mode;
+
+ for (i = 0; i < TX_QUEUE_SIZE; i++) {
+ priv->tx_queue[i].id = i;
+ priv->tx_queue[i].mask = 1 << i;
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL(alloc_mscandev);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver for a mscan based chips");
--- /dev/null
+/*
+ * $Id$
+ *
+ * DESCRIPTION:
+ * Definitions of consts/structs to drive the Freescale MSCAN.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2004-2006, Varma Electronics Oy
+ *
+ * LICENCE:
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __MSCAN_H__
+#define __MSCAN_H__
+
+#include <linux/autoconf.h>
+#include <linux/types.h>
+
+/* MSCAN control register 0 (CANCTL0) bits */
+#define MSCAN_RXFRM 0x80
+#define MSCAN_RXACT 0x40
+#define MSCAN_CSWAI 0x20
+#define MSCAN_SYNCH 0x10
+#define MSCAN_TIME 0x08
+#define MSCAN_WUPE 0x04
+#define MSCAN_SLPRQ 0x02
+#define MSCAN_INITRQ 0x01
+
+/* MSCAN control register 1 (CANCTL1) bits */
+#define MSCAN_CANE 0x80
+#define MSCAN_CLKSRC 0x40
+#define MSCAN_LOOPB 0x20
+#define MSCAN_LISTEN 0x10
+#define MSCAN_WUPM 0x04
+#define MSCAN_SLPAK 0x02
+#define MSCAN_INITAK 0x01
+
+#ifdef CONFIG_PPC_MPC52xx
+#define MSCAN_CLKSRC_BUS 0
+#define MSCAN_CLKSRC_XTAL MSCAN_CLKSRC
+#else
+#define MSCAN_CLKSRC_BUS MSCAN_CLKSRC
+#define MSCAN_CLKSRC_XTAL 0
+#endif
+
+/* MSCAN receiver flag register (CANRFLG) bits */
+#define MSCAN_WUPIF 0x80
+#define MSCAN_CSCIF 0x40
+#define MSCAN_RSTAT1 0x20
+#define MSCAN_RSTAT0 0x10
+#define MSCAN_TSTAT1 0x08
+#define MSCAN_TSTAT0 0x04
+#define MSCAN_OVRIF 0x02
+#define MSCAN_RXF 0x01
+#define MSCAN_ERR_IF (MSCAN_OVRIF | MSCAN_CSCIF)
+#define MSCAN_RSTAT_MSK (MSCAN_RSTAT1 | MSCAN_RSTAT0)
+#define MSCAN_TSTAT_MSK (MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STAT_MSK (MSCAN_RSTAT_MSK | MSCAN_TSTAT_MSK)
+
+#define MSCAN_STATE_BUS_OFF (MSCAN_RSTAT1 | MSCAN_RSTAT0 | \
+ MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STATE_TX(canrflg) (((canrflg)&MSCAN_TSTAT_MSK)>>2)
+#define MSCAN_STATE_RX(canrflg) (((canrflg)&MSCAN_RSTAT_MSK)>>4)
+#define MSCAN_STATE_ACTIVE 0
+#define MSCAN_STATE_WARNING 1
+#define MSCAN_STATE_PASSIVE 2
+#define MSCAN_STATE_BUSOFF 3
+
+/* MSCAN receiver interrupt enable register (CANRIER) bits */
+#define MSCAN_WUPIE 0x80
+#define MSCAN_CSCIE 0x40
+#define MSCAN_RSTATE1 0x20
+#define MSCAN_RSTATE0 0x10
+#define MSCAN_TSTATE1 0x08
+#define MSCAN_TSTATE0 0x04
+#define MSCAN_OVRIE 0x02
+#define MSCAN_RXFIE 0x01
+
+/* MSCAN transmitter flag register (CANTFLG) bits */
+#define MSCAN_TXE2 0x04
+#define MSCAN_TXE1 0x02
+#define MSCAN_TXE0 0x01
+#define MSCAN_TXE (MSCAN_TXE2 | MSCAN_TXE1 | MSCAN_TXE0)
+
+/* MSCAN transmitter interrupt enable register (CANTIER) bits */
+#define MSCAN_TXIE2 0x04
+#define MSCAN_TXIE1 0x02
+#define MSCAN_TXIE0 0x01
+#define MSCAN_TXIE (MSCAN_TXIE2 | MSCAN_TXIE1 | MSCAN_TXIE0)
+
+/* MSCAN transmitter message abort request (CANTARQ) bits */
+#define MSCAN_ABTRQ2 0x04
+#define MSCAN_ABTRQ1 0x02
+#define MSCAN_ABTRQ0 0x01
+
+/* MSCAN transmitter message abort ack (CANTAAK) bits */
+#define MSCAN_ABTAK2 0x04
+#define MSCAN_ABTAK1 0x02
+#define MSCAN_ABTAK0 0x01
+
+/* MSCAN transmit buffer selection (CANTBSEL) bits */
+#define MSCAN_TX2 0x04
+#define MSCAN_TX1 0x02
+#define MSCAN_TX0 0x01
+
+/* MSCAN ID acceptance control register (CANIDAC) bits */
+#define MSCAN_IDAM1 0x20
+#define MSCAN_IDAM0 0x10
+#define MSCAN_IDHIT2 0x04
+#define MSCAN_IDHIT1 0x02
+#define MSCAN_IDHIT0 0x01
+
+#define MSCAN_AF_32BIT 0x00
+#define MSCAN_AF_16BIT MSCAN_IDAM0
+#define MSCAN_AF_8BIT MSCAN_IDAM1
+#define MSCAN_AF_CLOSED (MSCAN_IDAM0|MSCAN_IDAM1)
+#define MSCAN_AF_MASK (~(MSCAN_IDAM0|MSCAN_IDAM1))
+
+/* MSCAN Miscellaneous Register (CANMISC) bits */
+#define MSCAN_BOHOLD 0x01
+
+#ifdef CONFIG_PPC_MPC52xx
+#define _MSCAN_RESERVED_(n, num) u8 _res##n[num]
+#define _MSCAN_RESERVED_DSR_SIZE 2
+#else
+#define _MSCAN_RESERVED_(n, num)
+#define _MSCAN_RESERVED_DSR_SIZE 0
+#endif
+
+/* Structure of the hardware registers */
+struct mscan_regs {
+ /* (see doco S12MSCANV3/D) MPC5200 MSCAN */
+ u8 canctl0; /* + 0x00 0x00 */
+ u8 canctl1; /* + 0x01 0x01 */
+ _MSCAN_RESERVED_(1, 2); /* + 0x02 */
+ u8 canbtr0; /* + 0x04 0x02 */
+ u8 canbtr1; /* + 0x05 0x03 */
+ _MSCAN_RESERVED_(2, 2); /* + 0x06 */
+ u8 canrflg; /* + 0x08 0x04 */
+ u8 canrier; /* + 0x09 0x05 */
+ _MSCAN_RESERVED_(3, 2); /* + 0x0a */
+ u8 cantflg; /* + 0x0c 0x06 */
+ u8 cantier; /* + 0x0d 0x07 */
+ _MSCAN_RESERVED_(4, 2); /* + 0x0e */
+ u8 cantarq; /* + 0x10 0x08 */
+ u8 cantaak; /* + 0x11 0x09 */
+ _MSCAN_RESERVED_(5, 2); /* + 0x12 */
+ u8 cantbsel; /* + 0x14 0x0a */
+ u8 canidac; /* + 0x15 0x0b */
+ u8 reserved; /* + 0x16 0x0c */
+ _MSCAN_RESERVED_(6, 5); /* + 0x17 */
+#ifndef CONFIG_PPC_MPC52xx
+ u8 canmisc; /* 0x0d */
+#endif
+ u8 canrxerr; /* + 0x1c 0x0e */
+ u8 cantxerr; /* + 0x1d 0x0f */
+ _MSCAN_RESERVED_(7, 2); /* + 0x1e */
+ u16 canidar1_0; /* + 0x20 0x10 */
+ _MSCAN_RESERVED_(8, 2); /* + 0x22 */
+ u16 canidar3_2; /* + 0x24 0x12 */
+ _MSCAN_RESERVED_(9, 2); /* + 0x26 */
+ u16 canidmr1_0; /* + 0x28 0x14 */
+ _MSCAN_RESERVED_(10, 2); /* + 0x2a */
+ u16 canidmr3_2; /* + 0x2c 0x16 */
+ _MSCAN_RESERVED_(11, 2); /* + 0x2e */
+ u16 canidar5_4; /* + 0x30 0x18 */
+ _MSCAN_RESERVED_(12, 2); /* + 0x32 */
+ u16 canidar7_6; /* + 0x34 0x1a */
+ _MSCAN_RESERVED_(13, 2); /* + 0x36 */
+ u16 canidmr5_4; /* + 0x38 0x1c */
+ _MSCAN_RESERVED_(14, 2); /* + 0x3a */
+ u16 canidmr7_6; /* + 0x3c 0x1e */
+ _MSCAN_RESERVED_(15, 2); /* + 0x3e */
+ struct {
+ u16 idr1_0; /* + 0x40 0x20 */
+ _MSCAN_RESERVED_(16, 2); /* + 0x42 */
+ u16 idr3_2; /* + 0x44 0x22 */
+ _MSCAN_RESERVED_(17, 2); /* + 0x46 */
+ u16 dsr1_0; /* + 0x48 0x24 */
+ _MSCAN_RESERVED_(18, 2); /* + 0x4a */
+ u16 dsr3_2; /* + 0x4c 0x26 */
+ _MSCAN_RESERVED_(19, 2); /* + 0x4e */
+ u16 dsr5_4; /* + 0x50 0x28 */
+ _MSCAN_RESERVED_(20, 2); /* + 0x52 */
+ u16 dsr7_6; /* + 0x54 0x2a */
+ _MSCAN_RESERVED_(21, 2); /* + 0x56 */
+ u8 dlr; /* + 0x58 0x2c */
+ u8:8; /* + 0x59 0x2d */
+ _MSCAN_RESERVED_(22, 2); /* + 0x5a */
+ u16 time; /* + 0x5c 0x2e */
+ } rx;
+ _MSCAN_RESERVED_(23, 2); /* + 0x5e */
+ struct {
+ u16 idr1_0; /* + 0x60 0x30 */
+ _MSCAN_RESERVED_(24, 2); /* + 0x62 */
+ u16 idr3_2; /* + 0x64 0x32 */
+ _MSCAN_RESERVED_(25, 2); /* + 0x66 */
+ u16 dsr1_0; /* + 0x68 0x34 */
+ _MSCAN_RESERVED_(26, 2); /* + 0x6a */
+ u16 dsr3_2; /* + 0x6c 0x36 */
+ _MSCAN_RESERVED_(27, 2); /* + 0x6e */
+ u16 dsr5_4; /* + 0x70 0x38 */
+ _MSCAN_RESERVED_(28, 2); /* + 0x72 */
+ u16 dsr7_6; /* + 0x74 0x3a */
+ _MSCAN_RESERVED_(29, 2); /* + 0x76 */
+ u8 dlr; /* + 0x78 0x3c */
+ u8 tbpr; /* + 0x79 0x3d */
+ _MSCAN_RESERVED_(30, 2); /* + 0x7a */
+ u16 time; /* + 0x7c 0x3e */
+ } tx;
+ _MSCAN_RESERVED_(31, 2); /* + 0x7e */
+} __attribute__ ((packed));
+
+#undef _MSCAN_RESERVED_
+#define MSCAN_REGION sizeof(struct mscan)
+
+#define MSCAN_WATCHDOG_TIMEOUT ((500*HZ)/1000)
+
+struct mscan_platform_data {
+ u8 clock_src; /* MSCAN_CLKSRC_BUS or MSCAN_CLKSRC_XTAL */
+ u32 clock_frq; /* can ref. clock, in Hz */
+};
+
+struct net_device *alloc_mscandev(void);
+/* @clock_src:
+ 1 = The MSCAN clock source is the onchip Bus Clock.
+ 0 = The MSCAN clock source is the chip Oscillator Clock.
+*/
+extern int register_mscandev(struct net_device *dev, int clock_src);
+extern void unregister_mscandev(struct net_device *dev);
+
+#endif /* __MSCAN_H__ */
--- /dev/null
+
+config CAN_SJA1000_OLD
+ depends on CAN_OLD_DRIVERS
+ tristate "Philips SJA1000 (old)"
+ ---help---
+ The SJA1000 is one of the top CAN controllers out there. As it
+ has a multiplexed interface it fits directly to 8051
+ microcontrollers or into the PC I/O port space. The SJA1000
+ is a full CAN controller, with shadow registers for RX and TX.
+ It can send and receive any kinds of CAN frames (SFF/EFF/RTR)
+ with a single (simple) filter setup.
+ REMARK: This is the 'old' driver originally written by Matthias
+ Brukner and Oliver Hartkopp which uses a non-standard hardware
+ abstaction layer (HAL) inspired by the OCAN driver.
+
+config CAN_I82527_OLD
+ depends on CAN_OLD_DRIVERS
+ tristate "Intel 82527 (old)"
+ ---help---
+ The i82527 is a complex CAN controller that can handle RTR
+ frame replies on it's own. This feature (and diffent RX filters)
+ lead to an amount of 15 message objects (for RX & TX). Message
+ object 15 has (as only) a shadow register for a reliable
+ receiption of EFF or(!) SFF frames at high CAN traffic.
+ This driver can send each type of CAN frames (SFF/EFF/RTR).
+ Using 4 message objects it can also receive each type of CAN
+ frames. But due to the onchip filter matching trigger method
+ it is not possible to determine the received RTR CAN-ID.
+ The reliable message object 15 receives SFF frames by default.
+ This message object 15 usage maybe changed with the mo15 param.
+ REMARK: This is the 'old' driver originally written by Oliver
+ Hartkopp which uses a non-standard hardware abstaction layer (HAL)
+ inspired by the OCAN driver. http://ar.linux.it/software/#ocan
+
+config CAN_MSCAN_OLD
+ depends on CAN_OLD_DRIVERS && (PPC || M68K || M68KNOMMU)
+ tristate "Support for a Freescale MSCAN based chips (old)"
+ ---help---
+ The Motorola Scalable Controller Area Network (MSCAN) definition
+ is based on the MSCAN12 definition which is the specific
+ implementation of the Motorola Scalable CAN concept targeted for
+ the Motorola MC68HC12 Microcontroller Family.
+
+config CAN_MPC52XX_OLD
+ tristate "Freescale MPC5200 onboard CAN controller (old)"
+ depends on CAN_MSCAN_OLD && (PPC_MPC52xx || PPC_52xx)
+ default LITE5200
+ ---help---
+ If you say yes here you get support for Freescale MPC5200
+ onboard dualCAN controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called mpc52xx_can.
+
+config CAN_CCAN_OLD
+ depends on CAN_OLD_DRIVERS
+ tristate "Bosch CCAN driver (old)"
+ ---help---
+ This is a driver for the Bosch CCAN controller found for example
+ on the hynix h7202 chip.
+
+config CAN_H7202_OLD
+ tristate "Hynix H7202 onboard CAN controller (old)"
+ depends on CAN_CCAN_OLD
+ ---help---
+ This is a driver for the hynix h7202 can controller.
+
--- /dev/null
+#
+# $Id: Makefile 438 2007-07-21 17:53:09Z hartkopp $
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+obj-$(CONFIG_CAN_CCAN) += ccan.o
+obj-$(CONFIG_CAN_H7202) += h7202_can.o
+
+endif
--- /dev/null
+/*
+ * drivers/can/c_can.c
+ *
+ * Copyright (C) 2007
+ *
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...)
+#endif
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/can.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include "ccan.h"
+
+static u32 ccan_read_reg32(struct net_device *dev, enum c_regs reg)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ u32 val = priv->read_reg(dev, reg);
+ val |= ((u32) priv->read_reg(dev, reg + 2)) << 16;
+
+ return val;
+}
+
+static void ccan_write_reg32(struct net_device *dev, enum c_regs reg, u32 val)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->write_reg(dev, reg, val & 0xffff);
+ priv->write_reg(dev, reg + 2, val >> 16);
+}
+
+static inline void ccan_object_get(struct net_device *dev,
+ int iface, int objno, int mask)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->write_reg(dev, CAN_IF_COMM(iface), mask);
+ priv->write_reg(dev, CAN_IF_COMR(iface), objno + 1);
+ while (priv->read_reg(dev, CAN_IF_COMR(iface)) & IF_COMR_BUSY)
+ DBG("busy\n");
+}
+
+static inline void ccan_object_put(struct net_device *dev,
+ int iface, int objno, int mask)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->write_reg(dev, CAN_IF_COMM(iface), IF_COMM_WR | mask);
+ priv->write_reg(dev, CAN_IF_COMR(iface), objno + 1);
+ while (priv->read_reg(dev, CAN_IF_COMR(iface)) & IF_COMR_BUSY)
+ DBG("busy\n");
+}
+
+static int ccan_write_object(struct net_device *dev,
+ int iface, struct can_frame *frame, int objno)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned int val;
+
+ if (frame->can_id & CAN_EFF_FLAG)
+ val = IF_ARB_MSGXTD | (frame->can_id & CAN_EFF_MASK);
+ else
+ val = ((frame->can_id & CAN_SFF_MASK) << 18);
+
+ if (!(frame->can_id & CAN_RTR_FLAG))
+ val |= IF_ARB_TRANSMIT;
+
+ val |= IF_ARB_MSGVAL;
+ ccan_write_reg32(dev, CAN_IF_ARB(iface), val);
+
+ memcpy(&val, &frame->data[0], 4);
+ ccan_write_reg32(dev, CAN_IF_DATAA(iface), val);
+ memcpy(&val, &frame->data[4], 4);
+ ccan_write_reg32(dev, CAN_IF_DATAB(iface), val);
+ priv->write_reg(dev, CAN_IF_MCONT(iface),
+ IF_MCONT_TXIE | IF_MCONT_TXRQST | IF_MCONT_EOB |
+ (frame->can_dlc & 0xf));
+
+ ccan_object_put(dev, 0, objno, IF_COMM_ALL);
+
+ return 0;
+}
+
+static int ccan_read_object(struct net_device *dev, int iface, int objno)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned int val, ctrl, data;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ skb->dev = dev;
+
+ ccan_object_get(dev, 0, objno, IF_COMM_ALL & ~IF_COMM_TXRQST);
+#ifdef CCAN_DEBUG
+ priv->bufstat[objno]++;
+#endif
+ frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+ ctrl = priv->read_reg(dev, CAN_IF_MCONT(iface));
+
+ if (ctrl & IF_MCONT_MSGLST) {
+ priv->can.net_stats.rx_errors++;
+ DBG("%s: msg lost in buffer %d\n", __func__, objno);
+ }
+
+ frame->can_dlc = ctrl & 0xf;
+
+ val = ccan_read_reg32(dev, CAN_IF_ARB(iface));
+
+ data = ccan_read_reg32(dev, CAN_IF_DATAA(iface));
+ memcpy(&frame->data[0], &data, 4);
+ data = ccan_read_reg32(dev, CAN_IF_DATAB(iface));
+ memcpy(&frame->data[4], &data, 4);
+
+ if (val & IF_ARB_MSGXTD)
+ frame->can_id = (val & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ else
+ frame->can_id = (val >> 18) & CAN_SFF_MASK;
+
+ if (val & IF_ARB_TRANSMIT)
+ frame->can_id |= CAN_RTR_FLAG;
+
+ priv->write_reg(dev, CAN_IF_MCONT(iface), ctrl &
+ ~(IF_MCONT_MSGLST | IF_MCONT_INTPND | IF_MCONT_NEWDAT));
+
+ ccan_object_put(dev, 0, objno, IF_COMM_CONTROL);
+
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ netif_rx(skb);
+
+ priv->can.net_stats.rx_packets++;
+ priv->can.net_stats.rx_bytes += frame->can_dlc;
+
+ return 0;
+}
+
+static int ccan_setup_receive_object(struct net_device *dev, int iface,
+ int objno, unsigned int mask,
+ unsigned int id, unsigned int mcont)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_write_reg32(dev, CAN_IF_MASK(iface), mask);
+ ccan_write_reg32(dev, CAN_IF_ARB(iface), IF_ARB_MSGVAL | id);
+
+ priv->write_reg(dev, CAN_IF_MCONT(iface), mcont);
+
+ ccan_object_put(dev, 0, objno, IF_COMM_ALL & ~IF_COMM_TXRQST);
+
+ DBG("%s: obj no %d msgval: 0x%08x\n", __func__,
+ objno, ccan_read_reg32(dev, CAN_MSGVAL));
+
+ return 0;
+}
+
+static int ccan_inval_object(struct net_device *dev, int iface, int objno)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_write_reg32(dev, CAN_IF_ARB(iface), 0);
+ priv->write_reg(dev, CAN_IF_MCONT(iface), 0);
+ ccan_object_put(dev, 0, objno, IF_COMM_ARB | IF_COMM_CONTROL);
+
+ DBG("%s: obj no %d msgval: 0x%08x\n", __func__,
+ objno, ccan_read_reg32(dev, CAN_MSGVAL));
+
+ return 0;
+}
+
+static int ccan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ struct can_frame *frame = (struct can_frame *)skb->data;
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ ccan_write_object(dev, 0, frame, priv->tx_object);
+#ifdef CCAN_DEBUG
+ priv->bufstat[priv->tx_object]++;
+#endif
+ priv->tx_object++;
+ if (priv->tx_object > 5)
+ netif_stop_queue(dev);
+
+ spin_unlock_irq(&priv->can.irq_lock);
+
+ priv->can.net_stats.tx_packets++;
+ priv->can.net_stats.tx_bytes += frame->can_dlc;
+
+ dev->trans_start = jiffies;
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+static void ccan_tx_timeout(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->can.net_stats.tx_errors++;
+}
+
+static int ccan_set_bittime(struct net_device *dev, struct can_bittime *br)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned int reg_timing, ctrl_save;
+ u8 brp, sjw, tseg1, tseg2;
+
+ if (br->type != CAN_BITTIME_STD)
+ return -EINVAL;
+
+ brp = br->std.brp - 1;
+ sjw = br->std.sjw - 1;
+ tseg1 = br->std.prop_seg + br->std.phase_seg1 - 1;
+ tseg2 = br->std.phase_seg2 - 1;
+
+ reg_timing = (brp & BTR_BRP_MASK) |
+ ((sjw << BTR_SJW_SHIFT) & BTR_SJW_MASK) |
+ ((tseg1 << BTR_TSEG1_SHIFT) & BTR_TSEG1_MASK) |
+ ((tseg2 << BTR_TSEG2_SHIFT) & BTR_TSEG2_MASK);
+
+ DBG("%s: brp = %d sjw = %d seg1 = %d seg2 = %d\n", __func__,
+ brp, sjw, tseg1, tseg2);
+ DBG("%s: setting BTR to %04x\n", __func__, reg_timing);
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ ctrl_save = priv->read_reg(dev, CAN_CONTROL);
+ priv->write_reg(dev, CAN_CONTROL,
+ ctrl_save | CONTROL_CCE | CONTROL_INIT);
+ priv->write_reg(dev, CAN_BTR, reg_timing);
+ priv->write_reg(dev, CAN_CONTROL, ctrl_save);
+
+ spin_unlock_irq(&priv->can.irq_lock);
+
+ return 0;
+}
+
+static int ccan_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ switch (mode) {
+ case CAN_MODE_START:
+ DBG("%s: CAN_MODE_START requested\n", __func__);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ccan_get_state(struct net_device *dev, enum can_state *state)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ u32 reg_status;
+#ifdef CCAN_DEBUG
+ int i;
+#endif
+
+ reg_status = priv->read_reg(dev, CAN_STATUS);
+
+ if (reg_status & STATUS_EPASS)
+ *state = CAN_STATE_BUS_PASSIVE;
+ else if (reg_status & STATUS_EWARN)
+ *state = CAN_STATE_BUS_WARNING;
+ else if (reg_status & STATUS_BOFF)
+ *state = CAN_STATE_BUS_OFF;
+ else
+ *state = CAN_STATE_ACTIVE;
+#ifdef CCAN_DEBUG
+ DBG("buffer statistic:\n");
+ for (i = 0; i <= MAX_OBJECT; i++)
+ DBG("%d: %d\n", i, priv->bufstat[i]);
+#endif
+ return 0;
+}
+
+static int ccan_do_status_irq(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ int status, diff;
+
+ status = priv->read_reg(dev, CAN_STATUS);
+ status &= ~(STATUS_TXOK | STATUS_RXOK);
+ diff = status ^ priv->last_status;
+
+ if (diff & STATUS_EPASS) {
+ if (status & STATUS_EPASS)
+ dev_info(ND2D(dev), "entered error passive state\n");
+ else
+ dev_info(ND2D(dev), "left error passive state\n");
+ }
+ if (diff & STATUS_EWARN) {
+ if (status & STATUS_EWARN)
+ dev_info(ND2D(dev), "entered error warning state\n");
+ else
+ dev_info(ND2D(dev), "left error warning state\n");
+ }
+ if (diff & STATUS_BOFF) {
+ if (status & STATUS_BOFF)
+ dev_info(ND2D(dev), "entered busoff state\n");
+ else
+ dev_info(ND2D(dev), "left busoff state\n");
+ }
+
+ if (diff & STATUS_LEC_MASK) {
+ switch (status & STATUS_LEC_MASK) {
+ case LEC_STUFF_ERROR:
+ dev_info(ND2D(dev), "suffing error\n");
+ break;
+ case LEC_FORM_ERROR:
+ dev_info(ND2D(dev), "form error\n");
+ break;
+ case LEC_ACK_ERROR:
+ dev_info(ND2D(dev), "ack error\n");
+ break;
+ case LEC_BIT1_ERROR:
+ dev_info(ND2D(dev), "bit1 error\n");
+ break;
+ }
+ }
+
+ priv->write_reg(dev, CAN_STATUS, 0);
+ priv->last_status = status;
+
+ return diff ? 1 : 0;
+}
+
+static void ccan_do_object_irq(struct net_device *dev, u16 irqstatus)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ int i;
+ u32 val;
+
+ if (irqstatus > MAX_TRANSMIT_OBJECT) {
+ val = ccan_read_reg32(dev, CAN_NEWDAT);
+ while (val & RECEIVE_OBJECT_BITS) {
+ for (i = MAX_TRANSMIT_OBJECT + 1; i <= MAX_OBJECT; i++)
+ if (val & (1<<i))
+ ccan_read_object(dev, 0, i);
+ val = ccan_read_reg32(dev, CAN_NEWDAT);
+ }
+ } else {
+ ccan_inval_object(dev, 0, irqstatus - 1);
+ val = ccan_read_reg32(dev, CAN_TXRQST);
+ if (!val) {
+ priv->tx_object = 0;
+ netif_wake_queue(dev);
+ }
+ }
+}
+
+static void do_statuspoll(struct work_struct *work)
+{
+ struct ccan_priv *priv = container_of(((struct delayed_work *) work),
+ struct ccan_priv, work);
+
+ priv->write_reg(priv->dev, CAN_CONTROL,
+ CONTROL_SIE | CONTROL_EIE | CONTROL_IE);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static irqreturn_t ccan_isr(int irq, void *dev_id, struct pt_regs *regs)
+#else
+static irqreturn_t ccan_isr(int irq, void *dev_id)
+#endif
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct ccan_priv *priv = netdev_priv(dev);
+ u16 irqstatus;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->can.irq_lock, flags);
+
+ irqstatus = priv->read_reg(dev, CAN_IR);
+ while (irqstatus) {
+ if (irqstatus == 0x8000) {
+ if (ccan_do_status_irq(dev)) {
+ /* The c_can core tends to flood us with
+ * interrupts when certain error states don't
+ * disappear. Disable interrupts and see if it's
+ * getting better later. This is at least the
+ * case on the Magnachip h7202.
+ */
+ priv->write_reg(dev, CAN_CONTROL, CONTROL_EIE |
+ CONTROL_IE);
+ schedule_delayed_work(&priv->work, HZ / 10);
+ goto exit;
+ }
+ } else {
+ ccan_do_object_irq(dev, irqstatus);
+ }
+ irqstatus = priv->read_reg(dev, CAN_IR);
+ }
+
+exit:
+ spin_unlock_irqrestore(&priv->can.irq_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int ccan_open(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ if (request_irq(dev->irq, &ccan_isr, 0, dev->name, dev)) {
+ dev_err(ND2D(dev), "failed to attach interrupt\n");
+ return -EAGAIN;
+ }
+
+ priv->write_reg(dev, CAN_CONTROL,
+ CONTROL_EIE | CONTROL_SIE | CONTROL_IE);
+
+ netif_wake_queue(dev);
+
+ return 0;
+}
+
+static int ccan_stop(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ netif_stop_queue(dev);
+
+ cancel_delayed_work(&priv->work);
+ flush_scheduled_work();
+
+ /* mask all IRQs */
+ spin_lock_irqsave(&priv->can.irq_lock, flags);
+ priv->write_reg(dev, CAN_CONTROL, 0);
+ spin_unlock_irqrestore(&priv->can.irq_lock, flags);
+
+ free_irq(dev->irq, dev);
+
+ return 0;
+}
+
+static int ccan_chip_config(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ int i;
+
+ /* setup message objects */
+ for (i = 0; i <= MAX_OBJECT; i++)
+ ccan_inval_object(dev, 0, i);
+
+ for (i = MAX_TRANSMIT_OBJECT + 1; i < MAX_OBJECT; i++)
+ ccan_setup_receive_object(dev, 0, i, 0, 0,
+ IF_MCONT_RXIE | IF_MCONT_UMASK);
+
+ ccan_setup_receive_object(dev, 0, MAX_OBJECT, 0, 0, IF_MCONT_EOB |
+ IF_MCONT_RXIE | IF_MCONT_UMASK);
+
+#ifdef CCAN_DEBUG
+ for (i = 0; i <= MAX_OBJECT; i++)
+ priv->bufstat[i] = 0;
+#endif
+
+ return 0;
+}
+
+struct net_device *alloc_ccandev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct ccan_priv *priv;
+
+ dev = alloc_candev(sizeof_priv);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+
+ dev->open = ccan_open;
+ dev->stop = ccan_stop;
+ dev->hard_start_xmit = ccan_hard_start_xmit;
+ dev->tx_timeout = ccan_tx_timeout;
+
+ priv->can.bitrate = 500000;
+
+ priv->can.do_set_bittime = ccan_set_bittime;
+ priv->can.do_get_state = ccan_get_state;
+ priv->can.do_set_mode = ccan_set_mode;
+
+ priv->dev = dev;
+ priv->tx_object = 0;
+
+ return dev;
+}
+EXPORT_SYMBOL(alloc_ccandev);
+
+void free_ccandev(struct net_device *dev)
+{
+ free_candev(dev);
+}
+EXPORT_SYMBOL(free_ccandev);
+
+int register_ccandev(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_set_mode(dev, CAN_MODE_START);
+
+ ccan_chip_config(dev);
+ INIT_DELAYED_WORK(&priv->work, do_statuspoll);
+
+ return register_netdev(dev);
+}
+EXPORT_SYMBOL(register_ccandev);
+
+void unregister_ccandev(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_set_mode(dev, CAN_MODE_START);
+
+ cancel_delayed_work(&priv->work);
+ flush_scheduled_work();
+
+ unregister_netdev(dev);
+}
+EXPORT_SYMBOL(unregister_ccandev);
+
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Simon Kallweit <simon.kallweit@intefo.ch>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver for C_CAN based chips");
--- /dev/null
+/*
+ * drivers/can/c_can.h
+ *
+ * Copyright (C) 2007
+ *
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CCAN_H__
+#define __CCAN_H__
+
+#include <linux/can.h>
+#include <linux/platform_device.h>
+
+#undef CCAN_DEBUG
+
+enum c_regs {
+ CAN_CONTROL = 0x00,
+ CAN_STATUS = 0x02,
+ CAN_ERROR = 0x04,
+ CAN_BTR = 0x06,
+ CAN_IR = 0x08,
+ CAN_TEST = 0x0a,
+ CAN_BRP_EXT = 0x0c,
+ CAN_IF1 = 0x10,
+ CAN_IF2 = 0x40,
+ CAN_TXRQST = 0x80, /* 32bit */
+ CAN_NEWDAT = 0x90, /* 32bit */
+ CAN_INTPND = 0xa0, /* 32bit */
+ CAN_MSGVAL = 0xb0, /* 32bit */
+};
+
+#define CAN_IF_COMR(x) (CAN_IF1 + (x) * 0x30 + 0x00)
+#define CAN_IF_COMM(x) (CAN_IF1 + (x) * 0x30 + 0x02)
+#define CAN_IF_MASK(x) (CAN_IF1 + (x) * 0x30 + 0x04) /* 32bit */
+#define CAN_IF_ARB(x) (CAN_IF1 + (x) * 0x30 + 0x08) /* 32bit */
+#define CAN_IF_MCONT(x) (CAN_IF1 + (x) * 0x30 + 0x0c)
+#define CAN_IF_DATAA(x) (CAN_IF1 + (x) * 0x30 + 0x0e) /* 32bit */
+#define CAN_IF_DATAB(x) (CAN_IF1 + (x) * 0x30 + 0x12) /* 32bit */
+
+#define CONTROL_TEST (1<<7)
+#define CONTROL_CCE (1<<6)
+#define CONTROL_DAR (1<<5)
+#define CONTROL_EIE (1<<3)
+#define CONTROL_SIE (1<<2)
+#define CONTROL_IE (1<<1)
+#define CONTROL_INIT (1<<0)
+
+#define TEST_RX (1<<7)
+#define TEST_TX1 (1<<6)
+#define TEST_TX2 (1<<5)
+#define TEST_LBACK (1<<4)
+#define TEST_SILENT (1<<3)
+#define TEST_BASIC (1<<2)
+
+#define STATUS_BOFF (1<<7)
+#define STATUS_EWARN (1<<6)
+#define STATUS_EPASS (1<<5)
+#define STATUS_RXOK (1<<4)
+#define STATUS_TXOK (1<<3)
+#define STATUS_LEC_MASK (1<<2)
+#define LEC_STUFF_ERROR 1
+#define LEC_FORM_ERROR 2
+#define LEC_ACK_ERROR 3
+#define LEC_BIT1_ERROR 4
+
+#define BTR_BRP_MASK 0x3f
+#define BTR_BRP_SHIFT 0
+#define BTR_SJW_SHIFT 6
+#define BTR_SJW_MASK (0x3 << BTR_SJW_SHIFT)
+#define BTR_TSEG1_SHIFT 8
+#define BTR_TSEG1_MASK (0xf << BTR_TSEG1_SHIFT)
+#define BTR_TSEG2_SHIFT 12
+#define BTR_TSEG2_MASK (0x7 << BTR_TSEG2_SHIFT)
+
+#define IF_COMR_BUSY (1<<15)
+
+#define IF_COMM_WR (1<<7)
+#define IF_COMM_MASK (1<<6)
+#define IF_COMM_ARB (1<<5)
+#define IF_COMM_CONTROL (1<<4)
+#define IF_COMM_CLR_INT_PND (1<<3)
+#define IF_COMM_TXRQST (1<<2)
+#define IF_COMM_DATAA (1<<1)
+#define IF_COMM_DATAB (1<<0)
+
+#define IF_COMM_ALL (IF_COMM_MASK | IF_COMM_ARB | IF_COMM_CONTROL | \
+ IF_COMM_TXRQST | IF_COMM_DATAA | IF_COMM_DATAB)
+
+#define IF_ARB_MSGVAL (1<<31)
+#define IF_ARB_MSGXTD (1<<30)
+#define IF_ARB_TRANSMIT (1<<29)
+
+#define IF_MCONT_NEWDAT (1<<15)
+#define IF_MCONT_MSGLST (1<<14)
+#define IF_MCONT_INTPND (1<<13)
+#define IF_MCONT_UMASK (1<<12)
+#define IF_MCONT_TXIE (1<<11)
+#define IF_MCONT_RXIE (1<<10)
+#define IF_MCONT_RMTEN (1<<9)
+#define IF_MCONT_TXRQST (1<<8)
+#define IF_MCONT_EOB (1<<7)
+
+#define MAX_OBJECT 31
+#define MAX_TRANSMIT_OBJECT 15
+#define RECEIVE_OBJECT_BITS 0xffff0000
+
+struct ccan_priv {
+ struct can_priv can;
+ struct net_device *dev;
+ int tx_object;
+ int last_status;
+ struct delayed_work work;
+ u16 (*read_reg)(struct net_device *dev, enum c_regs reg);
+ void (*write_reg)(struct net_device *dev, enum c_regs reg, u16 val);
+#ifdef CCAN_DEBUG
+ unsigned int bufstat[MAX_OBJECT + 1];
+#endif
+};
+
+extern struct net_device *alloc_ccandev(int sizeof_priv);
+extern void free_ccandev(struct net_device *dev);
+extern int register_ccandev(struct net_device *dev);
+extern void unregister_ccandev(struct net_device *dev);
+
+#endif /* __CCAN_H__ */
--- /dev/null
+/*
+ * drivers/can/h7202_can.c
+ *
+ * Copyright (C) 2007
+ *
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+#include <asm/hardware.h>
+
+#include "ccan.h"
+
+#define DRV_NAME "h7202can"
+#define DELAY 5
+#define CAN_ENABLE 0x0e
+
+static u16 h7202can_read_reg(struct net_device *dev, enum c_regs reg)
+{
+ u16 val;
+ volatile int i;
+
+ /* The big kernel lock is used to prevent any other AMBA devices from
+ * interfering with the current register read operation. The register
+ * is read twice because of braindamaged hynix cpu.
+ */
+ lock_kernel();
+ val = inw(dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ val = inw(dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ unlock_kernel();
+
+ return val;
+}
+
+static void h7202can_write_reg(struct net_device *dev, enum c_regs reg, u16 val)
+{
+ volatile int i;
+
+ lock_kernel();
+ outw(val, dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ unlock_kernel();
+}
+
+static int h7202can_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct ccan_priv *priv;
+ struct resource *mem;
+ u32 mem_size;
+ int ret = -ENODEV;
+
+ dev = alloc_ccandev(sizeof(struct ccan_priv));
+ if (!dev)
+ return -ENOMEM;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->irq = platform_get_irq(pdev, 0);
+ if (!mem || !dev->irq)
+ goto req_error;
+
+ mem_size = mem->end - mem->start + 1;
+ if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
+ dev_err(&pdev->dev, "resource unavailable\n");
+ goto req_error;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
+
+ if (!dev->base_addr) {
+ dev_err(&pdev->dev, "failed to map can port\n");
+ ret = -ENOMEM;
+ goto fail_map;
+ }
+
+ priv = netdev_priv(dev);
+ priv->can.can_sys_clock = 8000000;
+ priv->read_reg = h7202can_read_reg;
+ priv->write_reg = h7202can_write_reg;
+
+ platform_set_drvdata(pdev, dev);
+
+ /* configure ports */
+ switch (mem->start) {
+ case CAN0_PHYS:
+ CPU_REG(GPIO_C_VIRT, GPIO_EN) &= ~(3<<1);
+ CPU_REG(GPIO_C_VIRT, GPIO_DIR) &= ~(1<<1);
+ CPU_REG(GPIO_C_VIRT, GPIO_DIR) |= (1<<2);
+ break;
+ case CAN1_PHYS:
+ CPU_REG(GPIO_E_VIRT, GPIO_EN) &= ~(3<<16);
+ CPU_REG(GPIO_E_VIRT, GPIO_DIR) |= (1<<16);
+ CPU_REG(GPIO_E_VIRT, GPIO_DIR) &= ~(1<<17);
+ break;
+ }
+
+ /* enable can */
+ h7202can_write_reg(dev, CAN_ENABLE, 1);
+
+ ret = register_ccandev(dev);
+ if (ret >= 0) {
+ dev_info(&pdev->dev, "probe for a port 0x%lX done\n",
+ dev->base_addr);
+ return ret;
+ }
+
+ iounmap((unsigned long *)dev->base_addr);
+fail_map:
+ release_mem_region(mem->start, mem_size);
+req_error:
+ free_ccandev(dev);
+ dev_err(&pdev->dev, "probe failed\n");
+ return ret;
+}
+
+static int h7202can_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_ccandev(dev);
+
+ iounmap((volatile void __iomem *)(dev->base_addr));
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+ free_ccandev(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int h7202can_drv_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int h7202can_drv_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct platform_driver h7202can_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = h7202can_drv_probe,
+ .remove = h7202can_drv_remove,
+#ifdef CONFIG_PM
+ .suspend = h7202can_drv_suspend,
+ .resume = h7202can_drv_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init h7202can_init(void)
+{
+ printk(KERN_INFO "%s initializing\n", h7202can_driver.driver.name);
+ return platform_driver_register(&h7202can_driver);
+}
+
+static void __exit h7202can_cleanup(void)
+{
+ platform_driver_unregister(&h7202can_driver);
+ printk(KERN_INFO "%s unloaded\n", h7202can_driver.driver.name);
+}
+
+module_init(h7202can_init);
+module_exit(h7202can_cleanup);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Simon Kallweit <simon.kallweit@intefo.ch>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver Hynix H7202 processor");
--- /dev/null
+/*
+ * c200.c - low cost parallelport CAN adaptor hardware abstraction layer
+ * ( direct register access without parport subsystem support )
+ *
+ * CAN200 project homepage http://private.addcom.de/horo/can200
+ *
+ * This hal is based on a patch from Uwe Bonnes.
+ *
+ * $Id$
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "c200"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x378UL;
+ irq[0] = 7;
+}
+
+#define ECR_REGS_OFFSET 0x400
+#define ECR_CTRL_OFFSET (ECR_REGS_OFFSET + 2)
+
+static u8 ecr_crtl_save;
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* grab ECR control registers and set parport to 'byte mode' */
+ if (request_region(rbase[dev_num] + ECR_REGS_OFFSET, 3, drv_name)) {
+
+ ecr_crtl_save = inb(rbase[dev_num] + ECR_CTRL_OFFSET);
+
+ outb((ecr_crtl_save & 0x1F) | 0x20,
+ rbase[dev_num] + ECR_CTRL_OFFSET);
+ } else
+ return 0;
+
+ if (request_region(rbase[dev_num], 4, drv_name))
+ return 1;
+
+ release_region(rbase[dev_num] + ECR_REGS_OFFSET, 3);
+
+ return 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ release_region(base[dev_num], 4);
+
+ /* restore original ECR control register value */
+ outb(ecr_crtl_save, base[dev_num] + ECR_CTRL_OFFSET);
+ release_region(base[dev_num] + ECR_REGS_OFFSET, 3);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num)
+{
+ extern unsigned long rbase[];
+ unsigned long pc = rbase[dev_num] + 2;
+
+ /* enable irq */
+ outb(inb(pc) | 0x10, pc);
+
+ return 0;
+}
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num)
+{
+ extern unsigned long rbase[];
+ unsigned long pc = rbase[dev_num] + 2;
+
+ /* disable irq */
+ outb(inb(pc) & ~0x10, pc);
+
+ return 0;
+}
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+#define WRITEP 0x01 /* inverted at port */
+#define DATASTB 0x02 /* inverted at port and at device*/
+#define ADDRSTB 0x08 /* inverted at port and at device*/
+#define PORTREAD 0x20
+
+static DEFINE_SPINLOCK(c200_lock);
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg)
+{
+ unsigned long pa = base;
+ unsigned long pc = pa + 2;
+ unsigned long flags;
+ u8 irqstatus = (inb(pc) & 0x10) | 0x04;
+ u8 val;
+
+ spin_lock_irqsave(&c200_lock, flags);
+
+ outb(irqstatus | ADDRSTB, pc);
+ outb((reg & 0x1F) | 0x80, pa);
+ outb(irqstatus, pc);
+ outb(irqstatus | PORTREAD, pc);
+ outb(irqstatus | DATASTB | PORTREAD, pc);
+ val = inb(pa);
+ outb(irqstatus, pc);
+
+ spin_unlock_irqrestore(&c200_lock, flags);
+
+ return val;
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val)
+{
+ unsigned long pa = base;
+ unsigned long pc = pa + 2;
+ unsigned long flags;
+ u8 irqstatus = (inb(pc) & 0x10) | 0x04;
+
+ spin_lock_irqsave(&c200_lock, flags);
+
+ outb(irqstatus | ADDRSTB, pc);
+ outb(reg & 0x1F, pa);
+ outb(irqstatus, pc);
+ outb(irqstatus | WRITEP, pc);
+ outb(irqstatus | DATASTB | WRITEP, pc);
+ outb(val, pa);
+ outb(irqstatus, pc);
+
+ spin_unlock_irqrestore(&c200_lock, flags);
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
--- /dev/null
+/*
+ * esdio.c - multiplex register access CAN hardware abstraction layer
+ * for the esd 3xCAN pc104 board
+ * http://www.esd-electronics.de/products/CAN/can-pc104-200_e.htm
+ *
+ * $Id$
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2007 Fraunhofer FOKUS
+ *
+ * Provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ * History:
+ * 2007-05-22 Bjoern Riemer: initial release
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...)
+#endif
+
+//#define DBG(args...) printk(args)
+
+int esd_ale_offset = 1; //default for the sja1000 chip
+int esd_cs_offset = 0; //default for the sja1000 chip
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "esdio"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x1e8UL;
+ irq[0] = 5;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ if (!memcmp(drv_name,"i82527-esdio",sizeof("i82527-esdio"))){
+ esd_ale_offset = 7;
+ esd_cs_offset = 4;
+ } else if (!memcmp(drv_name,"sja1000-esdio",sizeof("sja1000-esdio"))){
+ esd_ale_offset = 1;
+ esd_cs_offset = 0;
+ }
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base + esd_ale_offset */
+ /* data register = base + esd_cs_offset */
+ if (request_region(base[dev_num] + esd_ale_offset, 1, drv_name)){
+ if (request_region(base[dev_num] + esd_cs_offset, 1,drv_name)){
+ return 1;
+ } else {
+ release_region(base[dev_num]+esd_ale_offset, 1);
+ return 0; // error
+ }
+ }
+
+ return 0; // error
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base + esd_ale_offset */
+ /* data register = base + esd_cs_offset */
+ release_region(base[dev_num] + esd_cs_offset, 1);
+ release_region(base[dev_num] + esd_ale_offset, 1);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num)
+{
+ int i, stat, i1;
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ i1 = irq[dev_num]; //get IRQ number
+ DBG(KERN_INFO "esdio.c: enabling IRQ %d for dev_num %d\n",i1,dev_num);
+
+ for (i=0; i<4; i++){
+ stat=i; // bit 0,1 selects the latch bit to write
+ if (i1 & 0x01){
+ stat |= 0x80; //bit7 carrys the value of the latch bit
+ }
+ outb(stat,base[dev_num]+3);
+ i1 = i1>>1;
+ }
+
+ outb(0x87,base[dev_num]+3); //enable irq selection
+ outb(0x86,base[dev_num]+3); //enable irq tristate buffer
+
+ return 1;
+}
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num)
+{
+ int i;
+ extern unsigned long base[];
+
+ DBG(KERN_INFO "esdio.c: diabling IRQ for dev_num %d\n",dev_num);
+
+ outb(0x07,base[dev_num]+3); //disable irq selection
+ outb(0x06,base[dev_num]+3); //disable irq tristate buffer
+
+ for (i=0; i<4; i++)
+ outb(i,base[dev_num]+3);
+
+ return 1;
+}
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ outb(reg, base + esd_ale_offset); /* address */
+ return inb(base + esd_cs_offset); /* data */
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(reg, base + esd_ale_offset); /* address */
+ outb(val, base + esd_cs_offset); /* data */
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) {
+
+ outb(0x86,dev->base_addr+3); //enable irq tristate buffer
+ return;
+}
--- /dev/null
+/*
+ * gw2.c - Trajet GW2 register access CAN hardware abstraction layer
+ *
+ * $Id$
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+#define ADDR_GAP 1
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "gw2"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+ extern unsigned int speed[];
+
+ base[0] = 0xF0100200UL;
+ irq[0] = 26;
+ speed[0] = 500;
+
+ base[1] = 0xF0100300UL;
+ irq[1] = 26;
+ speed[1] = 100;
+
+ base[2] = 0xF0100400UL;
+ irq[2] = 26;
+ speed[2] = 100;
+
+ base[3] = 0xF0100500UL;
+ irq[3] = 26;
+ speed[3] = 500;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ unsigned int gw2_regs = num_regs * (ADDR_GAP + 1);
+
+ /* creating the region for IOMEM is pretty easy */
+ if (!request_mem_region(base[dev_num], gw2_regs, drv_name))
+ return 0; /* failed */
+
+ /* set device base_addr */
+ rbase[dev_num] = (unsigned long)ioremap(base[dev_num], gw2_regs);
+
+ if (rbase[dev_num])
+ return 1; /* success */
+
+ /* cleanup due to failed ioremap() */
+ release_mem_region(base[dev_num], gw2_regs);
+ return 0; /* failed */
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ unsigned int gw2_regs = num_regs * (ADDR_GAP + 1);
+
+ iounmap((void *)rbase[dev_num]);
+ release_mem_region(base[dev_num], gw2_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ static u8 val;
+ void __iomem *addr = (void __iomem *)base +
+ reg * (ADDR_GAP + 1) + ADDR_GAP;
+
+ val = (u8)readw(addr);
+ rmb();
+
+ return val;
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ void __iomem *addr = (void __iomem *)base +
+ reg * (ADDR_GAP + 1) + ADDR_GAP;
+
+ writew(val, addr);
+ wmb();
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
--- /dev/null
+/*
+ * hal.h - definitions for CAN controller hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_HAL_H
+#define CAN_HAL_H
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+
+/* Number of supported CAN devices for each HAL (default) */
+#define MAXDEV 8
+
+/* general function prototypes for CAN HAL */
+
+/* init the HAL - call at driver module init */
+int hal_init(void);
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void);
+
+/* get name of this CAN HAL */
+char *hal_name(void);
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void);
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name);
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs);
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num);
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num);
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num);
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg);
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val);
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev);
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev);
+
+#endif /* CAN_HAL_H */
--- /dev/null
+/*
+ * io.c - linear register access CAN hardware abstraction layer
+ *
+ * $Id$
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "io"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x2C0UL;
+ irq[0] = 10;
+
+ base[1] = 0x320UL;
+ irq[1] = 5;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* creating the region for IO is pretty easy */
+ return (request_region(base[dev_num], num_regs, drv_name))? 1 : 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ release_region(base[dev_num], num_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ return inb(base + reg);
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(val, base + reg);
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
--- /dev/null
+/*
+ * iomem.c - linear register access CAN hardware abstraction layer
+ *
+ * $Id$
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "iomem"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0xd8000UL;
+ irq[0] = 5;
+
+ base[1] = 0xd8100UL;
+ irq[1] = 15;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* creating the region for IOMEM is pretty easy */
+ if (!request_mem_region(base[dev_num], num_regs, drv_name))
+ return 0; /* failed */
+
+ /* set device base_addr */
+ rbase[dev_num] = (unsigned long)ioremap(base[dev_num], num_regs);
+
+ if (rbase[dev_num])
+ return 1; /* success */
+
+ /* cleanup due to failed ioremap() */
+ release_mem_region(base[dev_num], num_regs);
+ return 0; /* failed */
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ iounmap((void *)rbase[dev_num]);
+ release_mem_region(base[dev_num], num_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ static u8 val;
+ void __iomem *addr = (void __iomem *)base + reg;
+
+ val = (u8)readb(addr);
+ rmb();
+
+ return val;
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ void __iomem *addr = (void __iomem *)base + reg;
+
+ writeb(val, addr);
+ wmb();
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
--- /dev/null
+/*
+ * iomux.c - multiplex register access CAN hardware abstraction layer
+ *
+ * $Id$
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "iomux"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x300UL;
+ irq[0] = 5;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base / data register = base + 1 */
+ return (request_region(base[dev_num], 2, drv_name))? 1 : 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base / data register = base + 1 */
+ release_region(base[dev_num], 2);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ outb(reg, base); /* address */
+ return inb(base + 1); /* data */
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(reg, base); /* address */
+ outb(val, base + 1); /* data */
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
--- /dev/null
+/*
+ * pc7io.c - linear register access CAN hardware abstraction layer
+ *
+ * $Id$
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "pc7io"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x1000UL;
+ irq[0] = 9;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* creating the region for IO is pretty easy */
+ return (request_region(base[dev_num], num_regs, drv_name))? 1 : 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ release_region(base[dev_num], num_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) {
+
+ /* Unlock special function register */
+ outb(5, 0x169);
+
+ return 0;
+}
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ return inb(base + reg);
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(val, base + reg);
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
--- /dev/null
+#
+# $Id$
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/can/old/hal
+
+obj-m := i82527-pc7io.o i82527-iomem.o i82527-esdio.o
+
+i82527-pc7io-objs := i82527.o proc.o ../hal/pc7io.o
+i82527-iomem-objs := i82527.o proc.o ../hal/iomem.o
+i82527-esdio-objs := i82527.o proc.o ../hal/esdio.o
+
+endif
--- /dev/null
+/*
+ * i82527.c - Intel I82527 network device driver
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h> /* for struct can_device_stats */
+#include "hal.h"
+#include "i82527.h"
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("LLCF/socketcan '" CHIP_NAME "' network device driver");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) ((priv->debug > 0) ? printk(args) : 0)
+/* logging in interrupt context! */
+#define iDBG(args...) ((priv->debug > 1) ? printk(args) : 0)
+#define iiDBG(args...) ((priv->debug > 2) ? printk(args) : 0)
+#else
+#define DBG(args...)
+#define iDBG(args...)
+#define iiDBG(args...)
+#endif
+
+char drv_name[DRV_NAME_LEN] = "undefined";
+
+/* driver and version information */
+static const char *drv_version = "0.0.4";
+static const char *drv_reldate = "2007-08-03";
+
+static const canid_t rxobjflags[] = {0, CAN_EFF_FLAG,
+ CAN_RTR_FLAG, CAN_RTR_FLAG | CAN_EFF_FLAG,
+ 0, CAN_EFF_FLAG};
+#define RXOBJBASE 10
+
+/* array of all can chips */
+struct net_device *can_dev[MAXDEV];
+
+/* module parameters */
+unsigned long base[MAXDEV] = { 0 }; /* hardware address */
+unsigned long rbase[MAXDEV] = { 0 }; /* (remapped) device address */
+unsigned int irq[MAXDEV] = { 0 };
+
+unsigned int speed[MAXDEV] = { DEFAULT_SPEED, DEFAULT_SPEED };
+unsigned int btr[MAXDEV] = { 0 };
+unsigned int bcr[MAXDEV] = { 0 }; /* bus configuration register */
+unsigned int cdv[MAXDEV] = { 0 }; /* CLKOUT clock divider */
+unsigned int mo15[MAXDEV] = { MO15_DEFLT, MO15_DEFLT }; /* msg obj 15 */
+
+static int rx_probe[MAXDEV] = { 0 };
+static int clk = DEFAULT_HW_CLK;
+static int force_dmc = DEFAULT_FORCE_DMC;
+static int irq_mode = DEFAULT_IRQ_MODE;
+static int debug = 0;
+static int restart_ms = 100;
+
+static int base_n;
+static int irq_n;
+static int speed_n;
+static int btr_n;
+static int bcr_n;
+static int cdv_n;
+static int mo15_n;
+static int rx_probe_n;
+
+static u8 dsc; /* devide system clock */
+static u8 dmc; /* devide memory clock */
+static unsigned long irqflags; /* for shared / disabled local interrupts */
+
+module_param_array(base, int, &base_n, 0);
+module_param_array(irq, int, &irq_n, 0);
+module_param_array(speed, int, &speed_n, 0);
+module_param_array(btr, int, &btr_n, 0);
+module_param_array(bcr, int, &bcr_n, 0);
+module_param_array(cdv, int, &cdv_n, 0);
+module_param_array(mo15, int, &mo15_n, 0);
+module_param_array(rx_probe, int, &rx_probe_n, 0);
+
+module_param(clk, int, 0);
+module_param(force_dmc, int, 0);
+module_param(irq_mode, int, 0);
+module_param(debug, int, 0);
+module_param(restart_ms, int, 0);
+
+MODULE_PARM_DESC(base, "CAN controller base address");
+MODULE_PARM_DESC(irq, "CAN controller interrupt");
+MODULE_PARM_DESC(speed, "CAN bus bitrate");
+MODULE_PARM_DESC(btr, "Bit Timing Register value 0x<btr0><btr1>, e.g. 0x4014");
+MODULE_PARM_DESC(bcr, "i82527 bus configuration register value (default: 0)");
+MODULE_PARM_DESC(cdv, "clockout devider value (0-14) (default: 0)");
+MODULE_PARM_DESC(mo15, "rx message object 15 usage. 0:none 1:sff(default) 2:eff");
+MODULE_PARM_DESC(rx_probe, "switch to trx mode after correct msg receiption. (default off)");
+
+MODULE_PARM_DESC(clk, "CAN controller chip clock (default: 16MHz)");
+MODULE_PARM_DESC(force_dmc, "set i82527 DMC bit (default: calculate from clk)");
+MODULE_PARM_DESC(irq_mode, "specify irq setup bits (1:shared 2:disable local irqs while processing) (default: 1)");
+MODULE_PARM_DESC(debug, "set debug mask (default: 0)");
+MODULE_PARM_DESC(restart_ms, "restart chip on heavy bus errors / bus off after x ms (default 100ms)");
+
+/* function declarations */
+
+static void chipset_init(struct net_device *dev, int wake);
+static void chipset_init_rx(struct net_device *dev);
+static void chipset_init_trx(struct net_device *dev);
+static void can_netdev_setup(struct net_device *dev);
+static struct net_device* can_create_netdev(int dev_num, int hw_regs);
+static int can_set_drv_name(void);
+int set_reset_mode(struct net_device *dev);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+static struct net_device_stats *can_get_stats(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* TODO: read statistics from chip */
+ return &priv->stats;
+}
+#endif
+
+static int i82527_probe_chip(unsigned long base)
+{
+ // Check if hardware reset is still inactive OR
+ // maybe there is no chip in this address space
+ if (CANin(base, cpuInterfaceReg) & iCPU_RST) {
+ printk(KERN_INFO "%s: probing @ 0x%lX failed (reset)\n",
+ drv_name, base);
+ return 0;
+ }
+
+ // Write test pattern
+ CANout(base, message1Reg.dataReg[1], 0x25);
+ CANout(base, message2Reg.dataReg[3], 0x52);
+ CANout(base, message10Reg.dataReg[6], 0xc3);
+
+ // Read back test pattern
+ if ((CANin(base, message1Reg.dataReg[1]) != 0x25 ) ||
+ (CANin(base, message2Reg.dataReg[3]) != 0x52 ) ||
+ (CANin(base, message10Reg.dataReg[6]) != 0xc3 )) {
+ printk(KERN_INFO "%s: probing @ 0x%lX failed (pattern)\n",
+ drv_name, base);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * set baud rate divisor values
+ */
+static void set_btr(struct net_device *dev, int btr0, int btr1)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ /* no bla bla when restarting the device */
+ if (priv->state == STATE_UNINITIALIZED)
+ printk(KERN_INFO "%s: setting BTR0=%02X BTR1=%02X\n",
+ dev->name, btr0, btr1);
+
+ CANout(base, bitTiming0Reg, btr0);
+ CANout(base, bitTiming1Reg, btr1);
+}
+
+/*
+ * calculate baud rate divisor values
+ */
+static void set_baud(struct net_device *dev, int baud, int clock)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ int error;
+ int brp;
+ int tseg;
+ int tseg1 = 0;
+ int tseg2 = 0;
+
+ int best_error = 1000000000;
+ int best_tseg = 0;
+ int best_brp = 0;
+ int best_baud = 0;
+
+ int SAM = (baud > 100000 ? 0 : 1);
+
+ if (dsc) /* devide system clock */
+ clock >>= 1; /* calculate BTR with this value */
+
+ for (tseg = (0 + 0 + 2) * 2;
+ tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1;
+ tseg++) {
+ brp = clock / ((1 + tseg / 2) * baud) + tseg % 2;
+ if ((brp > 0) && (brp <= 64)) {
+ error = baud - clock / (brp * (1 + tseg / 2));
+ if (error < 0) {
+ error = -error;
+ }
+ if (error <= best_error) {
+ best_error = error;
+ best_tseg = tseg / 2;
+ best_brp = brp - 1;
+ best_baud = clock / (brp * (1 + tseg / 2));
+ }
+ }
+ }
+ if (best_error && (baud / best_error < 10)) {
+ printk("%s: unable to set baud rate %d (ext clock %dHz)\n",
+ dev->name, baud, clock * 2);
+ return;
+// return -EINVAL;
+ }
+ tseg2 = best_tseg - (SAMPLE_POINT * (best_tseg + 1)) / 100;
+ if (tseg2 < 0) {
+ tseg2 = 0;
+ } else if (tseg2 > MAX_TSEG2) {
+ tseg2 = MAX_TSEG2;
+ }
+ tseg1 = best_tseg - tseg2 - 2;
+ if (tseg1 > MAX_TSEG1) {
+ tseg1 = MAX_TSEG1;
+ tseg2 = best_tseg - tseg1 - 2;
+ }
+
+ priv->btr = ((best_brp | JUMPWIDTH)<<8) +
+ ((SAM << 7) | (tseg2 << 4) | tseg1);
+
+ printk(KERN_INFO "%s: calculated best baudrate: %d / btr is 0x%04X\n",
+ dev->name, best_baud, priv->btr);
+
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+// set_btr(dev, best_brp | JUMPWIDTH, (SAM << 7) | (tseg2 << 4) | tseg1);
+}
+
+static inline int obj2rxo(int obj)
+{
+ /* obj4 = obj15 SFF, obj5 = obj15 EFF */
+ if (obj < 4)
+ return RXOBJBASE + obj;
+ else
+ return 15;
+}
+
+void enable_rx_obj(unsigned long base, int obj)
+{
+ u8 mcfg = 0;
+ int rxo = obj2rxo(obj);
+
+ // Configure message object for receiption
+ if (rxobjflags[obj] & CAN_EFF_FLAG)
+ mcfg = MCFG_XTD;
+
+ if (rxobjflags[obj] & CAN_RTR_FLAG) {
+ CANout(base, msgArr[rxo].messageReg.messageConfigReg,
+ mcfg | MCFG_DIR);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl0Reg,
+ MVAL_SET | TXIE_RES | RXIE_SET | INTPD_RES);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | CPUU_SET | TXRQ_RES | RMPD_RES);
+ } else {
+ CANout(base, msgArr[rxo].messageReg.messageConfigReg, mcfg);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl0Reg,
+ MVAL_SET | TXIE_RES | RXIE_SET | INTPD_RES);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_RES | RMPD_RES);
+ }
+}
+
+void disable_rx_obj(unsigned long base, int obj)
+{
+ int rxo = obj2rxo(obj);
+
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_RES | RMPD_RES);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl0Reg,
+ MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES);
+}
+
+int set_reset_mode(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ // Configure cpu interface
+ CANout(base, cpuInterfaceReg,(dsc | dmc | iCPU_CEN));
+
+ // Enable configuration and puts chip in bus-off, disable interrupts
+ CANout(base, controlReg, iCTL_CCE | iCTL_INI);
+
+ // Clear interrupts
+ CANin(base, interruptReg);
+
+ // Clear status register
+ CANout(base, statusReg, 0);
+
+ // Clear message objects for receiption
+ if (priv->mo15 == MO15_SFF)
+ disable_rx_obj(base, 4); /* rx via obj15 SFF */
+ else
+ disable_rx_obj(base, 0); /* rx via obj10 SFF */
+
+ if (priv->mo15 == MO15_EFF)
+ disable_rx_obj(base, 5); /* rx via obj15 EFF */
+ else
+ disable_rx_obj(base, 1); /* rx via obj11 EFF */
+
+ disable_rx_obj(base, 2);
+ disable_rx_obj(base, 3);
+
+ // Clear message object for send
+ CANout(base, message1Reg.msgCtrl1Reg,
+ RMPD_RES | TXRQ_RES | CPUU_RES | NEWD_RES);
+ CANout(base, message1Reg.msgCtrl0Reg,
+ MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+
+ return 0;
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ // Clear interrupts
+ CANin(base, interruptReg);
+
+ // Clear status register
+ CANout(base, statusReg, 0);
+
+ // Configure message objects for receiption
+ if (priv->mo15 == MO15_SFF) {
+ enable_rx_obj(base, 4); /* rx via obj15 SFF */
+ printk(KERN_INFO "%s: %s: using msg object 15 for "
+ "SFF receiption.\n",
+ dev->name, CHIP_NAME);
+ } else
+ enable_rx_obj(base, 0); /* rx via obj10 SFF */
+
+ if (priv->mo15 == MO15_EFF) {
+ enable_rx_obj(base, 5); /* rx via obj15 EFF */
+ printk(KERN_INFO "%s: %s: using msg object 15 for "
+ "EFF receiption.\n",
+ dev->name, CHIP_NAME);
+ } else
+ enable_rx_obj(base, 1); /* rx via obj11 EFF */
+
+ enable_rx_obj(base, 2);
+ enable_rx_obj(base, 3);
+
+ // Clear message object for send
+ CANout(base, message1Reg.msgCtrl1Reg,
+ RMPD_RES | TXRQ_RES | CPUU_RES | NEWD_RES);
+ CANout(base, message1Reg.msgCtrl0Reg,
+ MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES);
+
+ return 0;
+}
+
+static int set_listen_mode(struct net_device *dev)
+{
+ return set_normal_mode(dev); /* for now */
+}
+
+/*
+ * Clear and invalidate message objects
+ */
+int i82527_clear_msg_objects(unsigned long base)
+{
+ int i;
+ int id;
+ int data;
+
+ for (i = 1; i <= 15; i++) {
+ CANout(base, msgArr[i].messageReg.msgCtrl0Reg,
+ INTPD_UNC | RXIE_RES | TXIE_RES | MVAL_RES);
+ CANout(base, msgArr[i].messageReg.msgCtrl0Reg,
+ INTPD_RES | RXIE_RES | TXIE_RES | MVAL_RES);
+ CANout(base, msgArr[i].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_RES | RMPD_RES);
+ for (data = 0; data < 8; data++)
+ CANout(base, msgArr[i].messageReg.dataReg[data], 0);
+ for (id = 0; id < 4; id++)
+ CANout(base, msgArr[i].messageReg.idReg[id], 0);
+ CANout(base, msgArr[i].messageReg.messageConfigReg, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * initialize I82527 chip:
+ * - reset chip
+ * - set output mode
+ * - set baudrate
+ * - enable interrupts
+ * - start operating mode
+ */
+static void chipset_init_regs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ // Enable configuration and puts chip in bus-off, disable interrupts
+ CANout(base, controlReg, (iCTL_CCE | iCTL_INI));
+
+ // Set CLKOUT devider and slew rates is was done in i82527_init_module
+
+ // Bus configuration was done in i82527_init_module
+
+ // Clear interrupts
+ CANin(base, interruptReg);
+
+ // Clear status register
+ CANout(base, statusReg, 0);
+
+ i82527_clear_msg_objects(base);
+
+ // Set all global ID masks to "don't care"
+ CANout(base, globalMaskStandardReg[0], 0);
+ CANout(base, globalMaskStandardReg[1], 0);
+ CANout(base, globalMaskExtendedReg[0], 0);
+ CANout(base, globalMaskExtendedReg[1], 0);
+ CANout(base, globalMaskExtendedReg[2], 0);
+ CANout(base, globalMaskExtendedReg[3], 0);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+
+ // Note: At this stage the CAN ship is still in bus-off condition
+ // and must be started using StartChip()
+
+ /* set baudrate */
+ if (priv->btr) { /* no calculation when btr is provided */
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+ } else {
+ if (priv->speed == 0) {
+ priv->speed = DEFAULT_SPEED;
+ }
+ set_baud(dev, priv->speed * 1000, priv->clock);
+ }
+
+}
+
+static void chipset_init(struct net_device *dev, int wake)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->rx_probe)
+ chipset_init_rx(dev); /* wait for valid reception first */
+ else
+ chipset_init_trx(dev);
+
+ if ((wake) && netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+}
+
+static void chipset_init_rx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* automatic bit rate detection */
+ set_listen_mode(dev);
+
+ priv->state = STATE_PROBE;
+
+ // Clear bus-off, Interrupts only for errors, not for status change
+ CANout(base, controlReg, iCTL_IE | iCTL_EIE);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+}
+
+static void chipset_init_trx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+
+ priv->state = STATE_ACTIVE;
+
+ // Clear bus-off, Interrupts only for errors, not for status change
+ CANout(base, controlReg, iCTL_IE | iCTL_EIE);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx ll 00 11 22 33 44 55 66 77
+ * [ can-id ] [len] [can data (up to 8 bytes]
+ */
+static int can_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ struct can_frame *cf = (struct can_frame*)skb->data;
+ unsigned long base = dev->base_addr;
+ uint8_t dlc;
+ uint8_t rtr;
+ canid_t id;
+ int i;
+
+ if ((CANin(base, message1Reg.msgCtrl1Reg) & TXRQ_UNC) == TXRQ_SET) {
+ printk(KERN_ERR "%s: %s: TX register is occupied!\n",
+ dev->name, drv_name);
+ return 0;
+ }
+
+ netif_stop_queue(dev);
+
+ dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ if ( cf->can_id & CAN_RTR_FLAG )
+ rtr = 0;
+ else
+ rtr = MCFG_DIR;
+
+ CANout(base, message1Reg.msgCtrl1Reg,
+ RMPD_RES | TXRQ_RES | CPUU_SET | NEWD_RES);
+ CANout(base, message1Reg.msgCtrl0Reg,
+ MVAL_SET | TXIE_SET | RXIE_RES | INTPD_RES);
+
+ if (id & CAN_EFF_FLAG) {
+ id &= CAN_EFF_MASK;
+ CANout(base, message1Reg.messageConfigReg,
+ (dlc << 4) + rtr + MCFG_XTD);
+ CANout(base, message1Reg.idReg[3], (id << 3) & 0xFFU);
+ CANout(base, message1Reg.idReg[2], (id >> 5) & 0xFFU);
+ CANout(base, message1Reg.idReg[1], (id >> 13) & 0xFFU);
+ CANout(base, message1Reg.idReg[0], (id >> 21) & 0xFFU);
+ }
+ else {
+ id &= CAN_SFF_MASK;
+ CANout(base, message1Reg.messageConfigReg,
+ ( dlc << 4 ) + rtr);
+ CANout(base, message1Reg.idReg[0], (id >> 3) & 0xFFU);
+ CANout(base, message1Reg.idReg[1], (id << 5) & 0xFFU);
+ }
+
+ dlc &= 0x0f; //restore length only
+ for ( i=0; i < dlc; i++ ) {
+ CANout(base, message1Reg.dataReg[i],
+ cf->data[i]);
+ }
+
+ CANout(base, message1Reg.msgCtrl1Reg,
+ (RMPD_RES | TXRQ_SET | CPUU_RES | NEWD_UNC));
+
+ // HM: We had some cases of repeated IRQs
+ // so make sure the INT is acknowledged
+ // I know it's already further up, but doing again fixed the issue
+ CANout(base, message1Reg.msgCtrl0Reg,
+ (MVAL_UNC | TXIE_UNC | RXIE_UNC | INTPD_RES));
+
+ stats->tx_bytes += dlc;
+
+ dev->trans_start = jiffies;
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void can_tx_timeout(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+
+ stats->tx_errors++;
+
+ /* do not conflict with e.g. bus error handling */
+ if (!(priv->timer.expires)){ /* no restart on the run */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ netif_wake_queue(dev); /* wakeup here */
+ }
+ else
+ DBG(KERN_INFO "%s: %s: can_restart_dev already active.\n",
+ dev->name, __FUNCTION__ );
+
+}
+
+# if 0
+static void can_restart_on(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (!(priv->timer.expires)){ /* no restart on the run */
+
+ set_reset_mode(dev);
+
+ priv->timer.function = can_restart_dev;
+ priv->timer.data = (unsigned long) dev;
+
+ /* restart chip on persistent error in <xxx> ms */
+ priv->timer.expires = jiffies + (priv->restart_ms * HZ) / 1000;
+ add_timer(&priv->timer);
+
+ iDBG(KERN_INFO "%s: %s start (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+ } else
+ iDBG(KERN_INFO "%s: %s already (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+}
+
+static void can_restart_dev(unsigned long data)
+{
+ struct net_device *dev = (struct net_device*) data;
+ struct can_priv *priv = netdev_priv(dev);
+
+ DBG(KERN_INFO "%s: can_restart_dev (%ld)\n",
+ dev->name, jiffies);
+
+ /* mark inactive timer */
+ priv->timer.expires = 0;
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+#endif
+
+#if 0
+/* the timerless version */
+
+static void can_restart_now(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+#endif
+
+/*
+ * Subroutine of ISR for RX interrupts.
+ *
+ */
+static void can_rx(struct net_device *dev, int obj)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ unsigned long base = dev->base_addr;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t msgctlreg;
+ uint8_t ctl1reg;
+ canid_t id;
+ uint8_t dlc;
+ int i;
+ int rxo = obj2rxo(obj);
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL) {
+ return;
+ }
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ ctl1reg = CANin(base, msgArr[rxo].messageReg.msgCtrl1Reg);
+ msgctlreg = CANin(base, msgArr[rxo].messageReg.messageConfigReg);
+
+ if( msgctlreg & MCFG_XTD ) {
+ id = CANin(base, msgArr[rxo].messageReg.idReg[3])
+ | (CANin(base, msgArr[rxo].messageReg.idReg[2]) << 8)
+ | (CANin(base, msgArr[rxo].messageReg.idReg[1]) << 16)
+ | (CANin(base, msgArr[rxo].messageReg.idReg[0]) << 24);
+ id >>= 3;
+ id |= CAN_EFF_FLAG;
+ } else {
+ id = CANin(base, msgArr[rxo].messageReg.idReg[1])
+ |(CANin(base, msgArr[rxo].messageReg.idReg[0]) << 8);
+ id >>= 5;
+ }
+
+ if (ctl1reg & RMPD_SET) {
+ id |= CAN_RTR_FLAG;
+ }
+
+ msgctlreg &= 0xf0;/* strip length code */
+ dlc = msgctlreg >> 4;
+ dlc %= 9; /* limit count to 8 bytes */
+
+ cf = (struct can_frame*)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++) {
+ cf->data[i] = CANin(base, msgArr[rxo].messageReg.dataReg[i]);
+ }
+
+ // Make the chip ready to receive the next message
+ enable_rx_obj(base, obj);
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += dlc;
+}
+
+/*
+ * I82527 interrupt handler
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static irqreturn_t can_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+#else
+static irqreturn_t can_interrupt(int irq, void *dev_id)
+#endif
+{
+ struct net_device *dev = (struct net_device*)dev_id;
+ struct can_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ unsigned long base = dev->base_addr;
+ uint8_t irqreg;
+ uint8_t lastIrqreg;
+ int n = 0;
+
+ hw_preirq(dev);
+
+ iiDBG(KERN_INFO "%s: interrupt\n", dev->name);
+
+ if (priv->state == STATE_UNINITIALIZED) {
+ printk(KERN_ERR "%s: %s: uninitialized controller!\n",
+ dev->name, __FUNCTION__);
+ //chipset_init(dev, 1); /* should be possible at this stage */
+ return IRQ_NONE;
+ }
+
+ if (priv->state == STATE_RESET_MODE) {
+ iiDBG(KERN_ERR "%s: %s: controller is in reset mode!\n",
+ dev->name, __FUNCTION__);
+ return IRQ_NONE;
+ }
+
+
+ // Read the highest pending interrupt request
+ irqreg = CANin(base, interruptReg);
+ lastIrqreg = irqreg;
+
+ while ( irqreg ) {
+ n++;
+ switch (irqreg) {
+
+ case 1: // Status register
+ {
+ uint8_t status;
+
+ // Read the STATUS reg
+ status = CANin(base, statusReg);
+ CANout (base, statusReg, 0);
+
+ if ( status & iSTAT_RXOK ) {
+ // Intel: Software must clear this bit in ISR
+ CANout (base, statusReg, status & ~iSTAT_RXOK);
+ }
+ if ( status & iSTAT_TXOK ) {
+ // Intel: Software must clear this bit in ISR
+ CANout (base, statusReg, status & ~iSTAT_TXOK);
+ }
+ if ( status & iSTAT_WARN ) {
+ // Note: status bit is read-only, don't clear
+ /* error warning interrupt */
+ iDBG(KERN_INFO "%s: error warning\n",
+ dev->name);
+ priv->can_stats.error_warning++;
+ }
+ if ( status & iSTAT_BOFF ) {
+ uint8_t flags;
+
+ // Note: status bit is read-only, don't clear
+
+ priv->can_stats.bus_error++;
+
+ // Clear init flag and reenable interrupts
+ flags = CANin(base, controlReg) |
+ ( iCTL_IE | iCTL_EIE );
+
+ flags &= ~iCTL_INI; // Reset init flag
+ CANout(base, controlReg, flags);
+ }
+ }
+ break;
+
+ case 0x2: // Receiption, message object 15
+ {
+ uint8_t ctl1reg;
+
+ ctl1reg = CANin(base, message15Reg.msgCtrl1Reg);
+ while (ctl1reg & NEWD_SET) {
+ if (ctl1reg & MLST_SET)
+ priv->can_stats.data_overrun++;
+
+ if (priv->mo15 == MO15_SFF)
+ can_rx(dev, 4); /* rx via obj15 SFF */
+ else
+ can_rx(dev, 5); /* rx via obj15 EFF */
+
+ ctl1reg = CANin(base, message15Reg.msgCtrl1Reg);
+ }
+
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ break;
+
+ case 0xC: // Receiption, message object 10
+ case 0xD: // Receiption, message object 11
+ {
+ int obj = irqreg - 0xC;
+ int rxo = obj2rxo(obj);
+ uint8_t ctl1reg;
+ ctl1reg = CANin(base, msgArr[rxo].messageReg.msgCtrl1Reg);
+ while (ctl1reg & NEWD_SET) {
+ if (ctl1reg & MLST_SET)
+ priv->can_stats.data_overrun++;
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_UNC | RMPD_UNC);
+ can_rx(dev, obj);
+ ctl1reg = CANin(base,
+ msgArr[rxo].messageReg.msgCtrl1Reg);
+ }
+
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ break;
+
+ case 0xE: // Receiption, message object 12 (RTR)
+ case 0xF: // Receiption, message object 13 (RTR)
+ {
+ int obj = irqreg - 0xC;
+ int rxo = obj2rxo(obj);
+ uint8_t ctl0reg;
+ ctl0reg = CANin(base, msgArr[rxo].messageReg.msgCtrl0Reg);
+ while (ctl0reg & INTPD_SET) {
+ can_rx(dev, obj);
+ ctl0reg = CANin(base, msgArr[rxo].messageReg.msgCtrl0Reg);
+ }
+
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ break;
+
+ case 3: // Message object 1 (our write object)
+ /* transmission complete interrupt */
+
+ // Nothing more to send, switch off interrupts
+ CANout(base, message1Reg.msgCtrl0Reg,
+ (MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES));
+ // We had some cases of repeated IRQ
+ // so make sure the INT is acknowledged
+ CANout(base, message1Reg.msgCtrl0Reg,
+ (MVAL_UNC | TXIE_UNC | RXIE_UNC | INTPD_RES));
+
+ stats->tx_packets++;
+ netif_wake_queue(dev);
+ break;
+
+ default: // Unexpected
+ iDBG(KERN_INFO "%s: Unexpected i82527 interrupt: "
+ "irqreq=0x%X\n", dev->name, irqreg);
+ break;
+ }
+
+ // Get irq status again for next loop iteration
+ irqreg = CANin(base, interruptReg);
+ if (irqreg == lastIrqreg)
+ iDBG(KERN_INFO "%s: i82527 interrupt repeated: "
+ "irqreq=0x%X\n", dev->name, irqreg);
+
+ lastIrqreg = irqreg;
+ } /* end while (irqreq) */
+
+ if (n > 1) {
+ iDBG(KERN_INFO "%s: handled %d IRQs\n", dev->name, n);
+ }
+
+ hw_postirq(dev);
+
+ return n == 0 ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/*
+ * initialize CAN bus driver
+ */
+static int can_open(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->state = STATE_UNINITIALIZED;
+
+ /* register interrupt handler */
+ if (request_irq(dev->irq, &can_interrupt, irqflags,
+ dev->name, (void*)dev))
+ return -EAGAIN;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ /* clear statistics */
+ memset(&priv->stats, 0, sizeof(priv->stats));
+#endif
+
+ /* init chip */
+ chipset_init(dev, 0);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/*
+ * stop CAN bus activity
+ */
+static int can_close(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->open_time = 0;
+
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0;
+ }
+
+ free_irq(dev->irq, (void*)dev);
+ priv->state = STATE_UNINITIALIZED;
+
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+void can_netdev_setup(struct net_device *dev)
+{
+ /* Fill in the the fields of the device structure
+ with CAN netdev generic values */
+
+ dev->change_mtu = NULL;
+ dev->set_mac_address = NULL;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ dev->hard_header = NULL;
+ dev->rebuild_header = NULL;
+ dev->hard_header_cache = NULL;
+ dev->header_cache_update = NULL;
+ dev->hard_header_parse = NULL;
+#else
+ dev->header_ops = NULL;
+#endif
+
+ dev->type = ARPHRD_CAN;
+ dev->hard_header_len = 0;
+ dev->mtu = sizeof(struct can_frame);
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_NO_CSUM;
+
+ dev->open = can_open;
+ dev->stop = can_close;
+ dev->hard_start_xmit = can_start_xmit;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ dev->get_stats = can_get_stats;
+#endif
+
+ dev->tx_timeout = can_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+}
+
+static struct net_device* can_create_netdev(int dev_num, int hw_regs)
+{
+ struct net_device *dev;
+ struct can_priv *priv;
+
+ const char mo15mode [3][6] = {"none", "sff", "eff"};
+
+ if (!(dev = alloc_netdev(sizeof(struct can_priv), CAN_NETDEV_NAME,
+ can_netdev_setup))) {
+ printk(KERN_ERR "%s: out of memory\n", CHIP_NAME);
+ return NULL;
+ }
+
+ printk(KERN_INFO "%s: base 0x%lX / irq %d / speed %d / "
+ "btr 0x%X / rx_probe %d / mo15 %s\n",
+ drv_name, rbase[dev_num], irq[dev_num],
+ speed[dev_num], btr[dev_num], rx_probe[dev_num],
+ mo15mode[mo15[dev_num]]);
+
+ /* fill net_device structure */
+
+ priv = netdev_priv(dev);
+
+ dev->irq = irq[dev_num];
+ dev->base_addr = rbase[dev_num];
+
+ priv->speed = speed[dev_num];
+ priv->btr = btr[dev_num];
+ priv->rx_probe = rx_probe[dev_num];
+ priv->mo15 = mo15[dev_num];
+ priv->clock = clk;
+ priv->hw_regs = hw_regs;
+ priv->restart_ms = restart_ms;
+ priv->debug = debug;
+
+ init_timer(&priv->timer);
+ priv->timer.expires = 0;
+
+ if (register_netdev(dev)) {
+ printk(KERN_INFO "%s: register netdev failed\n", CHIP_NAME);
+ free_netdev(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+int can_set_drv_name(void)
+{
+ char *hname = hal_name();
+
+ if (strlen(CHIP_NAME) + strlen(hname) >= DRV_NAME_LEN-1) {
+ printk(KERN_ERR "%s: driver name too long!\n", CHIP_NAME);
+ return -EINVAL;
+ }
+ sprintf(drv_name, "%s-%s", CHIP_NAME, hname);
+ return 0;
+}
+
+static void i82527_exit_module(void)
+{
+ int i, ret;
+
+ for (i = 0; i < MAXDEV; i++) {
+ if (can_dev[i] != NULL) {
+ struct can_priv *priv = netdev_priv(can_dev[i]);
+ unregister_netdev(can_dev[i]);
+ del_timer(&priv->timer);
+ hw_detach(i);
+ hal_release_region(i, I82527_IO_SIZE);
+ free_netdev(can_dev[i]);
+ }
+ }
+ can_proc_remove(drv_name);
+
+ if ((ret = hal_exit()))
+ printk(KERN_INFO "%s: hal_exit error %d.\n", drv_name, ret);
+}
+
+static __init int i82527_init_module(void)
+{
+ int i, ret;
+ struct net_device *dev;
+
+ if ((sizeof(canmessage_t) != 15) || (sizeof(canregs_t) != 256)) {
+ printk(KERN_WARNING "%s sizes: canmessage_t %d canregs_t %d\n",
+ CHIP_NAME, (int)sizeof(canmessage_t),
+ (int)sizeof(canregs_t));
+ return -EBUSY;
+ }
+
+ if ((ret = hal_init()))
+ return ret;
+
+ if ((ret = can_set_drv_name()))
+ return ret;
+
+ if (clk < 1000 ) /* MHz command line value */
+ clk *= 1000000;
+
+ if (clk < 1000000 ) /* kHz command line value */
+ clk *= 1000;
+
+ printk(KERN_INFO "%s driver v%s (%s)\n",
+ drv_name, drv_version, drv_reldate);
+ printk(KERN_INFO "%s - options [clk %d.%06d MHz] [restart_ms %dms]"
+ " [debug %d]\n",
+ drv_name, clk/1000000, clk%1000000, restart_ms, debug);
+ printk(KERN_INFO "%s - options [force_dmc %d] [irq_mode %d]\n",
+ drv_name, force_dmc, irq_mode);
+
+ if (!base[0]) {
+ printk(KERN_INFO "%s: loading defaults.\n", drv_name);
+ hal_use_defaults();
+ }
+
+ /* to ensure the proper access to the i82527 registers */
+ /* the timing dependend settings have to be done first */
+ if (clk > 10000000)
+ dsc = iCPU_DSC; /* devide system clock => MCLK is 8MHz save */
+ else if (clk > 8000000) /* 8MHz < clk <= 10MHz */
+ dmc = iCPU_DMC; /* devide memory clock */
+
+ /* devide memory clock even if it's not needed (regarding the spec) */
+ if (force_dmc)
+ dmc = iCPU_DMC;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ if (irq_mode & IRQ_MODE_SHARED)
+ irqflags |= SA_SHIRQ;
+ if (irq_mode & IRQ_MODE_DISABLE_LOCAL_IRQS)
+ irqflags |= SA_INTERRUPT;
+#else
+ if (irq_mode & IRQ_MODE_SHARED)
+ irqflags |= IRQF_SHARED;
+ if (irq_mode & IRQ_MODE_DISABLE_LOCAL_IRQS)
+ irqflags |= IRQF_DISABLED;
+#endif
+
+ for (i = 0; base[i]; i++) {
+ int clkout;
+ u8 clockdiv;
+
+ printk(KERN_DEBUG "%s: checking for %s on address 0x%lX ...\n",
+ drv_name, CHIP_NAME, base[i]);
+
+ if (!hal_request_region(i, I82527_IO_SIZE, drv_name)) {
+ printk(KERN_ERR "%s: memory already in use\n",
+ drv_name);
+ i82527_exit_module();
+ return -EBUSY;
+ }
+
+ hw_attach(i);
+ hw_reset_dev(i);
+
+ // Enable configuration, put chip in bus-off, disable ints
+ CANout(rbase[i], controlReg, iCTL_CCE | iCTL_INI);
+
+ // Configure cpu interface / CLKOUT disable
+ CANout(rbase[i], cpuInterfaceReg,(dsc | dmc));
+
+ if (!i82527_probe_chip(rbase[i])) {
+ printk(KERN_ERR "%s: probably missing controller"
+ " hardware\n", drv_name);
+ hw_detach(i);
+ hal_release_region(i, I82527_IO_SIZE);
+ i82527_exit_module();
+ return -ENODEV;
+ }
+
+ /* CLKOUT devider and slew rate calculation */
+ if ((cdv[i] < 0) || (cdv[i] > 14)) {
+ printk(KERN_WARNING "%s: adjusted cdv[%d]=%d to 0.\n",
+ drv_name, i, cdv[i]);
+ cdv[i] = 0;
+ }
+
+ clkout = clk / (cdv[i] + 1); /* CLKOUT frequency */
+ clockdiv = (u8)cdv[i]; /* devider value (see i82527 spec) */
+
+ if (clkout <= 16000000) {
+ clockdiv |= iCLK_SL1;
+ if (clkout <= 8000000)
+ clockdiv |= iCLK_SL0;
+ } else if (clkout <= 24000000)
+ clockdiv |= iCLK_SL0;
+
+ // Set CLKOUT devider and slew rates
+ CANout(rbase[i], clkOutReg, clockdiv);
+
+ // Configure cpu interface / CLKOUT enable
+ CANout(rbase[i], cpuInterfaceReg,(dsc | dmc | iCPU_CEN));
+
+ CANout(rbase[i], busConfigReg, bcr[i]);
+
+ dev = can_create_netdev(i, I82527_IO_SIZE);
+
+ if (dev != NULL) {
+ can_dev[i] = dev;
+ set_reset_mode(dev);
+ can_proc_create(drv_name);
+ } else {
+ can_dev[i] = NULL;
+ hw_detach(i);
+ hal_release_region(i, I82527_IO_SIZE);
+ }
+ }
+ return 0;
+}
+
+module_init(i82527_init_module);
+module_exit(i82527_exit_module);
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * i82527.h - Intel I82527 network device driver
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Original version Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ *
+ * Major Refactoring and Integration into can4linux version 3.1 by
+ * Henrik W Maier of FOCUS Software Engineering Pty Ltd <www.focus-sw.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef I82527_H
+#define I82527_H
+
+#define I82527_IO_SIZE 0x100
+
+#define CHIP_NAME "i82527"
+
+#define DRV_NAME_LEN 30 /* for "<chip_name>-<hal_name>" */
+
+#define PROCBASE "driver" /* /proc/ ... */
+
+#define DEFAULT_HW_CLK 16000000
+#define DEFAULT_SPEED 500 /* kBit/s */
+#define DEFAULT_FORCE_DMC 0 /* for critical register access, e.g. ser1274 */
+
+#define IRQ_MODE_SHARED 1 /* enable shared interrupts */
+#define IRQ_MODE_DISABLE_LOCAL_IRQS 2 /* when processing the irq handler */
+#define DEFAULT_IRQ_MODE IRQ_MODE_SHARED
+
+/* The message object 15 has a shadow register for reliable data receiption */
+/* under heavy bus load. Therefore it makes sense to use this message object */
+/* (mo15) for the needed use case. The frame type (EFF/SFF) for the mo15 can */
+/* be defined on the module command line. The default is 11 bit SFF format. */
+
+#define MO15_NONE 0
+#define MO15_SFF 1
+#define MO15_EFF 2
+
+#define MO15_DEFLT MO15_SFF /* the default */
+
+#define CAN_NETDEV_NAME "can%d"
+
+#define TX_TIMEOUT (50*HZ/1000) /* 50ms */
+#define RESTART_MS 100 /* restart chip on persistent errors in 100ms */
+#define MAX_BUS_ERRORS 200 /* prevent from flooding bus error interrupts */
+
+/* bus timing */
+#define MAX_TSEG1 15
+#define MAX_TSEG2 7
+#define SAMPLE_POINT 62
+#define JUMPWIDTH 0x40
+
+typedef struct canmessage {
+ uint8_t msgCtrl0Reg;
+ uint8_t msgCtrl1Reg;
+ uint8_t idReg[4];
+ uint8_t messageConfigReg;
+ uint8_t dataReg[8];
+} canmessage_t; // __attribute__ ((packed));
+
+typedef struct canregs {
+ union
+ {
+ struct
+ {
+ canmessage_t messageReg;
+ uint8_t someOtherReg; // padding
+ } msgArr[16];
+ struct
+ {
+ uint8_t controlReg; // Control Register
+ uint8_t statusReg; // Status Register
+ uint8_t cpuInterfaceReg; // CPU Interface Register
+ uint8_t reserved1Reg;
+ uint8_t highSpeedReadReg[2]; // High Speed Read
+ uint8_t globalMaskStandardReg[2]; // Standard Global Mask byte 0
+ uint8_t globalMaskExtendedReg[4]; // Extended Global Mask bytes
+ uint8_t message15MaskReg[4]; // Message 15 Mask bytes
+ canmessage_t message1Reg;
+ uint8_t clkOutReg; // Clock Out Register
+ canmessage_t message2Reg;
+ uint8_t busConfigReg; // Bus Configuration Register
+ canmessage_t message3Reg;
+ uint8_t bitTiming0Reg; // Bit Timing Register byte 0
+ canmessage_t message4Reg;
+ uint8_t bitTiming1Reg; // Bit Timing Register byte 1
+ canmessage_t message5Reg;
+ uint8_t interruptReg; // Interrupt Register
+ canmessage_t message6Reg;
+ uint8_t reserved2Reg;
+ canmessage_t message7Reg;
+ uint8_t reserved3Reg;
+ canmessage_t message8Reg;
+ uint8_t reserved4Reg;
+ canmessage_t message9Reg;
+ uint8_t p1ConfReg;
+ canmessage_t message10Reg;
+ uint8_t p2ConfReg;
+ canmessage_t message11Reg;
+ uint8_t p1InReg;
+ canmessage_t message12Reg;
+ uint8_t p2InReg;
+ canmessage_t message13Reg;
+ uint8_t p1OutReg;
+ canmessage_t message14Reg;
+ uint8_t p2OutReg;
+ canmessage_t message15Reg;
+ uint8_t serialResetAddressReg;
+ };
+ };
+} canregs_t; // __attribute__ ((packed));
+
+/* Control Register (0x00) */
+enum i82527_iCTL {
+ iCTL_INI = 1, // Initialization
+ iCTL_IE = 1<<1, // Interrupt Enable
+ iCTL_SIE = 1<<2, // Status Interrupt Enable
+ iCTL_EIE = 1<<3, // Error Interrupt Enable
+ iCTL_CCE = 1<<6 // Change Configuration Enable
+};
+
+/* Status Register (0x01) */
+enum i82527_iSTAT {
+ iSTAT_TXOK = 1<<3, // Transmit Message Successfully
+ iSTAT_RXOK = 1<<4, // Receive Message Successfully
+ iSTAT_WAKE = 1<<5, // Wake Up Status
+ iSTAT_WARN = 1<<6, // Warning Status
+ iSTAT_BOFF = 1<<7 // Bus Off Status
+};
+
+/* CPU Interface Register (0x02) */
+enum i82527_iCPU {
+ iCPU_CEN = 1, // Clock Out Enable
+ iCPU_MUX = 1<<2, // Multiplex
+ iCPU_SLP = 1<<3, // Sleep
+ iCPU_PWD = 1<<4, // Power Down Mode
+ iCPU_DMC = 1<<5, // Divide Memory Clock
+ iCPU_DSC = 1<<6, // Divide System Clock
+ iCPU_RST = 1<<7, // Hardware Reset Status
+};
+
+/* Clock Out Register (0x1f) */
+enum i82527_iCLK {
+ iCLK_CD0 = 1, // Clock Divider bit 0
+ iCLK_CD1 = 1<<1,
+ iCLK_CD2 = 1<<2,
+ iCLK_CD3 = 1<<3,
+ iCLK_SL0 = 1<<4, // Slew Rate bit 0
+ iCLK_SL1 = 1<<5
+};
+
+/* Bus Configuration Register (0x2f) */
+enum i82527_iBUS {
+ iBUS_DR0 = 1, // Disconnect RX0 Input
+ iBUS_DR1 = 1<<1, // Disconnect RX1 Input
+ iBUS_DT1 = 1<<3, // Disconnect TX1 Output
+ iBUS_POL = 1<<5, // Polarity
+ iBUS_CBY = 1<<6 // Comparator Bypass
+};
+
+#define RESET 1 // Bit Pair Reset Status
+#define SET 2 // Bit Pair Set Status
+#define UNCHANGED 3 // Bit Pair Unchanged
+
+/* Message Control Register 0 (Base Address + 0x0) */
+enum i82527_iMSGCTL0 {
+ INTPD_SET = SET, // Interrupt pending
+ INTPD_RES = RESET, // No Interrupt pending
+ INTPD_UNC = UNCHANGED,
+ RXIE_SET = SET<<2, // Receive Interrupt Enable
+ RXIE_RES = RESET<<2, // Receive Interrupt Disable
+ RXIE_UNC = UNCHANGED<<2,
+ TXIE_SET = SET<<4, // Transmit Interrupt Enable
+ TXIE_RES = RESET<<4, // Transmit Interrupt Disable
+ TXIE_UNC = UNCHANGED<<4,
+ MVAL_SET = SET<<6, // Message Valid
+ MVAL_RES = RESET<<6, // Message Invalid
+ MVAL_UNC = UNCHANGED<<6
+};
+
+/* Message Control Register 1 (Base Address + 0x01) */
+enum i82527_iMSGCTL1 {
+ NEWD_SET = SET, // New Data
+ NEWD_RES = RESET, // No New Data
+ NEWD_UNC = UNCHANGED,
+ MLST_SET = SET<<2, // Message Lost
+ MLST_RES = RESET<<2, // No Message Lost
+ MLST_UNC = UNCHANGED<<2,
+ CPUU_SET = SET<<2, // CPU Updating
+ CPUU_RES = RESET<<2, // No CPU Updating
+ CPUU_UNC = UNCHANGED<<2,
+ TXRQ_SET = SET<<4, // Transmission Request
+ TXRQ_RES = RESET<<4, // No Transmission Request
+ TXRQ_UNC = UNCHANGED<<4,
+ RMPD_SET = SET<<6, // Remote Request Pending
+ RMPD_RES = RESET<<6, // No Remote Request Pending
+ RMPD_UNC = UNCHANGED<<6
+};
+
+/* Message Configuration Register (Base Address + 0x06) */
+enum i82527_iMSGCFG {
+ MCFG_XTD = 1<<2, // Extended Identifier
+ MCFG_DIR = 1<<3 // Direction is Transmit
+};
+
+#undef IOPRINT
+#undef IODEBUG
+
+#ifdef IOPRINT
+#define CANout(base,adr,v) \
+ printk("CANout: (%lx+%x)=%x\n", base,\
+ (int)(long)&((canregs_t *)0)->adr,v)
+
+#define CANin(base,adr) \
+ printk("CANin: (%lx+%x)\n", base, (int)(long)&((canregs_t *)0)->adr)
+
+#else /* IOPRINT */
+
+#ifdef IODEBUG
+#define CANout(base,adr,v) \
+ (printk("CANout: (%lx+%x)=%x\n", base,\
+ (int)(long)&((canregs_t *)0)->adr,v),\
+ hw_writereg(base, (int)(long)&((canregs_t *)0)->adr, v))
+#else
+#define CANout(base,adr,v) hw_writereg(base,\
+ (int)(long)&((canregs_t *)0)->adr, v)
+#endif
+
+#define CANin(base,adr) hw_readreg(base, (int)(long)&((canregs_t *)0)->adr)
+
+#endif /* IOPRINT */
+
+/* CAN private data structure */
+
+struct can_priv {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats stats;
+#endif
+ struct can_device_stats can_stats;
+ long open_time;
+ int clock;
+ int hw_regs;
+ int restart_ms;
+ int debug;
+ int speed;
+ int btr;
+ int rx_probe;
+ int mo15;
+ struct timer_list timer;
+ int state;
+};
+
+#define STATE_UNINITIALIZED 0
+#define STATE_PROBE 1
+#define STATE_ACTIVE 2
+#define STATE_ERROR_ACTIVE 3
+#define STATE_ERROR_PASSIVE 4
+#define STATE_BUS_OFF 5
+#define STATE_RESET_MODE 6
+
+void can_proc_create(const char *drv_name);
+void can_proc_remove(const char *drv_name);
+
+#endif /* I82527_H */
--- /dev/null
+/*
+ * proc.c - proc file system functions for I82527 CAN driver.
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h>
+#include "i82527.h"
+#include "hal.h"
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
+
+extern struct net_device *can_dev[];
+
+static struct proc_dir_entry *pde = NULL;
+static struct proc_dir_entry *pde_regs = NULL;
+static struct proc_dir_entry *pde_reset = NULL;
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "CAN bus device statistics:\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " errwarn overrun wakeup buserr "
+ "errpass arbitr restarts clock baud\n");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ struct net_device *dev = can_dev[i];
+ struct can_priv *priv = netdev_priv(dev);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: %8d %8d %8d %8d %8d "
+ "%8d %8d %10d %8d\n", dev->name,
+ priv->can_stats.error_warning,
+ priv->can_stats.data_overrun,
+ priv->can_stats.wakeup,
+ priv->can_stats.bus_error,
+ priv->can_stats.error_passive,
+ priv->can_stats.arbitration_lost,
+ priv->can_stats.restarts,
+ priv->clock,
+ priv->speed
+ );
+
+ }
+ }
+
+ *eof = 1;
+ return len;
+}
+
+
+static int can_proc_dump_regs(char *page, int len, struct net_device *dev)
+{
+ int r,s;
+ struct can_priv *priv = netdev_priv(dev);
+ int regs = priv->hw_regs;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s registers:\n", dev->name);
+
+ for (r = 0; r < regs; r += 0x10) {
+ len += snprintf(page + len, PAGE_SIZE - len, "%02X: ", r);
+ for (s = 0; s < 0x10; s++) {
+ if (r+s < regs)
+ len += snprintf(page + len, PAGE_SIZE-len,
+ "%02X ",
+ hw_readreg(dev->base_addr,
+ r+s));
+ }
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ return len;
+}
+
+static int can_proc_read_regs(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i])
+ len = can_proc_dump_regs(page, len, can_dev[i]);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct net_device *dev;
+ int i;
+ struct can_priv *priv;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "resetting ");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ dev = can_dev[i];
+ priv = netdev_priv(can_dev[i]);
+ if ((priv->state != STATE_UNINITIALIZED)
+ && (priv->state != STATE_RESET_MODE)) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s ", dev->name);
+ dev->stop(dev);
+ dev->open(dev);
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ } else {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "(%s|%d) ", dev->name,
+ priv->state);
+ }
+ }
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len, "done\n");
+
+ *eof = 1;
+ return len;
+}
+
+void can_proc_create(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde == NULL) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ pde = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_stats, NULL);
+ }
+ if (pde_regs == NULL) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ pde_regs = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_regs, NULL);
+ }
+ if (pde_reset == NULL) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ pde_reset = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_reset, NULL);
+ }
+}
+
+void can_proc_remove(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_regs) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_reset) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+}
--- /dev/null
+#
+# $Id$
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+obj-$(CONFIG_CAN_MPC52XX_OLD) += mscan-mpc52xx-old.o
+
+mscan-mpc52xx-old-objs := mscan.o mpc52xx_can.o
+
+endif
--- /dev/null
+/*
+ * DESCRIPTION:
+ * CAN bus driver for the Freescale MPC52xx embedded CPU.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2004-2005, Varma Electronics Oy
+ *
+ * LICENCE:
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * HISTORY:
+ * 2005-02-03 created
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+
+#include "mscan.h"
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+
+RCSID("$Id$");
+
+#define PDEV_MAX 2
+
+struct platform_device *pdev[PDEV_MAX];
+
+static int __devinit mpc52xx_can_probe(struct platform_device *pdev)
+{
+ struct resource *mem;
+ struct net_device *dev;
+ struct mscan_platform_data *pdata = pdev->dev.platform_data;
+ struct can_priv *can;
+ u32 mem_size;
+ int ret = -ENODEV;
+
+ if (!pdata)
+ return ret;
+
+ dev = alloc_mscandev();
+ if (!dev)
+ return -ENOMEM;
+ can = netdev_priv(dev);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->irq = platform_get_irq(pdev, 0);
+ if (!mem || !dev->irq)
+ goto req_error;
+
+ mem_size = mem->end - mem->start + 1;
+ if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
+ dev_err(&pdev->dev, "resource unavailable\n");
+ goto req_error;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
+
+ if (!dev->base_addr) {
+ dev_err(&pdev->dev, "failed to map can port\n");
+ ret = -ENOMEM;
+ goto fail_map;
+ }
+
+ can->can_sys_clock = pdata->clock_frq;
+
+ platform_set_drvdata(pdev, dev);
+
+ ret = register_mscandev(dev, pdata->clock_src);
+ if (ret >= 0) {
+ dev_info(&pdev->dev, "probe for a port 0x%lX done\n",
+ dev->base_addr);
+ return ret;
+ }
+
+ iounmap((unsigned long *)dev->base_addr);
+ fail_map:
+ release_mem_region(mem->start, mem_size);
+ req_error:
+ free_candev(dev);
+ dev_err(&pdev->dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit mpc52xx_can_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_mscandev(dev);
+
+ iounmap((volatile void __iomem *)dev->base_addr);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+ free_candev(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static struct mscan_regs saved_regs;
+static int mpc52xx_can_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ _memcpy_fromio(&saved_regs, regs, sizeof(*regs));
+
+ return 0;
+}
+
+static int mpc52xx_can_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ regs->canctl0 |= MSCAN_INITRQ;
+ while ((regs->canctl1 & MSCAN_INITAK) == 0)
+ udelay(10);
+
+ regs->canctl1 = saved_regs.canctl1;
+ regs->canbtr0 = saved_regs.canbtr0;
+ regs->canbtr1 = saved_regs.canbtr1;
+ regs->canidac = saved_regs.canidac;
+
+ /* restore masks, buffers etc. */
+ _memcpy_toio(®s->canidar1_0, (void *)&saved_regs.canidar1_0,
+ sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0));
+
+ regs->canctl0 &= ~MSCAN_INITRQ;
+ regs->cantbsel = saved_regs.cantbsel;
+ regs->canrier = saved_regs.canrier;
+ regs->cantier = saved_regs.cantier;
+ regs->canctl0 = saved_regs.canctl0;
+
+ return 0;
+}
+#endif
+
+static struct platform_driver mpc52xx_can_driver = {
+ .driver = {
+ .name = "mpc52xx-mscan",
+ },
+ .probe = mpc52xx_can_probe,
+ .remove = __devexit_p(mpc52xx_can_remove),
+#ifdef CONFIG_PM
+ .suspend = mpc52xx_can_suspend,
+ .resume = mpc52xx_can_resume,
+#endif
+};
+
+#ifdef CONFIG_PPC_MERGE
+static int __init mpc52xx_of_to_pdev(void)
+{
+ struct device_node *np = NULL;
+ unsigned int i;
+ int err = -ENODEV;
+
+ for (i = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ (np = of_find_compatible_node(np, "mscan", "mpc5200-mscan"));
+#else
+ (np = of_find_compatible_node(np, NULL, "fsl,mpc5200-mscan"));
+#endif
+ i++) {
+ struct resource r[2] = { };
+ struct mscan_platform_data pdata;
+
+ if (i >= PDEV_MAX) {
+ printk(KERN_WARNING "%s: increase PDEV_MAX for more "
+ "than %i devices\n", __func__, PDEV_MAX);
+ break;
+ }
+
+ err = of_address_to_resource(np, 0, &r[0]);
+ if (err)
+ break;
+
+ of_irq_to_resource(np, 0, &r[1]);
+
+ pdev[i] =
+ platform_device_register_simple("mpc52xx-mscan", i, r, 2);
+ if (IS_ERR(pdev[i])) {
+ err = PTR_ERR(pdev[i]);
+ break;
+ }
+
+ pdata.clock_src = MSCAN_CLKSRC_BUS;
+ pdata.clock_frq = mpc52xx_find_ipb_freq(np);
+ err = platform_device_add_data(pdev[i], &pdata, sizeof(pdata));
+ if (err)
+ break;
+ }
+ return err;
+}
+#else
+#define mscan_of_to_pdev()
+#endif
+
+int __init mpc52xx_can_init(void)
+{
+ printk(KERN_WARNING
+ "This %s driver is DEPRECATED, please switch!\n",
+ mpc52xx_can_driver.driver.name);
+#ifdef CONFIG_PPC_MERGE
+ int err = mpc52xx_of_to_pdev();
+
+ if (err) {
+ printk(KERN_ERR "%s init failed with err=%d\n",
+ mpc52xx_can_driver.driver.name, err);
+ return err;
+ }
+#endif
+ return platform_driver_register(&mpc52xx_can_driver);
+}
+
+void __exit mpc52xx_can_exit(void)
+{
+ int i;
+ platform_driver_unregister(&mpc52xx_can_driver);
+ for (i = 0; i < PDEV_MAX; i++)
+ platform_device_unregister(pdev[i]);
+ printk(KERN_INFO "%s unloaded\n", mpc52xx_can_driver.driver.name);
+}
+
+module_init(mpc52xx_can_init);
+module_exit(mpc52xx_can_exit);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_DESCRIPTION("Freescale MPC5200 CAN driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * mscan.c
+ *
+ * DESCRIPTION:
+ * CAN bus driver for the alone generic (as possible as) MSCAN controller.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2005-2006, Varma Electronics Oy
+ *
+ * LICENCE:
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/can.h>
+#include <linux/list.h>
+#include <asm/io.h>
+
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include "mscan.h"
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
+
+#define MSCAN_NORMAL_MODE 0
+#define MSCAN_SLEEP_MODE MSCAN_SLPRQ
+#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ)
+#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ)
+#define MSCAN_SET_MODE_RETRIES 255
+
+
+#define BTR0_BRP_MASK 0x3f
+#define BTR0_SJW_SHIFT 6
+#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT)
+
+#define BTR1_TSEG1_MASK 0xf
+#define BTR1_TSEG2_SHIFT 4
+#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT)
+#define BTR1_SAM_SHIFT 7
+
+#define BTR0_SET_BRP(brp) (((brp) - 1) & BTR0_BRP_MASK)
+#define BTR0_SET_SJW(sjw) ((((sjw) - 1) << BTR0_SJW_SHIFT) & \
+ BTR0_SJW_MASK)
+
+#define BTR1_SET_TSEG1(tseg1) (((tseg1) - 1) & BTR1_TSEG1_MASK)
+#define BTR1_SET_TSEG2(tseg2) ((((tseg2) - 1) << BTR1_TSEG2_SHIFT) & \
+ BTR1_TSEG2_MASK)
+#define BTR1_SET_SAM(sam) (((sam) & 1) << BTR1_SAM_SHIFT)
+
+struct mscan_state {
+ u8 mode;
+ u8 canrier;
+ u8 cantier;
+};
+
+#define TX_QUEUE_SIZE 3
+
+typedef struct {
+ struct list_head list;
+ u8 mask;
+} tx_queue_entry_t;
+
+struct mscan_priv {
+ struct can_priv can;
+ volatile unsigned long flags;
+ u8 shadow_statflg;
+ u8 shadow_canrier;
+ u8 cur_pri;
+ u8 tx_active;
+
+ struct list_head tx_head;
+ tx_queue_entry_t tx_queue[TX_QUEUE_SIZE];
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ struct napi_struct napi;
+ struct net_device *dev;
+#endif
+};
+
+#define F_RX_PROGRESS 0
+#define F_TX_PROGRESS 1
+#define F_TX_WAIT_ALL 2
+
+static int mscan_set_mode(struct net_device *dev, u8 mode)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ int ret = 0;
+ int i;
+ u8 canctl1;
+
+ if (mode != MSCAN_NORMAL_MODE) {
+ canctl1 = in_8(®s->canctl1);
+ if ((mode & MSCAN_SLPRQ) && (canctl1 & MSCAN_SLPAK) == 0) {
+ out_8(®s->canctl0,
+ in_8(®s->canctl0) | MSCAN_SLPRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(®s->canctl1) & MSCAN_SLPAK)
+ break;
+ udelay(100);
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+
+ if (!ret && (mode & MSCAN_INITRQ)
+ && (canctl1 & MSCAN_INITAK) == 0) {
+ out_8(®s->canctl0,
+ in_8(®s->canctl0) | MSCAN_INITRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(®s->canctl1) & MSCAN_INITAK)
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+
+ if (!ret && (mode & MSCAN_CSWAI))
+ out_8(®s->canctl0,
+ in_8(®s->canctl0) | MSCAN_CSWAI);
+
+ } else {
+ canctl1 = in_8(®s->canctl1);
+ if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) {
+ out_8(®s->canctl0, in_8(®s->canctl0) &
+ ~(MSCAN_SLPRQ | MSCAN_INITRQ));
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ canctl1 = in_8(®s->canctl1);
+ if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK)))
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+ }
+ return ret;
+}
+
+static void mscan_push_state(struct net_device *dev, struct mscan_state *state)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ state->mode = in_8(®s->canctl0) & (MSCAN_SLPRQ | MSCAN_INITRQ |
+ MSCAN_CSWAI);
+ state->canrier = in_8(®s->canrier);
+ state->cantier = in_8(®s->cantier);
+}
+
+static int mscan_pop_state(struct net_device *dev, struct mscan_state *state)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ int ret;
+ ret = mscan_set_mode(dev, state->mode);
+ if (!ret) {
+ out_8(®s->canrier, state->canrier);
+ out_8(®s->cantier, state->cantier);
+ }
+ return ret;
+}
+
+static int mscan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_frame *frame = (struct can_frame *)skb->data;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+
+ int i, rtr, buf_id;
+ u32 can_id;
+
+ if (frame->can_dlc > 8)
+ return -EINVAL;
+
+ dev_dbg(ND2D(dev), "%s\n", __FUNCTION__);
+ out_8(®s->cantier, 0);
+
+ i = ~priv->tx_active & MSCAN_TXE;
+ buf_id = ffs(i) - 1;
+ switch (hweight8(i)) {
+ case 0:
+ netif_stop_queue(dev);
+ dev_err(ND2D(dev), "BUG! Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ case 1:
+ /* if buf_id < 3, then current frame will be send out of order,
+ since buffer with lower id have higher priority (hell..) */
+ if (buf_id < 3)
+ priv->cur_pri++;
+ if (priv->cur_pri == 0xff)
+ set_bit(F_TX_WAIT_ALL, &priv->flags);
+ netif_stop_queue(dev);
+ case 2:
+ set_bit(F_TX_PROGRESS, &priv->flags);
+ }
+ out_8(®s->cantbsel, i);
+
+ rtr = frame->can_id & CAN_RTR_FLAG;
+
+ if (frame->can_id & CAN_EFF_FLAG) {
+ dev_dbg(ND2D(dev), "sending extended frame\n");
+
+ can_id = (frame->can_id & CAN_EFF_MASK) << 1;
+ if (rtr)
+ can_id |= 1;
+ out_be16(®s->tx.idr3_2, can_id);
+
+ can_id >>= 16;
+ can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) | (3 << 3);
+ } else {
+ dev_dbg(ND2D(dev), "sending standard frame\n");
+ can_id = (frame->can_id & CAN_SFF_MASK) << 5;
+ if (rtr)
+ can_id |= 1 << 4;
+ }
+ out_be16(®s->tx.idr1_0, can_id);
+
+ if (!rtr) {
+ volatile void __iomem *data = ®s->tx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ /*Its safe to write into dsr[dlc+1] */
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ out_be16(data, *payload++);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ out_8(®s->tx.dlr, frame->can_dlc);
+ out_8(®s->tx.tbpr, priv->cur_pri);
+
+ /* Start transmission. */
+ out_8(®s->cantflg, 1 << buf_id);
+
+ if (!test_bit(F_TX_PROGRESS, &priv->flags))
+ dev->trans_start = jiffies;
+
+ list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head);
+
+ kfree_skb(skb);
+
+ /* Enable interrupt. */
+ priv->tx_active |= 1 << buf_id;
+ out_8(®s->cantier, priv->tx_active);
+
+ return NETDEV_TX_OK;
+}
+
+static void mscan_tx_timeout(struct net_device *dev)
+{
+ struct sk_buff *skb;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct can_frame *frame;
+ u8 mask;
+
+ printk("%s\n", __FUNCTION__);
+
+ out_8(®s->cantier, 0);
+
+ mask = list_entry(priv->tx_head.next, tx_queue_entry_t, list)->mask;
+ dev->trans_start = jiffies;
+ out_8(®s->cantarq, mask);
+ out_8(®s->cantier, priv->tx_active);
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_notice(ND2D(dev), "TIMEOUT packet dropped\n");
+ return;
+ }
+ frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+ frame->can_id = CAN_ERR_FLAG | CAN_ERR_TX_TIMEOUT;
+ frame->can_dlc = CAN_ERR_DLC;
+
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ netif_rx(skb);
+
+}
+
+static can_state_t state_map[] = {
+ CAN_STATE_ACTIVE,
+ CAN_STATE_BUS_WARNING,
+ CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_BUS_OFF
+};
+
+static inline int check_set_state(struct net_device *dev, u8 canrflg)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ can_state_t state;
+ int ret = 0;
+
+ if (!(canrflg & MSCAN_CSCIF) || priv->can.state > CAN_STATE_BUS_OFF)
+ return 0;
+
+ state =
+ state_map[max(MSCAN_STATE_RX(canrflg), MSCAN_STATE_TX(canrflg))];
+ if (priv->can.state < state)
+ ret = 1;
+ if (state == CAN_STATE_BUS_OFF)
+ netif_carrier_off(dev);
+ else if (priv->can.state == CAN_STATE_BUS_OFF
+ && state != CAN_STATE_BUS_OFF)
+ netif_carrier_on(dev);
+ priv->can.state = state;
+ return ret;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+static int mscan_rx_poll(struct napi_struct *napi, int quota)
+#else
+static int mscan_rx_poll(struct net_device *dev, int *budget)
+#endif
+{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi);
+ struct net_device *dev = priv->dev;
+#else
+ struct mscan_priv *priv = netdev_priv(dev);
+ int quota = min(dev->quota, *budget);
+#endif
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct net_device_stats *stats = dev->get_stats(dev);
+ int npackets = 0;
+ int ret = 1;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u32 can_id;
+ u8 canrflg;
+ int i;
+
+ while (npackets < quota && ((canrflg = in_8(®s->canrflg)) &
+ (MSCAN_RXF | MSCAN_ERR_IF))) {
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_notice(ND2D(dev), "packet dropped\n");
+ stats->rx_dropped++;
+ out_8(®s->canrflg, canrflg);
+ continue;
+ }
+
+ frame = (struct can_frame *)skb_put(skb,
+ sizeof(struct can_frame));
+
+ if (canrflg & MSCAN_RXF) {
+ can_id = in_be16(®s->rx.idr1_0);
+ if (can_id & (1 << 3)) {
+ frame->can_id = CAN_EFF_FLAG;
+ can_id = ((can_id << 16) |
+ in_be16(®s->rx.idr3_2));
+ can_id = ((can_id & 0xffe00000) |
+ ((can_id & 0x7ffff) << 2)) >> 2;
+ } else {
+ can_id >>= 4;
+ frame->can_id = 0;
+ }
+
+ frame->can_id |= can_id >> 1;
+ if (can_id & 1)
+ frame->can_id |= CAN_RTR_FLAG;
+ frame->can_dlc = in_8(®s->rx.dlr) & 0xf;
+
+ if (!(frame->can_id & CAN_RTR_FLAG)) {
+ volatile void __iomem *data = ®s->rx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ *payload++ = in_be16(data);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ dev_dbg(ND2D(dev),
+ "received pkt: id: %u dlc: %u data: ",
+ frame->can_id, frame->can_dlc);
+#ifdef DEBUG
+ for (i = 0;
+ i < frame->can_dlc && !(frame->can_id &
+ CAN_FLAG_RTR); i++)
+ printk("%2x ", frame->data[i]);
+ printk("\n");
+#endif
+
+ out_8(®s->canrflg, MSCAN_RXF);
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+ } else if (canrflg & MSCAN_ERR_IF) {
+ frame->can_id = CAN_ERR_FLAG;
+
+ if (canrflg & MSCAN_OVRIF) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ stats->rx_over_errors++;
+ } else
+ frame->data[1] = 0;
+
+ if (check_set_state(dev, canrflg)) {
+ frame->can_id |= CAN_ERR_CRTL;
+ switch (priv->can.state) {
+ case CAN_STATE_BUS_WARNING:
+ if ((priv->shadow_statflg &
+ MSCAN_RSTAT_MSK) <
+ (canrflg & MSCAN_RSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_WARNING;
+
+ if ((priv->shadow_statflg &
+ MSCAN_TSTAT_MSK) <
+ (canrflg & MSCAN_TSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING;
+ break;
+ case CAN_STATE_BUS_PASSIVE:
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_PASSIVE;
+ break;
+ case CAN_STATE_BUS_OFF:
+ frame->can_id |= CAN_ERR_BUSOFF;
+ frame->can_id &= ~CAN_ERR_CRTL;
+ break;
+ default:
+ break;
+ }
+ }
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ frame->can_dlc = CAN_ERR_DLC;
+ out_8(®s->canrflg, MSCAN_ERR_IF);
+ }
+
+ npackets++;
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_receive_skb(skb);
+ }
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
+ *budget -= npackets;
+ dev->quota -= npackets;
+#endif
+
+ if (!(in_8(®s->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ netif_rx_complete(dev, &priv->napi);
+#else
+ netif_rx_complete(dev);
+#endif
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ out_8(®s->canrier,
+ in_8(®s->canrier) | MSCAN_ERR_IF | MSCAN_RXFIE);
+ ret = 0;
+ }
+ return ret;
+}
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static irqreturn_t mscan_isr(int irq, void *dev_id, struct pt_regs *r)
+#else
+static irqreturn_t mscan_isr(int irq, void *dev_id)
+#endif
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct net_device_stats *stats = dev->get_stats(dev);
+ u8 cantier, cantflg, canrflg;
+ irqreturn_t ret = IRQ_NONE;
+
+ if ((cantier = in_8(®s->cantier) & MSCAN_TXE) &&
+ (cantflg = in_8(®s->cantflg) & cantier)) {
+ struct list_head *tmp, *pos;
+
+ list_for_each_safe(pos, tmp, &priv->tx_head) {
+ tx_queue_entry_t *entry =
+ list_entry(pos, tx_queue_entry_t, list);
+ u8 mask = entry->mask;
+
+ if (!(cantflg & mask))
+ continue;
+
+ if (in_8(®s->cantaak) & mask) {
+ stats->tx_dropped++;
+ stats->tx_aborted_errors++;
+ } else {
+ out_8(®s->cantbsel, mask);
+ stats->tx_bytes +=
+ in_8(®s->tx.dlr);
+ stats->tx_packets++;
+ }
+ priv->tx_active &= ~mask;
+ list_del(pos);
+ }
+
+ if (list_empty(&priv->tx_head)) {
+ clear_bit(F_TX_WAIT_ALL, &priv->flags);
+ clear_bit(F_TX_PROGRESS, &priv->flags);
+ priv->cur_pri = 0;
+ } else
+ dev->trans_start = jiffies;
+
+ if (!test_bit(F_TX_WAIT_ALL, &priv->flags))
+ netif_wake_queue(dev);
+
+ out_8(®s->cantier, priv->tx_active);
+ ret = IRQ_HANDLED;
+ }
+
+ if ((((canrflg = in_8(®s->canrflg)) & ~MSCAN_STAT_MSK)) &&
+ !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) {
+ if (check_set_state(dev, canrflg)) {
+ out_8(®s->canrflg, MSCAN_CSCIF);
+ ret = IRQ_HANDLED;
+ }
+ if (canrflg & ~MSCAN_STAT_MSK) {
+ priv->shadow_canrier = in_8(®s->canrier);
+ out_8(®s->canrier, 0);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ netif_rx_schedule(dev, &priv->napi);
+#else
+ netif_rx_schedule(dev);
+#endif
+ ret = IRQ_HANDLED;
+ } else
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ }
+ return ret;
+}
+
+static int mscan_do_set_mode(struct net_device *dev, can_mode_t mode)
+{
+ switch (mode) {
+ case CAN_MODE_SLEEP:
+ case CAN_MODE_STOP:
+ netif_stop_queue(dev);
+ mscan_set_mode(dev,
+ (mode ==
+ CAN_MODE_STOP) ? MSCAN_INIT_MODE :
+ MSCAN_SLEEP_MODE);
+ break;
+ case CAN_MODE_START:
+ printk("%s: CAN_MODE_START requested\n", __FUNCTION__);
+ mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int mscan_do_set_bit_time(struct net_device *dev,
+ struct can_bittime *bt)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ int ret = 0;
+ u8 reg;
+ struct mscan_state state;
+
+ if (bt->type != CAN_BITTIME_STD)
+ return -EINVAL;
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ mscan_push_state(dev, &state);
+ ret = mscan_set_mode(dev, MSCAN_INIT_MODE);
+ if (!ret) {
+ reg = BTR0_SET_BRP(bt->std.brp) | BTR0_SET_SJW(bt->std.sjw);
+ out_8(®s->canbtr0, reg);
+
+ reg = (BTR1_SET_TSEG1(bt->std.prop_seg + bt->std.phase_seg1) |
+ BTR1_SET_TSEG2(bt->std.phase_seg2) |
+ BTR1_SET_SAM(bt->std.sam));
+ out_8(®s->canbtr1, reg);
+
+ ret = mscan_pop_state(dev, &state);
+ }
+
+ spin_unlock_irq(&priv->can.irq_lock);
+ return ret;
+}
+
+static int mscan_open(struct net_device *dev)
+{
+ int ret;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ napi_enable(&priv->napi);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ ret = request_irq(dev->irq, mscan_isr, SA_SHIRQ, dev->name, dev);
+#else
+ ret = request_irq(dev->irq, mscan_isr, IRQF_SHARED, dev->name, dev);
+#endif
+
+ if (ret < 0) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ napi_disable(&priv->napi);
+#endif
+ printk(KERN_ERR "%s - failed to attach interrupt\n",
+ dev->name);
+ return ret;
+ }
+
+ INIT_LIST_HEAD(&priv->tx_head);
+ /* acceptance mask/acceptance code (accept everything) */
+ out_be16(®s->canidar1_0, 0);
+ out_be16(®s->canidar3_2, 0);
+ out_be16(®s->canidar5_4, 0);
+ out_be16(®s->canidar7_6, 0);
+
+ out_be16(®s->canidmr1_0, 0xffff);
+ out_be16(®s->canidmr3_2, 0xffff);
+ out_be16(®s->canidmr5_4, 0xffff);
+ out_be16(®s->canidmr7_6, 0xffff);
+ /* Two 32 bit Acceptance Filters */
+ out_8(®s->canidac, MSCAN_AF_32BIT);
+
+ out_8(®s->canctl1, in_8(®s->canctl1) & ~MSCAN_LISTEN);
+ mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+
+ priv->shadow_statflg = in_8(®s->canrflg) & MSCAN_STAT_MSK;
+ priv->cur_pri = 0;
+ priv->tx_active = 0;
+
+ out_8(®s->cantier, 0);
+ /* Enable receive interrupts. */
+ out_8(®s->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE |
+ MSCAN_RSTATE1 | MSCAN_RSTATE0 | MSCAN_TSTATE1 | MSCAN_TSTATE0);
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int mscan_close(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ netif_stop_queue(dev);
+
+ /* disable interrupts */
+ out_8(®s->cantier, 0);
+ out_8(®s->canrier, 0);
+ free_irq(dev->irq, dev);
+
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ return 0;
+}
+
+int register_mscandev(struct net_device *dev, int clock_src)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ u8 ctl1;
+
+ ctl1 = in_8(®s->canctl1);
+ if (clock_src)
+ ctl1 |= MSCAN_CLKSRC;
+ else
+ ctl1 &= ~MSCAN_CLKSRC;
+
+ ctl1 |= MSCAN_CANE;
+ out_8(®s->canctl1, ctl1);
+ udelay(100);
+
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+
+ return register_netdev(dev);
+}
+
+EXPORT_SYMBOL(register_mscandev);
+
+void unregister_mscandev(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ out_8(®s->canctl1, in_8(®s->canctl1) & ~MSCAN_CANE);
+ unregister_netdev(dev);
+}
+
+EXPORT_SYMBOL(unregister_mscandev);
+
+struct net_device *alloc_mscandev(void)
+{
+ struct net_device *dev;
+ struct mscan_priv *priv;
+ int i;
+
+ dev = alloc_candev(sizeof(struct mscan_priv));
+ if (!dev)
+ return NULL;
+ priv = netdev_priv(dev);
+
+ dev->watchdog_timeo = MSCAN_WATCHDOG_TIMEOUT;
+ dev->open = mscan_open;
+ dev->stop = mscan_close;
+ dev->hard_start_xmit = mscan_hard_start_xmit;
+ dev->tx_timeout = mscan_tx_timeout;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ priv->dev = dev;
+ netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8);
+#else
+ dev->poll = mscan_rx_poll;
+ dev->weight = 8;
+#endif
+
+ priv->can.do_set_bittime = mscan_do_set_bit_time;
+ priv->can.do_set_mode = mscan_do_set_mode;
+
+ for (i = 0; i < TX_QUEUE_SIZE; i++)
+ priv->tx_queue[i].mask = 1 << i;
+
+ return dev;
+}
+
+EXPORT_SYMBOL(alloc_mscandev);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver for a mscan based chips");
--- /dev/null
+/*
+ * $Id$
+ *
+ * DESCRIPTION:
+ * Definitions of consts/structs to drive the Freescale MSCAN.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2004-2006, Varma Electronics Oy
+ *
+ * LICENCE:
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __MSCAN_H__
+#define __MSCAN_H__
+
+#include <linux/autoconf.h>
+#include <asm/types.h>
+
+/* MSCAN control register 0 (CANCTL0) bits */
+#define MSCAN_RXFRM 0x80
+#define MSCAN_RXACT 0x40
+#define MSCAN_CSWAI 0x20
+#define MSCAN_SYNCH 0x10
+#define MSCAN_TIME 0x08
+#define MSCAN_WUPE 0x04
+#define MSCAN_SLPRQ 0x02
+#define MSCAN_INITRQ 0x01
+
+/* MSCAN control register 1 (CANCTL1) bits */
+#define MSCAN_CANE 0x80
+#define MSCAN_CLKSRC 0x40
+#define MSCAN_LOOPB 0x20
+#define MSCAN_LISTEN 0x10
+#define MSCAN_WUPM 0x04
+#define MSCAN_SLPAK 0x02
+#define MSCAN_INITAK 0x01
+
+#ifdef CONFIG_PPC_MPC52xx
+#define MSCAN_CLKSRC_BUS 0
+#define MSCAN_CLKSRC_XTAL MSCAN_CLKSRC
+#else
+#define MSCAN_CLKSRC_BUS MSCAN_CLKSRC
+#define MSCAN_CLKSRC_XTAL 0
+#endif
+
+/* MSCAN receiver flag register (CANRFLG) bits */
+#define MSCAN_WUPIF 0x80
+#define MSCAN_CSCIF 0x40
+#define MSCAN_RSTAT1 0x20
+#define MSCAN_RSTAT0 0x10
+#define MSCAN_TSTAT1 0x08
+#define MSCAN_TSTAT0 0x04
+#define MSCAN_OVRIF 0x02
+#define MSCAN_RXF 0x01
+#define MSCAN_ERR_IF (MSCAN_OVRIF | MSCAN_CSCIF)
+#define MSCAN_RSTAT_MSK (MSCAN_RSTAT1 | MSCAN_RSTAT0)
+#define MSCAN_TSTAT_MSK (MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STAT_MSK (MSCAN_RSTAT_MSK | MSCAN_TSTAT_MSK)
+
+#define MSCAN_STATE_BUS_OFF (MSCAN_RSTAT1 | MSCAN_RSTAT0 | \
+ MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STATE_TX(canrflg) (((canrflg)&MSCAN_TSTAT_MSK)>>2)
+#define MSCAN_STATE_RX(canrflg) (((canrflg)&MSCAN_RSTAT_MSK)>>4)
+#define MSCAN_STATE_ACTIVE 0
+#define MSCAN_STATE_WARNING 1
+#define MSCAN_STATE_PASSIVE 2
+#define MSCAN_STATE_BUSOFF 3
+
+/* MSCAN receiver interrupt enable register (CANRIER) bits */
+#define MSCAN_WUPIE 0x80
+#define MSCAN_CSCIE 0x40
+#define MSCAN_RSTATE1 0x20
+#define MSCAN_RSTATE0 0x10
+#define MSCAN_TSTATE1 0x08
+#define MSCAN_TSTATE0 0x04
+#define MSCAN_OVRIE 0x02
+#define MSCAN_RXFIE 0x01
+
+/* MSCAN transmitter flag register (CANTFLG) bits */
+#define MSCAN_TXE2 0x04
+#define MSCAN_TXE1 0x02
+#define MSCAN_TXE0 0x01
+#define MSCAN_TXE (MSCAN_TXE2 | MSCAN_TXE1 | MSCAN_TXE0)
+
+/* MSCAN transmitter interrupt enable register (CANTIER) bits */
+#define MSCAN_TXIE2 0x04
+#define MSCAN_TXIE1 0x02
+#define MSCAN_TXIE0 0x01
+#define MSCAN_TXIE (MSCAN_TXIE2 | MSCAN_TXIE1 | MSCAN_TXIE0)
+
+/* MSCAN transmitter message abort request (CANTARQ) bits */
+#define MSCAN_ABTRQ2 0x04
+#define MSCAN_ABTRQ1 0x02
+#define MSCAN_ABTRQ0 0x01
+
+/* MSCAN transmitter message abort ack (CANTAAK) bits */
+#define MSCAN_ABTAK2 0x04
+#define MSCAN_ABTAK1 0x02
+#define MSCAN_ABTAK0 0x01
+
+/* MSCAN transmit buffer selection (CANTBSEL) bits */
+#define MSCAN_TX2 0x04
+#define MSCAN_TX1 0x02
+#define MSCAN_TX0 0x01
+
+/* MSCAN ID acceptance control register (CANIDAC) bits */
+#define MSCAN_IDAM1 0x20
+#define MSCAN_IDAM0 0x10
+#define MSCAN_IDHIT2 0x04
+#define MSCAN_IDHIT1 0x02
+#define MSCAN_IDHIT0 0x01
+
+#define MSCAN_AF_32BIT 0x00
+#define MSCAN_AF_16BIT MSCAN_IDAM0
+#define MSCAN_AF_8BIT MSCAN_IDAM1
+#define MSCAN_AF_CLOSED (MSCAN_IDAM0|MSCAN_IDAM1)
+#define MSCAN_AF_MASK (~(MSCAN_IDAM0|MSCAN_IDAM1))
+
+/* MSCAN Miscellaneous Register (CANMISC) bits */
+#define MSCAN_BOHOLD 0x01
+
+#ifdef CONFIG_PPC_MPC52xx
+#define _MSCAN_RESERVED_(n,num) u8 _res##n[num]
+#define _MSCAN_RESERVED_DSR_SIZE 2
+#else
+#define _MSCAN_RESERVED_(n,num)
+#define _MSCAN_RESERVED_DSR_SIZE 0
+#endif
+
+/* Structure of the hardware registers */
+struct mscan_regs {
+ /* (see doco S12MSCANV3/D) MPC5200 MSCAN */
+ u8 canctl0; /* + 0x00 0x00 */
+ u8 canctl1; /* + 0x01 0x01 */
+ _MSCAN_RESERVED_(1, 2); /* + 0x02 */
+ u8 canbtr0; /* + 0x04 0x02 */
+ u8 canbtr1; /* + 0x05 0x03 */
+ _MSCAN_RESERVED_(2, 2); /* + 0x06 */
+ u8 canrflg; /* + 0x08 0x04 */
+ u8 canrier; /* + 0x09 0x05 */
+ _MSCAN_RESERVED_(3, 2); /* + 0x0a */
+ u8 cantflg; /* + 0x0c 0x06 */
+ u8 cantier; /* + 0x0d 0x07 */
+ _MSCAN_RESERVED_(4, 2); /* + 0x0e */
+ u8 cantarq; /* + 0x10 0x08 */
+ u8 cantaak; /* + 0x11 0x09 */
+ _MSCAN_RESERVED_(5, 2); /* + 0x12 */
+ u8 cantbsel; /* + 0x14 0x0a */
+ u8 canidac; /* + 0x15 0x0b */
+ u8 reserved; /* + 0x16 0x0c */
+ _MSCAN_RESERVED_(6, 5); /* + 0x17 */
+#ifndef CONFIG_PPC_MPC52xx
+ u8 canmisc; /* 0x0d */
+#endif
+ u8 canrxerr; /* + 0x1c 0x0e */
+ u8 cantxerr; /* + 0x1d 0x0f */
+ _MSCAN_RESERVED_(7, 2); /* + 0x1e */
+ u16 canidar1_0; /* + 0x20 0x10 */
+ _MSCAN_RESERVED_(8, 2); /* + 0x22 */
+ u16 canidar3_2; /* + 0x24 0x12 */
+ _MSCAN_RESERVED_(9, 2); /* + 0x26 */
+ u16 canidmr1_0; /* + 0x28 0x14 */
+ _MSCAN_RESERVED_(10, 2); /* + 0x2a */
+ u16 canidmr3_2; /* + 0x2c 0x16 */
+ _MSCAN_RESERVED_(11, 2); /* + 0x2e */
+ u16 canidar5_4; /* + 0x30 0x18 */
+ _MSCAN_RESERVED_(12, 2); /* + 0x32 */
+ u16 canidar7_6; /* + 0x34 0x1a */
+ _MSCAN_RESERVED_(13, 2); /* + 0x36 */
+ u16 canidmr5_4; /* + 0x38 0x1c */
+ _MSCAN_RESERVED_(14, 2); /* + 0x3a */
+ u16 canidmr7_6; /* + 0x3c 0x1e */
+ _MSCAN_RESERVED_(15, 2); /* + 0x3e */
+ struct {
+ u16 idr1_0; /* + 0x40 0x20 */
+ _MSCAN_RESERVED_(16, 2); /* + 0x42 */
+ u16 idr3_2; /* + 0x44 0x22 */
+ _MSCAN_RESERVED_(17, 2); /* + 0x46 */
+ u16 dsr1_0; /* + 0x48 0x24 */
+ _MSCAN_RESERVED_(18, 2); /* + 0x4a */
+ u16 dsr3_2; /* + 0x4c 0x26 */
+ _MSCAN_RESERVED_(19, 2); /* + 0x4e */
+ u16 dsr5_4; /* + 0x50 0x28 */
+ _MSCAN_RESERVED_(20, 2); /* + 0x52 */
+ u16 dsr7_6; /* + 0x54 0x2a */
+ _MSCAN_RESERVED_(21, 2); /* + 0x56 */
+ u8 dlr; /* + 0x58 0x2c */
+ u8:8; /* + 0x59 0x2d */
+ _MSCAN_RESERVED_(22, 2); /* + 0x5a */
+ u16 time; /* + 0x5c 0x2e */
+ } rx;
+ _MSCAN_RESERVED_(23, 2); /* + 0x5e */
+ struct {
+ u16 idr1_0; /* + 0x60 0x30 */
+ _MSCAN_RESERVED_(24, 2); /* + 0x62 */
+ u16 idr3_2; /* + 0x64 0x32 */
+ _MSCAN_RESERVED_(25, 2); /* + 0x66 */
+ u16 dsr1_0; /* + 0x68 0x34 */
+ _MSCAN_RESERVED_(26, 2); /* + 0x6a */
+ u16 dsr3_2; /* + 0x6c 0x36 */
+ _MSCAN_RESERVED_(27, 2); /* + 0x6e */
+ u16 dsr5_4; /* + 0x70 0x38 */
+ _MSCAN_RESERVED_(28, 2); /* + 0x72 */
+ u16 dsr7_6; /* + 0x74 0x3a */
+ _MSCAN_RESERVED_(29, 2); /* + 0x76 */
+ u8 dlr; /* + 0x78 0x3c */
+ u8 tbpr; /* + 0x79 0x3d */
+ _MSCAN_RESERVED_(30, 2); /* + 0x7a */
+ u16 time; /* + 0x7c 0x3e */
+ } tx;
+ _MSCAN_RESERVED_(31, 2); /* + 0x7e */
+} __attribute__ ((packed));
+
+#undef _MSCAN_RESERVED_
+#define MSCAN_REGION sizeof(struct mscan)
+
+#define MSCAN_WATCHDOG_TIMEOUT ((500*HZ)/1000)
+
+struct mscan_platform_data {
+ u8 clock_src; /* MSCAN_CLKSRC_BUS or MSCAN_CLKSRC_XTAL */
+ u32 clock_frq; /* can ref. clock, in Hz */
+};
+
+struct net_device *alloc_mscandev(void);
+/* @clock_src:
+ 1 = The MSCAN clock source is the onchip Bus Clock.
+ 0 = The MSCAN clock source is the chip Oscillator Clock.
+*/
+extern int register_mscandev(struct net_device *dev, int clock_src);
+extern void unregister_mscandev(struct net_device *dev);
+
+#endif /* __MSCAN_H__ */
--- /dev/null
+#
+# $Id$
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/can/old/hal
+
+obj-m := sja1000-io.o sja1000-iomem.o sja1000-iomux.o sja1000-gw2.o sja1000-esdio.o sja1000-c200.o
+
+sja1000-io-objs := sja1000.o proc.o ../hal/io.o
+sja1000-iomem-objs := sja1000.o proc.o ../hal/iomem.o
+sja1000-iomux-objs := sja1000.o proc.o ../hal/iomux.o
+sja1000-gw2-objs := sja1000.o proc.o ../hal/gw2.o
+sja1000-esdio-objs := sja1000.o proc.o ../hal/esdio.o
+sja1000-c200-objs := sja1000.o proc.o ../hal/c200.o
+
+endif
--- /dev/null
+/*
+ * proc.c - proc file system functions for SJA1000 CAN driver.
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h>
+#include "sja1000.h"
+#include "hal.h"
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
+
+extern struct net_device *can_dev[];
+
+static struct proc_dir_entry *pde = NULL;
+static struct proc_dir_entry *pde_regs = NULL;
+static struct proc_dir_entry *pde_reset = NULL;
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "CAN bus device statistics:\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " errwarn overrun wakeup buserr "
+ "errpass arbitr restarts clock baud\n");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ struct net_device *dev = can_dev[i];
+ struct can_priv *priv = netdev_priv(dev);
+#ifdef SJA1000_H
+ u8 stat = hw_readreg(dev->base_addr, REG_SR);
+
+ if (stat & 0x80) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: bus status: "
+ "BUS OFF, ", dev->name);
+ } else if (stat & 0x40) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: bus status: ERROR "
+ "PASSIVE, ", dev->name);
+ } else {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: bus status: OK, ",
+ dev->name);
+ }
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "RXERR: %d, TXERR: %d\n",
+ hw_readreg(dev->base_addr, REG_RXERR),
+ hw_readreg(dev->base_addr, REG_TXERR));
+#endif
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: %8d %8d %8d %8d %8d "
+ "%8d %8d %10d %8d\n", dev->name,
+ priv->can_stats.error_warning,
+ priv->can_stats.data_overrun,
+ priv->can_stats.wakeup,
+ priv->can_stats.bus_error,
+ priv->can_stats.error_passive,
+ priv->can_stats.arbitration_lost,
+ priv->can_stats.restarts,
+ priv->clock,
+ priv->speed
+ );
+
+ }
+ }
+
+ *eof = 1;
+ return len;
+}
+
+
+static int can_proc_dump_regs(char *page, int len, struct net_device *dev)
+{
+ int r,s;
+ struct can_priv *priv = netdev_priv(dev);
+ int regs = priv->hw_regs;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s registers:\n", dev->name);
+
+ for (r = 0; r < regs; r += 0x10) {
+ len += snprintf(page + len, PAGE_SIZE - len, "%02X: ", r);
+ for (s = 0; s < 0x10; s++) {
+ if (r+s < regs)
+ len += snprintf(page + len, PAGE_SIZE-len,
+ "%02X ",
+ hw_readreg(dev->base_addr,
+ r+s));
+ }
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ return len;
+}
+
+static int can_proc_read_regs(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i])
+ len = can_proc_dump_regs(page, len, can_dev[i]);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct net_device *dev;
+ int i;
+ struct can_priv *priv;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "resetting ");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ dev = can_dev[i];
+ priv = netdev_priv(can_dev[i]);
+ if ((priv->state != STATE_UNINITIALIZED)
+ && (priv->state != STATE_RESET_MODE)) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s ", dev->name);
+ dev->stop(dev);
+ dev->open(dev);
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ } else {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "(%s|%d) ", dev->name,
+ priv->state);
+ }
+ }
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len, "done\n");
+
+ *eof = 1;
+ return len;
+}
+
+void can_proc_create(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde == NULL) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ pde = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_stats, NULL);
+ }
+ if (pde_regs == NULL) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ pde_regs = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_regs, NULL);
+ }
+ if (pde_reset == NULL) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ pde_reset = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_reset, NULL);
+ }
+}
+
+void can_proc_remove(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_regs) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_reset) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+}
--- /dev/null
+/*
+ * sja1000.c - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h> /* for struct can_device_stats */
+#include "sja1000.h"
+#include "hal.h"
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("LLCF/socketcan '" CHIP_NAME "' network device driver");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) ((priv->debug > 0) ? printk(args) : 0)
+/* logging in interrupt context! */
+#define iDBG(args...) ((priv->debug > 1) ? printk(args) : 0)
+#define iiDBG(args...) ((priv->debug > 2) ? printk(args) : 0)
+#else
+#define DBG(args...)
+#define iDBG(args...)
+#define iiDBG(args...)
+#endif
+
+char drv_name[DRV_NAME_LEN] = "undefined";
+
+/* driver and version information */
+static const char *drv_version = "0.1.1";
+static const char *drv_reldate = "2007-04-13";
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static const char *ecc_errors[] = {
+ NULL,
+ NULL,
+ "ID.28 to ID.28",
+ "start of frame",
+ "bit SRTR",
+ "bit IDE",
+ "ID.20 to ID.18",
+ "ID.17 to ID.13",
+ "CRC sequence",
+ "reserved bit 0",
+ "data field",
+ "data length code",
+ "bit RTR",
+ "reserved bit 1",
+ "ID.4 to ID.0",
+ "ID.12 to ID.5",
+ NULL,
+ "active error flag",
+ "intermission",
+ "tolerate dominant bits",
+ NULL,
+ NULL,
+ "passive error flag",
+ "error delimiter",
+ "CRC delimiter",
+ "acknowledge slot",
+ "end of frame",
+ "acknowledge delimiter",
+ "overload flag",
+ NULL,
+ NULL,
+ NULL
+};
+
+static const char *ecc_types[] = {
+ "bit error",
+ "form error",
+ "stuff error",
+ "other type of error"
+};
+#endif
+
+/* array of all can chips */
+struct net_device *can_dev[MAXDEV];
+
+/* module parameters */
+unsigned long base[MAXDEV] = { 0 }; /* hardware address */
+unsigned long rbase[MAXDEV] = { 0 }; /* (remapped) device address */
+unsigned int irq[MAXDEV] = { 0 };
+
+unsigned int speed[MAXDEV] = { DEFAULT_SPEED, DEFAULT_SPEED };
+unsigned int btr[MAXDEV] = { 0 };
+
+static int rx_probe[MAXDEV] = { 0 };
+static int clk = DEFAULT_HW_CLK;
+static int debug = 0;
+static int restart_ms = 100;
+static int echo = 1;
+
+static int base_n;
+static int irq_n;
+static int speed_n;
+static int btr_n;
+static int rx_probe_n;
+
+module_param_array(base, int, &base_n, 0);
+module_param_array(irq, int, &irq_n, 0);
+module_param_array(speed, int, &speed_n, 0);
+module_param_array(btr, int, &btr_n, 0);
+module_param_array(rx_probe, int, &rx_probe_n, 0);
+
+module_param(clk, int, 0);
+module_param(debug, int, 0);
+module_param(restart_ms, int, 0);
+module_param(echo, int, S_IRUGO);
+
+MODULE_PARM_DESC(base, "CAN controller base address");
+MODULE_PARM_DESC(irq, "CAN controller interrupt");
+MODULE_PARM_DESC(speed, "CAN bus bitrate");
+MODULE_PARM_DESC(btr, "Bit Timing Register value 0x<btr0><btr1>, e.g. 0x4014");
+MODULE_PARM_DESC(rx_probe, "switch to trx mode after correct msg receiption. (default off)");
+
+MODULE_PARM_DESC(clk, "CAN controller chip clock (default: 16MHz)");
+MODULE_PARM_DESC(debug, "set debug mask (default: 0)");
+MODULE_PARM_DESC(restart_ms, "restart chip on heavy bus errors / bus off after x ms (default 100ms)");
+MODULE_PARM_DESC(echo, "Echo sent frames. default: 1 (On)");
+
+/*
+ * CAN network devices *should* support a local echo functionality
+ * (see Documentation/networking/can.txt). To test the handling of CAN
+ * interfaces that do not support the local echo both driver types are
+ * implemented inside this sja1000 driver. In the case that the driver does
+ * not support the echo the IFF_ECHO remains clear in dev->flags.
+ * This causes the PF_CAN core to perform the echo as a fallback solution.
+ */
+
+/* function declarations */
+
+static void can_restart_dev(unsigned long data);
+static void chipset_init(struct net_device *dev, int wake);
+static void chipset_init_rx(struct net_device *dev);
+static void chipset_init_trx(struct net_device *dev);
+static void can_netdev_setup(struct net_device *dev);
+static struct net_device* can_create_netdev(int dev_num, int hw_regs);
+static int can_set_drv_name(void);
+int set_reset_mode(struct net_device *dev);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+static struct net_device_stats *can_get_stats(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* TODO: read statistics from chip */
+ return &priv->stats;
+}
+#endif
+
+static int sja1000_probe_chip(unsigned long base)
+{
+ if (base && (hw_readreg(base, 0) == 0xFF)) {
+ printk(KERN_INFO "%s: probing @0x%lX failed\n",
+ drv_name, base);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * set baud rate divisor values
+ */
+static void set_btr(struct net_device *dev, int btr0, int btr1)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* no bla bla when restarting the device */
+ if (priv->state == STATE_UNINITIALIZED)
+ printk(KERN_INFO "%s: setting BTR0=%02X BTR1=%02X\n",
+ dev->name, btr0, btr1);
+
+ hw_writereg(dev->base_addr, REG_BTR0, btr0);
+ hw_writereg(dev->base_addr, REG_BTR1, btr1);
+}
+
+/*
+ * calculate baud rate divisor values
+ */
+static void set_baud(struct net_device *dev, int baud, int clock)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ int error;
+ int brp;
+ int tseg;
+ int tseg1 = 0;
+ int tseg2 = 0;
+
+ int best_error = 1000000000;
+ int best_tseg = 0;
+ int best_brp = 0;
+ int best_baud = 0;
+
+ int SAM = (baud > 100000 ? 0 : 1);
+
+ clock >>= 1;
+
+ for (tseg = (0 + 0 + 2) * 2;
+ tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1;
+ tseg++) {
+ brp = clock / ((1 + tseg / 2) * baud) + tseg % 2;
+ if ((brp > 0) && (brp <= 64)) {
+ error = baud - clock / (brp * (1 + tseg / 2));
+ if (error < 0) {
+ error = -error;
+ }
+ if (error <= best_error) {
+ best_error = error;
+ best_tseg = tseg / 2;
+ best_brp = brp - 1;
+ best_baud = clock / (brp * (1 + tseg / 2));
+ }
+ }
+ }
+ if (best_error && (baud / best_error < 10)) {
+ printk("%s: unable to set baud rate %d (ext clock %dHz)\n",
+ dev->name, baud, clock * 2);
+ return;
+// return -EINVAL;
+ }
+ tseg2 = best_tseg - (SAMPLE_POINT * (best_tseg + 1)) / 100;
+ if (tseg2 < 0) {
+ tseg2 = 0;
+ } else if (tseg2 > MAX_TSEG2) {
+ tseg2 = MAX_TSEG2;
+ }
+ tseg1 = best_tseg - tseg2 - 2;
+ if (tseg1 > MAX_TSEG1) {
+ tseg1 = MAX_TSEG1;
+ tseg2 = best_tseg - tseg1 - 2;
+ }
+
+ priv->btr = ((best_brp | JUMPWIDTH)<<8) +
+ ((SAM << 7) | (tseg2 << 4) | tseg1);
+
+ printk(KERN_INFO "%s: calculated best baudrate: %d / btr is 0x%04X\n",
+ dev->name, best_baud, priv->btr);
+
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+// set_btr(dev, best_brp | JUMPWIDTH, (SAM << 7) | (tseg2 << 4) | tseg1);
+}
+
+int set_reset_mode(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ priv->can_stats.bus_error_at_init = priv->can_stats.bus_error;
+
+ /* disable interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_OFF);
+
+ for (i = 0; i < 10; i++) {
+ /* check reset bit */
+ if (status & MOD_RM) {
+ if (i > 1) {
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+ priv->state = STATE_RESET_MODE;
+ return 0;
+ }
+
+ hw_writereg(dev->base_addr, REG_MOD, MOD_RM); /* reset chip */
+ status = hw_readreg(dev->base_addr, REG_MOD);
+
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into reset mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ /* check reset bit */
+ if ((status & MOD_RM) == 0) {
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ if (i > 1) {
+ struct can_priv *priv = netdev_priv(dev);
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+#endif
+ return 0;
+ }
+
+ /* set chip to normal mode */
+ hw_writereg(dev->base_addr, REG_MOD, 0x00);
+ status = hw_readreg(dev->base_addr, REG_MOD);
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into normal mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+static int set_listen_mode(struct net_device *dev)
+{
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ /* check reset mode bit */
+ if ((status & MOD_RM) == 0) {
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ if (i > 1) {
+ struct can_priv *priv = netdev_priv(dev);
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+#endif
+ return 0;
+ }
+
+ /* set listen only mode, clear reset */
+ hw_writereg(dev->base_addr, REG_MOD, MOD_LOM);
+ status = hw_readreg(dev->base_addr, REG_MOD);
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into listen mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+/*
+ * initialize SJA1000 chip:
+ * - reset chip
+ * - set output mode
+ * - set baudrate
+ * - enable interrupts
+ * - start operating mode
+ */
+static void chipset_init_regs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ /* go into Pelican mode, disable clkout, disable comparator */
+ hw_writereg(base, REG_CDR, 0xCF);
+
+ /* output control */
+ /* connected to external transceiver */
+ hw_writereg(base, REG_OCR, 0x1A);
+
+ /* set acceptance filter (accept all) */
+ hw_writereg(base, REG_ACCC0, 0x00);
+ hw_writereg(base, REG_ACCC1, 0x00);
+ hw_writereg(base, REG_ACCC2, 0x00);
+ hw_writereg(base, REG_ACCC3, 0x00);
+
+ hw_writereg(base, REG_ACCM0, 0xFF);
+ hw_writereg(base, REG_ACCM1, 0xFF);
+ hw_writereg(base, REG_ACCM2, 0xFF);
+ hw_writereg(base, REG_ACCM3, 0xFF);
+
+ /* set baudrate */
+ if (priv->btr) { /* no calculation when btr is provided */
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+ } else {
+ if (priv->speed == 0) {
+ priv->speed = DEFAULT_SPEED;
+ }
+ set_baud(dev, priv->speed * 1000, priv->clock);
+ }
+
+ /* output control */
+ /* connected to external transceiver */
+ hw_writereg(base, REG_OCR, 0x1A);
+}
+
+static void chipset_init(struct net_device *dev, int wake)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->rx_probe)
+ chipset_init_rx(dev); /* wait for valid reception first */
+ else
+ chipset_init_trx(dev);
+
+ if ((wake) && netif_queue_stopped(dev)) {
+ if (priv->echo_skb) { /* pending echo? */
+ kfree_skb(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+ netif_wake_queue(dev);
+ }
+}
+
+static void chipset_init_rx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* automatic bit rate detection */
+ set_listen_mode(dev);
+
+ priv->state = STATE_PROBE;
+
+ /* enable receive and error interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_RI | IRQ_EI);
+}
+
+static void chipset_init_trx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+
+ priv->state = STATE_ACTIVE;
+
+ /* enable all interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_ALL);
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx ff ll 00 11 22 33 44 55 66 77
+ * [ can-id ] [flags] [len] [can data (up to 8 bytes]
+ */
+static int can_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ struct can_frame *cf = (struct can_frame*)skb->data;
+ unsigned long base = dev->base_addr;
+ uint8_t fi;
+ uint8_t dlc;
+ canid_t id;
+ uint8_t dreg;
+ int loop;
+ int i;
+
+ netif_stop_queue(dev);
+
+ fi = dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ if (id & CAN_RTR_FLAG)
+ fi |= FI_RTR;
+
+ if (id & CAN_EFF_FLAG) {
+ fi |= FI_FF;
+ dreg = EFF_BUF;
+ hw_writereg(base, REG_FI, fi);
+ hw_writereg(base, REG_ID1, (id & 0x1fe00000) >> (5 + 16));
+ hw_writereg(base, REG_ID2, (id & 0x001fe000) >> (5 + 8));
+ hw_writereg(base, REG_ID3, (id & 0x00001fe0) >> 5);
+ hw_writereg(base, REG_ID4, (id & 0x0000001f) << 3);
+ } else {
+ dreg = SFF_BUF;
+ hw_writereg(base, REG_FI, fi);
+ hw_writereg(base, REG_ID1, (id & 0x000007f8) >> 3);
+ hw_writereg(base, REG_ID2, (id & 0x00000007) << 5);
+ }
+
+ for (i = 0; i < dlc; i++) {
+ hw_writereg(base, dreg++, cf->data[i]);
+ }
+
+ hw_writereg(base, REG_CMR, CMD_TR);
+
+ stats->tx_bytes += dlc;
+
+ dev->trans_start = jiffies;
+
+ /* set flag whether this packet has to be looped back */
+ loop = skb->pkt_type == PACKET_LOOPBACK;
+
+ if (!echo || !loop) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (!priv->echo_skb) {
+ struct sock *srcsk = skb->sk;
+
+ if (atomic_read(&skb->users) != 1) {
+ struct sk_buff *old_skb = skb;
+
+ skb = skb_clone(old_skb, GFP_ATOMIC);
+ DBG(KERN_INFO "%s: %s: freeing old skbuff %p, "
+ "using new skbuff %p\n",
+ dev->name, __FUNCTION__, old_skb, skb);
+ kfree_skb(old_skb);
+ if (!skb) {
+ return 0;
+ }
+ } else
+ skb_orphan(skb);
+
+ skb->sk = srcsk;
+
+ /* make settings for echo to reduce code in irq context */
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->dev = dev;
+
+ /* save this skb for tx interrupt echo handling */
+ priv->echo_skb = skb;
+
+ } else {
+ /* locking problem with netif_stop_queue() ?? */
+ printk(KERN_ERR "%s: %s: occupied echo_skb!\n",
+ dev->name, __FUNCTION__ );
+ kfree_skb(skb);
+ }
+
+ return 0;
+}
+
+static void can_tx_timeout(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+
+ stats->tx_errors++;
+
+ /* do not conflict with e.g. bus error handling */
+ if (!(priv->timer.expires)){ /* no restart on the run */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ if (priv->echo_skb) { /* pending echo? */
+ kfree_skb(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+ netif_wake_queue(dev); /* wakeup here */
+ }
+ else
+ DBG(KERN_INFO "%s: %s: can_restart_dev already active.\n",
+ dev->name, __FUNCTION__ );
+
+}
+
+static void can_restart_on(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (!(priv->timer.expires)){ /* no restart on the run */
+
+ set_reset_mode(dev);
+
+ priv->timer.function = can_restart_dev;
+ priv->timer.data = (unsigned long) dev;
+
+ /* restart chip on persistent error in <xxx> ms */
+ priv->timer.expires = jiffies + (priv->restart_ms * HZ) / 1000;
+ add_timer(&priv->timer);
+
+ iDBG(KERN_INFO "%s: %s start (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+ } else
+ iDBG(KERN_INFO "%s: %s already (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+}
+
+static void can_restart_dev(unsigned long data)
+{
+ struct net_device *dev = (struct net_device*) data;
+ struct can_priv *priv = netdev_priv(dev);
+
+ DBG(KERN_INFO "%s: can_restart_dev (%ld)\n",
+ dev->name, jiffies);
+
+ /* mark inactive timer */
+ priv->timer.expires = 0;
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+
+#if 0
+/* the timerless version */
+
+static void can_restart_now(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+#endif
+
+static void can_rx(struct net_device *dev)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ unsigned long base = dev->base_addr;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t fi;
+ uint8_t dreg;
+ canid_t id;
+ uint8_t dlc;
+ int i;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL) {
+ return;
+ }
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ fi = hw_readreg(base, REG_FI);
+ dlc = fi & 0x0F;
+
+ if (fi & FI_FF) {
+ /* extended frame format (EFF) */
+ dreg = EFF_BUF;
+ id = (hw_readreg(base, REG_ID1) << (5+16))
+ | (hw_readreg(base, REG_ID2) << (5+8))
+ | (hw_readreg(base, REG_ID3) << 5)
+ | (hw_readreg(base, REG_ID4) >> 3);
+ id |= CAN_EFF_FLAG;
+ } else {
+ /* standard frame format (SFF) */
+ dreg = SFF_BUF;
+ id = (hw_readreg(base, REG_ID1) << 3)
+ | (hw_readreg(base, REG_ID2) >> 5);
+ }
+
+ if (fi & FI_RTR)
+ id |= CAN_RTR_FLAG;
+
+ cf = (struct can_frame*)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++) {
+ cf->data[i] = hw_readreg(base, dreg++);
+ }
+ while (i < 8)
+ cf->data[i++] = 0;
+
+ /* release receive buffer */
+ hw_writereg(base, REG_CMR, CMD_RRB);
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += dlc;
+}
+
+/*
+ * SJA1000 interrupt handler
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static irqreturn_t can_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+#else
+static irqreturn_t can_interrupt(int irq, void *dev_id)
+#endif
+{
+ struct net_device *dev = (struct net_device*)dev_id;
+ struct can_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ unsigned long base = dev->base_addr;
+ uint8_t isrc, status, ecc, alc;
+ int n = 0;
+
+ hw_preirq(dev);
+
+ iiDBG(KERN_INFO "%s: interrupt\n", dev->name);
+
+ if (priv->state == STATE_UNINITIALIZED) {
+ printk(KERN_ERR "%s: %s: uninitialized controller!\n",
+ dev->name, __FUNCTION__);
+ chipset_init(dev, 1); /* should be possible at this stage */
+ return IRQ_NONE;
+ }
+
+ if (priv->state == STATE_RESET_MODE) {
+ iiDBG(KERN_ERR "%s: %s: controller is in reset mode! "
+ "MOD=0x%02X IER=0x%02X IR=0x%02X SR=0x%02X!\n",
+ dev->name, __FUNCTION__, hw_readreg(base, REG_MOD),
+ hw_readreg(base, REG_IER), hw_readreg(base, REG_IR),
+ hw_readreg(base, REG_SR));
+ return IRQ_NONE;
+ }
+
+ while ((isrc = hw_readreg(base, REG_IR)) && (n < 20)) {
+ n++;
+ status = hw_readreg(base, REG_SR);
+
+ if (isrc & IRQ_WUI) {
+ /* wake-up interrupt */
+ priv->can_stats.wakeup++;
+ }
+ if (isrc & IRQ_TI) {
+ /* transmission complete interrupt */
+ stats->tx_packets++;
+
+ if (echo && priv->echo_skb) {
+ netif_rx(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+
+ netif_wake_queue(dev);
+ }
+ if (isrc & IRQ_RI) {
+ /* receive interrupt */
+
+ while (status & SR_RBS) {
+ can_rx(dev);
+ status = hw_readreg(base, REG_SR);
+ }
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ iDBG(KERN_INFO "%s: RI #%d#\n", dev->name, n);
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ if (isrc & IRQ_DOI) {
+ /* data overrun interrupt */
+ iiDBG(KERN_INFO "%s: data overrun isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: DOI #%d#\n", dev->name, n);
+ priv->can_stats.data_overrun++;
+ hw_writereg(base, REG_CMR, CMD_CDO); /* clear bit */
+ }
+ if (isrc & IRQ_EI) {
+ /* error warning interrupt */
+ iiDBG(KERN_INFO "%s: error warning isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EI #%d#\n", dev->name, n);
+ priv->can_stats.error_warning++;
+ if (status & SR_BS) {
+ printk(KERN_INFO "%s: BUS OFF, "
+ "restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ } else if (status & SR_ES) {
+ iDBG(KERN_INFO "%s: error\n", dev->name);
+ }
+ }
+ if (isrc & IRQ_BEI) {
+ /* bus error interrupt */
+ iiDBG(KERN_INFO "%s: bus error isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: BEI #%d# [%d]\n", dev->name, n,
+ priv->can_stats.bus_error -
+ priv->can_stats.bus_error_at_init);
+ priv->can_stats.bus_error++;
+ ecc = hw_readreg(base, REG_ECC);
+ iDBG(KERN_INFO "%s: ECC = 0x%02X (%s, %s, %s)\n",
+ dev->name, ecc,
+ (ecc & ECC_DIR) ? "RX" : "TX",
+ ecc_types[ecc >> ECC_ERR],
+ ecc_errors[ecc & ECC_SEG]);
+
+ /* when the bus errors flood the system, */
+ /* restart the controller */
+ if (priv->can_stats.bus_error_at_init +
+ MAX_BUS_ERRORS < priv->can_stats.bus_error) {
+ iDBG(KERN_INFO "%s: heavy bus errors,"
+ " restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ }
+#if 1
+ /* don't know, if this is a good idea, */
+ /* but it works fine ... */
+ if (hw_readreg(base, REG_RXERR) > 128) {
+ iDBG(KERN_INFO "%s: RX_ERR > 128,"
+ " restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ }
+#endif
+ }
+ if (isrc & IRQ_EPI) {
+ /* error passive interrupt */
+ iiDBG(KERN_INFO "%s: error passive isrc=0x%02X"
+ " status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EPI #%d#\n", dev->name, n);
+ priv->can_stats.error_passive++;
+ if (status & SR_ES) {
+ iDBG(KERN_INFO "%s: -> ERROR PASSIVE, "
+ "restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ } else {
+ iDBG(KERN_INFO "%s: -> ERROR ACTIVE\n",
+ dev->name);
+ }
+ }
+ if (isrc & IRQ_ALI) {
+ /* arbitration lost interrupt */
+ iiDBG(KERN_INFO "%s: error arbitration lost "
+ "isrc=0x%02X status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: ALI #%d#\n", dev->name, n);
+ priv->can_stats.arbitration_lost++;
+ alc = hw_readreg(base, REG_ALC);
+ iDBG(KERN_INFO "%s: ALC = 0x%02X\n", dev->name, alc);
+ }
+ }
+ if (n > 1) {
+ iDBG(KERN_INFO "%s: handled %d IRQs\n", dev->name, n);
+ }
+out:
+ hw_postirq(dev);
+
+ return n == 0 ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/*
+ * initialize CAN bus driver
+ */
+static int can_open(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->state = STATE_UNINITIALIZED;
+
+ /* register interrupt handler */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ if (request_irq(dev->irq, &can_interrupt, SA_SHIRQ,
+ dev->name, (void*)dev)) {
+#else
+ if (request_irq(dev->irq, &can_interrupt, IRQF_SHARED,
+ dev->name, (void*)dev)) {
+#endif
+ return -EAGAIN;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ /* clear statistics */
+ memset(&priv->stats, 0, sizeof(priv->stats));
+#endif
+
+ /* init chip */
+ chipset_init(dev, 0);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/*
+ * stop CAN bus activity
+ */
+static int can_close(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->open_time = 0;
+
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0;
+ }
+
+ free_irq(dev->irq, (void*)dev);
+ priv->state = STATE_UNINITIALIZED;
+
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+#if 0
+static void test_if(struct net_device *dev)
+{
+ int i;
+ int j;
+ int x;
+
+ hw_writereg(base, REG_CDR, 0xCF);
+ for (i = 0; i < 10000; i++) {
+ for (j = 0; j < 256; j++) {
+ hw_writereg(base, REG_EWL, j);
+ x = hw_readreg(base, REG_EWL);
+ if (x != j) {
+ printk(KERN_INFO "%s: is: %02X expected: "
+ "%02X (%d)\n", dev->name, x, j, i);
+ }
+ }
+ }
+}
+#endif
+
+void can_netdev_setup(struct net_device *dev)
+{
+ /* Fill in the the fields of the device structure
+ with CAN netdev generic values */
+
+ dev->change_mtu = NULL;
+ dev->set_mac_address = NULL;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ dev->hard_header = NULL;
+ dev->rebuild_header = NULL;
+ dev->hard_header_cache = NULL;
+ dev->header_cache_update = NULL;
+ dev->hard_header_parse = NULL;
+#else
+ dev->header_ops = NULL;
+#endif
+
+ dev->type = ARPHRD_CAN;
+ dev->hard_header_len = 0;
+ dev->mtu = sizeof(struct can_frame);
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->flags = IFF_NOARP;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#define IFF_ECHO IFF_LOOPBACK
+#endif
+ /* set flags according to driver capabilities */
+ if (echo)
+ dev->flags |= IFF_ECHO;
+
+ dev->features = NETIF_F_NO_CSUM;
+
+ dev->open = can_open;
+ dev->stop = can_close;
+ dev->hard_start_xmit = can_start_xmit;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ dev->get_stats = can_get_stats;
+#endif
+
+ dev->tx_timeout = can_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+}
+
+static struct net_device* can_create_netdev(int dev_num, int hw_regs)
+{
+ struct net_device *dev;
+ struct can_priv *priv;
+
+ if (!(dev = alloc_netdev(sizeof(struct can_priv), CAN_NETDEV_NAME,
+ can_netdev_setup))) {
+ printk(KERN_ERR "%s: out of memory\n", CHIP_NAME);
+ return NULL;
+ }
+
+ printk(KERN_INFO "%s: base 0x%lX / irq %d / speed %d / "
+ "btr 0x%X / rx_probe %d\n",
+ drv_name, rbase[dev_num], irq[dev_num],
+ speed[dev_num], btr[dev_num], rx_probe[dev_num]);
+
+ /* fill net_device structure */
+
+ priv = netdev_priv(dev);
+
+ dev->irq = irq[dev_num];
+ dev->base_addr = rbase[dev_num];
+
+ priv->speed = speed[dev_num];
+ priv->btr = btr[dev_num];
+ priv->rx_probe = rx_probe[dev_num];
+ priv->clock = clk;
+ priv->hw_regs = hw_regs;
+ priv->restart_ms = restart_ms;
+ priv->debug = debug;
+
+ init_timer(&priv->timer);
+ priv->timer.expires = 0;
+
+ if (register_netdev(dev)) {
+ printk(KERN_INFO "%s: register netdev failed\n", CHIP_NAME);
+ free_netdev(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+int can_set_drv_name(void)
+{
+ char *hname = hal_name();
+
+ if (strlen(CHIP_NAME) + strlen(hname) >= DRV_NAME_LEN-1) {
+ printk(KERN_ERR "%s: driver name too long!\n", CHIP_NAME);
+ return -EINVAL;
+ }
+ sprintf(drv_name, "%s-%s", CHIP_NAME, hname);
+ return 0;
+}
+
+static void sja1000_exit_module(void)
+{
+ int i, ret;
+
+ for (i = 0; i < MAXDEV; i++) {
+ if (can_dev[i] != NULL) {
+ struct can_priv *priv = netdev_priv(can_dev[i]);
+ unregister_netdev(can_dev[i]);
+ del_timer(&priv->timer);
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ free_netdev(can_dev[i]);
+ }
+ }
+ can_proc_remove(drv_name);
+
+ if ((ret = hal_exit()))
+ printk(KERN_INFO "%s: hal_exit error %d.\n", drv_name, ret);
+}
+
+static __init int sja1000_init_module(void)
+{
+ int i, ret;
+ struct net_device *dev;
+
+ if ((ret = hal_init()))
+ return ret;
+
+ if ((ret = can_set_drv_name()))
+ return ret;
+
+ if (clk < 1000 ) /* MHz command line value */
+ clk *= 1000000;
+
+ if (clk < 1000000 ) /* kHz command line value */
+ clk *= 1000;
+
+ printk(KERN_INFO "%s driver v%s (%s)\n",
+ drv_name, drv_version, drv_reldate);
+ printk(KERN_INFO "%s - options [clk %d.%06d MHz] [restart_ms %dms]"
+ " [debug %d]\n",
+ drv_name, clk/1000000, clk%1000000, restart_ms, debug);
+
+ if (!base[0]) {
+ printk(KERN_INFO "%s: loading defaults.\n", drv_name);
+ hal_use_defaults();
+ }
+
+ for (i = 0; base[i]; i++) {
+ printk(KERN_DEBUG "%s: checking for %s on address 0x%lX ...\n",
+ drv_name, CHIP_NAME, base[i]);
+
+ if (!hal_request_region(i, SJA1000_IO_SIZE_BASIC, drv_name)) {
+ printk(KERN_ERR "%s: memory already in use\n",
+ drv_name);
+ sja1000_exit_module();
+ return -EBUSY;
+ }
+
+ hw_attach(i);
+ hw_reset_dev(i);
+
+ if (!sja1000_probe_chip(rbase[i])) {
+ printk(KERN_ERR "%s: probably missing controller"
+ " hardware\n", drv_name);
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ sja1000_exit_module();
+ return -ENODEV;
+ }
+
+ dev = can_create_netdev(i, SJA1000_IO_SIZE_BASIC);
+
+ if (dev != NULL) {
+ can_dev[i] = dev;
+ set_reset_mode(dev);
+ can_proc_create(drv_name);
+ } else {
+ can_dev[i] = NULL;
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ }
+ }
+ return 0;
+}
+
+module_init(sja1000_init_module);
+module_exit(sja1000_exit_module);
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * sja1000.h - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef SJA1000_H
+#define SJA1000_H
+
+#define SJA1000_IO_SIZE_BASIC 0x20
+#define SJA1000_IO_SIZE_PELICAN 0x80 /* unused */
+
+#define CHIP_NAME "sja1000"
+
+#define DRV_NAME_LEN 30 /* for "<chip_name>-<hal_name>" */
+
+#define PROCBASE "driver" /* /proc/ ... */
+
+#define DEFAULT_HW_CLK 16000000
+#define DEFAULT_SPEED 500 /* kBit/s */
+
+#define CAN_NETDEV_NAME "can%d"
+
+#define TX_TIMEOUT (50*HZ/1000) /* 50ms */
+#define RESTART_MS 100 /* restart chip on persistent errors in 100ms */
+#define MAX_BUS_ERRORS 200 /* prevent from flooding bus error interrupts */
+
+/* SJA1000 registers - manual section 6.4 (Pelican Mode) */
+#define REG_MOD 0x00
+#define REG_CMR 0x01
+#define REG_SR 0x02
+#define REG_IR 0x03
+#define REG_IER 0x04
+#define REG_ALC 0x0B
+#define REG_ECC 0x0C
+#define REG_EWL 0x0D
+#define REG_RXERR 0x0E
+#define REG_TXERR 0x0F
+#define REG_ACCC0 0x10
+#define REG_ACCC1 0x11
+#define REG_ACCC2 0x12
+#define REG_ACCC3 0x13
+#define REG_ACCM0 0x14
+#define REG_ACCM1 0x15
+#define REG_ACCM2 0x16
+#define REG_ACCM3 0x17
+#define REG_RMC 0x1D
+#define REG_RBSA 0x1E
+
+/* Common registers - manual section 6.5 */
+#define REG_BTR0 0x06
+#define REG_BTR1 0x07
+#define REG_OCR 0x08
+#define REG_CDR 0x1F
+
+#define REG_FI 0x10
+#define SFF_BUF 0x13
+#define EFF_BUF 0x15
+
+#define FI_FF 0x80
+#define FI_RTR 0x40
+
+#define REG_ID1 0x11
+#define REG_ID2 0x12
+#define REG_ID3 0x13
+#define REG_ID4 0x14
+
+#define CAN_RAM 0x20
+
+/* mode register */
+#define MOD_RM 0x01
+#define MOD_LOM 0x02
+#define MOD_STM 0x04
+#define MOD_AFM 0x08
+#define MOD_SM 0x10
+
+/* commands */
+#define CMD_SRR 0x10
+#define CMD_CDO 0x08
+#define CMD_RRB 0x04
+#define CMD_AT 0x02
+#define CMD_TR 0x01
+
+/* interrupt sources */
+#define IRQ_BEI 0x80
+#define IRQ_ALI 0x40
+#define IRQ_EPI 0x20
+#define IRQ_WUI 0x10
+#define IRQ_DOI 0x08
+#define IRQ_EI 0x04
+#define IRQ_TI 0x02
+#define IRQ_RI 0x01
+#define IRQ_ALL 0xFF
+#define IRQ_OFF 0x00
+
+/* status register content */
+#define SR_BS 0x80
+#define SR_ES 0x40
+#define SR_TS 0x20
+#define SR_RS 0x10
+#define SR_TCS 0x08
+#define SR_TBS 0x04
+#define SR_DOS 0x02
+#define SR_RBS 0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_DIR 0x20
+#define ECC_SEG 0x1F
+#define ECC_ERR 6
+
+/* bus timing */
+#define MAX_TSEG1 15
+#define MAX_TSEG2 7
+#define SAMPLE_POINT 75
+#define JUMPWIDTH 0x40
+
+/* CAN private data structure */
+
+struct can_priv {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats stats;
+#endif
+ struct can_device_stats can_stats;
+ long open_time;
+ int clock;
+ int hw_regs;
+ int restart_ms;
+ int debug;
+ int speed;
+ int btr;
+ int rx_probe;
+ struct timer_list timer;
+ int state;
+ struct sk_buff *echo_skb;
+};
+
+#define STATE_UNINITIALIZED 0
+#define STATE_PROBE 1
+#define STATE_ACTIVE 2
+#define STATE_ERROR_ACTIVE 3
+#define STATE_ERROR_PASSIVE 4
+#define STATE_BUS_OFF 5
+#define STATE_RESET_MODE 6
+
+void can_proc_create(const char *drv_name);
+void can_proc_remove(const char *drv_name);
+
+#endif /* SJA1000_H */
--- /dev/null
+#
+# $Id: Makefile 443 2007-07-25 11:41:27Z hartkopp $
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+#EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/can/hal
+
+obj-$(CONFIG_CAN_SJA1000) += sja1000.o
+obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o
+obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o
+obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o
+obj-$(CONFIG_CAN_IXXAT_PCI) += ixxat_pci.o
+obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o
+obj-$(CONFIG_CAN_PIPCAN) += pipcan.o
+obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o
+
+endif
--- /dev/null
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2008 Markus Plessing <plessing@ems-wuensche.com>
+ * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com>
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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/can.h>
+#include <linux/can/dev.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+
+#include "sja1000.h"
+
+#define DRV_NAME "ems_pci"
+
+MODULE_AUTHOR("Sebastian Haas <haas@ems-wuenche.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-PCI/PCIe CAN cards");
+MODULE_SUPPORTED_DEVICE("EMS CPC-PCI/PCIe CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define EMS_PCI_MAX_CHAN 2
+
+struct ems_pci_card {
+ int channels;
+
+ struct pci_dev *pci_dev;
+ struct net_device *net_dev[EMS_PCI_MAX_CHAN];
+
+ void __iomem *conf_addr;
+ void __iomem *base_addr;
+};
+
+#define EMS_PCI_CAN_CLOCK (16000000 / 2)
+
+/*
+ * Register definitions and descriptions are from LinCAN 0.3.3.
+ *
+ * PSB4610 PITA-2 bridge control registers
+ */
+#define PITA2_ICR 0x00 /* Interrupt Control Register */
+#define PITA2_ICR_INT0 0x00000002 /* [RC] INT0 Active/Clear */
+#define PITA2_ICR_INT0_EN 0x00020000 /* [RW] Enable INT0 */
+
+#define PITA2_MISC 0x1c /* Miscellaneous Register */
+#define PITA2_MISC_CONFIG 0x04000000 /* Multiplexed parallel interface */
+
+/*
+ * The board configuration is probably following:
+ * RX1 is connected to ground.
+ * TX1 is not connected.
+ * CLKO is not connected.
+ * Setting the OCR register to 0xDA is a good idea.
+ * This means normal output mode , push-pull and the correct polarity.
+ */
+#define EMS_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
+
+/*
+ * In the CDR register, you should set CBP to 1.
+ * You will probably also want to set the clock divider value to 7
+ * (meaning direct oscillator output) because the second SJA1000 chip
+ * is driven by the first one CLKOUT output.
+ */
+#define EMS_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+#define EMS_PCI_MEM_SIZE 4096 /* Size of the remapped io-memory */
+#define EMS_PCI_CAN_BASE_OFFSET 0x400 /* offset where the controllers starts */
+#define EMS_PCI_CAN_CTRL_SIZE 0x200 /* memory size for each controller */
+
+#define EMS_PCI_PORT_BYTES 0x4 /* Each register occupies 4 bytes */
+
+#define EMS_PCI_VENDOR_ID 0x110a /* PCI device and vendor ID */
+#define EMS_PCI_DEVICE_ID 0x2104
+
+static struct pci_device_id ems_pci_tbl[] = {
+ {EMS_PCI_VENDOR_ID, EMS_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, ems_pci_tbl);
+
+/*
+ * Helper to read internal registers from card logic (not CAN)
+ */
+static u8 ems_pci_readb(struct ems_pci_card *card, unsigned int port)
+{
+ return readb((void __iomem *)card->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static u8 ems_pci_read_reg(struct net_device *dev, int port)
+{
+ return readb((void __iomem *)dev->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static void ems_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ writeb(val, (void __iomem *)dev->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static void ems_pci_post_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct ems_pci_card *card = (struct ems_pci_card *)priv->priv;
+
+ /* reset int flag of pita */
+ writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0, card->conf_addr
+ + PITA2_ICR);
+}
+
+/*
+ * Check if a CAN controller is present at the specified location
+ * by trying to set 'em into the PeliCAN mode
+ */
+static inline int ems_pci_check_chan(struct net_device *dev)
+{
+ unsigned char res;
+
+ /* Make sure SJA1000 is in reset mode */
+ ems_pci_write_reg(dev, REG_MOD, 1);
+
+ ems_pci_write_reg(dev, REG_CDR, CDR_PELICAN);
+
+ /* read reset-values */
+ res = ems_pci_read_reg(dev, REG_CDR);
+
+ if (res == CDR_PELICAN)
+ return 1;
+
+ return 0;
+}
+
+static void ems_pci_del_card(struct pci_dev *pdev)
+{
+ struct ems_pci_card *card = pci_get_drvdata(pdev);
+ struct net_device *dev;
+ int i = 0;
+
+ for (i = 0; i < card->channels; i++) {
+ dev = card->net_dev[i];
+
+ if (!dev)
+ continue;
+
+ dev_info(&pdev->dev, "Removing %s.\n", dev->name);
+ unregister_sja1000dev(dev);
+ free_sja1000dev(dev);
+ }
+
+ if (card->base_addr != NULL)
+ pci_iounmap(card->pci_dev, card->base_addr);
+
+ if (card->conf_addr != NULL)
+ pci_iounmap(card->pci_dev, card->conf_addr);
+
+ kfree(card);
+
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static void ems_pci_card_reset(struct ems_pci_card *card)
+{
+ /* Request board reset */
+ writeb(0, card->base_addr);
+}
+
+/*
+ * Probe PCI device for EMS CAN signature and register each available
+ * CAN channel to SJA1000 Socket-CAN subsystem.
+ */
+static int __devinit ems_pci_add_card(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct sja1000_priv *priv;
+ struct net_device *dev;
+ struct ems_pci_card *card;
+ int err, i;
+
+ /* Enabling PCI device */
+ if (pci_enable_device(pdev) < 0) {
+ dev_err(&pdev->dev, "Enabling PCI device failed\n");
+ return -ENODEV;
+ }
+
+ /* Allocating card structures to hold addresses, ... */
+ card = kzalloc(sizeof(struct ems_pci_card), GFP_KERNEL);
+ if (card == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate memory\n");
+ pci_disable_device(pdev);
+ return -ENOMEM;
+ }
+
+ pci_set_drvdata(pdev, card);
+
+ card->pci_dev = pdev;
+
+ card->channels = 0;
+
+ /* Remap PITA configuration space, and controller memory area */
+ card->conf_addr = pci_iomap(pdev, 0, EMS_PCI_MEM_SIZE);
+ if (card->conf_addr == NULL) {
+ err = -ENOMEM;
+
+ goto failure_cleanup;
+ }
+
+ card->base_addr = pci_iomap(pdev, 1, EMS_PCI_MEM_SIZE);
+ if (card->base_addr == NULL) {
+ err = -ENOMEM;
+
+ goto failure_cleanup;
+ }
+
+ /* Configure PITA-2 parallel interface (enable MUX) */
+ writel(PITA2_MISC_CONFIG, card->conf_addr + PITA2_MISC);
+
+ /* Check for unique EMS CAN signature */
+ if (ems_pci_readb(card, 0) != 0x55 ||
+ ems_pci_readb(card, 1) != 0xAA ||
+ ems_pci_readb(card, 2) != 0x01 ||
+ ems_pci_readb(card, 3) != 0xCB ||
+ ems_pci_readb(card, 4) != 0x11) {
+ dev_err(&pdev->dev, "Not EMS Dr. Thomas Wuensche interface\n");
+
+ err = -ENODEV;
+ goto failure_cleanup;
+ }
+
+ ems_pci_card_reset(card);
+
+ /* Detect available channels */
+ for (i = 0; i < EMS_PCI_MAX_CHAN; i++) {
+ dev = alloc_sja1000dev(0);
+ if (dev == NULL) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ card->net_dev[i] = dev;
+ priv = netdev_priv(dev);
+ priv->priv = card;
+
+ dev->irq = pdev->irq;
+ dev->base_addr = (unsigned long)(card->base_addr
+ + EMS_PCI_CAN_BASE_OFFSET
+ + (i * EMS_PCI_CAN_CTRL_SIZE));
+
+ /* Check if channel is present */
+ if (ems_pci_check_chan(dev)) {
+ priv->read_reg = ems_pci_read_reg;
+ priv->write_reg = ems_pci_write_reg;
+ priv->post_irq = ems_pci_post_irq;
+ priv->can.bittiming.clock = EMS_PCI_CAN_CLOCK;
+ priv->ocr = EMS_PCI_OCR;
+ priv->cdr = EMS_PCI_CDR;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Enable interrupts from card */
+ writel(PITA2_ICR_INT0_EN, card->conf_addr + PITA2_ICR);
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Registering device failed "
+ "(err=%d)\n", err);
+ free_sja1000dev(dev);
+ goto failure_cleanup;
+ }
+
+ card->channels++;
+
+ dev_info(&pdev->dev, "Channel #%d at %lX, irq %d\n",
+ i + 1, dev->base_addr,
+ dev->irq);
+ } else {
+ free_sja1000dev(dev);
+ }
+ }
+
+ return 0;
+
+failure_cleanup:
+ dev_err(&pdev->dev, "Error: %d. Cleaning Up.\n", err);
+
+ ems_pci_del_card(pdev);
+
+ return err;
+}
+
+static struct pci_driver ems_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ems_pci_tbl,
+ .probe = ems_pci_add_card,
+ .remove = ems_pci_del_card,
+};
+
+static int __init ems_pci_init(void)
+{
+ return pci_register_driver(&ems_pci_driver);
+}
+
+static void __exit ems_pci_exit(void)
+{
+ pci_unregister_driver(&ems_pci_driver);
+}
+
+module_init(ems_pci_init);
+module_exit(ems_pci_exit);
+
--- /dev/null
+/*
+ * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com>
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "ems_pcmcia"
+
+MODULE_AUTHOR("Sebastian Haas <haas@ems-wuenche.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-CARD cards");
+MODULE_SUPPORTED_DEVICE("EMS CPC-CARD CAN card");
+MODULE_LICENSE("GPL v2");
+
+static int debug;
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+
+MODULE_PARM_DESC(debug, "Set debug level (default: 0)");
+
+#define EMS_PCMCIA_MAX_CHAN 2
+
+struct ems_pcmcia_card {
+ int channels;
+
+ struct pcmcia_device *pcmcia_dev;
+ struct net_device *net_dev[EMS_PCMCIA_MAX_CHAN];
+
+ void __iomem *base_addr;
+};
+
+#define EMS_PCMCIA_CAN_CLOCK (16000000 / 2)
+
+/*
+ * The board configuration is probably following:
+ * RX1 is connected to ground.
+ * TX1 is not connected.
+ * CLKO is not connected.
+ * Setting the OCR register to 0xDA is a good idea.
+ * This means normal output mode , push-pull and the correct polarity.
+ */
+#define EMS_PCMCIA_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
+
+/*
+ * In the CDR register, you should set CBP to 1.
+ * You will probably also want to set the clock divider value to 7
+ * (meaning direct oscillator output) because the second SJA1000 chip
+ * is driven by the first one CLKOUT output.
+ */
+#define EMS_PCMCIA_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+#define EMS_PCMCIA_MEM_SIZE 4096 /* Size of the remapped io-memory */
+#define EMS_PCMCIA_CAN_BASE_OFFSET 0x100 /* Offset where controllers starts */
+#define EMS_PCMCIA_CAN_CTRL_SIZE 0x80 /* Memory size for each controller */
+
+#define EMS_CMD_RESET 0x00 /* Perform a reset of the card */
+#define EMS_CMD_MAP 0x03 /* Map CAN controllers into card' memory */
+#define EMS_CMD_UMAP 0x02 /* Unmap CAN controllers from card' memory */
+
+static struct pcmcia_device_id ems_pcmcia_tbl[] = {
+ PCMCIA_DEVICE_PROD_ID123("EMS_T_W", "CPC-Card", "V2.0", 0xeab1ea23,
+ 0xa338573f, 0xe4575800),
+ PCMCIA_DEVICE_NULL,
+};
+
+MODULE_DEVICE_TABLE (pcmcia, ems_pcmcia_tbl);
+
+static void ems_pcmcia_config(struct pcmcia_device *dev);
+
+static u8 ems_pcmcia_read_reg(struct net_device *dev, int port)
+{
+ return readb((void __iomem *)dev->base_addr + port);
+}
+
+static void ems_pcmcia_write_reg(struct net_device *dev, int port, u8 val)
+{
+ writeb(val, (void __iomem *)dev->base_addr + port);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static irqreturn_t ems_pcmcia_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+#else
+static irqreturn_t ems_pcmcia_interrupt(int irq, void *dev_id)
+#endif
+{
+ struct ems_pcmcia_card *card = dev_id;
+ struct net_device *dev;
+ irqreturn_t retval = IRQ_NONE;
+ int i, again;
+
+ do {
+ again = 0;
+
+ /* Check interrupt for each channel */
+ for (i = 0; i < EMS_PCMCIA_MAX_CHAN; i++) {
+ dev = card->net_dev[i];
+ if (!dev)
+ continue;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ if (sja1000_interrupt(irq, dev, regs) == IRQ_HANDLED)
+ again = 1;
+#else
+ if (sja1000_interrupt(irq, dev) == IRQ_HANDLED)
+ again = 1;
+#endif
+ }
+ /* At least one channel handled the interrupt */
+ if (again)
+ retval = IRQ_HANDLED;
+
+ } while (again);
+
+ return retval;
+}
+
+/*
+ * Check if a CAN controller is present at the specified location
+ * by trying to set 'em into the PeliCAN mode
+ */
+static inline int ems_pcmcia_check_chan(struct net_device *dev)
+{
+ unsigned char res;
+
+ /* Make sure SJA1000 is in reset mode */
+ ems_pcmcia_write_reg(dev, REG_MOD, 1);
+
+ ems_pcmcia_write_reg(dev, REG_CDR, CDR_PELICAN);
+
+ /* read reset-values */
+ res = ems_pcmcia_read_reg(dev, REG_CDR);
+
+ if (res == CDR_PELICAN)
+ return 1;
+
+ return 0;
+}
+
+static void ems_pcmcia_del_card(struct pcmcia_device *pdev)
+{
+ struct ems_pcmcia_card *card = pdev->priv;
+ struct net_device *dev;
+ int i = 0;
+
+ if (!card)
+ return;
+
+ free_irq(pdev->irq.AssignedIRQ, card);
+
+ for (i = 0; i < card->channels; i++) {
+ dev = card->net_dev[i];
+
+ if (!dev)
+ continue;
+
+ printk(KERN_INFO "%s: removing %s on channel #%d\n",
+ DRV_NAME, dev->name, i);
+ unregister_sja1000dev(dev);
+ free_sja1000dev(dev);
+ }
+
+ writeb(EMS_CMD_UMAP, card->base_addr);
+
+ if (card->base_addr != NULL )
+ iounmap(card->base_addr);
+
+ kfree(card);
+
+ pdev->priv = NULL;
+}
+
+static void ems_pcmcia_card_reset(struct ems_pcmcia_card *card)
+{
+ /* Request board reset */
+ writeb(EMS_CMD_RESET, card->base_addr);
+}
+
+/*
+ * Probe PCI device for EMS CAN signature and register each available
+ * CAN channel to SJA1000 Socket-CAN subsystem.
+ */
+static int __devinit ems_pcmcia_add_card(struct pcmcia_device *pdev,
+ unsigned long base)
+{
+ struct sja1000_priv *priv;
+ struct net_device *dev;
+ struct ems_pcmcia_card *card;
+ int err, i;
+
+ /* Allocating card structures to hold addresses, ... */
+ card = kzalloc(sizeof(struct ems_pcmcia_card), GFP_KERNEL);
+ if (card == NULL) {
+ printk(KERN_ERR "%s: unable to allocate memory\n", DRV_NAME);
+ return -ENOMEM;
+ }
+
+ pdev->priv = card;
+
+ card->channels = 0;
+
+ card->base_addr = ioremap(base, EMS_PCMCIA_MEM_SIZE);
+ if (card->base_addr == NULL) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ /* Check for unique EMS CAN signature */
+ if (readw(card->base_addr) != 0xAA55) {
+ printk(KERN_ERR "%s: No EMS CPC Card hardware found.\n",
+ DRV_NAME);
+
+ err = -ENODEV;
+ goto failure_cleanup;
+ }
+
+ ems_pcmcia_card_reset(card);
+
+ /* Make sure CAN controllers are mapped into card's memory space */
+ writeb(EMS_CMD_MAP, card->base_addr);
+
+ /* Detect available channels */
+ for (i = 0; i < EMS_PCMCIA_MAX_CHAN; i++) {
+ dev = alloc_sja1000dev(0);
+ if (dev == NULL) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ card->net_dev[i] = dev;
+ priv = netdev_priv(dev);
+ priv->priv = card;
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ dev->irq = pdev->irq.AssignedIRQ;
+ dev->base_addr = (unsigned long)(card->base_addr
+ + EMS_PCMCIA_CAN_BASE_OFFSET
+ + (i * EMS_PCMCIA_CAN_CTRL_SIZE));
+
+ /* Check if channel is present */
+ if (ems_pcmcia_check_chan(dev)) {
+ priv->read_reg = ems_pcmcia_read_reg;
+ priv->write_reg = ems_pcmcia_write_reg;
+ priv->can.bittiming.clock = EMS_PCMCIA_CAN_CLOCK;
+ priv->ocr = EMS_PCMCIA_OCR;
+ priv->cdr = EMS_PCMCIA_CDR;
+ priv->flags |= SJA1000_CUSTOM_IRQ_HANDLER;
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ printk(KERN_INFO "%s: registering device "
+ "failed (err=%d)\n", DRV_NAME, err);
+ free_sja1000dev(dev);
+ goto failure_cleanup;
+ }
+
+ card->channels++;
+
+ printk(KERN_INFO "%s: registered %s on channel "
+ "#%d at %lX, irq %d\n", DRV_NAME, dev->name,
+ i, dev->base_addr, dev->irq);
+ } else {
+ free_sja1000dev(dev);
+ }
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ err = request_irq(dev->irq, &ems_pcmcia_interrupt, SA_SHIRQ,
+ DRV_NAME, (void *)card);
+#else
+ err = request_irq(dev->irq, &ems_pcmcia_interrupt, IRQF_SHARED,
+ DRV_NAME, (void *)card);
+#endif
+ if (err) {
+ printk(KERN_INFO "Registering device failed (err=%d)\n", err);
+
+ goto failure_cleanup;
+ }
+
+ return 0;
+
+failure_cleanup:
+ printk(KERN_ERR "Error: %d. Cleaning Up.\n", err);
+
+ ems_pcmcia_del_card(pdev);
+
+ return err;
+}
+
+/*
+ * Setup PCMCIA socket and probe for EMS CPC-CARD
+ */
+static int __devinit ems_pcmcia_probe(struct pcmcia_device *dev)
+{
+ /* The io structure describes IO port mapping */
+ dev->io.NumPorts1 = 16;
+ dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ dev->io.NumPorts2 = 16;
+ dev->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
+ dev->io.IOAddrLines = 5;
+
+ /* Interrupt setup */
+ dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+ dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+ /* General socket configuration */
+ dev->conf.Attributes = CONF_ENABLE_IRQ;
+ dev->conf.IntType = INT_MEMORY_AND_IO;
+ dev->conf.ConfigIndex = 1;
+ dev->conf.Present = PRESENT_OPTION;
+
+ dev->win = NULL;
+
+ ems_pcmcia_config(dev);
+
+ return 0;
+}
+
+/*
+ * Configure PCMCIA socket
+ */
+static void ems_pcmcia_config(struct pcmcia_device *dev)
+{
+ win_req_t req;
+ memreq_t mem;
+
+ int csval;
+
+ /* Allocate a memory window */
+ req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+ req.Base = req.Size = 0;
+ req.AccessSpeed = 0;
+
+ csval = pcmcia_request_window(&dev, &req, &dev->win);
+ if (csval) {
+ cs_error(dev, RequestWindow, csval);
+ return;
+ }
+
+ mem.CardOffset = mem.Page = 0;
+ mem.CardOffset = dev->conf.ConfigBase;
+
+ csval = pcmcia_map_mem_page(dev->win, &mem);
+ if (csval) {
+ cs_error(dev, MapMemPage, csval);
+ return;
+ }
+
+ csval = pcmcia_request_irq(dev, &dev->irq);
+ if (csval) {
+ cs_error(dev, RequestIRQ, csval);
+ return;
+ }
+
+ /* This actually configures the PCMCIA socket */
+ csval = pcmcia_request_configuration(dev, &dev->conf);
+ if (csval) {
+ cs_error(dev, RequestConfiguration, csval);
+ return;
+ }
+
+ ems_pcmcia_add_card(dev, req.Base);
+}
+
+/*
+ * Release claimed resources
+ */
+static void ems_pcmcia_remove(struct pcmcia_device *dev)
+{
+ ems_pcmcia_del_card(dev);
+
+ pcmcia_disable_device(dev);
+}
+
+/*
+ * The dev_info variable is the "key" that is used to match up this
+ * device driver with appropriate cards, through the card configuration
+ * database.
+ */
+static dev_info_t dev_info = "can-ems-pcmcia";
+
+static struct pcmcia_driver ems_pcmcia_driver = {
+ .drv = {
+ .name = dev_info,
+ },
+
+ .probe = ems_pcmcia_probe,
+ .remove = ems_pcmcia_remove,
+
+ .id_table = ems_pcmcia_tbl,
+};
+
+static int __init ems_pcmcia_init(void)
+{
+ return pcmcia_register_driver(&ems_pcmcia_driver);
+}
+
+static void __exit ems_pcmcia_exit(void)
+{
+ pcmcia_unregister_driver(&ems_pcmcia_driver);
+}
+
+module_init(ems_pcmcia_init);
+module_exit(ems_pcmcia_exit);
+
--- /dev/null
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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/can.h>
+#include <linux/can/dev.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+
+#include "sja1000.h"
+
+#define DRV_NAME "ixxat_pci"
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
+MODULE_DESCRIPTION("Socket-CAN driver for IXXAT PC-I 04/PCI PCI cards");
+MODULE_SUPPORTED_DEVICE("IXXAT PC-I 04/PCI card");
+MODULE_LICENSE("GPL v2");
+
+/* Maximum number of interfaces supported on one card. Currently
+ * we only support a maximum of two interfaces, which is the maximum
+ * of what Ixxat sells anyway.
+ */
+#define IXXAT_PCI_MAX_CAN 2
+
+struct ixxat_pci {
+ struct pci_dev *pci_dev;
+ struct net_device *dev[IXXAT_PCI_MAX_CAN];
+ int conf_addr;
+ void __iomem *base_addr;
+};
+
+#define IXXAT_PCI_CAN_CLOCK (16000000 / 2)
+
+#define IXXAT_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX0_INVERT | \
+ OCR_TX1_PUSHPULL)
+#define IXXAT_PCI_CDR 0
+
+#define CHANNEL_RESET_OFFSET 0x110
+#define CHANNEL_OFFSET 0x200
+
+#define INTCSR_OFFSET 0x4c /* Offset in PLX9050 conf registers */
+#define INTCSR_LINTI1 (1 << 0)
+#define INTCSR_LINTI2 (1 << 3)
+#define INTCSR_PCI (1 << 6)
+
+/* PCI vender, device and sub-device ID */
+#define IXXAT_PCI_VENDOR_ID 0x10b5
+#define IXXAT_PCI_DEVICE_ID 0x9050
+#define IXXAT_PCI_SUB_SYS_ID 0x2540
+
+#define IXXAT_PCI_BASE_SIZE 0x400
+
+static struct pci_device_id ixxat_pci_tbl[] = {
+ {IXXAT_PCI_VENDOR_ID, IXXAT_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, ixxat_pci_tbl);
+
+static u8 ixxat_pci_read_reg(struct net_device *ndev, int port)
+{
+ u8 val;
+ val = readb((void __iomem *)(ndev->base_addr + port));
+ return val;
+}
+
+static void ixxat_pci_write_reg(struct net_device *ndev, int port, u8 val)
+{
+ writeb(val, (void __iomem *)(ndev->base_addr + port));
+}
+
+static void ixxat_pci_del_chan(struct pci_dev *pdev, struct net_device *ndev)
+{
+ dev_info(&pdev->dev, "Removing device %s\n", ndev->name);
+
+ unregister_sja1000dev(ndev);
+
+ free_sja1000dev(ndev);
+}
+
+static struct net_device *ixxat_pci_add_chan(struct pci_dev *pdev,
+ void __iomem *base_addr)
+{
+ struct net_device *ndev;
+ struct sja1000_priv *priv;
+ int err;
+
+ ndev = alloc_sja1000dev(0);
+ if (ndev == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ priv = netdev_priv(ndev);
+
+ ndev->base_addr = (unsigned long)base_addr;
+
+ priv->read_reg = ixxat_pci_read_reg;
+ priv->write_reg = ixxat_pci_write_reg;
+
+ priv->can.bittiming.clock = IXXAT_PCI_CAN_CLOCK;
+
+ priv->ocr = IXXAT_PCI_OCR;
+ priv->cdr = IXXAT_PCI_CDR;
+
+ /* Set and enable PCI interrupts */
+ ndev->irq = pdev->irq;
+
+ dev_dbg(&pdev->dev, "base_addr=%#lx irq=%d\n",
+ ndev->base_addr, ndev->irq);
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ err = register_sja1000dev(ndev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register (err=%d)\n", err);
+ goto failure;
+ }
+
+ return ndev;
+
+failure:
+ free_sja1000dev(ndev);
+ return ERR_PTR(err);
+}
+
+static int __devinit ixxat_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct ixxat_pci *board;
+ int err, intcsr = INTCSR_LINTI1 | INTCSR_PCI;
+ u16 sub_sys_id;
+ void __iomem *base_addr;
+
+ dev_info(&pdev->dev, "Initializing device %04x:%04x\n",
+ pdev->vendor, pdev->device);
+
+ board = kzalloc(sizeof(*board), GFP_KERNEL);
+ if (!board)
+ return -ENOMEM;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto failure;
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto failure;
+
+ err = pci_read_config_word(pdev, 0x2e, &sub_sys_id);
+ if (err)
+ goto failure_release_pci;
+
+ if (sub_sys_id != IXXAT_PCI_SUB_SYS_ID)
+ return -ENODEV;
+
+ /* Enable memory and I/O space */
+ err = pci_write_config_word(pdev, 0x04, 0x3);
+ if (err)
+ goto failure_release_pci;
+
+ board->conf_addr = pci_resource_start(pdev, 1);
+
+ base_addr = pci_iomap(pdev, 2, IXXAT_PCI_BASE_SIZE);
+ if (base_addr == 0) {
+ err = -ENODEV;
+ goto failure_release_pci;
+ }
+
+ board->base_addr = base_addr;
+
+ writeb(0x1, base_addr + CHANNEL_RESET_OFFSET);
+ writeb(0x1, base_addr + CHANNEL_OFFSET + CHANNEL_RESET_OFFSET);
+ udelay(100);
+
+ board->dev[0] = ixxat_pci_add_chan(pdev, base_addr);
+ if (IS_ERR(board->dev[0]))
+ goto failure_iounmap;
+
+ /* Check if second channel is available */
+ if ((readb(base_addr + CHANNEL_OFFSET + REG_MOD) & 0xa1) == 0x21 &&
+ readb(base_addr + CHANNEL_OFFSET + REG_SR) == 0x0c &&
+ readb(base_addr + CHANNEL_OFFSET + REG_IR) == 0xe0) {
+ board->dev[1] = ixxat_pci_add_chan(pdev,
+ base_addr + CHANNEL_OFFSET);
+ if (IS_ERR(board->dev[1]))
+ goto failure_unreg_dev0;
+
+ intcsr |= INTCSR_LINTI2;
+ }
+
+ /* enable interrupt(s) in PLX9050 */
+ outb(intcsr, board->conf_addr + INTCSR_OFFSET);
+
+ pci_set_drvdata(pdev, board);
+
+ return 0;
+
+failure_unreg_dev0:
+ ixxat_pci_del_chan(pdev, board->dev[0]);
+
+failure_iounmap:
+ pci_iounmap(pdev, board->base_addr);
+
+failure_release_pci:
+ pci_release_regions(pdev);
+
+failure:
+ kfree(board);
+
+ return err;
+}
+
+static void __devexit ixxat_pci_remove_one(struct pci_dev *pdev)
+{
+ struct ixxat_pci *board = pci_get_drvdata(pdev);
+ int i;
+
+ /* Disable interrupts in PLX9050*/
+ outb(0, board->conf_addr + INTCSR_OFFSET);
+
+ for (i = 0; i < IXXAT_PCI_MAX_CAN; i++) {
+ if (!board->dev[i])
+ break;
+ ixxat_pci_del_chan(pdev, board->dev[i]);
+ }
+
+ pci_iounmap(pdev, board->base_addr);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+
+ kfree(board);
+}
+
+static struct pci_driver ixxat_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ixxat_pci_tbl,
+ .probe = ixxat_pci_init_one,
+ .remove = __devexit_p(ixxat_pci_remove_one),
+};
+
+static int __init ixxat_pci_init(void)
+{
+ return pci_register_driver(&ixxat_pci_driver);
+}
+
+static void __exit ixxat_pci_exit(void)
+{
+ pci_unregister_driver(&ixxat_pci_driver);
+}
+
+module_init(ixxat_pci_init);
+module_exit(ixxat_pci_exit);
--- /dev/null
+/*
+ * Copyright (C) 2008 Per Dalen <per.dalen@cnw.se>
+ *
+ * Parts of this software are based on (derived) the following:
+ *
+ * - Kvaser linux driver, version 4.72 BETA
+ * Copyright (C) 2002-2007 KVASER AB
+ *
+ * - Lincan driver, version 0.3.3, OCERA project
+ * Copyright (C) 2004 Pavel Pisa
+ * Copyright (C) 2001 Arnaud Westenberg
+ *
+ * - Socketcan SJA1000 drivers
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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/can.h>
+#include <linux/can/dev.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+
+#include "sja1000.h"
+
+#define DRV_NAME "kvaser_pci"
+
+MODULE_AUTHOR("Per Dalen <per.dalen@cnw.se>");
+MODULE_DESCRIPTION("Socket-CAN driver for KVASER PCAN PCI cards");
+MODULE_SUPPORTED_DEVICE("KVASER PCAN PCI CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define MAX_NO_OF_CHANNELS 4 /* max no of channels on
+ a single card */
+
+struct kvaser_pci {
+ int channel;
+ struct pci_dev *pci_dev;
+ struct net_device *slave_dev[MAX_NO_OF_CHANNELS-1];
+ void __iomem *conf_addr;
+ void __iomem *res_addr;
+ int no_channels;
+ u8 xilinx_ver;
+};
+
+#define KVASER_PCI_CAN_CLOCK (16000000 / 2)
+
+/*
+ * The board configuration is probably following:
+ * RX1 is connected to ground.
+ * TX1 is not connected.
+ * CLKO is not connected.
+ * Setting the OCR register to 0xDA is a good idea.
+ * This means normal output mode , push-pull and the correct polarity.
+ */
+#define KVASER_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
+
+/*
+ * In the CDR register, you should set CBP to 1.
+ * You will probably also want to set the clock divider value to 0
+ * (meaning divide-by-2), the Pelican bit, and the clock-off bit
+ * (you will have no need for CLKOUT anyway).
+ */
+#define KVASER_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+
+/*
+ * These register values are valid for revision 14 of the Xilinx logic.
+ */
+#define XILINX_VERINT 7 /* Lower nibble simulate interrupts,
+ high nibble version number. */
+
+#define XILINX_PRESUMED_VERSION 14
+
+/*
+ * Important S5920 registers
+ */
+#define S5920_INTCSR 0x38
+#define S5920_PTCR 0x60
+#define INTCSR_ADDON_INTENABLE_M 0x2000
+
+
+#define KVASER_PCI_PORT_BYTES 0x20
+
+#define PCI_CONFIG_PORT_SIZE 0x80 /* size of the config io-memory */
+#define PCI_PORT_SIZE 0x80 /* size of a channel io-memory */
+#define PCI_PORT_XILINX_SIZE 0x08 /* size of a xilinx io-memory */
+
+#define KVASER_PCI_VENDOR_ID1 0x10e8 /* the PCI device and vendor IDs */
+#define KVASER_PCI_DEVICE_ID1 0x8406
+
+#define KVASER_PCI_VENDOR_ID2 0x1a07 /* the PCI device and vendor IDs */
+#define KVASER_PCI_DEVICE_ID2 0x0008
+
+static struct pci_device_id kvaser_pci_tbl[] = {
+ {KVASER_PCI_VENDOR_ID1, KVASER_PCI_DEVICE_ID1, PCI_ANY_ID, PCI_ANY_ID,},
+ {KVASER_PCI_VENDOR_ID2, KVASER_PCI_DEVICE_ID2, PCI_ANY_ID, PCI_ANY_ID,},
+ { 0,}
+};
+
+MODULE_DEVICE_TABLE(pci, kvaser_pci_tbl);
+
+static u8 kvaser_pci_read_reg(struct net_device *dev, int port)
+{
+ return ioread8((void __iomem *)(dev->base_addr + port));
+}
+
+static void kvaser_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ iowrite8(val, (void __iomem *)(dev->base_addr + port));
+}
+
+static void kvaser_pci_disable_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct kvaser_pci *board = priv->priv;
+ u32 tmp;
+
+ /* Disable interrupts from card */
+ tmp = ioread32(board->conf_addr + S5920_INTCSR);
+ tmp &= ~INTCSR_ADDON_INTENABLE_M;
+ iowrite32(tmp, board->conf_addr + S5920_INTCSR);
+}
+
+static void kvaser_pci_enable_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct kvaser_pci *board = priv->priv;
+ u32 tmp;
+
+ /* Enable interrupts from card */
+ tmp = ioread32(board->conf_addr + S5920_INTCSR);
+ tmp |= INTCSR_ADDON_INTENABLE_M;
+ iowrite32(tmp, board->conf_addr + S5920_INTCSR);
+}
+
+static int number_of_sja1000_chip(void __iomem *base_addr)
+{
+ u8 status;
+ int i;
+
+ for (i = 0; i < MAX_NO_OF_CHANNELS; i++) {
+ /* reset chip */
+ iowrite8(MOD_RM, base_addr +
+ (i * KVASER_PCI_PORT_BYTES) + REG_MOD);
+ status = ioread8(base_addr +
+ (i * KVASER_PCI_PORT_BYTES) + REG_MOD);
+ udelay(10);
+ /* check reset bit */
+ if (!(status & MOD_RM))
+ break;
+ }
+
+ return i;
+}
+
+static void kvaser_pci_del_chan(struct net_device *dev)
+{
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int i;
+
+ if (!dev)
+ return;
+ priv = netdev_priv(dev);
+ if (!priv)
+ return;
+ board = priv->priv;
+ if (!board)
+ return;
+
+ dev_info(&board->pci_dev->dev, "Removing device %s\n",
+ dev->name);
+
+ for (i = 0; i < board->no_channels - 1; i++) {
+ if (board->slave_dev[i]) {
+ dev_info(&board->pci_dev->dev, "Removing device %s\n",
+ board->slave_dev[i]->name);
+ unregister_sja1000dev(board->slave_dev[i]);
+ free_sja1000dev(board->slave_dev[i]);
+ }
+ }
+ unregister_sja1000dev(dev);
+
+ /* Disable PCI interrupts */
+ kvaser_pci_disable_irq(dev);
+
+ pci_iounmap(board->pci_dev, (void __iomem *)dev->base_addr);
+ pci_iounmap(board->pci_dev, board->conf_addr);
+ pci_iounmap(board->pci_dev, board->res_addr);
+
+ free_sja1000dev(dev);
+}
+
+static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel,
+ struct net_device **master_dev,
+ void __iomem *conf_addr,
+ void __iomem *res_addr,
+ unsigned long base_addr)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int err, init_step;
+
+ dev = alloc_sja1000dev(sizeof(struct kvaser_pci));
+ if (dev == NULL)
+ return -ENOMEM;
+
+ priv = netdev_priv(dev);
+ board = priv->priv;
+
+ board->pci_dev = pdev;
+ board->channel = channel;
+
+ /*S5920*/
+ board->conf_addr = conf_addr;
+
+ /*XILINX board wide address*/
+ board->res_addr = res_addr;
+
+ if (channel == 0) {
+ board->xilinx_ver =
+ ioread8(board->res_addr + XILINX_VERINT) >> 4;
+ init_step = 2;
+
+ /* Assert PTADR# - we're in passive mode so the other bits are
+ not important */
+ iowrite32(0x80808080UL, board->conf_addr + S5920_PTCR);
+
+ /* Disable interrupts from card */
+ kvaser_pci_disable_irq(dev);
+ /* Enable interrupts from card */
+ kvaser_pci_enable_irq(dev);
+ } else {
+ struct sja1000_priv *master_priv = netdev_priv(*master_dev);
+ struct kvaser_pci *master_board = master_priv->priv;
+ master_board->slave_dev[channel - 1] = dev;
+ master_board->no_channels = channel + 1;
+ board->xilinx_ver = master_board->xilinx_ver;
+ }
+
+ dev->base_addr = base_addr + channel * KVASER_PCI_PORT_BYTES;
+
+ priv->read_reg = kvaser_pci_read_reg;
+ priv->write_reg = kvaser_pci_write_reg;
+
+ priv->can.bittiming.clock = KVASER_PCI_CAN_CLOCK;
+
+ priv->ocr = KVASER_PCI_OCR;
+ priv->cdr = KVASER_PCI_CDR;
+
+ /* Register and setup interrupt handling */
+ dev->irq = pdev->irq;
+ init_step = 4;
+
+ dev_info(&pdev->dev, "base_addr=%#lx conf_addr=%p irq=%d\n",
+ dev->base_addr, board->conf_addr, dev->irq);
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Registering device failed (err=%d)\n",
+ err);
+ goto failure;
+ }
+
+ if (channel == 0)
+ *master_dev = dev;
+
+ return 0;
+
+failure:
+ kvaser_pci_del_chan(dev);
+ return err;
+}
+
+static int __devinit kvaser_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err;
+ struct net_device *master_dev = NULL;
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int no_channels;
+ void __iomem *base_addr = NULL;
+ void __iomem *conf_addr = NULL;
+ void __iomem *res_addr = NULL;
+ int i;
+
+ dev_info(&pdev->dev, "initializing device %04x:%04x\n",
+ pdev->vendor, pdev->device);
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto failure;
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto failure_release_pci;
+
+ /*S5920*/
+ conf_addr = pci_iomap(pdev, 0, PCI_CONFIG_PORT_SIZE);
+ if (conf_addr == 0) {
+ err = -ENODEV;
+ goto failure_iounmap;
+ }
+
+ /*XILINX board wide address*/
+ res_addr = pci_iomap(pdev, 2, PCI_PORT_XILINX_SIZE);
+ if (res_addr == 0) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ base_addr = pci_iomap(pdev, 1, PCI_PORT_SIZE);
+ if (base_addr == 0) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ no_channels = number_of_sja1000_chip(base_addr);
+ if (no_channels == 0) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ for (i = 0; i < no_channels; i++) {
+ err = kvaser_pci_add_chan(pdev, i, &master_dev,
+ conf_addr, res_addr,
+ (unsigned long)base_addr);
+ if (err)
+ goto failure_cleanup;
+ }
+
+ priv = netdev_priv(master_dev);
+ board = priv->priv;
+
+ dev_info(&pdev->dev, "xilinx version=%d number of channels=%d\n",
+ board->xilinx_ver, board->no_channels);
+
+ pci_set_drvdata(pdev, master_dev);
+ return 0;
+
+failure_cleanup:
+ kvaser_pci_del_chan(master_dev);
+
+failure_iounmap:
+ if (conf_addr == 0)
+ pci_iounmap(pdev, conf_addr);
+ if (res_addr == 0)
+ pci_iounmap(pdev, res_addr);
+ if (base_addr == 0)
+ pci_iounmap(pdev, base_addr);
+
+ pci_release_regions(pdev);
+
+failure_release_pci:
+ pci_disable_device(pdev);
+
+failure:
+ return err;
+
+}
+
+static void __devexit kvaser_pci_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ kvaser_pci_del_chan(dev);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver kvaser_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = kvaser_pci_tbl,
+ .probe = kvaser_pci_init_one,
+ .remove = __devexit_p(kvaser_pci_remove_one),
+};
+
+static int __init kvaser_pci_init(void)
+{
+ return pci_register_driver(&kvaser_pci_driver);
+}
+
+static void __exit kvaser_pci_exit(void)
+{
+ pci_unregister_driver(&kvaser_pci_driver);
+}
+
+module_init(kvaser_pci_init);
+module_exit(kvaser_pci_exit);
--- /dev/null
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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/can.h>
+#include <linux/can/dev.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
+#include <asm/io.h>
+#endif
+
+#include "sja1000.h"
+
+#define DRV_NAME "peak_pci"
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI cards");
+MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI CAN card");
+MODULE_LICENSE("GPL v2");
+
+struct peak_pci {
+ int channel;
+ struct pci_dev *pci_dev;
+ struct net_device *slave_dev;
+ volatile void __iomem *conf_addr;
+};
+
+#define PEAK_PCI_SINGLE 0 /* single channel device */
+#define PEAK_PCI_MASTER 1 /* multi channel master device */
+#define PEAK_PCI_SLAVE 2 /* multi channel slave device */
+
+#define PEAK_PCI_CAN_CLOCK (16000000 / 2)
+
+#define PEAK_PCI_CDR_SINGLE (CDR_CBP | CDR_CLKOUT_MASK | CDR_CLK_OFF)
+#define PEAK_PCI_CDR_MASTER (CDR_CBP | CDR_CLKOUT_MASK)
+
+#define PEAK_PCI_OCR OCR_TX0_PUSHPULL
+
+/*
+ * Important PITA registers
+ */
+#define PITA_ICR 0x00 /* interrupt control register */
+#define PITA_GPIOICR 0x18 /* general purpose I/O interface
+ control register */
+#define PITA_MISC 0x1C /* miscellanoes register */
+
+#define PCI_CONFIG_PORT_SIZE 0x1000 /* size of the config io-memory */
+#define PCI_PORT_SIZE 0x0400 /* size of a channel io-memory */
+
+#define PEAK_PCI_VENDOR_ID 0x001C /* the PCI device and vendor IDs */
+#define PEAK_PCI_DEVICE_ID 0x0001
+
+static struct pci_device_id peak_pci_tbl[] = {
+ {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, peak_pci_tbl);
+
+static u8 peak_pci_read_reg(struct net_device *dev, int port)
+{
+ u8 val;
+ val = readb((const volatile void __iomem *)
+ (dev->base_addr + (port << 2)));
+ return val;
+}
+
+static void peak_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ writeb(val, (volatile void __iomem *)
+ (dev->base_addr + (port << 2)));
+}
+
+static void peak_pci_post_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board = priv->priv;
+ u16 icr_low;
+
+ /* Select and clear in Pita stored interrupt */
+ icr_low = readw(board->conf_addr + PITA_ICR);
+ if (board->channel == PEAK_PCI_SLAVE) {
+ if (icr_low & 0x0001)
+ writew(0x0001, board->conf_addr + PITA_ICR);
+ } else {
+ if (icr_low & 0x0002)
+ writew(0x0002, board->conf_addr + PITA_ICR);
+ }
+}
+
+static void peak_pci_del_chan(struct net_device *dev, int init_step)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board;
+ u16 icr_high;
+
+ if (!dev)
+ return;
+ priv = netdev_priv(dev);
+ if (!priv)
+ return;
+ board = priv->priv;
+ if (!board)
+ return;
+
+ switch (init_step) {
+ case 0: /* Full cleanup */
+ printk(KERN_INFO "Removing %s device %s\n",
+ DRV_NAME, dev->name);
+ unregister_sja1000dev(dev);
+ case 4:
+ icr_high = readw(board->conf_addr + PITA_ICR + 2);
+ if (board->channel == PEAK_PCI_SLAVE)
+ icr_high &= ~0x0001;
+ else
+ icr_high &= ~0x0002;
+ writew(icr_high, board->conf_addr + PITA_ICR + 2);
+ case 3:
+ iounmap((void *)dev->base_addr);
+ case 2:
+ if (board->channel != PEAK_PCI_SLAVE)
+ iounmap((void *)board->conf_addr);
+ case 1:
+ free_sja1000dev(dev);
+ break;
+ }
+
+}
+
+static int peak_pci_add_chan(struct pci_dev *pdev, int channel,
+ struct net_device **master_dev)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct peak_pci *board;
+ u16 icr_high;
+ unsigned long addr;
+ int err, init_step;
+
+ dev = alloc_sja1000dev(sizeof(struct peak_pci));
+ if (dev == NULL)
+ return -ENOMEM;
+ init_step = 1;
+
+ priv = netdev_priv(dev);
+ board = priv->priv;
+
+ board->pci_dev = pdev;
+ board->channel = channel;
+
+ if (channel != PEAK_PCI_SLAVE) {
+
+ addr = pci_resource_start(pdev, 0);
+ board->conf_addr = ioremap(addr, PCI_CONFIG_PORT_SIZE);
+ if (board->conf_addr == 0) {
+ err = -ENODEV;
+ goto failure;
+ }
+ init_step = 2;
+
+ /* Set GPIO control register */
+ writew(0x0005, board->conf_addr + PITA_GPIOICR + 2);
+
+ /* Enable single or dual channel */
+ if (channel == PEAK_PCI_MASTER)
+ writeb(0x00, board->conf_addr + PITA_GPIOICR);
+ else
+ writeb(0x04, board->conf_addr + PITA_GPIOICR);
+ /* Toggle reset */
+ writeb(0x05, board->conf_addr + PITA_MISC + 3);
+ mdelay(5);
+ /* Leave parport mux mode */
+ writeb(0x04, board->conf_addr + PITA_MISC + 3);
+ } else {
+ struct sja1000_priv *master_priv = netdev_priv(*master_dev);
+ struct peak_pci *master_board = master_priv->priv;
+ master_board->slave_dev = dev;
+ board->conf_addr = master_board->conf_addr;
+ }
+
+ addr = pci_resource_start(pdev, 1);
+ if (channel == PEAK_PCI_SLAVE)
+ addr += PCI_PORT_SIZE;
+
+ dev->base_addr = (unsigned long)ioremap(addr, PCI_PORT_SIZE);
+ if (dev->base_addr == 0) {
+ err = -ENOMEM;
+ goto failure;
+ }
+ init_step = 3;
+
+ priv->read_reg = peak_pci_read_reg;
+ priv->write_reg = peak_pci_write_reg;
+ priv->post_irq = peak_pci_post_irq;
+
+ priv->can.bittiming.clock = PEAK_PCI_CAN_CLOCK;
+
+ priv->ocr = PEAK_PCI_OCR;
+
+ if (channel == PEAK_PCI_MASTER)
+ priv->cdr = PEAK_PCI_CDR_MASTER;
+ else
+ priv->cdr = PEAK_PCI_CDR_SINGLE;
+
+ /* Register and setup interrupt handling */
+ dev->irq = pdev->irq;
+ icr_high = readw(board->conf_addr + PITA_ICR + 2);
+ if (channel == PEAK_PCI_SLAVE)
+ icr_high |= 0x0001;
+ else
+ icr_high |= 0x0002;
+ writew(icr_high, board->conf_addr + PITA_ICR + 2);
+ init_step = 4;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ printk(KERN_ERR "Registering %s device failed (err=%d)\n",
+ DRV_NAME, err);
+ goto failure;
+ }
+
+ if (channel != PEAK_PCI_SLAVE)
+ *master_dev = dev;
+
+ printk(KERN_INFO "%s: %s at base_addr=%#lx conf_addr=%p irq=%d\n",
+ DRV_NAME, dev->name, dev->base_addr, board->conf_addr, dev->irq);
+
+ return 0;
+
+failure:
+ peak_pci_del_chan(dev, init_step);
+ return err;
+}
+
+static int __devinit peak_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err;
+ u16 sub_sys_id;
+ struct net_device *master_dev = NULL;
+
+ printk(KERN_INFO "%s: initializing device %04x:%04x\n",
+ DRV_NAME, pdev->vendor, pdev->device);
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto failure;
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto failure;
+
+ err = pci_read_config_word(pdev, 0x2e, &sub_sys_id);
+ if (err)
+ goto failure_cleanup;
+
+ err = pci_write_config_word(pdev, 0x44, 0);
+ if (err)
+ goto failure_cleanup;
+
+ if (sub_sys_id > 3) {
+ err = peak_pci_add_chan(pdev,
+ PEAK_PCI_MASTER, &master_dev);
+ if (err)
+ goto failure_cleanup;
+
+ err = peak_pci_add_chan(pdev,
+ PEAK_PCI_SLAVE, &master_dev);
+ if (err)
+ goto failure_cleanup;
+ } else {
+ err = peak_pci_add_chan(pdev, PEAK_PCI_SINGLE,
+ &master_dev);
+ if (err)
+ goto failure_cleanup;
+ }
+
+ pci_set_drvdata(pdev, master_dev);
+ return 0;
+
+failure_cleanup:
+ if (master_dev)
+ peak_pci_del_chan(master_dev, 0);
+
+ pci_release_regions(pdev);
+
+failure:
+ return err;
+
+}
+
+static void __devexit peak_pci_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board = priv->priv;
+
+ if (board->slave_dev)
+ peak_pci_del_chan(board->slave_dev, 0);
+ peak_pci_del_chan(dev, 0);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver peak_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = peak_pci_tbl,
+ .probe = peak_pci_init_one,
+ .remove = __devexit_p(peak_pci_remove_one),
+};
+
+static int __init peak_pci_init(void)
+{
+ return pci_register_driver(&peak_pci_driver);
+}
+
+static void __exit peak_pci_exit(void)
+{
+ pci_unregister_driver(&peak_pci_driver);
+}
+
+module_init(peak_pci_init);
+module_exit(peak_pci_exit);
--- /dev/null
+/*
+ * Copyright (C) 2008 David Müller, <d.mueller@elsoft.ch>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "pipcan"
+
+MODULE_AUTHOR("David Müller <d.mueller@elsoft.ch>");
+MODULE_DESCRIPTION("Socket-CAN driver for MPL PIPCAN module");
+MODULE_SUPPORTED_DEVICE("MPL PIPCAN module");
+MODULE_LICENSE("GPL v2");
+
+#define PIPCAN_CAN_CLOCK (16000000 / 2)
+
+#define PIPCAN_OCR (OCR_TX1_PUSHPULL)
+#define PIPCAN_CDR (CDR_CBP | CDR_CLK_OFF)
+
+#define PIPCAN_IOSIZE (0x100)
+
+#define PIPCAN_RES (0x804)
+#define PIPCAN_RST (0x805)
+
+static u8 pc_read_reg(struct net_device *dev, int reg)
+{
+ return inb(dev->base_addr + reg);
+}
+
+static void pc_write_reg(struct net_device *dev, int reg, u8 val)
+{
+ outb(val, dev->base_addr + reg);
+}
+
+static int __init pc_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct resource *res;
+ int rc, irq;
+
+ rc = -ENODEV;
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || !irq)
+ goto exit;
+
+ rc = -EBUSY;
+ if (!request_region(res->start, res->end - res->start + 1, DRV_NAME))
+ goto exit;
+
+ rc = -ENOMEM;
+ dev = alloc_sja1000dev(0);
+ if (!dev)
+ goto exit_release;
+
+ priv = netdev_priv(dev);
+
+ priv->read_reg = pc_read_reg;
+ priv->write_reg = pc_write_reg;
+ priv->can.bittiming.clock = PIPCAN_CAN_CLOCK;
+ priv->ocr = PIPCAN_OCR;
+ priv->cdr = PIPCAN_CDR;
+
+ dev->irq = irq;
+ dev->base_addr = res->start;
+
+ dev_set_drvdata(&pdev->dev, dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* deactivate RST */
+ outb(inb(PIPCAN_RST) & ~0x01, PIPCAN_RST);
+
+ rc = register_sja1000dev(dev);
+ if (rc) {
+ dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
+ DRV_NAME, rc);
+ goto exit_free;
+ }
+
+ dev_info(&pdev->dev, "device registered (base_addr=%#lx, irq=%d)\n",
+ dev->base_addr, dev->irq);
+ return 0;
+
+exit_free:
+ free_sja1000dev(dev);
+
+exit_release:
+ release_region(res->start, res->end - res->start + 1);
+
+exit:
+ return rc;
+}
+
+static int __exit pc_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = dev_get_drvdata(&pdev->dev);
+ struct resource *res;
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ unregister_sja1000dev(dev);
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+
+ free_sja1000dev(dev);
+
+ release_region(res->start, res->end - res->start + 1);
+
+ /* activate RST */
+ outb(inb(PIPCAN_RST) | 0x01, PIPCAN_RST);
+
+ return 0;
+}
+
+static struct platform_driver pc_driver = {
+ .remove = __exit_p(pc_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device *pc_pdev;
+static const u16 pipcan_ioport[] = {0x1000, 0x8000, 0xE000};
+
+static int __init pc_init(void)
+{
+ struct resource r[2];
+ int rc, addr, irq, idx;
+ u8 pc_res;
+
+ /* get PIPCAN resources from EPLD */
+ pc_res = inb(PIPCAN_RES);
+
+ idx = (pc_res & 0x0F);
+ if ((idx <= 0) || (idx > ARRAY_SIZE(pipcan_ioport))) {
+ printk(KERN_ERR DRV_NAME " invalid base address\n");
+ return -EINVAL;
+ }
+ addr = pipcan_ioport[idx-1];
+
+ irq = (pc_res >> 4) & 0x0F;
+ if ((irq < 3) || (irq == 8) || (irq == 13)) {
+ printk(KERN_ERR DRV_NAME " invalid IRQ\n");
+ return -EINVAL;
+ }
+
+ /* fill in resources */
+ memset(&r, 0, sizeof(r));
+ r[0].start = addr;
+ r[0].end = addr + PIPCAN_IOSIZE - 1;
+ r[0].name = DRV_NAME;
+ r[0].flags = IORESOURCE_IO;
+ r[1].start = r[1].end = irq;
+ r[1].name = DRV_NAME;
+ r[1].flags = IORESOURCE_IRQ;
+
+ pc_pdev = platform_device_register_simple(DRV_NAME, 0, r,
+ ARRAY_SIZE(r));
+ if (IS_ERR(pc_pdev))
+ return PTR_ERR(pc_pdev);
+
+ rc = platform_driver_probe(&pc_driver, pc_probe);
+ if (rc) {
+ platform_device_unregister(pc_pdev);
+ printk(KERN_ERR DRV_NAME
+ " platform_driver_probe() failed (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static void __exit pc_exit(void)
+{
+ platform_driver_unregister(&pc_driver);
+ platform_device_unregister(pc_pdev);
+}
+
+module_init(pc_init);
+module_exit(pc_exit);
--- /dev/null
+/*
+ * sja1000.c - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/dev.h>
+
+#include "sja1000.h"
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id: sja1000.c 531 2007-10-19 07:38:29Z hartkopp $");
+
+#define DRV_NAME "sja1000"
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION(DRV_NAME " CAN netdevice driver");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) ((debug > 0) ? printk(args) : 0)
+/* logging in interrupt context! */
+#define iDBG(args...) ((debug > 1) ? printk(args) : 0)
+#define iiDBG(args...) ((debug > 2) ? printk(args) : 0)
+#else
+#define DBG(args...)
+#define iDBG(args...)
+#define iiDBG(args...)
+#endif
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static const char *ecc_errors[] = {
+ NULL,
+ NULL,
+ "ID.28 to ID.28",
+ "start of frame",
+ "bit SRTR",
+ "bit IDE",
+ "ID.20 to ID.18",
+ "ID.17 to ID.13",
+ "CRC sequence",
+ "reserved bit 0",
+ "data field",
+ "data length code",
+ "bit RTR",
+ "reserved bit 1",
+ "ID.4 to ID.0",
+ "ID.12 to ID.5",
+ NULL,
+ "active error flag",
+ "intermission",
+ "tolerate dominant bits",
+ NULL,
+ NULL,
+ "passive error flag",
+ "error delimiter",
+ "CRC delimiter",
+ "acknowledge slot",
+ "end of frame",
+ "acknowledge delimiter",
+ "overload flag",
+ NULL,
+ NULL,
+ NULL
+};
+
+static const char *ecc_types[] = {
+ "bit error",
+ "form error",
+ "stuff error",
+ "other type of error"
+};
+#endif
+
+static struct can_bittiming_const sja1000_bittiming_const = {
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+static int debug;
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+
+MODULE_PARM_DESC(debug, "Set debug mask (default: 0)");
+
+static int sja1000_probe_chip(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ if (dev->base_addr && (priv->read_reg(dev, 0) == 0xFF)) {
+ printk(KERN_INFO "%s: probing @0x%lX failed\n",
+ DRV_NAME, dev->base_addr);
+ return 0;
+ }
+ return 1;
+}
+
+int set_reset_mode(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ unsigned char status = priv->read_reg(dev, REG_MOD);
+ int i;
+
+ /* disable interrupts */
+ priv->write_reg(dev, REG_IER, IRQ_OFF);
+
+ for (i = 0; i < 100; i++) {
+ /* check reset bit */
+ if (status & MOD_RM) {
+ if (i > 1) {
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __func__, i);
+ }
+ priv->can.state = CAN_STATE_STOPPED;
+ return 0;
+ }
+
+ priv->write_reg(dev, REG_MOD, MOD_RM); /* reset chip */
+ status = priv->read_reg(dev, REG_MOD);
+ udelay(10);
+ }
+
+ dev_err(ND2D(dev), "setting SJA1000 into reset mode failed!\n");
+ return 1;
+
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ unsigned char status = priv->read_reg(dev, REG_MOD);
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ /* check reset bit */
+ if ((status & MOD_RM) == 0) {
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ if (i > 1) {
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __func__, i);
+ }
+#endif
+ priv->can.state = CAN_STATE_ACTIVE;
+ /* enable all interrupts */
+ priv->write_reg(dev, REG_IER, IRQ_ALL);
+
+ return 0;
+ }
+
+ /* set chip to normal mode */
+ priv->write_reg(dev, REG_MOD, 0x00);
+ status = priv->read_reg(dev, REG_MOD);
+ udelay(10);
+ }
+
+ dev_err(ND2D(dev), "setting SJA1000 into normal mode failed!\n");
+ return 1;
+
+}
+
+static void sja1000_start(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __func__);
+
+ /* leave reset mode */
+ if (priv->can.state != CAN_STATE_STOPPED)
+ set_reset_mode(dev);
+
+ /* Clear error counters and error code capture */
+ priv->write_reg(dev, REG_TXERR, 0x0);
+ priv->write_reg(dev, REG_RXERR, 0x0);
+ priv->read_reg(dev, REG_ECC);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+}
+
+static int sja1000_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ switch (mode) {
+ case CAN_MODE_START:
+ DBG("%s: CAN_MODE_START requested\n", __func__);
+ if (!priv->open_time)
+ return -EINVAL;
+
+ sja1000_start(dev);
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int sja1000_get_state(struct net_device *dev, enum can_state *state)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ u8 status;
+
+ /* FIXME: inspecting the status register to get the current state
+ * is not really necessary, because state changes are handled by
+ * in the ISR and the variable priv->can.state gets updated. The
+ * CAN devicde interface needs fixing!
+ */
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ if (priv->can.state == CAN_STATE_STOPPED) {
+ *state = CAN_STATE_STOPPED;
+ } else {
+ status = priv->read_reg(dev, REG_SR);
+ if (status & SR_BS)
+ *state = CAN_STATE_BUS_OFF;
+ else if (status & SR_ES) {
+ if (priv->read_reg(dev, REG_TXERR) > 127 ||
+ priv->read_reg(dev, REG_RXERR) > 127)
+ *state = CAN_STATE_BUS_PASSIVE;
+ else
+ *state = CAN_STATE_BUS_WARNING;
+ } else
+ *state = CAN_STATE_ACTIVE;
+ }
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ /* Check state */
+ if (*state != priv->can.state)
+ dev_err(ND2D(dev),
+ "Oops, state mismatch: hard %d != soft %d\n",
+ *state, priv->can.state);
+#endif
+ spin_unlock_irq(&priv->can.irq_lock);
+ return 0;
+}
+
+static int sja1000_set_bittiming(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ u8 btr0, btr1;
+
+ btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
+ btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
+ (((bt->phase_seg2 - 1) & 0x7) << 4) |
+ ((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) << 7);
+
+ dev_info(ND2D(dev), "BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
+
+ priv->write_reg(dev, REG_BTR0, btr0);
+ priv->write_reg(dev, REG_BTR1, btr1);
+
+ return 0;
+}
+
+/*
+ * initialize SJA1000 chip:
+ * - reset chip
+ * - set output mode
+ * - set baudrate
+ * - enable interrupts
+ * - start operating mode
+ */
+static void chipset_init(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ /* set clock divider and output control register */
+ priv->write_reg(dev, REG_CDR, priv->cdr | CDR_PELICAN);
+
+ /* set acceptance filter (accept all) */
+ priv->write_reg(dev, REG_ACCC0, 0x00);
+ priv->write_reg(dev, REG_ACCC1, 0x00);
+ priv->write_reg(dev, REG_ACCC2, 0x00);
+ priv->write_reg(dev, REG_ACCC3, 0x00);
+
+ priv->write_reg(dev, REG_ACCM0, 0xFF);
+ priv->write_reg(dev, REG_ACCM1, 0xFF);
+ priv->write_reg(dev, REG_ACCM2, 0xFF);
+ priv->write_reg(dev, REG_ACCM3, 0xFF);
+
+ priv->write_reg(dev, REG_OCR, priv->ocr | OCR_MODE_NORMAL);
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx ff ll 00 11 22 33 44 55 66 77
+ * [ can-id ] [flags] [len] [can data (up to 8 bytes]
+ */
+static int sja1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ uint8_t fi;
+ uint8_t dlc;
+ canid_t id;
+ uint8_t dreg;
+ int i;
+
+ netif_stop_queue(dev);
+
+ fi = dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ if (id & CAN_RTR_FLAG)
+ fi |= FI_RTR;
+
+ if (id & CAN_EFF_FLAG) {
+ fi |= FI_FF;
+ dreg = EFF_BUF;
+ priv->write_reg(dev, REG_FI, fi);
+ priv->write_reg(dev, REG_ID1, (id & 0x1fe00000) >> (5 + 16));
+ priv->write_reg(dev, REG_ID2, (id & 0x001fe000) >> (5 + 8));
+ priv->write_reg(dev, REG_ID3, (id & 0x00001fe0) >> 5);
+ priv->write_reg(dev, REG_ID4, (id & 0x0000001f) << 3);
+ } else {
+ dreg = SFF_BUF;
+ priv->write_reg(dev, REG_FI, fi);
+ priv->write_reg(dev, REG_ID1, (id & 0x000007f8) >> 3);
+ priv->write_reg(dev, REG_ID2, (id & 0x00000007) << 5);
+ }
+
+ for (i = 0; i < dlc; i++)
+ priv->write_reg(dev, dreg++, cf->data[i]);
+
+ stats->tx_bytes += dlc;
+ dev->trans_start = jiffies;
+
+ can_put_echo_skb(skb, dev, 0);
+
+ priv->write_reg(dev, REG_CMR, CMD_TR);
+
+ return 0;
+}
+
+static void sja1000_rx(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t fi;
+ uint8_t dreg;
+ canid_t id;
+ uint8_t dlc;
+ int i;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ fi = priv->read_reg(dev, REG_FI);
+ dlc = fi & 0x0F;
+
+ if (fi & FI_FF) {
+ /* extended frame format (EFF) */
+ dreg = EFF_BUF;
+ id = (priv->read_reg(dev, REG_ID1) << (5 + 16))
+ | (priv->read_reg(dev, REG_ID2) << (5 + 8))
+ | (priv->read_reg(dev, REG_ID3) << 5)
+ | (priv->read_reg(dev, REG_ID4) >> 3);
+ id |= CAN_EFF_FLAG;
+ } else {
+ /* standard frame format (SFF) */
+ dreg = SFF_BUF;
+ id = (priv->read_reg(dev, REG_ID1) << 3)
+ | (priv->read_reg(dev, REG_ID2) >> 5);
+ }
+
+ if (fi & FI_RTR)
+ id |= CAN_RTR_FLAG;
+
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++)
+ cf->data[i] = priv->read_reg(dev, dreg++);
+
+ while (i < 8)
+ cf->data[i++] = 0;
+
+ /* release receive buffer */
+ priv->write_reg(dev, REG_CMR, CMD_RRB);
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += dlc;
+}
+
+static int sja1000_err(struct net_device *dev,
+ uint8_t isrc, uint8_t status, int n)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ enum can_state state = priv->can.state;
+ uint8_t ecc, alc;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return -ENOMEM;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = CAN_ERR_FLAG;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ if (isrc & IRQ_DOI) {
+ /* data overrun interrupt */
+ iiDBG(KERN_INFO "%s: data overrun isrc=0x%02X "
+ "status=0x%02X\n", dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: DOI #%d#\n", dev->name, n);
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ priv->can.can_stats.data_overrun++;
+ priv->write_reg(dev, REG_CMR, CMD_CDO); /* clear bit */
+ }
+
+ if (isrc & IRQ_EI) {
+ /* error warning interrupt */
+ iiDBG(KERN_INFO "%s: error warning isrc=0x%02X "
+ "status=0x%02X\n", dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EI #%d#\n", dev->name, n);
+ priv->can.can_stats.error_warning++;
+
+ if (status & SR_BS) {
+ state = CAN_STATE_BUS_OFF;
+ cf->can_id |= CAN_ERR_BUSOFF;
+ can_bus_off(dev);
+ iDBG(KERN_INFO "%s: BUS OFF\n", dev->name);
+ } else if (status & SR_ES) {
+ state = CAN_STATE_BUS_WARNING;
+ iDBG(KERN_INFO "%s: error\n", dev->name);
+ } else
+ state = CAN_STATE_ACTIVE;
+ }
+ if (isrc & IRQ_BEI) {
+ /* bus error interrupt */
+ iiDBG(KERN_INFO "%s: bus error isrc=0x%02X "
+ "status=0x%02X\n", dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: BEI #%d# [%d]\n", dev->name, n,
+ priv->can.can_stats.bus_error);
+ priv->can.can_stats.bus_error++;
+ ecc = priv->read_reg(dev, REG_ECC);
+ iDBG(KERN_INFO "%s: ECC = 0x%02X (%s, %s, %s)\n",
+ dev->name, ecc,
+ (ecc & ECC_DIR) ? "RX" : "TX",
+ ecc_types[ecc >> ECC_ERR],
+ ecc_errors[ecc & ECC_SEG]);
+
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ switch (ecc & ECC_MASK) {
+ case ECC_BIT:
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ break;
+ case ECC_FORM:
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ break;
+ case ECC_STUFF:
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ break;
+ default:
+ cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+ cf->data[3] = ecc & ECC_SEG;
+ break;
+ }
+ /* Error occured during transmission? */
+ if ((ecc & ECC_DIR) == 0)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+ if (isrc & IRQ_EPI) {
+ /* error passive interrupt */
+ iiDBG(KERN_INFO "%s: error passive isrc=0x%02X"
+ " status=0x%02X\n", dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EPI #%d#\n", dev->name, n);
+ priv->can.can_stats.error_passive++;
+ if (status & SR_ES) {
+ iDBG(KERN_INFO "%s: ERROR PASSIVE\n", dev->name);
+ state = CAN_STATE_BUS_PASSIVE;
+ } else {
+ iDBG(KERN_INFO "%s: ERROR ACTIVE\n", dev->name);
+ state = CAN_STATE_ACTIVE;
+ }
+ }
+ if (isrc & IRQ_ALI) {
+ /* arbitration lost interrupt */
+ iiDBG(KERN_INFO "%s: error arbitration lost "
+ "isrc=0x%02X status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: ALI #%d#\n", dev->name, n);
+ alc = priv->read_reg(dev, REG_ALC);
+ iDBG(KERN_INFO "%s: ALC = 0x%02X\n", dev->name, alc);
+ priv->can.can_stats.arbitration_lost++;
+ cf->can_id |= CAN_ERR_LOSTARB;
+ cf->data[0] = alc & 0x1f;
+ }
+
+ if (state != priv->can.state && (state == CAN_STATE_BUS_WARNING ||
+ state == CAN_STATE_BUS_PASSIVE)) {
+ uint8_t rxerr = priv->read_reg(dev, REG_RXERR);
+ uint8_t txerr = priv->read_reg(dev, REG_TXERR);
+ cf->can_id |= CAN_ERR_CRTL;
+ if (state == CAN_STATE_BUS_WARNING)
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_WARNING :
+ CAN_ERR_CRTL_RX_WARNING;
+ else
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_PASSIVE :
+ CAN_ERR_CRTL_RX_PASSIVE;
+ }
+
+ priv->can.state = state;
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+irqreturn_t sja1000_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+#else
+irqreturn_t sja1000_interrupt(int irq, void *dev_id)
+#endif
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct sja1000_priv *priv = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = can_get_stats(dev);
+#else
+ struct net_device_stats *stats = &dev->stats;
+#endif
+ uint8_t isrc, status;
+ int n = 0;
+
+ if (priv->pre_irq)
+ priv->pre_irq(dev);
+
+ iiDBG(KERN_INFO "%s: interrupt\n", dev->name);
+
+ if (priv->can.state == CAN_STATE_STOPPED) {
+ iiDBG(KERN_ERR "%s: %s: controller is in reset mode! "
+ "MOD=0x%02X IER=0x%02X IR=0x%02X SR=0x%02X!\n",
+ dev->name, __func__, priv->read_reg(dev, REG_MOD),
+ priv->read_reg(dev, REG_IER), priv->read_reg(dev, REG_IR),
+ priv->read_reg(dev, REG_SR));
+ goto out;
+ }
+
+ while ((isrc = priv->read_reg(dev, REG_IR)) && (n < 20)) {
+ n++;
+ status = priv->read_reg(dev, REG_SR);
+
+ if (isrc & IRQ_WUI) {
+ /* wake-up interrupt */
+ priv->can.can_stats.wakeup++;
+ }
+ if (isrc & IRQ_TI) {
+ /* transmission complete interrupt */
+ stats->tx_packets++;
+ can_get_echo_skb(dev, 0);
+ netif_wake_queue(dev);
+ }
+ if (isrc & IRQ_RI) {
+ /* receive interrupt */
+ while (status & SR_RBS) {
+ sja1000_rx(dev);
+ status = priv->read_reg(dev, REG_SR);
+ }
+ }
+ if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) {
+ /* error interrupt */
+ if (sja1000_err(dev, isrc, status, n))
+ break;
+ }
+ }
+ if (n > 1)
+ iDBG(KERN_INFO "%s: handled %d IRQs\n", dev->name, n);
+
+out:
+ if (priv->post_irq)
+ priv->post_irq(dev);
+
+ return (n) ? IRQ_HANDLED : IRQ_NONE;
+}
+EXPORT_SYMBOL_GPL(sja1000_interrupt);
+
+static int sja1000_open(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ int err;
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* determine and set bittime */
+ err = can_set_bittiming(dev);
+ if (err)
+ return err;
+
+ /* register interrupt handler, if not done by the device driver */
+ if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ err = request_irq(dev->irq, &sja1000_interrupt, SA_SHIRQ,
+ dev->name, (void *)dev);
+#else
+ err = request_irq(dev->irq, &sja1000_interrupt, IRQF_SHARED,
+ dev->name, (void *)dev);
+#endif
+ if (err)
+ return -EAGAIN;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ /* clear statistics */
+ memset(&priv->can.net_stats, 0, sizeof(priv->can.net_stats));
+#endif
+
+ /* init and start chi */
+ sja1000_start(dev);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int sja1000_close(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ set_reset_mode(dev);
+ netif_stop_queue(dev);
+ priv->open_time = 0;
+ can_close_cleanup(dev);
+
+ if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER))
+ free_irq(dev->irq, (void *)dev);
+
+ return 0;
+}
+
+struct net_device *alloc_sja1000dev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+
+ dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+ priv->dev = dev;
+
+ if (sizeof_priv)
+ priv->priv = (void *)priv + sizeof(struct sja1000_priv);
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_sja1000dev);
+
+void free_sja1000dev(struct net_device *dev)
+{
+ free_candev(dev);
+}
+EXPORT_SYMBOL(free_sja1000dev);
+
+int register_sja1000dev(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ int err;
+
+ if (!sja1000_probe_chip(dev))
+ return -ENODEV;
+
+ dev->flags |= IFF_ECHO; /* we support local echo */
+
+ dev->open = sja1000_open;
+ dev->stop = sja1000_close;
+
+ dev->hard_start_xmit = sja1000_start_xmit;
+
+ priv->can.bittiming_const = &sja1000_bittiming_const;
+ priv->can.do_set_bittiming = sja1000_set_bittiming;
+ priv->can.do_get_state = sja1000_get_state;
+ priv->can.do_set_mode = sja1000_set_mode;
+ priv->dev = dev;
+
+ err = register_netdev(dev);
+ if (err) {
+ printk(KERN_INFO
+ "%s: registering netdev failed\n", DRV_NAME);
+ free_netdev(dev);
+ return err;
+ }
+
+ set_reset_mode(dev);
+ chipset_init(dev);
+ return 0;
+}
+EXPORT_SYMBOL(register_sja1000dev);
+
+void unregister_sja1000dev(struct net_device *dev)
+{
+ set_reset_mode(dev);
+ unregister_netdev(dev);
+}
+EXPORT_SYMBOL(unregister_sja1000dev);
+
+static __init int sja1000_init(void)
+{
+ printk(KERN_INFO "%s CAN netdevice driver\n", DRV_NAME);
+
+ if (debug)
+ printk(KERN_INFO "%s: debug level set to %d.\n",
+ DRV_NAME, debug);
+
+ return 0;
+}
+
+module_init(sja1000_init);
+
+static __exit void sja1000_exit(void)
+{
+ printk(KERN_INFO "%s: driver removed\n", DRV_NAME);
+}
+
+module_exit(sja1000_exit);
--- /dev/null
+/*
+ * $Id: sja1000.h 505 2007-09-30 13:32:41Z hartkopp $
+ *
+ * sja1000.h - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef SJA1000DEV_H
+#define SJA1000DEV_H
+
+#include <linux/version.h>
+#include <linux/can/dev.h>
+#include <linux/can/platform/sja1000.h>
+
+#define TX_TIMEOUT (50*HZ/1000) /* 50ms */
+#define RESTART_MS 100 /* restart chip on persistent errors in 100ms */
+#define MAX_BUS_ERRORS 200 /* prevent from flooding bus error interrupts */
+
+/* SJA1000 registers - manual section 6.4 (Pelican Mode) */
+#define REG_MOD 0x00
+#define REG_CMR 0x01
+#define REG_SR 0x02
+#define REG_IR 0x03
+#define REG_IER 0x04
+#define REG_ALC 0x0B
+#define REG_ECC 0x0C
+#define REG_EWL 0x0D
+#define REG_RXERR 0x0E
+#define REG_TXERR 0x0F
+#define REG_ACCC0 0x10
+#define REG_ACCC1 0x11
+#define REG_ACCC2 0x12
+#define REG_ACCC3 0x13
+#define REG_ACCM0 0x14
+#define REG_ACCM1 0x15
+#define REG_ACCM2 0x16
+#define REG_ACCM3 0x17
+#define REG_RMC 0x1D
+#define REG_RBSA 0x1E
+
+/* Common registers - manual section 6.5 */
+#define REG_BTR0 0x06
+#define REG_BTR1 0x07
+#define REG_OCR 0x08
+#define REG_CDR 0x1F
+
+#define REG_FI 0x10
+#define SFF_BUF 0x13
+#define EFF_BUF 0x15
+
+#define FI_FF 0x80
+#define FI_RTR 0x40
+
+#define REG_ID1 0x11
+#define REG_ID2 0x12
+#define REG_ID3 0x13
+#define REG_ID4 0x14
+
+#define CAN_RAM 0x20
+
+/* mode register */
+#define MOD_RM 0x01
+#define MOD_LOM 0x02
+#define MOD_STM 0x04
+#define MOD_AFM 0x08
+#define MOD_SM 0x10
+
+/* commands */
+#define CMD_SRR 0x10
+#define CMD_CDO 0x08
+#define CMD_RRB 0x04
+#define CMD_AT 0x02
+#define CMD_TR 0x01
+
+/* interrupt sources */
+#define IRQ_BEI 0x80
+#define IRQ_ALI 0x40
+#define IRQ_EPI 0x20
+#define IRQ_WUI 0x10
+#define IRQ_DOI 0x08
+#define IRQ_EI 0x04
+#define IRQ_TI 0x02
+#define IRQ_RI 0x01
+#define IRQ_ALL 0xFF
+#define IRQ_OFF 0x00
+
+/* status register content */
+#define SR_BS 0x80
+#define SR_ES 0x40
+#define SR_TS 0x20
+#define SR_RS 0x10
+#define SR_TCS 0x08
+#define SR_TBS 0x04
+#define SR_DOS 0x02
+#define SR_RBS 0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_SEG 0x1F
+#define ECC_DIR 0x20
+#define ECC_ERR 6
+#define ECC_BIT 0x00
+#define ECC_FORM 0x40
+#define ECC_STUFF 0x80
+#define ECC_MASK 0xc0
+
+/*
+ * Flags for sja1000priv.flags
+ */
+
+#define SJA1000_CUSTOM_IRQ_HANDLER 0x1
+
+
+/*
+ * SJA1000 private data structure
+ */
+struct sja1000_priv {
+ struct can_priv can; /* must be the first member! */
+ long open_time;
+ struct sk_buff *echo_skb;
+
+ u8 (*read_reg) (struct net_device *dev, int reg);
+ void (*write_reg) (struct net_device *dev, int reg, u8 val);
+ void (*pre_irq) (struct net_device *dev);
+ void (*post_irq) (struct net_device *dev);
+
+ void *priv; /* for board-specific data */
+ struct net_device *dev;
+
+ u8 ocr;
+ u8 cdr;
+ u32 flags;
+};
+
+struct net_device *alloc_sja1000dev(int sizeof_priv);
+void free_sja1000dev(struct net_device *dev);
+int register_sja1000dev(struct net_device *dev);
+void unregister_sja1000dev(struct net_device *dev);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+irqreturn_t sja1000_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+#else
+irqreturn_t sja1000_interrupt(int irq, void *dev_id);
+#endif
+
+#if 0
+void can_proc_create(const char *drv_name);
+void can_proc_remove(const char *drv_name);
+#endif
+
+#endif /* SJA1000_DEV_H */
--- /dev/null
+/*
+ * Copyright (C) 2005 Sascha Hauer, Pengutronix
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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/platform_device.h>
+#include <linux/irq.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/platform/sja1000.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "sja1000_platform"
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus");
+MODULE_LICENSE("GPL v2");
+
+static u8 sp_read_reg(struct net_device *dev, int reg)
+{
+ return ioread8((void __iomem *)(dev->base_addr + reg));
+}
+
+static void sp_write_reg(struct net_device *dev, int reg, u8 val)
+{
+ iowrite8(val, (void __iomem *)(dev->base_addr + reg));
+}
+
+static int sp_probe(struct platform_device *pdev)
+{
+ int err, irq;
+ void __iomem *addr;
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct resource *res_mem, *res_irq;
+ struct sja1000_platform_data *pdata;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data provided!\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res_mem || !res_irq) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ if (!request_mem_region(res_mem->start,
+ res_mem->end - res_mem->start + 1,
+ DRV_NAME)) {
+ err = -EBUSY;
+ goto exit;
+ }
+
+ addr = ioremap_nocache(res_mem->start, res_mem->end - res_mem->start + 1);
+ if (!addr) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+
+ irq = res_irq->start;
+ if (res_irq->flags & IRQF_TRIGGER_MASK)
+ set_irq_type(irq, res_irq->flags & IRQF_TRIGGER_MASK);
+
+ dev = alloc_sja1000dev(0);
+ if (!dev) {
+ err = -ENOMEM;
+ goto exit_iounmap;
+ }
+ priv = netdev_priv(dev);
+
+ priv->read_reg = sp_read_reg;
+ priv->write_reg = sp_write_reg;
+ priv->can.bittiming.clock = pdata->clock;
+ priv->ocr = pdata->ocr;
+ priv->cdr = pdata->cdr;
+
+ dev->irq = irq;
+ dev->base_addr = (unsigned long)addr;
+
+ dev_set_drvdata(&pdev->dev, dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
+ DRV_NAME, err);
+ goto exit_free;
+ }
+
+ dev_info(&pdev->dev, "%s device registered (base_addr=%#lx, irq=%d)\n",
+ DRV_NAME, dev->base_addr, dev->irq);
+ return 0;
+
+ exit_free:
+ free_sja1000dev(dev);
+ exit_iounmap:
+ iounmap(addr);
+ exit_release:
+ release_mem_region(res_mem->start, res_mem->end - res_mem->start + 1);
+ exit:
+ return err;
+}
+
+static int sp_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = dev_get_drvdata(&pdev->dev);
+ struct resource *res;
+
+ unregister_sja1000dev(dev);
+ dev_set_drvdata(&pdev->dev, NULL);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ if (dev->base_addr)
+ iounmap((void __iomem *)dev->base_addr);
+
+ free_sja1000dev(dev);
+
+ return 0;
+}
+
+static struct platform_driver sp_driver = {
+ .probe = sp_probe,
+ .remove = sp_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sp_init(void)
+{
+ return platform_driver_register(&sp_driver);
+}
+
+static void __exit sp_exit(void)
+{
+ platform_driver_unregister(&sp_driver);
+}
+
+module_init(sp_init);
+module_exit(sp_exit);
--- /dev/null
+/*
+ * slcan.c - serial line CAN interface driver (using tty line discipline)
+ *
+ * Copyright (c) 2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+/*
+ * This file is derived from linux/drivers/net/slip.c
+ *
+ * Therefore it has the same (strange?) behaviour not to unregister the
+ * netdevice when detaching the tty. Is there any better solution?
+ *
+ * Do not try to attach, detach and re-attach a tty for this reason ...
+ *
+ * slip.c Authors: Laurence Culhane, <loz@holmes.demon.co.uk>
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <asm/system.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)
+#include <linux/uaccess.h>
+#else
+#include <asm/uaccess.h>
+#endif
+#include <linux/bitops.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/if_slip.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <linux/can.h>
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
+
+static __initdata const char banner[] =
+ KERN_INFO "slcan: serial line CAN interface driver\n";
+
+MODULE_ALIAS_LDISC(N_SLCAN);
+MODULE_DESCRIPTION("serial line CAN interface");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static int debug;
+module_param(debug, int, S_IRUGO);
+#define DBG(args...) (debug & 1 ? \
+ (printk(KERN_DEBUG "slcan %s: ", __func__), \
+ printk(args)) : 0)
+#else
+#define DBG(args...)
+#endif
+
+#ifndef N_SLCAN
+#error Your kernel does not support tty line discipline N_SLCAN
+#endif
+/*
+ * As there is currently no line discipline N_SLCAN in the mainstream kernel
+ * you will have to modify two kernel includes & recompile the kernel.
+ *
+ * Add this in include/asm/termios.h after the definition of N_HCI:
+ * #define N_SLCAN 16
+ *
+ * Increment NR_LDICS in include/linux/tty.h from 16 to 17
+ *
+ * NEW: Since Kernel 2.6.21 you only have to change include/linux/tty.h
+ *
+ */
+
+#define SLC_CHECK_TRANSMIT
+#define SLCAN_MAGIC 0x53CA
+
+static int maxdev = 10; /* MAX number of SLCAN channels;
+ This can be overridden with
+ insmod slcan.ko maxdev=nnn */
+module_param(maxdev, int, 0);
+MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces");
+
+/* maximum rx buffer len: extended CAN frame with timestamp */
+#define SLC_MTU (sizeof("T1111222281122334455667788EA5F\r")+1)
+
+struct slcan {
+ int magic;
+
+ /* Various fields. */
+ struct tty_struct *tty; /* ptr to TTY structure */
+ struct net_device *dev; /* easy for intr handling */
+ spinlock_t lock;
+
+ /* These are pointers to the malloc()ed frame buffers. */
+ unsigned char rbuff[SLC_MTU]; /* receiver buffer */
+ int rcount; /* received chars counter */
+ unsigned char xbuff[SLC_MTU]; /* transmitter buffer */
+ unsigned char *xhead; /* pointer to next XMIT byte */
+ int xleft; /* bytes left in XMIT queue */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ /* SLCAN interface statistics. */
+ struct net_device_stats stats;
+#endif
+
+ unsigned long flags; /* Flag values/ mode etc */
+#define SLF_INUSE 0 /* Channel in use */
+#define SLF_ERROR 1 /* Parity, etc. error */
+
+ unsigned char leased;
+ dev_t line;
+ pid_t pid;
+};
+
+static struct net_device **slcan_devs;
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+/* Netdevice get statistics request */
+static struct net_device_stats *slc_get_stats(struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ return &sl->stats;
+}
+#endif
+
+ /************************************************************************
+ * SLCAN ENCAPSULATION FORMAT *
+ ************************************************************************/
+
+/*
+ * A CAN frame has a can_id (11 bit standard frame format OR 29 bit extended
+ * frame format) a data length code (can_dlc) which can be from 0 to 8
+ * and up to <can_dlc> data bytes as payload.
+ * Additionally a CAN frame may become a remote transmission frame if the
+ * RTR-bit is set. This causes another ECU to send a CAN frame with the
+ * given can_id.
+ *
+ * The SLCAN ASCII representation of these different frame types is:
+ * <type> <id> <dlc> <data>*
+ *
+ * Extended frames (29 bit) are defined by capital characters in the type.
+ * RTR frames are defined as 'r' types - normal frames have 't' type:
+ * t => 11 bit data frame
+ * r => 11 bit RTR frame
+ * T => 29 bit data frame
+ * R => 29 bit RTR frame
+ *
+ * The <id> is 3 (standard) or 8 (extended) bytes in ASCII Hex (base64).
+ * The <dlc> is a one byte ASCII number ('0' - '8')
+ * The <data> section has at much ASCII Hex bytes as defined by the <dlc>
+ *
+ * Examples:
+ *
+ * t1230 : can_id 0x123, can_dlc 0, no data
+ * t4563112233 : can_id 0x456, can_dlc 3, data 0x11 0x22 0x33
+ * T12ABCDEF2AA55 : extended can_id 0x12ABCDEF, can_dlc 2, data 0xAA 0x55
+ * r1230 : can_id 0x123, can_dlc 0, no data, remote transmission request
+ *
+ */
+
+ /************************************************************************
+ * STANDARD SLCAN DECAPSULATION *
+ ************************************************************************/
+
+static int asc2nibble(char c)
+{
+
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+
+ if ((c >= 'A') && (c <= 'F'))
+ return c - 'A' + 10;
+
+ if ((c >= 'a') && (c <= 'f'))
+ return c - 'a' + 10;
+
+ return 16; /* error */
+}
+
+/* Send one completely decapsulated can_frame to the network layer */
+static void slc_bump(struct slcan *sl)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = slc_get_stats(sl->dev);
+#else
+ struct net_device_stats *stats = &sl->dev->stats;
+#endif
+ struct sk_buff *skb;
+ struct can_frame cf;
+ int i, dlc_pos, tmp;
+ char cmd = sl->rbuff[0];
+
+ if ((cmd != 't') && (cmd != 'T') && (cmd != 'r') && (cmd != 'R'))
+ return;
+
+ if (cmd & 0x20) /* tiny chars 'r' 't' => standard frame format */
+ dlc_pos = 4; /* dlc position tiiid */
+ else
+ dlc_pos = 9; /* dlc position Tiiiiiiiid */
+
+ if (!((sl->rbuff[dlc_pos] >= '0') && (sl->rbuff[dlc_pos] < '9')))
+ return;
+
+ cf.can_dlc = sl->rbuff[dlc_pos] & 0x0F; /* get can_dlc */
+
+ sl->rbuff[dlc_pos] = 0; /* terminate can_id string */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ cf.can_id = simple_strtoul(sl->rbuff+1, NULL, 16);
+#else
+ if (strict_strtoul(sl->rbuff+1, 16, (unsigned long *) &cf.can_id))
+ return;
+#endif
+
+ if (!(cmd & 0x20)) /* NO tiny chars => extended frame format */
+ cf.can_id |= CAN_EFF_FLAG;
+
+ if ((cmd | 0x20) == 'r') /* RTR frame */
+ cf.can_id |= CAN_RTR_FLAG;
+
+ *(u64 *) (&cf.data) = 0; /* clear payload */
+
+ for (i = 0, dlc_pos++; i < cf.can_dlc; i++) {
+
+ tmp = asc2nibble(sl->rbuff[dlc_pos++]);
+ if (tmp > 0x0F)
+ return;
+ cf.data[i] = (tmp << 4);
+ tmp = asc2nibble(sl->rbuff[dlc_pos++]);
+ if (tmp > 0x0F)
+ return;
+ cf.data[i] |= tmp;
+ }
+
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb)
+ return;
+
+ skb->dev = sl->dev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(struct can_frame)),
+ &cf, sizeof(struct can_frame));
+ netif_rx(skb);
+
+ sl->dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf.can_dlc;
+}
+
+/* parse tty input stream */
+static void slcan_unesc(struct slcan *sl, unsigned char s)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = slc_get_stats(sl->dev);
+#else
+ struct net_device_stats *stats = &sl->dev->stats;
+#endif
+
+ if ((s == '\r') || (s == '\a')) { /* CR or BEL ends the pdu */
+ if (!test_and_clear_bit(SLF_ERROR, &sl->flags) &&
+ (sl->rcount > 4)) {
+ slc_bump(sl);
+ }
+ sl->rcount = 0;
+ } else {
+ if (!test_bit(SLF_ERROR, &sl->flags)) {
+ if (sl->rcount < SLC_MTU) {
+ sl->rbuff[sl->rcount++] = s;
+ return;
+ } else {
+ stats->rx_over_errors++;
+ set_bit(SLF_ERROR, &sl->flags);
+ }
+ }
+ }
+}
+
+ /************************************************************************
+ * STANDARD SLCAN ENCAPSULATION *
+ ************************************************************************/
+
+/* Encapsulate one can_frame and stuff into a TTY queue. */
+static void slc_encaps(struct slcan *sl, struct can_frame *cf)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = slc_get_stats(sl->dev);
+#else
+ struct net_device_stats *stats = &sl->dev->stats;
+#endif
+ int actual, idx, i;
+ char cmd;
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ cmd = 'R'; /* becomes 'r' in standard frame format */
+ else
+ cmd = 'T'; /* becomes 't' in standard frame format */
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ sprintf(sl->xbuff, "%c%08X%d", cmd,
+ cf->can_id & CAN_EFF_MASK, cf->can_dlc);
+ else
+ sprintf(sl->xbuff, "%c%03X%d", cmd | 0x20,
+ cf->can_id & CAN_SFF_MASK, cf->can_dlc);
+
+ idx = strlen(sl->xbuff);
+
+ for (i = 0; i < cf->can_dlc; i++)
+ sprintf(&sl->xbuff[idx + 2*i], "%02X", cf->data[i]);
+
+ DBG("ASCII frame = '%s'\n", sl->xbuff);
+
+ strcat(sl->xbuff, "\r"); /* add terminating character */
+
+ /* Order of next two lines is *very* important.
+ * When we are sending a little amount of data,
+ * the transfer may be completed inside driver.write()
+ * routine, because it's running with interrupts enabled.
+ * In this case we *never* got WRITE_WAKEUP event,
+ * if we did not request it before write operation.
+ * 14 Oct 1994 Dmitry Gorodchanin.
+ */
+ sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+ actual = sl->tty->driver->write(sl->tty, sl->xbuff, strlen(sl->xbuff));
+#else
+ actual = sl->tty->ops->write(sl->tty, sl->xbuff, strlen(sl->xbuff));
+#endif
+#ifdef SLC_CHECK_TRANSMIT
+ sl->dev->trans_start = jiffies;
+#endif
+ sl->xleft = strlen(sl->xbuff) - actual;
+ sl->xhead = sl->xbuff + actual;
+ stats->tx_bytes += cf->can_dlc;
+}
+
+/*
+ * Called by the driver when there's room for more data. If we have
+ * more packets to send, we send them here.
+ */
+static void slcan_write_wakeup(struct tty_struct *tty)
+{
+ int actual;
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = slc_get_stats(sl->dev);
+#else
+ struct net_device_stats *stats = &sl->dev->stats;
+#endif
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev))
+ return;
+
+ if (sl->xleft <= 0) {
+ /* Now serial buffer is almost free & we can start
+ * transmission of another packet */
+ stats->tx_packets++;
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ netif_wake_queue(sl->dev);
+ return;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+ actual = tty->driver->write(tty, sl->xhead, sl->xleft);
+#else
+ actual = tty->ops->write(tty, sl->xhead, sl->xleft);
+#endif
+ sl->xleft -= actual;
+ sl->xhead += actual;
+}
+
+static void slc_tx_timeout(struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ spin_lock(&sl->lock);
+
+ if (netif_queue_stopped(dev)) {
+ if (!netif_running(dev))
+ goto out;
+
+ /* May be we must check transmitter timeout here ?
+ * 14 Oct 1994 Dmitry Gorodchanin.
+ */
+#ifdef SLC_CHECK_TRANSMIT
+ if (time_before(jiffies, dev->trans_start + 20 * HZ)) {
+ /* 20 sec timeout not reached */
+ goto out;
+ }
+ printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+ (sl->tty->driver->chars_in_buffer(sl->tty) || sl->xleft)
+#else
+ (tty_chars_in_buffer(sl->tty) || sl->xleft)
+#endif
+ ? "bad line quality" : "driver error");
+ sl->xleft = 0;
+ sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ netif_wake_queue(sl->dev);
+#endif
+ }
+out:
+ spin_unlock(&sl->lock);
+}
+
+
+/******************************************
+ * Routines looking at netdevice side.
+ ******************************************/
+
+/* Send a can_frame to a TTY queue. */
+static int slc_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ if (skb->len != sizeof(struct can_frame))
+ goto out;
+
+ spin_lock(&sl->lock);
+ if (!netif_running(dev)) {
+ spin_unlock(&sl->lock);
+ printk(KERN_WARNING "%s: xmit: iface is down\n", dev->name);
+ goto out;
+ }
+
+ if (sl->tty == NULL) {
+ spin_unlock(&sl->lock);
+ goto out;
+ }
+
+ netif_stop_queue(sl->dev);
+ slc_encaps(sl, (struct can_frame *) skb->data); /* encaps & send */
+ spin_unlock(&sl->lock);
+
+out:
+ kfree_skb(skb);
+ return 0;
+}
+
+
+/* Netdevice UP -> DOWN routine */
+static int slc_close(struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ spin_lock_bh(&sl->lock);
+ if (sl->tty) {
+ /* TTY discipline is running. */
+ sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ }
+ netif_stop_queue(dev);
+ sl->rcount = 0;
+ sl->xleft = 0;
+ spin_unlock_bh(&sl->lock);
+
+ return 0;
+}
+
+/* Netdevice DOWN -> UP routine */
+static int slc_open(struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ if (sl->tty == NULL)
+ return -ENODEV;
+
+ sl->flags &= (1 << SLF_INUSE);
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Netdevice register callback */
+static void slc_setup(struct net_device *dev)
+{
+ dev->open = slc_open;
+ dev->destructor = free_netdev;
+ dev->stop = slc_close;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ dev->get_stats = slc_get_stats;
+#endif
+ dev->hard_start_xmit = slc_xmit;
+
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->mtu = sizeof(struct can_frame);
+ dev->type = ARPHRD_CAN;
+#ifdef SLC_CHECK_TRANSMIT
+ dev->tx_timeout = slc_tx_timeout;
+ dev->watchdog_timeo = 20*HZ;
+#endif
+
+ /* New-style flags. */
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_NO_CSUM;
+}
+
+/******************************************
+ * Routines looking at TTY side.
+ ******************************************/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+static int slcan_receive_room(struct tty_struct *tty)
+{
+ return 65536; /* We can handle an infinite amount of data. :-) */
+}
+#endif
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of SLCAN data has been received, which can now be decapsulated
+ * and sent on to some IP layer for further processing. This will not
+ * be re-entered while running but other ldisc functions may be called
+ * in parallel
+ */
+
+static void slcan_receive_buf(struct tty_struct *tty,
+ const unsigned char *cp, char *fp, int count)
+{
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats *stats = slc_get_stats(sl->dev);
+#else
+ struct net_device_stats *stats = &sl->dev->stats;
+#endif
+
+ if (!sl || sl->magic != SLCAN_MAGIC ||
+ !netif_running(sl->dev))
+ return;
+
+ /* Read the characters out of the buffer */
+ while (count--) {
+ if (fp && *fp++) {
+ if (!test_and_set_bit(SLF_ERROR, &sl->flags))
+ stats->rx_errors++;
+ cp++;
+ continue;
+ }
+ slcan_unesc(sl, *cp++);
+ }
+}
+
+/************************************
+ * slcan_open helper routines.
+ ************************************/
+
+/* Collect hanged up channels */
+
+static void slc_sync(void)
+{
+ int i;
+ struct net_device *dev;
+ struct slcan *sl;
+
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (dev == NULL)
+ break;
+
+ sl = netdev_priv(dev);
+ if (sl->tty || sl->leased)
+ continue;
+ if (dev->flags&IFF_UP)
+ dev_close(dev);
+ }
+}
+
+
+/* Find a free SLCAN channel, and link in this `tty' line. */
+static struct slcan *slc_alloc(dev_t line)
+{
+ int i;
+ int sel = -1;
+ int score = -1;
+ struct net_device *dev = NULL;
+ struct slcan *sl;
+
+ if (slcan_devs == NULL)
+ return NULL; /* Master array missing ! */
+
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (dev == NULL)
+ break;
+
+ sl = netdev_priv(dev);
+ if (sl->leased) {
+ if (sl->line != line)
+ continue;
+ if (sl->tty)
+ return NULL;
+
+ /* Clear ESCAPE & ERROR flags */
+ sl->flags &= (1 << SLF_INUSE);
+ return sl;
+ }
+
+ if (sl->tty)
+ continue;
+
+ if (current->pid == sl->pid) {
+ if (sl->line == line && score < 3) {
+ sel = i;
+ score = 3;
+ continue;
+ }
+ if (score < 2) {
+ sel = i;
+ score = 2;
+ }
+ continue;
+ }
+ if (sl->line == line && score < 1) {
+ sel = i;
+ score = 1;
+ continue;
+ }
+ if (score < 0) {
+ sel = i;
+ score = 0;
+ }
+ }
+
+ if (sel >= 0) {
+ i = sel;
+ dev = slcan_devs[i];
+ if (score > 1) {
+ sl = netdev_priv(dev);
+ sl->flags &= (1 << SLF_INUSE);
+ return sl;
+ }
+ }
+
+ /* Sorry, too many, all slots in use */
+ if (i >= maxdev)
+ return NULL;
+
+ if (dev) {
+ sl = netdev_priv(dev);
+ if (test_bit(SLF_INUSE, &sl->flags)) {
+ unregister_netdevice(dev);
+ free_netdev(dev); /* new in slcan.c */
+ dev = NULL;
+ slcan_devs[i] = NULL;
+ }
+ }
+
+ if (!dev) {
+ char name[IFNAMSIZ];
+ sprintf(name, "slc%d", i);
+
+ dev = alloc_netdev(sizeof(*sl), name, slc_setup);
+ if (!dev)
+ return NULL;
+ dev->base_addr = i;
+ }
+
+ sl = netdev_priv(dev);
+
+ /* Initialize channel control data */
+ sl->magic = SLCAN_MAGIC;
+ sl->dev = dev;
+ spin_lock_init(&sl->lock);
+ slcan_devs[i] = dev;
+
+ return sl;
+}
+
+/*
+ * Open the high-level part of the SLCAN channel.
+ * This function is called by the TTY module when the
+ * SLCAN line discipline is called for. Because we are
+ * sure the tty line exists, we only have to link it to
+ * a free SLCAN channel...
+ *
+ * Called in process context serialized from other ldisc calls.
+ */
+
+static int slcan_open(struct tty_struct *tty)
+{
+ struct slcan *sl;
+ int err;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)
+ if (tty->ops->write == NULL)
+ return -EOPNOTSUPP;
+#endif
+
+ /* RTnetlink lock is misused here to serialize concurrent
+ opens of slcan channels. There are better ways, but it is
+ the simplest one.
+ */
+ rtnl_lock();
+
+ /* Collect hanged up channels. */
+ slc_sync();
+
+ sl = (struct slcan *) tty->disc_data;
+
+ err = -EEXIST;
+ /* First make sure we're not already connected. */
+ if (sl && sl->magic == SLCAN_MAGIC)
+ goto err_exit;
+
+ /* OK. Find a free SLCAN channel to use. */
+ err = -ENFILE;
+ sl = slc_alloc(tty_devnum(tty));
+ if (sl == NULL)
+ goto err_exit;
+
+ sl->tty = tty;
+ tty->disc_data = sl;
+ sl->line = tty_devnum(tty);
+ sl->pid = current->pid;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+ /* FIXME: already done before we were called - seems this can go */
+ if (tty->driver->flush_buffer)
+ tty->driver->flush_buffer(tty);
+#endif
+
+ if (!test_bit(SLF_INUSE, &sl->flags)) {
+ /* Perform the low-level SLCAN initialization. */
+ sl->rcount = 0;
+ sl->xleft = 0;
+
+ set_bit(SLF_INUSE, &sl->flags);
+
+ err = register_netdevice(sl->dev);
+ if (err)
+ goto err_free_chan;
+ }
+
+ /* Done. We have linked the TTY line to a channel. */
+ rtnl_unlock();
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+ tty->receive_room = 65536; /* We don't flow control */
+#endif
+
+ return sl->dev->base_addr;
+
+err_free_chan:
+ sl->tty = NULL;
+ tty->disc_data = NULL;
+ clear_bit(SLF_INUSE, &sl->flags);
+
+err_exit:
+ rtnl_unlock();
+
+ /* Count references from TTY module */
+ return err;
+}
+
+/*
+
+ FIXME: 1,2 are fixed 3 was never true anyway.
+
+ Let me to blame a bit.
+ 1. TTY module calls this funstion on soft interrupt.
+ 2. TTY module calls this function WITH MASKED INTERRUPTS!
+ 3. TTY module does not notify us about line discipline
+ shutdown,
+
+ Seems, now it is clean. The solution is to consider netdevice and
+ line discipline sides as two independent threads.
+
+ By-product (not desired): slc? does not feel hangups and remains open.
+ It is supposed, that user level program (dip, diald, slattach...)
+ will catch SIGHUP and make the rest of work.
+
+ I see no way to make more with current tty code. --ANK
+ */
+
+/*
+ * Close down a SLCAN channel.
+ * This means flushing out any pending queues, and then returning. This
+ * call is serialized against other ldisc functions.
+ */
+static void slcan_close(struct tty_struct *tty)
+{
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SLCAN_MAGIC || sl->tty != tty)
+ return;
+
+ tty->disc_data = NULL;
+ sl->tty = NULL;
+ if (!sl->leased)
+ sl->line = 0;
+
+ /* Count references from TTY module */
+}
+
+/* Perform I/O control on an active SLCAN channel. */
+static int slcan_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+ unsigned int tmp;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SLCAN_MAGIC)
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCGIFNAME:
+ tmp = strlen(sl->dev->name) + 1;
+ if (copy_to_user((void __user *)arg, sl->dev->name, tmp))
+ return -EFAULT;
+ return 0;
+
+ case SIOCSIFHWADDR:
+ return -EINVAL;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+ /* Allow stty to read, but not set, the serial port */
+ case TCGETS:
+ case TCGETA:
+ return n_tty_ioctl(tty, file, cmd, arg);
+
+ default:
+ return -ENOIOCTLCMD;
+#else
+ default:
+ return tty_mode_ioctl(tty, file, cmd, arg);
+#endif
+ }
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+static struct tty_ldisc slc_ldisc = {
+#else
+static struct tty_ldisc_ops slc_ldisc = {
+#endif
+ .owner = THIS_MODULE,
+ .magic = TTY_LDISC_MAGIC,
+ .name = "slcan",
+ .open = slcan_open,
+ .close = slcan_close,
+ .ioctl = slcan_ioctl,
+ .receive_buf = slcan_receive_buf,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+ .receive_room = slcan_receive_room,
+#endif
+ .write_wakeup = slcan_write_wakeup,
+};
+
+/************************************
+ * general slcan module init/exit
+ ************************************/
+
+static int __init slcan_init(void)
+{
+ int status;
+
+ if (maxdev < 4)
+ maxdev = 4; /* Sanity */
+
+ printk(banner);
+ printk(KERN_INFO "slcan: %d dynamic interface channels.\n", maxdev);
+
+ slcan_devs = kmalloc(sizeof(struct net_device *)*maxdev, GFP_KERNEL);
+ if (!slcan_devs) {
+ printk(KERN_ERR "slcan: can't allocate slcan device array!\n");
+ return -ENOMEM;
+ }
+
+ /* Clear the pointer array, we allocate devices when we need them */
+ memset(slcan_devs, 0, sizeof(struct net_device *)*maxdev);
+
+ /* Fill in our line protocol discipline, and register it */
+ status = tty_register_ldisc(N_SLCAN, &slc_ldisc);
+ if (status != 0) {
+ printk(KERN_ERR "slcan: can't register line discipline\n");
+ kfree(slcan_devs);
+ }
+ return status;
+}
+
+static void __exit slcan_exit(void)
+{
+ int i;
+ struct net_device *dev;
+ struct slcan *sl;
+ unsigned long timeout = jiffies + HZ;
+ int busy = 0;
+
+ if (slcan_devs == NULL)
+ return;
+
+ /* First of all: check for active disciplines and hangup them.
+ */
+ do {
+ if (busy)
+ msleep_interruptible(100);
+
+ busy = 0;
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (!dev)
+ continue;
+ sl = netdev_priv(dev);
+ spin_lock_bh(&sl->lock);
+ if (sl->tty) {
+ busy++;
+ tty_hangup(sl->tty);
+ }
+ spin_unlock_bh(&sl->lock);
+ }
+ } while (busy && time_before(jiffies, timeout));
+
+
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (!dev)
+ continue;
+ slcan_devs[i] = NULL;
+
+ sl = netdev_priv(dev);
+ if (sl->tty) {
+ printk(KERN_ERR "%s: tty discipline still running\n",
+ dev->name);
+ /* Intentionally leak the control block. */
+ dev->destructor = NULL;
+ }
+
+ unregister_netdev(dev);
+ }
+
+ kfree(slcan_devs);
+ slcan_devs = NULL;
+
+ i = tty_unregister_ldisc(N_SLCAN);
+ if (i)
+ printk(KERN_ERR "slcan: can't unregister ldisc (err %d)\n", i);
+}
+
+module_init(slcan_init);
+module_exit(slcan_exit);
--- /dev/null
+# Makefile for softing CAN driver
+
+ifeq ($(KERNELRELEASE),)
+# necessary when used outside kernel
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+softing-y := softing_main.o softing_fw.o
+obj-$(CONFIG_CAN_SOFTING) += softing.o
+obj-$(CONFIG_CAN_SOFTING_CS) += softing_cs.o
+
+endif
--- /dev/null
+/*
+ * softing common interfaces
+ *
+ * by Kurt Van Dijck, 06-2008
+ */
+
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+
+struct softing;
+struct sofing_desc;
+
+/* special attribute, so we should not rely on the ->priv pointers
+ * before knowing how to interpret these
+ */
+struct softing_attribute;
+
+struct softing_priv {
+ struct can_priv can; /* must be the first member! */
+ struct net_device *netdev;
+ struct softing *card;
+ struct {
+ int pending;
+ /* variables wich hold the circular buffer */
+ int echo_put;
+ int echo_get;
+ } tx;
+ struct can_bittiming_const btr_const;
+ int index;
+ u8 output;
+ u16 chip;
+ struct attribute_group sysfs;
+};
+#define netdev2softing(netdev) ((struct softing_priv *)netdev_priv(netdev))
+
+/* the 'all cards have the same' fields definition */
+extern const struct can_bittiming_const softing_btr_const;
+
+struct softing_desc {
+ unsigned int manf;
+ unsigned int prod;
+ /* generation
+ * 1st with NEC or SJA1000
+ * 8bit, exclusive interrupt, ...
+ * 2nd only SJA11000
+ * 16bit, shared interrupt
+ */
+ int generation;
+ unsigned int freq; /*crystal in MHz */
+ unsigned int max_brp;
+ unsigned int max_sjw;
+ unsigned long dpram_size;
+ char name[32];
+ struct {
+ unsigned long offs;
+ unsigned long addr;
+ char fw[32];
+ } boot, load, app;
+};
+
+struct softing {
+ int nbus;
+ struct softing_priv *bus[2];
+ spinlock_t spin; /* protect this structure & DPRAM access */
+
+ struct {
+ /* indication of firmware status */
+ int up;
+ /* protection of the 'up' variable */
+ struct mutex lock;
+ } fw;
+ struct {
+ int nr;
+ int requested;
+ struct tasklet_struct bh;
+ int svc_count;
+ } irq;
+ struct {
+ int pending;
+ int last_bus;
+ /* keep the bus that last tx'd a message,
+ * in order to let every netdev queue resume
+ */
+ } tx;
+ struct {
+ unsigned long phys;
+ unsigned long size;
+ unsigned char *virt;
+ unsigned char *end;
+ struct softing_fct *fct;
+ struct softing_info *info;
+ struct softing_rx *rx;
+ struct softing_tx *tx;
+ struct softing_irq *irq;
+ unsigned short *command;
+ unsigned short *receipt;
+ } dpram;
+ struct {
+ unsigned short manf;
+ unsigned short prod;
+ u32 serial, fw, hw, lic;
+ u16 chip [2];
+ u32 freq;
+ const char *name;
+ } id;
+ const struct softing_desc *desc;
+ struct {
+ int (*reset) (struct softing *, int);
+ int (*enable_irq)(struct softing *, int);
+ } fn;
+ struct device *dev;
+ /* sysfs */
+ struct attribute_group sysfs;
+ struct softing_attribute *attr;
+ struct attribute **grp;
+};
+
+extern int mk_softing(struct softing *);
+/* fields that must be set already are :
+ * ncan
+ * id.manf
+ * id.prod
+ * fn.reset
+ * fn.enable_irq
+ */
+extern void rm_softing(struct softing *);
+/* usefull functions during operation */
+
+extern const struct softing_desc *
+ softing_lookup_desc(unsigned int manf, unsigned int prod);
+
+extern int softing_default_output(struct softing *card
+ , struct softing_priv *priv);
+extern u32 softing_time2usec(struct softing *card, u32 raw);
+
+extern int softing_fct_cmd(struct softing *card
+ , int cmd, int vector, const char *msg);
+
+extern int softing_bootloader_command(struct softing *card
+ , int command, const char *msg);
+
+/* Load firmware after reset */
+extern int softing_load_fw(const char *file, struct softing *card,
+ unsigned char *virt, unsigned int size, int offset);
+
+/* Load final application firmware after bootloader */
+extern int softing_load_app_fw(const char *file, struct softing *card);
+
+extern int softing_reset_chip(struct softing *card);
+
+/* enable or disable irq
+ * only called with fw.lock locked
+ */
+extern int softing_card_irq(struct softing *card, int enable);
+
+/* called when tx queue is flushed */
+extern void softing_flush_echo_skb(struct softing_priv *priv);
+
+/* reinitaliase the card, apply -1 for bus[01] for 'no change' */
+extern int softing_reinit(struct softing *card, int bus0, int bus1);
+
+/* SOFTING DPRAM mappings */
+struct softing_rx {
+ u8 fifo[16][32];
+ u8 dummy1;
+ u16 rd;
+ u16 dummy2;
+ u16 wr;
+ u16 dummy3;
+ u16 lost_msg;
+} __attribute__((packed));
+
+#define TXMAX 31
+struct softing_tx {
+ u8 fifo[32][16];
+ u8 dummy1;
+ u16 rd;
+ u16 dummy2;
+ u16 wr;
+ u8 dummy3;
+} __attribute__((packed));
+
+struct softing_irq {
+ u8 to_host;
+ u8 to_card;
+} __attribute__((packed));
+
+struct softing_fct {
+ s16 param[20]; /* 0 is index */
+ s16 returned;
+ u8 dummy;
+ u16 host_access;
+} __attribute__((packed));
+
+struct softing_info {
+ u8 dummy1;
+ u16 bus_state;
+ u16 dummy2;
+ u16 bus_state2;
+ u16 dummy3;
+ u16 error_state;
+ u16 dummy4;
+ u16 error_state2;
+ u16 dummy5;
+ u16 reset;
+ u16 dummy6;
+ u16 clear_rcv_fifo;
+ u16 dummy7;
+ u16 dummyxx;
+ u16 dummy8;
+ u16 time_reset;
+ u8 dummy9;
+ u32 time;
+ u32 time_wrap;
+ u8 wr_start;
+ u8 wr_end;
+ u8 dummy10;
+ u16 dummy12;
+ u16 dummy12x;
+ u16 dummy13;
+ u16 reset_rcv_fifo;
+ u8 dummy14;
+ u8 reset_xmt_fifo;
+ u8 read_fifo_levels;
+ u16 rcv_fifo_level;
+ u16 xmt_fifo_level;
+} __attribute__((packed));
+
+/* DPRAM return codes */
+#define RES_NONE 0
+#define RES_OK 1
+#define RES_NOK 2
+#define RES_UNKNOWN 3
+/* DPRAM flags */
+#define CMD_TX 0x01
+#define CMD_ACK 0x02
+#define CMD_XTD 0x04
+#define CMD_RTR 0x08
+#define CMD_ERR 0x10
+#define CMD_BUS2 0x80
+
+/* debug */
+extern int softing_debug;
+
+#define mod_alert(fmt,arg...) { \
+ if (softing_debug >= 0) \
+ printk(KERN_ALERT "[%s] %s:" fmt "\n" \
+ , THIS_MODULE->name \
+ , __func__ \
+ , ##arg); \
+ }
+#define mod_info(fmt,arg...) { \
+ if (softing_debug >= 1) \
+ printk(KERN_INFO "[%s] %s:" fmt "\n"\
+ , THIS_MODULE->name \
+ , __func__ \
+ , ##arg); \
+ }
+#define mod_trace(fmt,arg...) { \
+ if (softing_debug >= 2) \
+ printk(KERN_DEBUG "[%s] %s:" fmt "\n" \
+ , THIS_MODULE->name \
+ , __func__ \
+ , ##arg); \
+ }
+
--- /dev/null
+/*
+* drivers/net/can/softing/softing_cs.c
+*
+* Copyright (C) 2008
+*
+* - Kurt Van Dijck, EIA Electronics
+*
+* 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, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/major.h>
+#include <linux/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <asm/system.h>
+
+#include "softing.h"
+
+struct softing_cs {
+ struct softing softing;
+ win_req_t win;
+};
+#define softing2cs(x) container_of((x), struct softing_cs, softing)
+
+struct lookup {
+ int i;
+ const char *a;
+};
+
+static const char __devinit *lookup_mask(const struct lookup *lp, int *i)
+{
+ for (; lp->a; ++lp) {
+ if (lp->i & *i) {
+ *i &= ~lp->i;
+ return lp->a;
+ }
+ }
+ return 0;
+}
+
+static int card_reset_via_pcmcia(struct softing *sdev, int v)
+{
+ struct pcmcia_device *pcmcia = to_pcmcia_dev(sdev->dev);
+ conf_reg_t reg;
+ reg.Function = 0; /* socket */
+ reg.Action = CS_WRITE;
+ reg.Offset = 2;
+ reg.Value = v ? 0 : 0x20;
+ return pcmcia_access_configuration_register(pcmcia, ®);
+}
+
+static int card_reset_via_dpram(struct softing *sdev, int v)
+{
+ if (v) {
+ spin_lock_bh(&sdev->spin);
+ sdev->dpram.virt[0xe00] &= ~1;
+ spin_unlock_bh(&sdev->spin);
+ card_reset_via_pcmcia(sdev, v);
+ } else {
+ card_reset_via_pcmcia(sdev, v);
+ spin_lock_bh(&sdev->spin);
+ sdev->dpram.virt[0xe00] |= 1;
+ spin_unlock_bh(&sdev->spin);
+ }
+ return 0;
+}
+
+static int card_enable_irq_via_pcmcia(struct softing *sdev, int v)
+{
+ int ret;
+ struct pcmcia_device *pcmcia = to_pcmcia_dev(sdev->dev);
+ conf_reg_t reg;
+ memset(®, 0, sizeof(reg));
+ reg.Function = 0; /* socket */
+ reg.Action = CS_WRITE;
+ reg.Offset = 0;
+ reg.Value = v ? 0x60 : 0;
+ ret = pcmcia_access_configuration_register(pcmcia, ®);
+ if (ret)
+ mod_alert("failed %u", ret);
+ return ret;
+}
+
+/* TODO: in 2.6.26, __devinitconst works*/
+static const __devinitdata struct lookup pcmcia_io_attr[] = {
+ { IO_DATA_PATH_WIDTH_AUTO , "[auto]" , },
+ { IO_DATA_PATH_WIDTH_8 , "8bit" , },
+ { IO_DATA_PATH_WIDTH_16 , "16bit" , },
+ { 0, 0, },
+};
+
+static const __devinitdata struct lookup pcmcia_mem_attr[] = {
+ { WIN_ADDR_SPACE_IO , "IO" , },
+ { WIN_MEMORY_TYPE_AM , "typeAM" , },
+ { WIN_ENABLE , "enable" , },
+ { WIN_DATA_WIDTH_8 , "8bit" , },
+ { WIN_DATA_WIDTH_16 , "16bit" , },
+ { WIN_DATA_WIDTH_32 , "32bit" , },
+ { WIN_PAGED , "paged" , },
+ { WIN_SHARED , "shared" , },
+ { WIN_FIRST_SHARED , "first_shared", },
+ { WIN_USE_WAIT , "wait" , },
+ { WIN_STRICT_ALIGN , "strict_align", },
+ { WIN_MAP_BELOW_1MB , "below_1MB" , },
+ { WIN_PREFETCH , "prefetch" , },
+ { WIN_CACHEABLE , "cacheable" , },
+ { 0, 0, },
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
+/* backported */
+struct pcmcia_cfg_mem {
+ tuple_t tuple;
+ cisparse_t parse;
+ u8 buf[256];
+ cistpl_cftable_entry_t dflt;
+};
+static int pcmcia_loop_config(struct pcmcia_device *p_dev,
+ int (*conf_check) (struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data),
+ void *priv_data)
+{
+ struct pcmcia_cfg_mem *cfg_mem;
+
+ tuple_t *tuple;
+ int ret = -ENODEV;
+ unsigned int vcc;
+
+ cfg_mem = kzalloc(sizeof(*cfg_mem), GFP_KERNEL);
+ if (cfg_mem == NULL)
+ return -ENOMEM;
+
+ /* get the current Vcc setting */
+ vcc = p_dev->socket->socket.Vcc;
+
+ tuple = &cfg_mem->tuple;
+ tuple->TupleData = cfg_mem->buf;
+ tuple->TupleDataMax = sizeof(cfg_mem->buf)-1;
+ tuple->TupleOffset = 0;
+ tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ tuple->Attributes = 0;
+
+ ret = pcmcia_get_first_tuple(p_dev, tuple);
+ while (!ret) {
+ cistpl_cftable_entry_t *cfg = &cfg_mem->parse.cftable_entry;
+
+ if (pcmcia_get_tuple_data(p_dev, tuple))
+ goto next_entry;
+
+ if (pcmcia_parse_tuple(p_dev, tuple, &cfg_mem->parse))
+ goto next_entry;
+
+ /* default values */
+ p_dev->conf.ConfigIndex = cfg->index;
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ cfg_mem->dflt = *cfg;
+
+ ret = conf_check(p_dev, cfg, &cfg_mem->dflt, vcc, priv_data);
+ if (!ret)
+ break;
+
+next_entry:
+ ret = pcmcia_get_next_tuple(p_dev, tuple);
+ }
+ kfree(cfg_mem);
+ return ret;
+}
+#endif
+
+static int dev_conf_check(struct pcmcia_device *pdev,
+ cistpl_cftable_entry_t *cf, cistpl_cftable_entry_t *def_cf,
+ unsigned int vcc, void *priv_data)
+{
+ struct softing_cs *csdev = priv_data;
+ struct softing *sdev = &csdev->softing;
+ int ret;
+
+ if (!cf->index)
+ goto do_next;
+ /* power settings (Vcc & Vpp) */
+ if (cf->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != cf->vcc.param[CISTPL_POWER_VNOM]/10000) {
+ mod_alert("%s: cf->Vcc mismatch\n", __FILE__);
+ goto do_next;
+ }
+ } else if (def_cf->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != def_cf->vcc.param[CISTPL_POWER_VNOM]/10000) {
+ mod_alert("%s: cf->Vcc mismatch\n", __FILE__);
+ goto do_next;
+ }
+ }
+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ pdev->conf.Vpp
+ = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ else if (def_cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ pdev->conf.Vpp
+ = def_cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ /* interrupt ? */
+ if (cf->irq.IRQInfo1 || def_cf->irq.IRQInfo1)
+ pdev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window */
+ pdev->io.NumPorts1
+ = pdev->io.NumPorts2
+ = 0;
+ /* Memory window */
+ if ((cf->mem.nwin > 0) || (def_cf->mem.nwin > 0)) {
+ memreq_t map;
+ cistpl_mem_t *mem
+ = (cf->mem.nwin) ? &cf->mem : &def_cf->mem;
+ /* softing specific: choose 8 or 16bit access */
+ csdev->win.Attributes = ((sdev->desc->generation >= 2)
+ ? WIN_DATA_WIDTH_16 : WIN_DATA_WIDTH_8)
+ | WIN_MEMORY_TYPE_CM
+ | WIN_ENABLE;
+ csdev->win.Base = mem->win[0].host_addr;
+ csdev->win.Size = mem->win[0].len;
+ csdev->win.AccessSpeed = 0;
+ ret = pcmcia_request_window(&pdev, &csdev->win, &pdev->win);
+ if (ret) {
+ mod_alert("pcmcia_request_window() mismatch\n");
+ goto do_next;
+ }
+ /* softing specific: choose slower access for old cards */
+ if (sdev->desc->generation < 2) {
+ pdev->win->ctl.flags
+ = MAP_ACTIVE | MAP_USE_WAIT;
+ pdev->win->ctl.speed = 3;
+ }
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(pdev->win, &map)) {
+ mod_alert("pcmcia_map_mem_page() mismatch\n");
+ goto do_next_win;
+ }
+ } else {
+ mod_info("no memory window in tuple %u", cf->index);
+ goto do_next;
+ }
+ return 0;
+do_next_win:
+do_next:
+ pcmcia_disable_device(pdev);
+ return -ENODEV;
+}
+
+static void driver_remove(struct pcmcia_device *pcmcia)
+{
+ struct softing *card = (struct softing *)pcmcia->priv;
+ struct softing_cs *cs = softing2cs(card);
+ mod_trace("%s,device'%s'", card->id.name, pcmcia->devname);
+ rm_softing(card);
+ /* release pcmcia stuff */
+ pcmcia_disable_device(pcmcia);
+ /* free bits */
+ kfree(cs);
+}
+
+static int __devinit driver_probe(struct pcmcia_device *pcmcia)
+{
+ struct softing_cs *cs;
+ struct softing *card;
+
+ mod_trace("on %s", pcmcia->devname);
+
+ /* Create new softing device */
+ cs = kzalloc(sizeof(*cs), GFP_KERNEL);
+ if (!cs)
+ goto no_mem;
+ /* setup links */
+ card = &cs->softing;
+ pcmcia->priv = card;
+ card->dev = &pcmcia->dev;
+ /* properties */
+ card->id.manf = pcmcia->manf_id;
+ card->id.prod = pcmcia->card_id;
+ card->desc = softing_lookup_desc(card->id.manf, card->id.prod);
+ if (card->desc->generation >= 2) {
+ card->fn.reset = card_reset_via_dpram;
+ } else {
+ card->fn.reset = card_reset_via_pcmcia;
+ card->fn.enable_irq = card_enable_irq_via_pcmcia;
+ }
+
+ card->nbus = 2;
+ /* pcmcia presets */
+ pcmcia->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+ pcmcia->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ pcmcia->irq.Handler = 0;
+ pcmcia->conf.Attributes = 0;
+ pcmcia->conf.IntType = INT_MEMORY_AND_IO;
+
+ if (pcmcia_loop_config(pcmcia, dev_conf_check, cs))
+ goto config_failed;
+
+ if (pcmcia_request_irq(pcmcia, &pcmcia->irq))
+ goto config_failed;
+
+ if (pcmcia_request_configuration(pcmcia, &pcmcia->conf))
+ goto config_failed;
+
+ card->dpram.phys = cs->win.Base;
+ card->dpram.size = cs->win.Size;
+
+ if (card->dpram.size != 0x1000) {
+ mod_alert("dpram size 0x%lx mismatch\n", card->dpram.size);
+ goto wrong_dpram;
+ }
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "[%s] %s: index 0x%02x",
+ THIS_MODULE->name,
+ pcmcia->devname,
+ pcmcia->conf.ConfigIndex);
+ if (pcmcia->conf.Vpp)
+ printk(", Vpp %d.%d", pcmcia->conf.Vpp/10, pcmcia->conf.Vpp%10);
+ if (pcmcia->conf.Attributes & CONF_ENABLE_IRQ) {
+ printk(", irq %d", pcmcia->irq.AssignedIRQ);
+ card->irq.nr = pcmcia->irq.AssignedIRQ;
+ }
+ if (pcmcia->win) {
+ int tmp;
+ const char *p;
+ printk(", mem 0x%08lx-0x%08lx"
+ , card->dpram.phys
+ , card->dpram.phys + card->dpram.size-1);
+ tmp = cs->win.Attributes;
+ while (tmp) {
+ p = lookup_mask(pcmcia_mem_attr, &tmp);
+ if (p)
+ printk(" %s", p);
+ }
+ }
+ printk("\n");
+
+ if (mk_softing(card))
+ goto softing_failed;
+ return 0;
+
+softing_failed:
+wrong_dpram:
+config_failed:
+ kfree(cs);
+no_mem:
+ pcmcia_disable_device(pcmcia);
+ return -ENODEV;
+}
+
+static struct pcmcia_device_id driver_ids[] = {
+ /* softing */
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0001),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0004),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0005),
+ /* vector , manufacturer? */
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0081),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0084),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0085),
+ /* EDIC */
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0102),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0105),
+ PCMCIA_DEVICE_NULL,
+};
+
+MODULE_DEVICE_TABLE(pcmcia, driver_ids);
+
+static struct pcmcia_driver softing_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "softing_cs",
+ },
+ .probe = driver_probe,
+ .remove = driver_remove,
+ .id_table = driver_ids,
+};
+
+static int __init mod_start(void)
+{
+ mod_trace("");
+ return pcmcia_register_driver(&softing_cs_driver);
+}
+
+static void __exit mod_stop(void)
+{
+ mod_trace("");
+ pcmcia_unregister_driver(&softing_cs_driver);
+}
+
+module_init(mod_start);
+module_exit(mod_stop);
+
+MODULE_DESCRIPTION("softing CANcard driver"
+ ", links PCMCIA card to softing driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("softing CANcard2");
+
--- /dev/null
+/*
+* drivers/net/can/softing/softing_fw.c
+*
+* Copyright (C) 2008
+*
+* - Kurt Van Dijck, EIA Electronics
+*
+* 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, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include "softing.h"
+
+#define fw_dir "softing-4.6/"
+
+const struct can_bittiming_const softing_btr_const = {
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4, /* overruled */
+ .brp_min = 1,
+ .brp_max = 32, /* overruled */
+ .brp_inc = 1,
+};
+
+static const struct softing_desc carddescs[] = {
+{
+ .name = "CANcard",
+ .manf = 0x0168, .prod = 0x001,
+ .generation = 1,
+ .freq = 16, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "CANcard-NEC",
+ .manf = 0x0168, .prod = 0x002,
+ .generation = 1,
+ .freq = 16, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "CANcard-SJA",
+ .manf = 0x0168, .prod = 0x004,
+ .generation = 1,
+ .freq = 20, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",},
+}, {
+ .name = "CANcard-2",
+ .manf = 0x0168, .prod = 0x005,
+ .generation = 2,
+ .freq = 24, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",},
+}, {
+ .name = "Vector-CANcard",
+ .manf = 0x0168, .prod = 0x081,
+ .generation = 1,
+ .freq = 16, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "Vector-CANcard-SJA",
+ .manf = 0x0168, .prod = 0x084,
+ .generation = 1,
+ .freq = 20, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",},
+}, {
+ .name = "Vector-CANcard-2",
+ .manf = 0x0168, .prod = 0x085,
+ .generation = 2,
+ .freq = 24, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",},
+}, {
+ .name = "EDICcard-NEC",
+ .manf = 0x0168, .prod = 0x102,
+ .generation = 1,
+ .freq = 16, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "EDICcard-2",
+ .manf = 0x0168, .prod = 0x105,
+ .generation = 2,
+ .freq = 24, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",},
+ },
+
+/* never tested, but taken from original softing */
+{ .name = "CAN-AC2-104",
+ .manf = 0x0000, .prod = 0x009,
+ .generation = 1,
+ .freq = 25, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x1000,
+ .boot = {0x0000, 0x000000, fw_dir "boot104.bin",},
+ .load = {0x0800, 0x035000, fw_dir "ld104.bin",},
+ .app = {0x0010, 0x120000, fw_dir "canpc104.bin",},
+ },
+{0, 0,},
+};
+
+const struct softing_desc *softing_lookup_desc
+ (unsigned int manf, unsigned int prod)
+{
+ const struct softing_desc *lp = carddescs;
+ for (; lp->name; ++lp) {
+ if ((lp->manf == manf) && (lp->prod == prod))
+ return lp;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(softing_lookup_desc);
+
+int softing_fct_cmd(struct softing *card, int cmd, int vector, const char *msg)
+{
+ int ret;
+ unsigned long stamp;
+ if (vector == RES_OK)
+ vector = RES_NONE;
+ card->dpram.fct->param[0] = cmd;
+ card->dpram.fct->host_access = vector;
+ /* be sure to flush this to the card */
+ wmb();
+ stamp = jiffies;
+ /*wait for card */
+ do {
+ ret = card->dpram.fct->host_access;
+ /* don't have any cached variables */
+ rmb();
+ if (ret == RES_OK) {
+ /*don't read return-value now */
+ ret = card->dpram.fct->returned;
+ if (ret)
+ mod_alert("%s returned %u", msg, ret);
+ return 0;
+ }
+ if ((jiffies - stamp) >= 1 * HZ)
+ break;
+ if (in_interrupt())
+ /* go as fast as possible */
+ continue;
+ /* process context => relax */
+ schedule();
+ } while (!signal_pending(current));
+
+ if (ret == RES_NONE) {
+ mod_alert("%s, no response from card on %u/0x%02x"
+ , msg, cmd, vector);
+ return 1;
+ } else {
+ mod_alert("%s, bad response from card on %u/0x%02x, 0x%04x"
+ , msg, cmd, vector, ret);
+ /*make sure to return something not 0 */
+ return ret ? ret : 1;
+ }
+}
+
+int softing_bootloader_command(struct softing *card
+ , int command, const char *msg)
+{
+ int ret;
+ unsigned long stamp;
+ card->dpram.receipt[0] = RES_NONE;
+ card->dpram.command[0] = command;
+ /* be sure to flush this to the card */
+ wmb();
+ stamp = jiffies;
+ /*wait for card */
+ do {
+ ret = card->dpram.receipt[0];
+ /* don't have any cached variables */
+ rmb();
+ if (ret == RES_OK)
+ return 0;
+ if ((jiffies - stamp) >= (3 * HZ))
+ break;
+ schedule();
+ } while (!signal_pending(current));
+
+ switch (ret) {
+ case RES_NONE:
+ mod_alert("%s: no response from card", msg);
+ break;
+ case RES_NOK:
+ mod_alert("%s: response from card nok", msg);
+ break;
+ case RES_UNKNOWN:
+ mod_alert("%s: command 0x%04x unknown", msg, command);
+ break;
+ default:
+ mod_alert("%s: bad response from card (%u)]", msg, ret);
+ break;
+ }
+ return ret ? ret : 1;
+}
+
+struct fw_hdr {
+ u16 type;
+ u32 addr;
+ u16 len;
+ u16 checksum;
+ const unsigned char *base;
+} __attribute__ ((packed));
+
+static int fw_parse(const unsigned char **pmem, struct fw_hdr *hdr)
+{
+ u16 tmp;
+ const unsigned char *mem;
+ const unsigned char *end;
+ mem = *pmem;
+ hdr->type = (mem[0] << 0) | (mem[1] << 8);
+ hdr->addr = (mem[2] << 0) | (mem[3] << 8)
+ | (mem[4] << 16) | (mem[5] << 24);
+ hdr->len = (mem[6] << 0) | (mem[7] << 8);
+ hdr->base = &mem[8];
+ hdr->checksum =
+ (hdr->base[hdr->len] << 0) | (hdr->base[hdr->len + 1] << 8);
+ for (tmp = 0, mem = *pmem, end = &hdr->base[hdr->len]; mem < end; ++mem)
+ tmp += *mem;
+ if (tmp != hdr->checksum)
+ return EINVAL;
+ *pmem += 10 + hdr->len;
+ return 0;
+}
+
+int softing_load_fw(const char *file, struct softing *card,
+ unsigned char *virt, unsigned int size, int offset)
+{
+ const struct firmware *fw;
+ const unsigned char *mem;
+ const unsigned char *end;
+ int ret;
+ u32 start_addr;
+ struct fw_hdr rec;
+ int ok = 0;
+ unsigned char buf[256];
+
+ ret = request_firmware(&fw, file, card->dev);
+ if (ret) {
+ mod_alert("request_firmware(%s) got %i", file, ret);
+ return ret;
+ }
+ mod_trace("%s, firmware(%s) got %u bytes, offset %c0x%04x"
+ , card->id.name, file, (unsigned int)fw->size,
+ (offset >= 0) ? '+' : '-', abs(offset));
+ /* parse the firmware */
+ mem = fw->data;
+ end = &mem[fw->size];
+ /* look for header record */
+ if (fw_parse(&mem, &rec))
+ goto fw_end;
+ if (rec.type != 0xffff) {
+ mod_alert("firware starts with type 0x%04x", rec.type);
+ goto fw_end;
+ }
+ if (strncmp("Structured Binary Format, Softing GmbH"
+ , rec.base, rec.len)) {
+ mod_info("firware string '%.*s'", rec.len, rec.base);
+ goto fw_end;
+ }
+ ok |= 1;
+ /* ok, we had a header */
+ while (mem < end) {
+ if (fw_parse(&mem, &rec))
+ break;
+ if (rec.type == 3) {
+ /*start address */
+ start_addr = rec.addr;
+ ok |= 2;
+ continue;
+ } else if (rec.type == 1) {
+ /*eof */
+ ok |= 4;
+ goto fw_end;
+ } else if (rec.type != 0) {
+ mod_alert("unknown record type 0x%04x", rec.type);
+ break;
+ }
+
+ if ((rec.addr + rec.len + offset) > size) {
+ mod_alert("firmware out of range (0x%08x / 0x%08x)"
+ , (rec.addr + rec.len + offset), size);
+ goto fw_end;
+ }
+ memcpy_toio(&virt[rec.addr + offset],
+ rec.base, rec.len);
+ /* be sure to flush caches from IO space */
+ mb();
+ if (rec.len > sizeof(buf)) {
+ mod_info("record is big (%u bytes), not verifying"
+ , rec.len);
+ continue;
+ }
+ /* verify record data */
+ memcpy_fromio(buf, &virt[rec.addr + offset], rec.len);
+ if (!memcmp(buf, rec.base, rec.len))
+ /* is ok */
+ continue;
+ mod_alert("0x%08x:0x%03x at 0x%p failed", rec.addr, rec.len
+ , &virt[rec.addr + offset]);
+ goto fw_end;
+ }
+fw_end:
+ release_firmware(fw);
+ if (0x5 == (ok & 0x5)) {
+ /*got eof & start */
+ return 0;
+ }
+ mod_alert("failed");
+ return EINVAL;
+}
+
+int softing_load_app_fw(const char *file, struct softing *card)
+{
+ const struct firmware *fw;
+ const unsigned char *mem;
+ const unsigned char *end;
+ int ret;
+ struct fw_hdr rec;
+ int ok = 0;
+ u32 start_addr = 0;
+ u16 rx_sum;
+ unsigned int sum;
+ const unsigned char *mem_lp;
+ const unsigned char *mem_end;
+ struct cpy {
+ u32 src;
+ u32 dst;
+ u16 len;
+ u8 do_cs;
+ } __attribute__((packed)) *pcpy =
+ (struct cpy *)&card->dpram.command[1];
+
+ ret = request_firmware(&fw, file, card->dev);
+ if (ret) {
+ mod_alert("request_firmware(%s) got %i", file, ret);
+ return ret;
+ }
+ mod_trace("%s, firmware(%s) got %lu bytes", card->id.name, file,
+ (unsigned long)fw->size);
+ /* parse the firmware */
+ mem = fw->data;
+ end = &mem[fw->size];
+ /* look for header record */
+ if (fw_parse(&mem, &rec))
+ goto fw_end;
+ if (rec.type != 0xffff) {
+ mod_alert("firware starts with type 0x%04x", rec.type);
+ goto fw_end;
+ }
+ if (strncmp("Structured Binary Format, Softing GmbH"
+ , rec.base, rec.len)) {
+ mod_info("firware string '%.*s'", rec.len, rec.base);
+ goto fw_end;
+ }
+ ok |= 1;
+ /* ok, we had a header */
+ while (mem < end) {
+ if (fw_parse(&mem, &rec))
+ break;
+
+ if (rec.type == 3) {
+ /*start address */
+ start_addr = rec.addr;
+ ok |= 2;
+ continue;
+ } else if (rec.type == 1) {
+ /*eof */
+ ok |= 4;
+ goto fw_end;
+ } else if (rec.type != 0) {
+ mod_alert("unknown record type 0x%04x", rec.type);
+ break;
+ }
+ /* regualar data */
+ for (sum = 0, mem_lp = rec.base, mem_end = &mem_lp[rec.len];
+ mem_lp < mem_end; ++mem_lp)
+ sum += *mem_lp;
+
+ memcpy_toio(&card->dpram. virt[card->desc->app.offs],
+ rec.base, rec.len);
+ pcpy->src = card->desc->app.offs + card->desc->app.addr;
+ pcpy->dst = rec.addr;
+ pcpy->len = rec.len;
+ pcpy->do_cs = 1;
+ if (softing_bootloader_command(card, 1, "loading app."))
+ goto fw_end;
+ /*verify checksum */
+ rx_sum = card->dpram.receipt[1];
+ if (rx_sum != (sum & 0xffff)) {
+ mod_alert("SRAM seems to be damaged"
+ ", wanted 0x%04x, got 0x%04x", sum, rx_sum);
+ goto fw_end;
+ }
+ }
+fw_end:
+ release_firmware(fw);
+ if (ok == 7) {
+ /*got start, start_addr, & eof */
+ struct cmd {
+ u32 start;
+ u8 autorestart;
+ } *pcmd = (struct cmd *)&card->dpram.command[1];
+ pcmd->start = start_addr;
+ pcmd->autorestart = 1;
+ if (!softing_bootloader_command(card, 3, "start app.")) {
+ mod_trace("%s: card app. run at 0x%06x"
+ , card->id.name, start_addr);
+ return 0;
+ }
+ }
+ mod_alert("failed");
+ return EINVAL;
+}
+
+int softing_reset_chip(struct softing *card)
+{
+ mod_trace("%s", card->id.name);
+ do {
+ /*reset chip */
+ card->dpram.info->reset_rcv_fifo = 0;
+ card->dpram.info->reset = 1;
+ if (!softing_fct_cmd(card, 0, 0, "reset_chip"))
+ break;
+ if (signal_pending(current))
+ goto failed;
+ /*sync */
+ if (softing_fct_cmd(card, 99, 0x55, "sync-a"))
+ goto failed;
+ if (softing_fct_cmd(card, 99, 0xaa, "sync-a"))
+ goto failed;
+ } while (1);
+ card->tx.pending = 0;
+ return 0;
+failed:
+ return -EIO;
+}
+
+int softing_reinit(struct softing *card, int bus0, int bus1)
+{
+ int ret;
+ int restarted_bus = -1;
+ mod_trace("%s", card->id.name);
+ if (!card->fw.up)
+ return -EIO;
+ if (bus0 < 0) {
+ bus0 = (card->bus[0]->netdev->flags & IFF_UP) ? 1 : 0;
+ if (bus0)
+ restarted_bus = 0;
+ } else if (bus1 < 0) {
+ bus1 = (card->bus[1]->netdev->flags & IFF_UP) ? 1 : 0;
+ if (bus1)
+ restarted_bus = 1;
+ }
+ /* collect info */
+ if (card->bus[0]) {
+ card->bus[0]->can.state = CAN_STATE_STOPPED;
+ softing_flush_echo_skb(card->bus[0]);
+ }
+ if (card->bus[1]) {
+ card->bus[1]->can.state = CAN_STATE_STOPPED;
+ softing_flush_echo_skb(card->bus[1]);
+ }
+
+ /* start acting */
+ if (!bus0 && !bus1) {
+ softing_card_irq(card, 0);
+ softing_reset_chip(card);
+ if (card->bus[0])
+ netif_carrier_off(card->bus[0]->netdev);
+ if (card->bus[1])
+ netif_carrier_off(card->bus[1]->netdev);
+ return 0;
+ }
+ ret = softing_reset_chip(card);
+ if (ret) {
+ softing_card_irq(card, 0);
+ return ret;
+ }
+ if (bus0) {
+ /*init chip */
+ card->dpram.fct->param[1] = card->bus[0]->can.bittiming.brp;
+ card->dpram.fct->param[2] = card->bus[0]->can.bittiming.sjw;
+ card->dpram.fct->param[3] =
+ card->bus[0]->can.bittiming.phase_seg1 +
+ card->bus[0]->can.bittiming.prop_seg;
+ card->dpram.fct->param[4] =
+ card->bus[0]->can.bittiming.phase_seg2;
+ card->dpram.fct->param[5] = (card->bus[0]->can.ctrlmode &
+ CAN_CTRLMODE_3_SAMPLES)?1:0;
+ if (softing_fct_cmd(card, 1, 0, "initialize_chip[0]"))
+ goto failed;
+ /*set mode */
+ card->dpram.fct->param[1] = 0;
+ card->dpram.fct->param[2] = 0;
+ if (softing_fct_cmd(card, 3, 0, "set_mode[0]"))
+ goto failed;
+ /*set filter */
+ card->dpram.fct->param[1] = 0x0000;/*card->bus[0].s.msg; */
+ card->dpram.fct->param[2] = 0x07ff;/*card->bus[0].s.msk; */
+ card->dpram.fct->param[3] = 0x0000;/*card->bus[0].l.msg; */
+ card->dpram.fct->param[4] = 0xffff;/*card->bus[0].l.msk; */
+ card->dpram.fct->param[5] = 0x0000;/*card->bus[0].l.msg >> 16;*/
+ card->dpram.fct->param[6] = 0x1fff;/*card->bus[0].l.msk >> 16;*/
+ if (softing_fct_cmd(card, 7, 0, "set_filter[0]"))
+ goto failed;
+ /*set output control */
+ card->dpram.fct->param[1] = card->bus[0]->output;
+ if (softing_fct_cmd(card, 5, 0, "set_output[0]"))
+ goto failed;
+ }
+ if (bus1) {
+ /*init chip2 */
+ card->dpram.fct->param[1] = card->bus[1]->can.bittiming.brp;
+ card->dpram.fct->param[2] = card->bus[1]->can.bittiming.sjw;
+ card->dpram.fct->param[3] =
+ card->bus[1]->can.bittiming.phase_seg1 +
+ card->bus[1]->can.bittiming.prop_seg;
+ card->dpram.fct->param[4] =
+ card->bus[1]->can.bittiming.phase_seg2;
+ card->dpram.fct->param[5] = (card->bus[1]->can.ctrlmode &
+ CAN_CTRLMODE_3_SAMPLES)?1:0;
+ if (softing_fct_cmd(card, 2, 0, "initialize_chip[1]"))
+ goto failed;
+ /*set mode2 */
+ card->dpram.fct->param[1] = 0;
+ card->dpram.fct->param[2] = 0;
+ if (softing_fct_cmd(card, 4, 0, "set_mode[1]"))
+ goto failed;
+ /*set filter2 */
+ card->dpram.fct->param[1] = 0x0000;/*card->bus[1].s.msg; */
+ card->dpram.fct->param[2] = 0x07ff;/*card->bus[1].s.msk; */
+ card->dpram.fct->param[3] = 0x0000;/*card->bus[1].l.msg; */
+ card->dpram.fct->param[4] = 0xffff;/*card->bus[1].l.msk; */
+ card->dpram.fct->param[5] = 0x0000;/*card->bus[1].l.msg >> 16;*/
+ card->dpram.fct->param[6] = 0x1fff;/*card->bus[1].l.msk >> 16;*/
+ if (softing_fct_cmd(card, 8, 0, "set_filter[1]"))
+ goto failed;
+ /*set output control2 */
+ card->dpram.fct->param[1] = card->bus[1]->output;
+ if (softing_fct_cmd(card, 6, 0, "set_output[1]"))
+ goto failed;
+ }
+ /*set interrupt */
+ /*enable_error_frame */
+ if (softing_fct_cmd(card, 51, 0, "enable_error_frame"))
+ goto failed;
+ /*initialize interface */
+ card->dpram.fct->param[1] = 1;
+ card->dpram.fct->param[2] = 1;
+ card->dpram.fct->param[3] = 1;
+ card->dpram.fct->param[4] = 1;
+ card->dpram.fct->param[5] = 1;
+ card->dpram.fct->param[6] = 1;
+ card->dpram.fct->param[7] = 1;
+ card->dpram.fct->param[8] = 1;
+ card->dpram.fct->param[9] = 1;
+ card->dpram.fct->param[10] = 1;
+ if (softing_fct_cmd(card, 17, 0, "initialize_interface"))
+ goto failed;
+ /*enable_fifo */
+ if (softing_fct_cmd(card, 36, 0, "enable_fifo"))
+ goto failed;
+ /*enable fifo tx ack */
+ if (softing_fct_cmd(card, 13, 0, "fifo_tx_ack[0]"))
+ goto failed;
+ /*enable fifo tx ack2 */
+ if (softing_fct_cmd(card, 14, 0, "fifo_tx_ack[1]"))
+ goto failed;
+ /*enable timestamps */
+ /*is default, no code found */
+ /*start_chip */
+ if (softing_fct_cmd(card, 11, 0, "start_chip"))
+ goto failed;
+ card->dpram.info->bus_state = 0;
+ card->dpram.info->bus_state2 = 0;
+ mod_info("ok for %s, %s/%s\n", card->bus[0]->netdev->name,
+ card->bus[1]->netdev->name, card->id.name);
+ if (card->desc->generation < 2) {
+ card->dpram.irq->to_host = 0;
+ /* flush the DPRAM caches */
+ wmb();
+ }
+ /*run once */
+ /*the bottom halve will start flushing the tx-queue too */
+ tasklet_schedule(&card->irq.bh);
+
+ ret = softing_card_irq(card, 1);
+ if (ret)
+ goto failed;
+
+ /*TODO: generate RESTARTED messages */
+
+ if (card->bus[0] && bus0) {
+ card->bus[0]->can.state = CAN_STATE_ACTIVE;
+ netif_carrier_on(card->bus[0]->netdev);
+ }
+ if (card->bus[1] && bus1) {
+ card->bus[1]->can.state = CAN_STATE_ACTIVE;
+ netif_carrier_on(card->bus[1]->netdev);
+ }
+ return 0;
+failed:
+ softing_card_irq(card, 0);
+ softing_reset_chip(card);
+ if (card->bus[0])
+ netif_carrier_off(card->bus[0]->netdev);
+ if (card->bus[1])
+ netif_carrier_off(card->bus[1]->netdev);
+ return -EIO;
+}
+
+
+int softing_default_output(struct softing *card, struct softing_priv *priv)
+{
+ switch (priv->chip) {
+ case 1000:
+ if (card->desc->generation < 2)
+ return 0xfb;
+ return 0xfa;
+ case 5:
+ return 0x60;
+ default:
+ return 0x40;
+ }
+}
+
+u32 softing_time2usec(struct softing *card, u32 raw)
+{
+ /*TODO : don't loose higher order bits in computation */
+ switch (card->desc->freq) {
+ case 20:
+ return raw * 4 / 5;
+ case 24:
+ return raw * 2 / 3;
+ case 25:
+ return raw * 16 / 25;
+ case 0:
+ case 16:
+ default:
+ return raw;
+ }
+}
+
+
--- /dev/null
+/*
+* drivers/net/can/softing/softing_main.c
+*
+* Copyright (C) 2008
+*
+* - Kurt Van Dijck, EIA Electronics
+*
+* 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, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include "softing.h"
+
+/* this is the worst thing on the softing API
+ * 2 busses are driven together, I don't know how
+ * to recover a single of them.
+ * Therefore, when one bus is modified, the other
+ * is flushed too
+ */
+void softing_flush_echo_skb(struct softing_priv *priv)
+{
+ can_close_cleanup(priv->netdev);
+ priv->tx.pending = 0;
+ priv->tx.echo_put = 0;
+ priv->tx.echo_get = 0;
+}
+
+/*softing_unlocked_tx_run:*/
+/*trigger the tx queue-ing*/
+/*no locks are grabbed, so be sure to have the spin spinlock*/
+static int netdev_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct softing_priv *priv = (struct softing_priv *)netdev_priv(dev);
+ struct softing *card = priv->card;
+ int ret;
+ int bhlock;
+ u8 *ptr;
+ u8 cmd;
+ unsigned int fifo_wr;
+ struct can_frame msg;
+
+ ret = -ENOTTY;
+ if (in_interrupt()) {
+ bhlock = 0;
+ spin_lock(&card->spin);
+ } else {
+ bhlock = 1;
+ spin_lock_bh(&card->spin);
+ }
+ if (!card->fw.up) {
+ ret = -EIO;
+ goto xmit_done;
+ }
+ if (netif_carrier_ok(priv->netdev) <= 0) {
+ ret = -EBADF;
+ goto xmit_done;
+ }
+ if (card->tx.pending >= TXMAX) {
+ ret = -EBUSY;
+ goto xmit_done;
+ }
+ if (priv->tx.pending >= CAN_ECHO_SKB_MAX) {
+ ret = -EBUSY;
+ goto xmit_done;
+ }
+ fifo_wr = card->dpram.tx->wr;
+ if (fifo_wr == card->dpram.tx->rd) {
+ /*fifo full */
+ ret = -EAGAIN;
+ goto xmit_done;
+ }
+ memcpy(&msg, skb->data, sizeof(msg));
+ ptr = &card->dpram.tx->fifo[fifo_wr][0];
+ cmd = CMD_TX;
+ if (msg.can_id & CAN_RTR_FLAG)
+ cmd |= CMD_RTR;
+ if (msg.can_id & CAN_EFF_FLAG)
+ cmd |= CMD_XTD;
+ if (priv->index)
+ cmd |= CMD_BUS2;
+ *ptr++ = cmd;
+ *ptr++ = msg.can_dlc;
+ *ptr++ = (msg.can_id >> 0);
+ *ptr++ = (msg.can_id >> 8);
+ if (msg.can_id & CAN_EFF_FLAG) {
+ *ptr++ = (msg.can_id >> 16);
+ *ptr++ = (msg.can_id >> 24);
+ } else {
+ /*increment 1, not 2 as you might think */
+ ptr += 1;
+ }
+ if (!(msg.can_id & CAN_RTR_FLAG))
+ memcpy_toio(ptr, &msg.data[0], msg.can_dlc);
+ if (++fifo_wr >=
+ sizeof(card->dpram.tx->fifo) /
+ sizeof(card->dpram.tx->fifo[0]))
+ fifo_wr = 0;
+ card->dpram.tx->wr = fifo_wr;
+ ret = 0;
+ ++card->tx.pending;
+ ++priv->tx.pending;
+ can_put_echo_skb(skb, dev, priv->tx.echo_put);
+ ++priv->tx.echo_put;
+ if (priv->tx.echo_put >= CAN_ECHO_SKB_MAX)
+ priv->tx.echo_put = 0;
+ /* clear pointer, so don't erase later */
+ skb = 0;
+xmit_done:
+ if (bhlock)
+ spin_unlock_bh(&card->spin);
+ else
+ spin_unlock(&card->spin);
+ if (card->tx.pending >= TXMAX) {
+ struct softing_priv *bus;
+ int j;
+ for (j = 0; j < card->nbus; ++j) {
+ bus = card->bus[j];
+ if (!bus)
+ continue;
+ netif_stop_queue(bus->netdev);
+ }
+ }
+
+ /* free skb, if not handled by the driver */
+ if (skb)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static int softing_dev_svc_once(struct softing *card)
+{
+ int j;
+ struct softing_priv *bus;
+ struct sk_buff *skb;
+ struct can_frame msg;
+
+ unsigned int fifo_rd;
+ unsigned int cnt = 0;
+ struct net_device_stats *stats;
+
+ memset(&msg, 0, sizeof(msg));
+ if (card->dpram.rx->lost_msg) {
+ /*reset condition */
+ card->dpram.rx->lost_msg = 0;
+ /* prepare msg */
+ msg.can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
+ msg.can_dlc = CAN_ERR_DLC;
+ msg.data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ /*service to both busses, we don't know which one generated */
+ for (j = 0; j < card->nbus; ++j) {
+ bus = card->bus[j];
+ if (!bus)
+ continue;
+ if (!netif_carrier_ok(bus->netdev))
+ continue;
+ ++bus->can.can_stats.data_overrun;
+ skb = dev_alloc_skb(sizeof(msg));
+ if (!skb)
+ return -ENOMEM;
+ skb->dev = bus->netdev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(msg)), &msg, sizeof(msg));
+ if (netif_rx(skb))
+ dev_kfree_skb_irq(skb);
+ }
+ memset(&msg, 0, sizeof(msg));
+ ++cnt;
+ }
+
+ fifo_rd = card->dpram.rx->rd;
+ if (++fifo_rd >=
+ sizeof(card->dpram.rx->fifo) / sizeof(card->dpram.rx->fifo[0]))
+ fifo_rd = 0;
+ if (card->dpram.rx->wr != fifo_rd) {
+ u8 *ptr;
+ u32 tmp;
+ u8 cmd;
+ int do_skb;
+
+ ptr = &card->dpram.rx->fifo[fifo_rd][0];
+
+ cmd = *ptr++;
+ if (cmd == 0xff) {
+ /*not quite usefull, probably the card has got out */
+ mod_alert("got cmd 0x%02x, I suspect the card is lost"
+ , cmd);
+ }
+ /*mod_trace("0x%02x", cmd);*/
+ bus = card->bus[0];
+ if (cmd & CMD_BUS2)
+ bus = card->bus[1];
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ stats = can_get_stats(bus->netdev);
+#else
+ stats = &bus->netdev->stats;
+#endif
+ if (cmd & CMD_ERR) {
+ u8 can_state;
+ u8 state;
+ state = *ptr++;
+
+ msg.can_id = CAN_ERR_FLAG;
+ msg.can_dlc = CAN_ERR_DLC;
+
+ if (state & 0x80) {
+ can_state = CAN_STATE_BUS_OFF;
+ msg.can_id |= CAN_ERR_BUSOFF;
+ state = 2;
+ } else if (state & 0x60) {
+ can_state = CAN_STATE_BUS_PASSIVE;
+ msg.can_id |= CAN_ERR_BUSERROR;
+ state = 1;
+ } else {
+ can_state = CAN_STATE_ACTIVE;
+ state = 0;
+ do_skb = 0;
+ }
+ /*update DPRAM */
+ if (!bus->index)
+ card->dpram.info->bus_state = state;
+ else
+ card->dpram.info->bus_state2 = state;
+ /*timestamp */
+ tmp = (ptr[0] << 0)
+ |(ptr[1] << 8)
+ |(ptr[2] << 16)
+ |(ptr[3] << 24);
+ ptr += 4;
+ /*msg.time = */ softing_time2usec(card, tmp);
+ /*trigger dual port RAM */
+ mb();
+ card->dpram.rx->rd = fifo_rd;
+ /*update internal status */
+ if (can_state != bus->can.state) {
+ bus->can.state = can_state;
+ if (can_state == 1)
+ bus->can.can_stats.error_passive += 1;
+ }
+ bus->can.can_stats.bus_error += 1;
+
+ /*trigger socketcan */
+ if (state == 2) {
+ /* this calls can_close_cleanup() */
+ softing_flush_echo_skb(bus);
+ can_bus_off(bus->netdev);
+ netif_stop_queue(bus->netdev);
+ }
+ if ((state == CAN_STATE_BUS_OFF)
+ || (state == CAN_STATE_BUS_PASSIVE)) {
+ skb = dev_alloc_skb(sizeof(msg));
+ if (!skb)
+ return -ENOMEM;
+ skb->dev = bus->netdev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(msg)), &msg,
+ sizeof(msg));
+ if (netif_rx(skb))
+ dev_kfree_skb_irq(skb);
+ }
+ } else {
+ if (cmd & CMD_RTR)
+ msg.can_id |= CAN_RTR_FLAG;
+ /* acknowledge, was tx msg
+ * no real tx flag to set
+ if (cmd & CMD_ACK) {
+ }
+ */
+ msg.can_dlc = *ptr++;
+ if (msg.can_dlc > 8)
+ msg.can_dlc = 8;
+ if (cmd & CMD_XTD) {
+ msg.can_id |= CAN_EFF_FLAG;
+ msg.can_id |=
+ (ptr[0] << 0)
+ | (ptr[1] << 8)
+ | (ptr[2] << 16)
+ | (ptr[3] << 24);
+ ptr += 4;
+ } else {
+ msg.can_id |= (ptr[0] << 0) | (ptr[1] << 8);
+ ptr += 2;
+ }
+ tmp = (ptr[0] << 0)
+ | (ptr[1] << 8)
+ | (ptr[2] << 16)
+ | (ptr[3] << 24);
+ ptr += 4;
+ /*msg.time = */ softing_time2usec(card, tmp);
+ memcpy_fromio(&msg.data[0], ptr, 8);
+ ptr += 8;
+ /*trigger dual port RAM */
+ mb();
+ card->dpram.rx->rd = fifo_rd;
+ /*update socket */
+ if (cmd & CMD_ACK) {
+ can_get_echo_skb(bus->netdev, bus->tx.echo_get);
+ ++bus->tx.echo_get;
+ if (bus->tx.echo_get >= CAN_ECHO_SKB_MAX)
+ bus->tx.echo_get = 0;
+ if (bus->tx.pending)
+ --bus->tx.pending;
+ if (card->tx.pending)
+ --card->tx.pending;
+ stats->tx_packets += 1;
+ stats->tx_bytes += msg.can_dlc;
+ } else {
+ stats->rx_packets += 1;
+ stats->rx_bytes += msg.can_dlc;
+ bus->netdev->last_rx = jiffies;
+ skb = dev_alloc_skb(sizeof(msg));
+ if (skb) {
+ skb->dev = bus->netdev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(msg)), &msg,
+ sizeof(msg));
+ if (netif_rx(skb))
+ dev_kfree_skb_irq(skb);
+ }
+ }
+ }
+ ++cnt;
+ }
+ return cnt;
+}
+
+static void softing_dev_svc(unsigned long param)
+{
+ struct softing *card = (struct softing *)param;
+ struct softing_priv *bus;
+ int j;
+ int offset;
+
+ spin_lock(&card->spin);
+ while (softing_dev_svc_once(card) > 0)
+ ++card->irq.svc_count;
+ /*resume tx queue's */
+ offset = card->tx.last_bus;
+ for (j = 0; j < card->nbus; ++j) {
+ if (card->tx.pending >= TXMAX)
+ break;
+ bus = card->bus[(j + offset) % card->nbus];
+ if (netif_carrier_ok(bus->netdev))
+ netif_wake_queue(bus->netdev);
+ }
+ spin_unlock(&card->spin);
+}
+
+static void card_seems_down(struct softing *card)
+{
+ /* free interrupt, but probably
+ * in wrong (interrupt) context
+ if (card->irq.requested) {
+ free_irq(card->irq.nr, card);
+ card->irq.requested = 0;
+ card->fw.up = 0;
+ }
+ */
+ mod_alert("I think the card is vanished");
+}
+
+static
+irqreturn_t dev_interrupt_shared(int irq, void *dev_id)
+{
+ struct softing *card = (struct softing *)dev_id;
+ unsigned char ir;
+ ir = card->dpram.virt[0xe02];
+ card->dpram.virt[0xe02] = 0;
+ if (card->dpram.rx->rd == 0xffff) {
+ card_seems_down(card);
+ return IRQ_NONE;
+ }
+ if (ir == 1) {
+ tasklet_schedule(&card->irq.bh);
+ return IRQ_HANDLED;
+ } else if (ir == 0x10) {
+ return IRQ_NONE;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+static
+irqreturn_t dev_interrupt_nshared(int irq, void *dev_id)
+{
+ struct softing *card = (struct softing *)dev_id;
+ unsigned char irq_host;
+ irq_host = card->dpram.irq->to_host;
+ /* make sure we have a copy, before clearing the variable in DPRAM */
+ rmb();
+ card->dpram.irq->to_host = 0;
+ /* make sure we cleared it */
+ wmb();
+ mod_trace("0x%02x", irq_host);
+ if (card->dpram.rx->rd == 0xffff) {
+ card_seems_down(card);
+ return IRQ_NONE;
+ }
+ tasklet_schedule(&card->irq.bh);
+ return IRQ_HANDLED;
+}
+
+static int netdev_open(struct net_device *ndev)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ struct softing *card = priv->card;
+ int fw;
+ int ret;
+
+ mod_trace("%s", ndev->name);
+ /* determine and set bittime */
+ ret = can_set_bittiming(ndev);
+ if (ret)
+ return ret;
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ fw = card->fw.up;
+ if (fw)
+ softing_reinit(card
+ , (card->bus[0] == priv) ? 1 : -1
+ , (card->bus[1] == priv) ? 1 : -1);
+ mutex_unlock(&card->fw.lock);
+ if (!fw)
+ return -EIO;
+ netif_start_queue(ndev);
+ return 0;
+}
+
+static int netdev_stop(struct net_device *ndev)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ struct softing *card = priv->card;
+ int fw;
+
+ mod_trace("%s", ndev->name);
+ netif_stop_queue(ndev);
+ netif_carrier_off(ndev);
+ softing_flush_echo_skb(priv);
+ can_close_cleanup(ndev);
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ fw = card->fw.up;
+ if (fw)
+ softing_reinit(card
+ , (card->bus[0] == priv) ? 0 : -1
+ , (card->bus[1] == priv) ? 0 : -1);
+ mutex_unlock(&card->fw.lock);
+ if (!fw)
+ return -EIO;
+ return 0;
+}
+
+static int candev_get_state(struct net_device *ndev, enum can_state *state)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ mod_trace("%s", ndev->name);
+ if (priv->netdev->flags & IFF_UP)
+ *state = CAN_STATE_STOPPED;
+ else if (priv->can.state == CAN_STATE_STOPPED)
+ *state = CAN_STATE_STOPPED;
+ else
+ *state = CAN_STATE_ACTIVE;
+ return 0;
+}
+
+static int candev_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ struct softing *card = priv->card;
+ mod_trace("%s %u", ndev->name, mode);
+ switch (mode) {
+ case CAN_MODE_START:
+ /*recovery from busoff? */
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ softing_reinit(card, -1, -1);
+ mutex_unlock(&card->fw.lock);
+ break;
+ case CAN_MODE_STOP:
+ case CAN_MODE_SLEEP:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/*assume the card->lock is held*/
+
+int softing_card_irq(struct softing *card, int enable)
+{
+ int ret;
+ if (!enable) {
+ if (card->irq.requested && card->irq.nr) {
+ free_irq(card->irq.nr, card);
+ card->irq.requested = 0;
+ }
+ return 0;
+ }
+ if (!card->irq.requested && (card->irq.nr)) {
+ irqreturn_t(*fn) (int, void *);
+ unsigned int flags;
+ flags = IRQF_DISABLED | IRQF_SHARED;/*| IRQF_TRIGGER_LOW; */
+ fn = dev_interrupt_nshared;
+ if (card->desc->generation >= 2)
+ fn = dev_interrupt_shared;
+ ret = request_irq(card->irq.nr, fn, flags, card->id.name, card);
+ if (ret) {
+ mod_alert("%s, request_irq(%u) failed"
+ , card->id.name, card->irq.nr
+ );
+ return ret;
+ }
+ card->irq.requested = 1;
+ }
+ return 0;
+}
+
+static void shutdown_card(struct softing *card)
+{
+ int fw_up = 0;
+ mod_trace("%s", card->id.name);
+ if (mutex_lock_interruptible(&card->fw.lock))
+ /* return -ERESTARTSYS*/;
+ fw_up = card->fw.up;
+ card->fw.up = 0;
+
+ if (card->irq.requested && card->irq.nr) {
+ free_irq(card->irq.nr, card);
+ card->irq.requested = 0;
+ }
+ if (fw_up) {
+ if (card->fn.enable_irq)
+ card->fn.enable_irq(card, 0);
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ }
+ mutex_unlock(&card->fw.lock);
+ tasklet_kill(&card->irq.bh);
+}
+
+static int boot_card(struct softing *card)
+{
+ mod_trace("%s", card->id.name);
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ if (card->fw.up) {
+ mutex_unlock(&card->fw.lock);
+ return 0;
+ }
+ /*reset board */
+
+ if (card->fn.enable_irq)
+ card->fn.enable_irq(card, 1);
+ /*boot card */
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ /*test dp ram */
+ if (card->dpram.virt) {
+ unsigned char *lp;
+ static const unsigned char stream[]
+ = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, };
+ unsigned char back[sizeof(stream)];
+ for (lp = card->dpram.virt;
+ &lp[sizeof(stream)] <= card->dpram.end;
+ lp += sizeof(stream)) {
+ memcpy_toio(lp, stream, sizeof(stream));
+ /* flush IO cache */
+ mb();
+ memcpy_fromio(back, lp, sizeof(stream));
+
+ if (memcmp(back, stream, sizeof(stream))) {
+ char line[3 * sizeof(stream)
+ / sizeof(stream[0]) + 1];
+ char *pline = line;
+ unsigned char *addr = lp;
+ for (lp = back; lp < &back[sizeof(stream)
+ / sizeof(stream[0])]; ++lp)
+ pline += sprintf(pline, " %02x", *lp);
+
+ mod_alert("write to dpram failed at 0x%p, %s"
+ , addr, line);
+ goto open_failed;
+ }
+ }
+ /*fill dpram with 0x55 */
+ /*for (lp = card->dpram.virt; lp <= card->dpram.end; ++lp) {
+ *lp = 0x55;
+ }*/
+ wmb();
+ } else {
+ goto open_failed;
+ }
+ /*load boot firmware */
+ if (softing_load_fw(card->desc->boot.fw, card, card->dpram.virt,
+ card->dpram.size,
+ card->desc->boot.offs -
+ card->desc->boot.addr))
+ goto open_failed;
+ /*load load firmware */
+ if (softing_load_fw(card->desc->load.fw, card, card->dpram.virt,
+ card->dpram.size,
+ card->desc->load.offs -
+ card->desc->load.addr))
+ goto open_failed;
+
+ if (card->fn.reset)
+ card->fn.reset(card, 0);
+ if (softing_bootloader_command(card, 0, "card boot"))
+ goto open_failed;
+ if (softing_load_app_fw(card->desc->app.fw, card))
+ goto open_failed;
+ /*reset chip */
+ card->dpram.info->reset_rcv_fifo = 0;
+ card->dpram.info->reset = 1;
+ /*sync */
+ if (softing_fct_cmd(card, 99, 0x55, "sync-a"))
+ goto open_failed;
+ if (softing_fct_cmd(card, 99, 0xaa, "sync-a"))
+ goto open_failed;
+ /*reset chip */
+ if (softing_fct_cmd(card, 0, 0, "reset_chip"))
+ goto open_failed;
+ /*get_serial */
+ if (softing_fct_cmd(card, 43, 0, "get_serial_number"))
+ goto open_failed;
+ card->id.serial =
+ (u16) card->dpram.fct->param[1] +
+ (((u16) card->dpram.fct->param[2]) << 16);
+ /*get_version */
+ if (softing_fct_cmd(card, 12, 0, "get_version"))
+ goto open_failed;
+ card->id.fw = (u16) card->dpram.fct->param[1];
+ card->id.hw = (u16) card->dpram.fct->param[2];
+ card->id.lic = (u16) card->dpram.fct->param[3];
+ card->id.chip[0] = (u16) card->dpram.fct->param[4];
+ card->id.chip[1] = (u16) card->dpram.fct->param[5];
+
+ mod_info("%s, card booted, "
+ "serial %u, fw %u, hw %u, lic %u, chip (%u,%u)",
+ card->id.name, card->id.serial, card->id.fw, card->id.hw,
+ card->id.lic, card->id.chip[0], card->id.chip[1]);
+
+ card->fw.up = 1;
+ mutex_unlock(&card->fw.lock);
+ return 0;
+open_failed:
+ card->fw.up = 0;
+ if (card->fn.enable_irq)
+ card->fn.enable_irq(card, 0);
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ mutex_unlock(&card->fw.lock);
+ return EINVAL;
+}
+
+/*sysfs stuff*/
+
+/* Because the struct softing may be used by pcmcia devices
+ * as well as pci devices, * we have no clue how to get
+ * from a struct device * towards the struct softing *.
+ * It may go over a pci_device->priv or over a pcmcia_device->priv.
+ * Therefore, provide the struct softing pointer within the attribute.
+ * Then we don't need driver/bus specific things in these attributes
+ */
+struct softing_attribute {
+ struct device_attribute dev;
+ ssize_t (*show) (struct softing *card, char *buf);
+ ssize_t (*store)(struct softing *card, const char *buf, size_t count);
+ struct softing *card;
+};
+
+static ssize_t rd_card_attr(struct device *dev, struct device_attribute *attr
+ , char *buf) {
+ struct softing_attribute *cattr
+ = container_of(attr, struct softing_attribute, dev);
+ return cattr->show ? cattr->show(cattr->card, buf) : 0;
+}
+static ssize_t wr_card_attr(struct device *dev, struct device_attribute *attr
+ , const char *buf, size_t count) {
+ struct softing_attribute *cattr
+ = container_of(attr, struct softing_attribute, dev);
+ return cattr->store ? cattr->store(cattr->card, buf, count) : 0;
+}
+
+#define declare_attr(_name, _mode, _show, _store) { \
+ .dev = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = _mode, \
+ }, \
+ .show = rd_card_attr, \
+ .store = wr_card_attr, \
+ }, \
+ .show = _show, \
+ .store = _store, \
+}
+
+#define CARD_SHOW(name, member) \
+static ssize_t show_##name(struct softing *card, char *buf) { \
+ return sprintf(buf, "%u\n", card->member); \
+}
+CARD_SHOW(serial , id.serial);
+CARD_SHOW(firmware , id.fw);
+CARD_SHOW(hardware , id.hw);
+CARD_SHOW(license , id.lic);
+CARD_SHOW(freq , id.freq);
+CARD_SHOW(txpending , tx.pending);
+
+static const struct softing_attribute card_attr_proto [] = {
+ declare_attr(serial , 0444, show_serial , 0),
+ declare_attr(firmware , 0444, show_firmware , 0),
+ declare_attr(hardware , 0444, show_hardware , 0),
+ declare_attr(license , 0444, show_license , 0),
+ declare_attr(freq , 0444, show_freq , 0),
+ declare_attr(txpending , 0644, show_txpending , 0),
+};
+
+static int mk_card_sysfs(struct softing *card)
+{
+ int size;
+ int j;
+
+ size = sizeof(card_attr_proto)/sizeof(card_attr_proto[0]);
+ card->attr = kmalloc((size+1)*sizeof(card->attr[0]), GFP_KERNEL);
+ if (!card->attr)
+ goto attr_mem_failed;
+ memcpy(card->attr, card_attr_proto, size * sizeof(card->attr[0]));
+ memset(&card->attr[size], 0, sizeof(card->attr[0]));
+
+ card->grp = kmalloc((size+1)*sizeof(card->grp [0]), GFP_KERNEL);
+ if (!card->grp)
+ goto grp_mem_failed;
+
+ for (j = 0; j < size; ++j) {
+ card->attr[j].card = card;
+ card->grp[j] = &card->attr[j].dev.attr;
+ if (!card->attr[j].show)
+ card->attr[j].dev.attr.mode &= ~(S_IRUGO);
+ if (!card->attr[j].store)
+ card->attr[j].dev.attr.mode &= ~(S_IWUGO);
+ }
+ card->grp[size] = 0;
+ card->sysfs.name = "softing";
+ card->sysfs.attrs = card->grp;
+ if (sysfs_create_group(&card->dev->kobj, &card->sysfs) < 0)
+ goto sysfs_failed;
+
+ return 0;
+
+sysfs_failed:
+ kfree(card->grp);
+grp_mem_failed:
+ kfree(card->attr);
+attr_mem_failed:
+ return -1;
+}
+static void rm_card_sysfs(struct softing *card)
+{
+ sysfs_remove_group(&card->dev->kobj, &card->sysfs);
+ kfree(card->grp);
+ kfree(card->attr);
+}
+
+static ssize_t show_chip(struct device *dev
+ , struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct softing_priv *priv = netdev2softing(ndev);
+ return sprintf(buf, "%i\n", priv->chip);
+}
+
+static ssize_t show_output(struct device *dev
+ , struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct softing_priv *priv = netdev2softing(ndev);
+ return sprintf(buf, "0x%02x\n", priv->output);
+}
+
+static ssize_t store_output(struct device *dev
+ , struct device_attribute *attr
+ , const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct softing_priv *priv = netdev2softing(ndev);
+ struct softing *card = priv->card;
+
+ u8 v = simple_strtoul(buf, NULL, 10) & 0xFFU;
+
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ if (ndev->flags & IFF_UP) {
+ int j;
+ /* we will need a restart */
+ for (j = 0; j < card->nbus; ++j) {
+ if (j == priv->index)
+ /* me, myself & I */
+ continue;
+ if (card->bus[j]->netdev->flags & IFF_UP) {
+ mutex_unlock(&card->fw.lock);
+ return -EBUSY;
+ }
+ }
+ priv->output = v;
+ softing_reinit(card, -1, -1);
+ } else {
+ priv->output = v;
+ }
+ mutex_unlock(&card->fw.lock);
+ return count;
+}
+/* TODO
+ * the latest softing cards support sleep mode too
+ */
+
+static const DEVICE_ATTR(chip, S_IRUGO, show_chip, 0);
+static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output);
+
+static const struct attribute *const netdev_sysfs_entries [] = {
+ &dev_attr_chip .attr,
+ &dev_attr_output .attr,
+ 0,
+};
+static const struct attribute_group netdev_sysfs = {
+ .name = 0,
+ .attrs = (struct attribute **)netdev_sysfs_entries,
+};
+
+static int mk_netdev_sysfs(struct softing_priv *priv)
+{
+ if (!priv->netdev->dev.kobj.sd) {
+ mod_alert("sysfs_create_group would fail");
+ return ENODEV;
+ }
+ return sysfs_create_group(&priv->netdev->dev.kobj, &netdev_sysfs);
+}
+static void rm_netdev_sysfs(struct softing_priv *priv)
+{
+ sysfs_remove_group(&priv->netdev->dev.kobj, &netdev_sysfs);
+}
+
+static struct softing_priv *mk_netdev(struct softing *card, u16 chip_id)
+{
+ struct net_device *ndev;
+ struct softing_priv *priv;
+
+ ndev = alloc_candev(sizeof(*priv));
+ if (!ndev) {
+ mod_alert("alloc_candev failed");
+ return 0;
+ }
+ priv = netdev_priv(ndev);
+ priv->netdev = ndev;
+ priv->card = card;
+ memcpy(&priv->btr_const, &softing_btr_const, sizeof(priv->btr_const));
+ priv->btr_const.brp_max = card->desc->max_brp;
+ priv->btr_const.sjw_max = card->desc->max_sjw;
+ priv->can.bittiming_const = &priv->btr_const;
+ priv->can.bittiming.clock = 8000000;
+ priv->chip = chip_id;
+ priv->output = softing_default_output(card, priv);
+ SET_NETDEV_DEV(ndev, card->dev);
+
+ ndev->flags |= IFF_ECHO;
+ ndev->open = netdev_open;
+ ndev->stop = netdev_stop;
+ ndev->hard_start_xmit = netdev_start_xmit;
+ priv->can.do_get_state = candev_get_state;
+ priv->can.do_set_mode = candev_set_mode;
+
+ return priv;
+}
+
+static void rm_netdev(struct softing_priv *priv)
+{
+ free_candev(priv->netdev);
+}
+
+static int reg_netdev(struct softing_priv *priv)
+{
+ int ret;
+ netif_carrier_off(priv->netdev);
+ ret = register_netdev(priv->netdev);
+ if (ret) {
+ mod_alert("%s, register failed", priv->card->id.name);
+ goto reg_failed;
+ }
+ ret = mk_netdev_sysfs(priv);
+ if (ret) {
+ mod_alert("%s, sysfs failed", priv->card->id.name);
+ goto sysfs_failed;
+ }
+ return 0;
+sysfs_failed:
+ unregister_netdev(priv->netdev);
+reg_failed:
+ return EINVAL;
+}
+
+static void unreg_netdev(struct softing_priv *priv)
+{
+ rm_netdev_sysfs(priv);
+ unregister_netdev(priv->netdev);
+}
+
+void rm_softing(struct softing *card)
+{
+ int j;
+
+ /*first, disable card*/
+ shutdown_card(card);
+
+ for (j = 0; j < card->nbus; ++j) {
+ unreg_netdev(card->bus[j]);
+ rm_netdev(card->bus[j]);
+ }
+
+ rm_card_sysfs(card);
+
+ iounmap(card->dpram.virt);
+}
+EXPORT_SYMBOL(rm_softing);
+
+int mk_softing(struct softing *card)
+{
+ int j;
+
+ /* try_module_get(THIS_MODULE); */
+ mutex_init(&card->fw.lock);
+ spin_lock_init(&card->spin);
+ tasklet_init(&card->irq.bh, softing_dev_svc, (unsigned long)card);
+
+ card->desc = softing_lookup_desc(card->id.manf, card->id.prod);
+ if (!card->desc) {
+ mod_alert("0x%04x:0x%04x not supported\n", card->id.manf,
+ card->id.prod);
+ goto lookup_failed;
+ }
+ card->id.name = card->desc->name;
+ mod_trace("can (%s)", card->id.name);
+
+ card->dpram.virt = ioremap(card->dpram.phys, card->dpram.size);
+ if (!card->dpram.virt) {
+ mod_alert("dpram ioremap failed\n");
+ goto ioremap_failed;
+ }
+
+ card->dpram.size = card->desc->dpram_size;
+ card->dpram.end = &card->dpram.virt[card->dpram.size];
+ /*initialize_board */
+ card->dpram.rx = (struct softing_rx *)&card->dpram.virt[0x0000];
+ card->dpram.tx = (struct softing_tx *)&card->dpram.virt[0x0400];
+ card->dpram.fct = (struct softing_fct *)&card->dpram.virt[0x0300];
+ card->dpram.info = (struct softing_info *)&card->dpram.virt[0x0330];
+ card->dpram.command = (unsigned short *)&card->dpram.virt[0x07e0];
+ card->dpram.receipt = (unsigned short *)&card->dpram.virt[0x07f0];
+ card->dpram.irq = (struct softing_irq *)&card->dpram.virt[0x07fe];
+
+ /*reset card */
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ if (boot_card(card)) {
+ mod_alert("%s, failed to boot", card->id.name);
+ goto boot_failed;
+ }
+
+ /*only now, the chip's are known */
+ card->id.freq = card->desc->freq * 1000000UL;
+
+ if (mk_card_sysfs(card)) {
+ mod_alert("%s, sysfs failed", card->id.name);
+ goto sysfs_failed;
+ }
+
+ if (card->nbus > (sizeof(card->bus) / sizeof(card->bus[0]))) {
+ card->nbus = sizeof(card->bus) / sizeof(card->bus[0]);
+ mod_alert("%s, going for %u busses", card->id.name, card->nbus);
+ }
+
+ for (j = 0; j < card->nbus; ++j) {
+ card->bus[j] = mk_netdev(card, card->id.chip[j]);
+ if (!card->bus[j]) {
+ mod_alert("%s: failed to make can[%i]", card->id.name,
+ j);
+ goto netdev_failed;
+ }
+ card->bus[j]->index = j;
+ }
+ for (j = 0; j < card->nbus; ++j) {
+ if (reg_netdev(card->bus[j])) {
+ mod_alert("%s: failed to register can[%i]",
+ card->id.name, j);
+ goto reg_failed;
+ }
+ }
+ mod_trace("card initialised");
+ return 0;
+
+reg_failed:
+ for (j = 0; j < card->nbus; ++j)
+ unreg_netdev(card->bus[j]);
+netdev_failed:
+ for (j = 0; j < card->nbus; ++j) {
+ if (card->bus[j])
+ rm_netdev(card->bus[j]);
+ }
+ rm_card_sysfs(card);
+sysfs_failed:
+ shutdown_card(card);
+boot_failed:
+ iounmap(card->dpram.virt);
+ card->dpram.virt = 0;
+ card->dpram.end = 0;
+ioremap_failed:
+lookup_failed:
+ tasklet_kill(&card->irq.bh);
+ return EINVAL;
+}
+EXPORT_SYMBOL(mk_softing);
+
+static int __init mod_start(void)
+{
+ mod_trace("");
+ return 0;
+}
+
+static void __exit mod_stop(void)
+{
+ mod_trace("");
+}
+
+module_init(mod_start);
+module_exit(mod_stop);
+
+MODULE_DESCRIPTION("socketcan softing driver");
+MODULE_AUTHOR("Kurt Van Dijck <kurt.van.dijck@eia.be>");
+MODULE_LICENSE("GPL");
+
+int softing_debug = 1;
+EXPORT_SYMBOL(softing_debug);
+module_param(softing_debug, int , S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(softing_debug, "trace softing functions");
--- /dev/null
+/*
+ * $Id: dev.c 542 2007-11-07 13:57:16Z thuermann $
+ *
+ * Copyright (C) 2007-2008 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/capability.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <net/sock.h>
+#include <linux/rtnetlink.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+
+#include "sysfs.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
+{
+ char *tail;
+ unsigned long val;
+ size_t len;
+
+ *res = 0;
+ len = strlen(cp);
+ if (len == 0)
+ return -EINVAL;
+
+ val = simple_strtoul(cp, &tail, base);
+ if ((*tail == '\0') ||
+ ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
+ *res = val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+#endif
+
+#ifdef CONFIG_SYSFS
+
+/*
+ * SYSFS access functions and attributes. Use same locking as
+ * net/core/net-sysfs.c does.
+ */
+static inline int dev_isalive(const struct net_device *dev)
+{
+ return dev->reg_state <= NETREG_REGISTERED;
+}
+
+/* use same locking rules as GIF* ioctl's */
+static ssize_t can_dev_show(struct device *d,
+ struct device_attribute *attr, char *buf,
+ ssize_t (*fmt)(struct net_device *, char *))
+{
+ struct net_device *dev = to_net_dev(d);
+ ssize_t ret = -EINVAL;
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev))
+ ret = (*fmt)(dev, buf);
+ read_unlock(&dev_base_lock);
+
+ return ret;
+}
+
+/* generate a show function for simple field */
+#define CAN_DEV_SHOW(field, fmt_string) \
+static ssize_t fmt_can_##field(struct net_device *dev, char *buf) \
+{ \
+ struct can_priv *priv = netdev_priv(dev); \
+ return sprintf(buf, fmt_string, priv->field); \
+} \
+static ssize_t show_can_##field(struct device *d, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return can_dev_show(d, attr, buf, fmt_can_##field); \
+}
+
+/* use same locking and permission rules as SIF* ioctl's */
+static ssize_t can_dev_store(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t len,
+ int (*set)(struct net_device *, unsigned long))
+{
+ struct net_device *dev = to_net_dev(d);
+ unsigned long new;
+ int ret = -EINVAL;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ ret = strict_strtoul(buf, 0, &new);
+ if (ret)
+ goto out;
+
+ rtnl_lock();
+ if (dev_isalive(dev)) {
+ ret = (*set)(dev, new);
+ if (!ret)
+ ret = len;
+ }
+ rtnl_unlock();
+out:
+ return ret;
+}
+
+#define CAN_CREATE_FILE(_dev, _name) \
+ if (device_create_file(&_dev->dev, &dev_attr_##_name)) \
+ dev_err(ND2D(_dev), \
+ "Couldn't create device file for ##_name\n")
+
+#define CAN_REMOVE_FILE(_dev, _name) \
+ device_remove_file(&_dev->dev, &dev_attr_##_name) \
+
+CAN_DEV_SHOW(ctrlmode, "0x%x\n");
+
+static int change_can_ctrlmode(struct net_device *dev, unsigned long ctrlmode)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ int err = 0;
+
+ if (priv->state != CAN_STATE_STOPPED)
+ return -EBUSY;
+
+ if (priv->do_set_ctrlmode)
+ err = priv->do_set_ctrlmode(dev, ctrlmode);
+
+ if (!err)
+ priv->ctrlmode = ctrlmode;
+
+ return err;
+}
+
+static ssize_t store_can_ctrlmode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_ctrlmode);
+}
+
+static DEVICE_ATTR(can_ctrlmode, S_IRUGO | S_IWUSR,
+ show_can_ctrlmode, store_can_ctrlmode);
+
+static const char *can_state_names[] = {
+ "active", "bus-warn", "bus-pass" , "bus-off",
+ "stopped", "sleeping", "unkown"
+};
+
+static ssize_t printf_can_state(struct net_device *dev, char *buf)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ enum can_state state;
+ int err = 0;
+
+ if (priv->do_get_state) {
+ err = priv->do_get_state(dev, &state);
+ if (err)
+ goto out;
+ priv->state = state;
+ } else
+ state = priv->state;
+
+ if (state >= ARRAY_SIZE(can_state_names))
+ state = ARRAY_SIZE(can_state_names) - 1;
+ err = sprintf(buf, "%s\n", can_state_names[state]);
+out:
+ return err;
+}
+
+static ssize_t show_can_state(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ return can_dev_show(d, attr, buf, printf_can_state);
+}
+
+static DEVICE_ATTR(can_state, S_IRUGO, show_can_state, NULL);
+
+CAN_DEV_SHOW(restart_ms, "%d\n");
+
+static int change_can_restart_ms(struct net_device *dev, unsigned long ms)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->restart_ms < 0)
+ return -EOPNOTSUPP;
+ priv->restart_ms = ms;
+ return 0;
+}
+
+static ssize_t store_can_restart_ms(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_restart_ms);
+}
+
+static DEVICE_ATTR(can_restart_ms, S_IRUGO | S_IWUSR,
+ show_can_restart_ms, store_can_restart_ms);
+
+static ssize_t printf_can_echo(struct net_device *dev, char *buf)
+{
+ return sprintf(buf, "%d\n", dev->flags & IFF_ECHO ? 1 : 0);
+}
+
+static ssize_t show_can_echo(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ return can_dev_show(d, attr, buf, printf_can_echo);
+}
+
+static int change_can_echo(struct net_device *dev, unsigned long on)
+{
+ if (on)
+ dev->flags |= IFF_ECHO;
+ else
+ dev->flags &= ~IFF_ECHO;
+ return 0;
+}
+
+static ssize_t store_can_echo(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_echo);
+}
+
+static DEVICE_ATTR(can_echo, S_IRUGO | S_IWUSR, show_can_echo, store_can_echo);
+
+static int change_can_restart(struct net_device *dev, unsigned long on)
+{
+ return can_restart_now(dev);
+}
+
+static ssize_t store_can_restart(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_restart);
+}
+
+static DEVICE_ATTR(can_restart, S_IWUSR, NULL, store_can_restart);
+
+/* Show a given attribute if the CAN bittiming group */
+static ssize_t can_btc_show(const struct device *d,
+ struct device_attribute *attr, char *buf,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming_const *btc = priv->bittiming_const;
+ ssize_t ret = -EINVAL;
+
+ WARN_ON(offset >= sizeof(struct can_bittiming_const) ||
+ offset % sizeof(u32) != 0);
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev) && btc)
+ ret = sprintf(buf, "%d\n",
+ *(u32 *)(((u8 *)btc) + offset));
+
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+/* Generate a read-only bittiming const attribute */
+#define CAN_BT_CONST_ENTRY(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_btc_show(d, attr, buf, \
+ offsetof(struct can_bittiming_const, name));\
+} \
+static DEVICE_ATTR(hw_##name, S_IRUGO, show_##name, NULL)
+
+CAN_BT_CONST_ENTRY(tseg1_min);
+CAN_BT_CONST_ENTRY(tseg1_max);
+CAN_BT_CONST_ENTRY(tseg2_min);
+CAN_BT_CONST_ENTRY(tseg2_max);
+CAN_BT_CONST_ENTRY(sjw_max);
+CAN_BT_CONST_ENTRY(brp_min);
+CAN_BT_CONST_ENTRY(brp_max);
+CAN_BT_CONST_ENTRY(brp_inc);
+
+static ssize_t can_bt_show(const struct device *d,
+ struct device_attribute *attr, char *buf,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ ssize_t ret = -EINVAL;
+ u32 *ptr, val;
+
+ WARN_ON(offset >= sizeof(struct can_bittiming) ||
+ offset % sizeof(u32) != 0);
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev)) {
+ ptr = (u32 *)(((u8 *)bt) + offset);
+ if (ptr == &bt->sample_point &&
+ priv->state != CAN_STATE_STOPPED)
+ val = can_sample_point(bt);
+ else
+ val = *ptr;
+ ret = sprintf(buf, "%d\n", val);
+ }
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+static ssize_t can_bt_store(const struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ unsigned long new;
+ ssize_t ret = -EINVAL;
+ u32 *ptr;
+
+ if (priv->state != CAN_STATE_STOPPED)
+ return -EBUSY;
+
+ WARN_ON(offset >= sizeof(struct can_bittiming) ||
+ offset % sizeof(u32) != 0);
+
+ ret = strict_strtoul(buf, 0, &new);
+ if (ret)
+ goto out;
+
+ ptr = (u32 *)(((u8 *)bt) + offset);
+ rtnl_lock();
+ if (dev_isalive(dev)) {
+ *ptr = (u32)new;
+
+ if ((ptr == &bt->bitrate) || (ptr == &bt->sample_point)) {
+ bt->tq = 0;
+ bt->brp = 0;
+ bt->sjw = 0;
+ bt->prop_seg = 0;
+ bt->phase_seg1 = 0;
+ bt->phase_seg2 = 0;
+ } else {
+ bt->bitrate = 0;
+ bt->sample_point = 0;
+ }
+ ret = count;
+ }
+ rtnl_unlock();
+out:
+ return ret;
+}
+
+#define CAN_BT_ENTRY_RO(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_bt_show(d, attr, buf, \
+ offsetof(struct can_bittiming, name)); \
+} \
+static DEVICE_ATTR(hw_##name, S_IRUGO, show_##name, NULL)
+
+CAN_BT_ENTRY_RO(clock);
+
+#define CAN_BT_ENTRY(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_bt_show(d, attr, buf, \
+ offsetof(struct can_bittiming, name)); \
+} \
+static ssize_t store_##name(struct device *d, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ return can_bt_store(d, attr, buf, count, \
+ offsetof(struct can_bittiming, name)); \
+} \
+static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_##name, store_##name)
+
+CAN_BT_ENTRY(bitrate);
+CAN_BT_ENTRY(sample_point);
+CAN_BT_ENTRY(tq);
+CAN_BT_ENTRY(prop_seg);
+CAN_BT_ENTRY(phase_seg1);
+CAN_BT_ENTRY(phase_seg2);
+CAN_BT_ENTRY(sjw);
+
+static struct attribute *can_bittiming_attrs[] = {
+ &dev_attr_hw_tseg1_min.attr,
+ &dev_attr_hw_tseg1_max.attr,
+ &dev_attr_hw_tseg2_max.attr,
+ &dev_attr_hw_tseg2_min.attr,
+ &dev_attr_hw_sjw_max.attr,
+ &dev_attr_hw_brp_min.attr,
+ &dev_attr_hw_brp_max.attr,
+ &dev_attr_hw_brp_inc.attr,
+ &dev_attr_hw_clock.attr,
+ &dev_attr_bitrate.attr,
+ &dev_attr_sample_point.attr,
+ &dev_attr_tq.attr,
+ &dev_attr_prop_seg.attr,
+ &dev_attr_phase_seg1.attr,
+ &dev_attr_phase_seg2.attr,
+ &dev_attr_sjw.attr,
+ NULL
+};
+
+static struct attribute_group can_bittiming_group = {
+ .name = "can_bittiming",
+ .attrs = can_bittiming_attrs,
+};
+
+/* Show a given attribute in the CAN statistics group */
+static ssize_t can_stat_show(const struct device *d,
+ struct device_attribute *attr, char *buf,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_device_stats *stats = &priv->can_stats;
+ ssize_t ret = -EINVAL;
+
+ WARN_ON(offset >= sizeof(struct can_device_stats) ||
+ offset % sizeof(unsigned long) != 0);
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev))
+ ret = sprintf(buf, "%ld\n",
+ *(unsigned long *)(((u8 *)stats) + offset));
+
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+/* Generate a read-only CAN statistics attribute */
+#define CAN_STAT_ENTRY(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_stat_show(d, attr, buf, \
+ offsetof(struct can_device_stats, name)); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
+
+CAN_STAT_ENTRY(error_warning);
+CAN_STAT_ENTRY(error_passive);
+CAN_STAT_ENTRY(bus_error);
+CAN_STAT_ENTRY(arbitration_lost);
+CAN_STAT_ENTRY(data_overrun);
+CAN_STAT_ENTRY(wakeup);
+CAN_STAT_ENTRY(restarts);
+
+static struct attribute *can_statistics_attrs[] = {
+ &dev_attr_error_warning.attr,
+ &dev_attr_error_passive.attr,
+ &dev_attr_bus_error.attr,
+ &dev_attr_arbitration_lost.attr,
+ &dev_attr_data_overrun.attr,
+ &dev_attr_wakeup.attr,
+ &dev_attr_restarts.attr,
+ NULL
+};
+
+static struct attribute_group can_statistics_group = {
+ .name = "can_statistics",
+ .attrs = can_statistics_attrs,
+};
+
+void can_create_sysfs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ int err;
+
+ CAN_CREATE_FILE(dev, can_ctrlmode);
+ CAN_CREATE_FILE(dev, can_echo);
+ CAN_CREATE_FILE(dev, can_restart);
+ CAN_CREATE_FILE(dev, can_state);
+ CAN_CREATE_FILE(dev, can_restart_ms);
+
+ err = sysfs_create_group(&(dev->dev.kobj),
+ &can_statistics_group);
+ if (err) {
+ printk(KERN_EMERG
+ "couldn't create sysfs group for CAN statistics\n");
+ }
+
+ if (priv->bittiming_const) {
+ err = sysfs_create_group(&(dev->dev.kobj),
+ &can_bittiming_group);
+ if (err) {
+ printk(KERN_EMERG "couldn't create sysfs "
+ "group for CAN bittiming\n");
+ }
+ }
+}
+
+void can_remove_sysfs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ CAN_REMOVE_FILE(dev, can_ctrlmode);
+ CAN_REMOVE_FILE(dev, can_echo);
+ CAN_REMOVE_FILE(dev, can_state);
+ CAN_REMOVE_FILE(dev, can_restart);
+ CAN_REMOVE_FILE(dev, can_restart_ms);
+
+ sysfs_remove_group(&(dev->dev.kobj), &can_statistics_group);
+ if (priv->bittiming_const)
+ sysfs_remove_group(&(dev->dev.kobj), &can_bittiming_group);
+}
+
+#endif /* CONFIG_SYSFS */
+
+
+
--- /dev/null
+/*
+ * $Id: dev.c 542 2007-11-07 13:57:16Z thuermann $
+ *
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CAN_SYSFS_H
+#define CAN_SYSFS_H
+
+void can_create_sysfs(struct net_device *dev);
+void can_remove_sysfs(struct net_device *dev);
+
+#endif /* CAN_SYSFS_H */
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/version.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/can.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
#include <net/rtnetlink.h>
+#endif
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
static __initdata const char banner[] =
KERN_INFO "vcan: Virtual CAN interface driver\n";
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+static void *kzalloc(size_t size, unsigned int __nocast flags)
+{
+ void *ret = kmalloc(size, flags);
+
+ if (ret)
+ memset(ret, 0, size);
+
+ return ret;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static int numdev = 4; /* default number of virtual CAN interfaces */
+module_param(numdev, int, S_IRUGO);
+MODULE_PARM_DESC(numdev, "Number of virtual CAN devices");
+#endif
/*
* CAN test feature:
module_param(echo, bool, S_IRUGO);
MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static struct net_device **vcan_devs; /* root pointer to netdevice structs */
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+#define PRIVSIZE sizeof(struct net_device_stats)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+#define PRIVSIZE 0
+#endif
static void vcan_rx(struct sk_buff *skb, struct net_device *dev)
{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
struct net_device_stats *stats = &dev->stats;
+#else
+ struct net_device_stats *stats = netdev_priv(dev);
+#endif
stats->rx_packets++;
stats->rx_bytes += skb->len;
static int vcan_tx(struct sk_buff *skb, struct net_device *dev)
{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
struct net_device_stats *stats = &dev->stats;
+#else
+ struct net_device_stats *stats = netdev_priv(dev);
+#endif
int loop;
stats->tx_packets++;
return NETDEV_TX_OK;
}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+static struct net_device_stats *vcan_get_stats(struct net_device *dev)
+{
+ struct net_device_stats *stats = netdev_priv(dev);
+
+ return stats;
+}
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+static const struct net_device_ops vcan_netdev_ops = {
+ .ndo_start_xmit = vcan_tx,
+};
+#endif
+
static void vcan_setup(struct net_device *dev)
{
- dev->type = ARPHRD_CAN;
- dev->mtu = sizeof(struct can_frame);
- dev->hard_header_len = 0;
- dev->addr_len = 0;
- dev->tx_queue_len = 0;
- dev->flags = IFF_NOARP;
+ dev->type = ARPHRD_CAN;
+ dev->mtu = sizeof(struct can_frame);
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 0;
+ dev->flags = IFF_NOARP;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#define IFF_ECHO IFF_LOOPBACK
+#endif
/* set flags according to driver capabilities */
if (echo)
dev->flags |= IFF_ECHO;
- dev->hard_start_xmit = vcan_tx;
- dev->destructor = free_netdev;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+ dev->netdev_ops = &vcan_netdev_ops;
+#else
+ dev->hard_start_xmit = vcan_tx;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ dev->destructor = free_netdev;
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ dev->get_stats = vcan_get_stats;
+#endif
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
static struct rtnl_link_ops vcan_link_ops __read_mostly = {
- .kind = "vcan",
- .setup = vcan_setup,
+ .kind = "vcan",
+ .setup = vcan_setup,
};
static __init int vcan_init_module(void)
{
rtnl_link_unregister(&vcan_link_ops);
}
+#else
+static __init int vcan_init_module(void)
+{
+ int i, result;
+
+ printk(banner);
+
+ /* register at least one interface */
+ if (numdev < 1)
+ numdev = 1;
+
+ printk(KERN_INFO
+ "vcan: registering %d virtual CAN interfaces. (echo %s)\n",
+ numdev, echo ? "enabled" : "disabled");
+
+ vcan_devs = kzalloc(numdev * sizeof(struct net_device *), GFP_KERNEL);
+ if (!vcan_devs) {
+ printk(KERN_ERR "vcan: Can't allocate vcan devices array!\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < numdev; i++) {
+ vcan_devs[i] = alloc_netdev(PRIVSIZE, "vcan%d", vcan_setup);
+ if (!vcan_devs[i]) {
+ printk(KERN_ERR "vcan: error allocating net_device\n");
+ result = -ENOMEM;
+ goto out;
+ }
+
+ result = register_netdev(vcan_devs[i]);
+ if (result < 0) {
+ printk(KERN_ERR
+ "vcan: error %d registering interface %s\n",
+ result, vcan_devs[i]->name);
+ free_netdev(vcan_devs[i]);
+ vcan_devs[i] = NULL;
+ goto out;
+ }
+ }
+
+ return 0;
+
+ out:
+ for (i = 0; i < numdev; i++) {
+ if (vcan_devs[i]) {
+ unregister_netdev(vcan_devs[i]);
+ free_netdev(vcan_devs[i]);
+ }
+ }
+
+ kfree(vcan_devs);
+
+ return result;
+}
+
+static __exit void vcan_cleanup_module(void)
+{
+ int i;
+
+ for (i = 0; i < numdev; i++) {
+ if (vcan_devs[i]) {
+ unregister_netdev(vcan_devs[i]);
+ free_netdev(vcan_devs[i]);
+ }
+ }
+
+ kfree(vcan_devs);
+}
+#endif
module_init(vcan_init_module);
module_exit(vcan_cleanup_module);
*
* Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
*
+ * $Id$
+ *
* Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
* Urs Thuermann <urs.thuermann@volkswagen.de>
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
#ifndef CAN_H
#define CAN_H
+#include <linux/version.h>
#include <linux/types.h>
#include <linux/socket.h>
*
* Definitions for CAN Broadcast Manager (BCM)
*
+ * $Id$
+ *
* Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* All rights reserved.
*
* Protoypes and definitions for CAN protocol modules using the PF_CAN core
*
+ * $Id$
+ *
* Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
* Urs Thuermann <urs.thuermann@volkswagen.de>
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
#include <linux/skbuff.h>
#include <linux/netdevice.h>
-#define CAN_VERSION "20071116"
+#define CAN_VERSION "20090105"
/* increment this number each time you change some user-space interface */
#define CAN_ABI_VERSION "8"
int protocol;
int capability;
struct proto_ops *ops;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
struct proto *prot;
+#else
+ struct module *owner;
+ int (*init)(struct sock *sk);
+ size_t obj_size;
+#endif
};
/* function prototypes for the CAN networklayer core (af_can.c) */
--- /dev/null
+/*
+ * linux/can/dev.h
+ *
+ * Definitions for CAN controller network devices lib (work in progress)
+ *
+ * $Id$
+ *
+ * Author: Andrey Volkov <avolkov@varma-el.com>
+ * Copyright (c) 2006 Varma Electronics Oy
+ *
+ */
+
+#ifndef CAN_DEVICE_H
+#define CAN_DEVICE_H
+
+#include <linux/version.h>
+#include <linux/can/error.h>
+
+/*
+ * CAN bitrate and bit-timing
+ */
+struct can_bittiming {
+ u32 bitrate;
+ u32 sample_point;
+ u32 tq;
+ u32 prop_seg;
+ u32 phase_seg1;
+ u32 phase_seg2;
+ u32 sjw;
+ u32 clock;
+ u32 brp;
+};
+
+struct can_bittiming_const {
+ u32 tseg1_min;
+ u32 tseg1_max;
+ u32 tseg2_min;
+ u32 tseg2_max;
+ u32 sjw_max;
+ u32 brp_min;
+ u32 brp_max;
+ u32 brp_inc;
+};
+
+/*
+ * CAN mode
+ */
+enum can_mode {
+ CAN_MODE_STOP = 0,
+ CAN_MODE_START,
+ CAN_MODE_SLEEP
+};
+
+/*
+ * CAN controller mode
+ */
+#define CAN_CTRLMODE_LOOPBACK 0x1
+#define CAN_CTRLMODE_LISTENONLY 0x2
+#define CAN_CTRLMODE_3_SAMPLES 0x4 /* Triple sampling mode */
+
+/*
+ * CAN operational and error states
+ */
+enum can_state {
+ CAN_STATE_ACTIVE = 0,
+ CAN_STATE_BUS_WARNING,
+ CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_BUS_OFF,
+ CAN_STATE_STOPPED,
+ CAN_STATE_SLEEPING
+};
+
+/*
+ * CAN device statistics
+ */
+struct can_device_stats {
+ unsigned long error_warning;
+ unsigned long data_overrun;
+ unsigned long wakeup;
+ unsigned long bus_error;
+ unsigned long error_passive;
+ unsigned long arbitration_lost;
+ unsigned long restarts;
+ unsigned long bus_error_at_init;
+};
+
+/*
+ * CAN common private data
+ */
+#define CAN_ECHO_SKB_MAX 4
+
+struct can_priv {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ struct net_device_stats net_stats;
+#endif
+ struct can_device_stats can_stats;
+
+ struct can_bittiming bittiming;
+ struct can_bittiming_const *bittiming_const;
+
+ spinlock_t irq_lock;
+ /* Please hold this lock when touching net_stats/can_stats */
+ spinlock_t stats_lock;
+
+ enum can_state state;
+ u32 ctrlmode;
+
+ int restart_ms;
+ struct timer_list timer;
+
+ struct sk_buff *echo_skb[CAN_ECHO_SKB_MAX];
+
+ int (*do_set_bittiming)(struct net_device *dev);
+ int (*do_get_state)(struct net_device *dev, enum can_state *state);
+ int (*do_set_mode)(struct net_device *dev, enum can_mode mode);
+ int (*do_set_ctrlmode)(struct net_device *dev, u32 ctrlmode);
+ int (*do_get_ctrlmode)(struct net_device *dev, u32 *ctrlmode);
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define ND2D(_ndev) (_ndev->class_dev.dev)
+#else
+#define ND2D(_ndev) (_ndev->dev.parent)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#define IFF_ECHO IFF_LOOPBACK
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+struct net_device_stats *can_get_stats(struct net_device *dev);
+#endif
+
+struct net_device *alloc_candev(int sizeof_priv);
+void free_candev(struct net_device *dev);
+
+int can_set_bittiming(struct net_device *dev);
+
+int can_restart_now(struct net_device *dev);
+
+void can_bus_off(struct net_device *dev);
+
+void can_close_cleanup(struct net_device *dev);
+
+void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx);
+void can_get_echo_skb(struct net_device *dev, int idx);
+
+int can_sample_point(struct can_bittiming *bt);
+
+#endif /* CAN_DEVICE_H */
*
* Definitions of the CAN error frame to be filtered and passed to the user.
*
+ * $Id$
+ *
* Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* All rights reserved.
--- /dev/null
+
+/*
+ * linux/can/ioctl.h
+ *
+ * Definitions for CAN controller setup (work in progress)
+ *
+ * $Id$
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_IOCTL_H
+#define CAN_IOCTL_H
+
+#include <linux/sockios.h>
+
+/*
+ * CAN bitrate
+ */
+#define CAN_BITRATE_UNCONFIGURED ((__u32) 0xFFFFFFFFU)
+#define CAN_BITRATE_UNKNOWN 0
+#define CAN_BITRATE_DEFAULT 500000
+
+/*
+ * CAN custom bit time
+ */
+enum can_bittimes {
+ CAN_BITTIME_STD,
+ CAN_BITTIME_BTR
+};
+
+/* TSEG1 of controllers usually is a sum of synch_seg (always 1),
+ * prop_seg and phase_seg1, TSEG2 = phase_seg2 */
+
+struct can_bittime_std {
+ __u32 brp; /* baud rate prescaler */
+ __u8 prop_seg; /* from 1 to 8 */
+ __u8 phase_seg1; /* from 1 to 8 */
+ __u8 phase_seg2; /* from 1 to 8 */
+ __u8 sjw:7; /* from 1 to 4 */
+ __u8 sam:1; /* 1 - enable triple sampling */
+};
+
+struct can_bittime_btr {
+ __u8 btr0;
+ __u8 btr1;
+};
+
+struct can_bittime {
+ enum can_bittimes type;
+ union {
+ struct can_bittime_std std;
+ struct can_bittime_btr btr;
+ };
+};
+
+/*
+ * CAN mode
+ */
+enum can_mode {
+ CAN_MODE_STOP = 0,
+ CAN_MODE_START,
+ CAN_MODE_SLEEP
+};
+
+/*
+ * CAN controller mode
+ */
+#define CAN_CTRLMODE_LOOPBACK 0x1
+#define CAN_CTRLMODE_LISTENONLY 0x2
+
+/*
+ * CAN operational and error states
+ */
+enum can_state {
+ CAN_STATE_ACTIVE = 0,
+ CAN_STATE_BUS_WARNING,
+ CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_BUS_OFF,
+ CAN_STATE_STOPPED,
+ CAN_STATE_SLEEPING
+};
+
+/*
+ * CAN device statistics
+ */
+struct can_device_stats {
+ int error_warning;
+ int data_overrun;
+ int wakeup;
+ int bus_error;
+ int error_passive;
+ int arbitration_lost;
+ int restarts;
+ int bus_error_at_init;
+};
+
+#endif /* CAN_IOCTL_H */
--- /dev/null
+/*
+ * linux/can/isotp.h
+ *
+ * Definitions for isotp CAN sockets
+ *
+ * $Id$
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2008 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ISOTP_H
+#define CAN_ISOTP_H
+
+#include <linux/can.h>
+
+#define SOL_CAN_ISOTP (SOL_CAN_BASE + CAN_ISOTP)
+
+/* for socket options affecting the socket (not the global system) */
+
+#define CAN_ISOTP_OPTS 1
+#define CAN_ISOTP_RECV_FC 2
+
+struct can_isotp_options {
+
+ __u32 flags; /* set flags for isotp behaviour. */
+ /* __u32 value : flags see below */
+
+ __u32 frame_txtime; /* frame transmission time (N_As/N_Ar) */
+ /* __u32 value : time in nano secs */
+
+ __u8 ext_address; /* set address for extended addressing */
+ /* __u8 value : extended address */
+
+ __u8 txpad_content; /* set content of padding byte (tx) */
+ /* __u8 value : content on tx path */
+
+ __u8 rxpad_content; /* set content of padding byte (rx) */
+ /* __u8 value : content on rx path */
+};
+
+struct can_isotp_fc_options {
+
+ __u8 bs; /* blocksize provided in FC frame */
+ /* __u8 value : blocksize. 0 = off */
+
+ __u8 stmin; /* separation time provided in FC frame */
+ /* __u8 value : */
+ /* 0x00 - 0x7F : 0 - 127 ms */
+ /* 0x80 - 0xF0 : reserved */
+ /* 0xF1 - 0xF9 : 100 us - 900 us */
+ /* 0xFA - 0xFF : reserved */
+
+ __u8 wftmax; /* max. number of wait frame transmiss. */
+ /* __u8 value : 0 = omit FC N_PDU WT */
+};
+
+
+/* flags for isotp behaviour */
+
+#define CAN_ISOTP_LISTEN_MODE 0x01 /* listen only (do not send FC) */
+#define CAN_ISOTP_EXTEND_ADDR 0x02 /* enable extended addressing */
+#define CAN_ISOTP_TX_PADDING 0x04 /* enable CAN frame padding tx path */
+#define CAN_ISOTP_RX_PADDING 0x08 /* enable CAN frame padding rx path */
+#define CAN_ISOTP_CHK_PAD_LEN 0x10 /* check received CAN frame padding */
+#define CAN_ISOTP_CHK_PAD_DATA 0x20 /* check received CAN frame padding */
+#define CAN_ISOTP_HALF_DUPLEX 0x40 /* half duplex error state handling */
+
+
+/* default values */
+
+#define CAN_ISOTP_DEFAULT_FLAGS 0
+#define CAN_ISOTP_DEFAULT_EXT_ADDRESS 0x00
+#define CAN_ISOTP_DEFAULT_RXPAD_CONTENT 0x00
+#define CAN_ISOTP_DEFAULT_TXPAD_CONTENT 0x00
+#define CAN_ISOTP_DEFAULT_FRAME_TXTIME 0
+#define CAN_ISOTP_DEFAULT_RECV_BS 0
+#define CAN_ISOTP_DEFAULT_RECV_STMIN 0x00
+#define CAN_ISOTP_DEFAULT_RECV_WFTMAX 0
+
+/*
+ * Remark on CAN_ISOTP_DEFAULT_RECV_* values:
+ *
+ * We can strongly assume, that the Linux Kernel implementation of
+ * CAN_ISOTP is capable to run with BS=0, STmin=0 and WFTmax=0.
+ * But as we like to be able to behave as a commonly available ECU,
+ * these default settings can be changed via sockopts.
+ * For that reason the STmin value is intentionally _not_ checked for
+ * consistency and copied directly into the flow control (FC) frame.
+ *
+ */
+
+#endif
--- /dev/null
+#ifndef __CAN_PLATFORM_MCP251X_H__
+#define __CAN_PLATFORM_MCP251X_H__
+
+/*
+ *
+ * CAN bus driver for Microchip 251x CAN Controller with SPI Interface
+ *
+ */
+
+/**
+ * struct mcp251x_platform_data - MCP251X SPI CAN controller platform data
+ * @oscillator_frequency: - oscillator frequency in Hz
+ * @model: - actual type of chip
+ * @board_specific_setup: - called before probing the chip (power,reset)
+ * @transceiver_enable: - called to power on/off the transceiver
+ * @power_enable: - called to power on/off the mcp *and* the
+ * transceiver
+ *
+ * Please note that you should define power_enable or transceiver_enable or
+ * none of them. Defining both of them is no use.
+ *
+ */
+
+struct mcp251x_platform_data {
+ unsigned long oscillator_frequency;
+ int model;
+#define CAN_MCP251X_MCP2510 0
+#define CAN_MCP251X_MCP2515 1
+ int (*board_specific_setup)(struct spi_device *spi);
+ int (*transceiver_enable)(int enable);
+ int (*power_enable) (int enable);
+};
+
+#endif /* __CAN_PLATFORM_MCP251X_H__ */
--- /dev/null
+#ifndef _CAN_PLATFORM_SJA1000_H_
+#define _CAN_PLATFORM_SJA1000_H_
+
+/* clock divider register */
+#define CDR_CLKOUT_MASK 0x07
+#define CDR_CLK_OFF 0x08 /* Clock off (CLKOUT pin) */
+#define CDR_RXINPEN 0x20 /* TX1 output is RX irq output */
+#define CDR_CBP 0x40 /* CAN input comparator bypass */
+#define CDR_PELICAN 0x80 /* PeliCAN mode */
+
+/* output control register */
+#define OCR_MODE_BIPHASE 0x00
+#define OCR_MODE_TEST 0x01
+#define OCR_MODE_NORMAL 0x02
+#define OCR_MODE_CLOCK 0x03
+#define OCR_TX0_INVERT 0x04
+#define OCR_TX0_PULLDOWN 0x08
+#define OCR_TX0_PULLUP 0x10
+#define OCR_TX0_PUSHPULL 0x18
+#define OCR_TX1_INVERT 0x20
+#define OCR_TX1_PULLDOWN 0x40
+#define OCR_TX1_PULLUP 0x80
+#define OCR_TX1_PUSHPULL 0xc0
+
+struct sja1000_platform_data {
+ u32 clock; /* CAN bus oscillator frequency in Hz */
+
+ u8 ocr; /* output control register */
+ u8 cdr; /* clock divider register */
+};
+
+#endif /* !_CAN_PLATFORM_SJA1000_H_ */
*
* Definitions for raw CAN sockets
*
+ * $Id$
+ *
* Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
* Urs Thuermann <urs.thuermann@volkswagen.de>
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
--- /dev/null
+/*
+ * linux/can/version.h
+ *
+ * Version information for the CAN network layer implementation
+
+ * Author: Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_VERSION_H
+#define CAN_VERSION_H
+
+#define RCSID(s) asm(".section .rodata.str1.1,\"aMS\",@progbits,1\n\t" \
+ ".string \"" s "\"\n\t.previous\n")
+
+RCSID("$Id$");
+
+#endif /* CAN_VERSION_H */
CAN messages are used on the bus (e.g. in automotive environments).
To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM.
+config CAN_ISOTP
+ tristate "ISO 15765-2 CAN transport protocol"
+ depends on CAN && EXPERIMENTAL
+ default N
+ ---help---
+ CAN Transport Protocols offer support for segmented Point-to-Point
+ communication between CAN nodes via two defined CAN Identifiers.
+ This protocol driver implements data transfers according ISO 15765-2.
+ WARNING: This is ALPHA code for discussions and first tests that
+ should not be used in productive environments.
+ In the discussion the Socket-API to the userspace or the ISO-TP
+ socket options or the return values may change! Current behaviour:
+ - no ISO-TP specific return values are provided to the userspace
+ - no support for sending wait frames to the data source in rx path
+ - when a transfer (tx) is on the run the next write() blocks
+ until the running transfer is done
source "drivers/net/can/Kconfig"
-#
-# Makefile for the Linux Controller Area Network core.
-#
-
obj-$(CONFIG_CAN) += can.o
can-objs := af_can.o proc.o
obj-$(CONFIG_CAN_BCM) += can-bcm.o
can-bcm-objs := bcm.o
+
+obj-$(CONFIG_CAN_ISOTP) += can-isotp.o
+can-isotp-objs := isotp.o
*/
#include <linux/module.h>
+#include <linux/version.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
#include <linux/uaccess.h>
+#else
+#include <asm/uaccess.h>
+#endif
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/can.h>
#include <linux/can/core.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#include <net/net_namespace.h>
+#endif
#include <net/sock.h>
#include "af_can.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#include "compat.h"
+#endif
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
static __initdata const char banner[] = KERN_INFO
"can: controller area network core (" CAN_VERSION_STRING ")\n";
static struct dev_rcv_lists can_rx_alldev_list;
static DEFINE_SPINLOCK(can_rcvlists_lock);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
static struct kmem_cache *rcv_cache __read_mostly;
+#else
+static kmem_cache_t *rcv_cache;
+#endif
/* table of registered CAN protocols */
static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
return sock_get_timestamp(sk, (struct timeval __user *)arg);
default:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
return -ENOIOCTLCMD;
+#else
+ return dev_ioctl(cmd, (void __user *)arg);
+#endif
}
}
static void can_sock_destruct(struct sock *sk)
{
skb_queue_purge(&sk->sk_receive_queue);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+ if (sk->sk_protinfo)
+ kfree(sk->sk_protinfo);
+#endif
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
static int can_create(struct net *net, struct socket *sock, int protocol)
+#else
+static int can_create(struct socket *sock, int protocol)
+#endif
{
struct sock *sk;
struct can_proto *cp;
if (protocol < 0 || protocol >= CAN_NPROTO)
return -EINVAL;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
if (net != &init_net)
return -EAFNOSUPPORT;
+#endif
#ifdef CONFIG_KMOD
/* try to load protocol module, when CONFIG_KMOD is defined */
spin_lock(&proto_tab_lock);
cp = proto_tab[protocol];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
if (cp && !try_module_get(cp->prot->owner))
cp = NULL;
+#else
+ if (cp && !try_module_get(cp->owner))
+ cp = NULL;
+#endif
spin_unlock(&proto_tab_lock);
/* check for available protocol and correct usage */
sock->ops = cp->ops;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+ sk = sk_alloc(PF_CAN, GFP_KERNEL, cp->prot, 1);
+#else
+ sk = sk_alloc(PF_CAN, GFP_KERNEL, 1, 0);
+#endif
if (!sk) {
err = -ENOMEM;
goto errout;
}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+ if (cp->obj_size) {
+ sk->sk_protinfo = kmalloc(cp->obj_size, GFP_KERNEL);
+ if (!sk->sk_protinfo) {
+ sk_free(sk);
+ err = -ENOMEM;
+ goto errout;
+ }
+ }
+ sk_set_owner(sk, proto_tab[protocol]->owner);
+#endif
+
sock_init_data(sock, sk);
sk->sk_destruct = can_sock_destruct;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
if (sk->sk_prot->init)
err = sk->sk_prot->init(sk);
+#else
+ if (cp->init)
+ err = cp->init(sk);
+#endif
if (err) {
/* release sk on errors */
}
errout:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
module_put(cp->prot->owner);
+#else
+ module_put(cp->owner);
+#endif
return err;
}
}
skb->protocol = htons(ETH_P_CAN);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
skb_reset_network_header(skb);
skb_reset_transport_header(skb);
+#else
+ skb->nh.raw = skb->data;
+ skb->h.raw = skb->data;
+#endif
if (loop) {
/* local loopback of sent CAN frames */
* after each skb_clone() or skb_orphan() usage.
*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#define IFF_ECHO IFF_LOOPBACK
+#endif
if (!(skb->dev->flags & IFF_ECHO)) {
/*
* If the interface is not capable to do loopback
return n ? d : NULL;
}
+/**
+ * find_rcv_list - determine optimal filterlist inside device filter struct
+ * @can_id: pointer to CAN identifier of a given can_filter
+ * @mask: pointer to CAN mask of a given can_filter
+ * @d: pointer to the device filter struct
+ *
+ * Description:
+ * Returns the optimal filterlist to reduce the filter handling in the
+ * receive path. This function is called by service functions that need
+ * to register or unregister a can_filter in the filter lists.
+ *
+ * A filter matches in general, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * so every bit set in the mask (even CAN_EFF_FLAG, CAN_RTR_FLAG) describe
+ * relevant bits for the filter.
+ *
+ * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error frames (CAN_ERR_FLAG bit set in mask). For error frames
+ * there is a special filterlist and a special rx path filter handling.
+ *
+ * Return:
+ * Pointer to optimal filterlist for the given can_id/mask pair.
+ * Constistency checked mask.
+ * Reduced can_id to have a preprocessed filter compare value.
+ */
static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
struct dev_rcv_lists *d)
{
canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */
- /* filter error frames */
+ /* filter for error frames in extra filterlist */
if (*mask & CAN_ERR_FLAG) {
- /* clear CAN_ERR_FLAG in list entry */
+ /* clear CAN_ERR_FLAG in filter entry */
*mask &= CAN_ERR_MASK;
return &d->rx[RX_ERR];
}
- /* ensure valid values in can_mask */
- if (*mask & CAN_EFF_FLAG)
- *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
- else
- *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+ /* with cleared CAN_ERR_FLAG we have a simple mask/value filterpair */
+
+#define CAN_EFF_RTR_FLAGS (CAN_EFF_FLAG | CAN_RTR_FLAG)
+
+ /* ensure valid values in can_mask for 'SFF only' frame filtering */
+ if ((*mask & CAN_EFF_FLAG) && !(*can_id & CAN_EFF_FLAG))
+ *mask &= (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS);
/* reduce condition testing at receive time */
*can_id &= *mask;
if (!(*mask))
return &d->rx[RX_ALL];
- /* use extra filterset for the subscription of exactly *ONE* can_id */
- if (*can_id & CAN_EFF_FLAG) {
- if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
- /* RFC: a use-case for hash-tables in the future? */
- return &d->rx[RX_EFF];
+ /* extra filterlists for the subscription of a single non-RTR can_id */
+ if (((*mask & CAN_EFF_RTR_FLAGS) == CAN_EFF_RTR_FLAGS)
+ && !(*can_id & CAN_RTR_FLAG)) {
+
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) {
+ /* RFC: a future use-case for hash-tables? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS))
+ return &d->rx_sff[*can_id];
}
- } else {
- if (*mask == CAN_SFF_MASK)
- return &d->rx_sff[*can_id];
}
/* default: filter via can_id/can_mask */
* The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
* filter for error frames (CAN_ERR_FLAG bit set in mask).
*
+ * The provided pointer to the sk_buff is guaranteed to be valid as long as
+ * the callback function is running. The callback function must *not* free
+ * the given sk_buff while processing it's task. When the given sk_buff is
+ * needed after the end of the callback function it must be cloned inside
+ * the callback function with skb_clone().
+ *
* Return:
* 0 on success
* -ENOMEM on missing cache mem to create subscription entry
static inline void deliver(struct sk_buff *skb, struct receiver *r)
{
- struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
-
- if (clone) {
- clone->sk = skb->sk;
- r->func(clone, r->data);
- r->matches++;
- }
+ r->func(skb, r->data);
+ r->matches++;
}
static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
}
}
- /* check CAN_ID specific entries */
+ /* check filterlists for single non-RTR can_ids */
+ if (can_id & CAN_RTR_FLAG)
+ return matches;
+
if (can_id & CAN_EFF_FLAG) {
hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
if (r->can_id == can_id) {
return matches;
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
static int can_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
+#else
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt)
+#endif
{
struct dev_rcv_lists *d;
struct can_frame *cf = (struct can_frame *)skb->data;
int matches;
- if (dev->type != ARPHRD_CAN || dev_net(dev) != &init_net) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ if (dev->type != ARPHRD_CAN || !net_eq(dev_net(dev), &init_net)) {
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
+#else
+ if (dev->type != ARPHRD_CAN) {
+#endif
kfree_skb(skb);
return 0;
}
return -EINVAL;
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
err = proto_register(cp->prot, 0);
if (err < 0)
return err;
+#endif
spin_lock(&proto_tab_lock);
if (proto_tab[proto]) {
}
spin_unlock(&proto_tab_lock);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
if (err < 0)
proto_unregister(cp->prot);
+#endif
return err;
}
proto_tab[proto] = NULL;
spin_unlock(&proto_tab_lock);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
proto_unregister(cp->prot);
+#endif
}
EXPORT_SYMBOL(can_proto_unregister);
struct net_device *dev = (struct net_device *)data;
struct dev_rcv_lists *d;
- if (dev_net(dev) != &init_net)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ if (!net_eq(dev_net(dev), &init_net))
+ return NOTIFY_DONE;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ if (dev->nd_net != &init_net)
return NOTIFY_DONE;
+#endif
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
{
printk(banner);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
0, 0, NULL);
+#else
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL, NULL);
+#endif
if (!rcv_cache)
return -ENOMEM;
/*
+ * $Id$
+ *
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* All rights reserved.
*
--- /dev/null
+/*
+ * bcm.c - Broadcast Manager to filter/send (cyclic) CAN content
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/uio.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <linux/can/bcm.h>
+#include <net/sock.h>
+#include "compat.h"
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
+
+/* use of last_frames[index].can_dlc */
+#define RX_RECV 0x40 /* received data for this element */
+#define RX_THR 0x80 /* element not been sent due to throttle feature */
+#define BCM_CAN_DLC_MASK 0x0F /* clean private flags in can_dlc by masking */
+
+/* get best masking value for can_rx_register() for a given single can_id */
+#define REGMASK(id) ((id & CAN_EFF_FLAG) ? \
+ (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \
+ (CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
+
+#define CAN_BCM_VERSION CAN_VERSION
+static __initdata const char banner[] = KERN_INFO
+ "can: broadcast manager protocol (rev " CAN_BCM_VERSION ")\n";
+
+MODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+#error This code only supports Kernel versions _below_ 2.6.22
+#error For 2.6.22+ Kernels please use bcm.c instead of bcm-prior-2-6-22.c
+#endif
+
+/* easy access to can_frame payload */
+static inline u64 GET_U64(const struct can_frame *cp)
+{
+ return *(u64 *)cp->data;
+}
+
+struct bcm_op {
+ struct list_head list;
+ int ifindex;
+ canid_t can_id;
+ int flags;
+ unsigned long frames_abs, frames_filtered;
+ struct timeval ival1, ival2;
+ struct timer_list timer, thrtimer;
+ struct timeval rx_stamp;
+ unsigned long j_ival1, j_ival2, j_lastmsg;
+ int rx_ifindex;
+ int count;
+ int nframes;
+ int currframe;
+ struct can_frame *frames;
+ struct can_frame *last_frames;
+ struct can_frame sframe;
+ struct can_frame last_sframe;
+ struct sock *sk;
+ struct net_device *rx_reg_dev;
+};
+
+static struct proc_dir_entry *proc_dir;
+
+struct bcm_sock {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+ struct sock sk;
+#else
+ struct sock *sk;
+#endif
+ int bound;
+ int ifindex;
+ struct notifier_block notifier;
+ struct list_head rx_ops;
+ struct list_head tx_ops;
+ unsigned long dropped_usr_msgs;
+ struct proc_dir_entry *bcm_proc_read;
+ char procname [9]; /* pointer printed in ASCII with \0 */
+};
+
+static inline struct bcm_sock *bcm_sk(const struct sock *sk)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+ return (struct bcm_sock *)sk;
+#else
+ return (struct bcm_sock *)sk->sk_protinfo;
+#endif
+}
+
+#define CFSIZ sizeof(struct can_frame)
+#define OPSIZ sizeof(struct bcm_op)
+#define MHSIZ sizeof(struct bcm_msg_head)
+
+/*
+ * rounded_tv2jif - calculate jiffies from timeval including optional up
+ * @tv: pointer to timeval
+ *
+ * Description:
+ * Unlike timeval_to_jiffies() provided in include/linux/jiffies.h, this
+ * function is intentionally more relaxed on precise timer ticks to get
+ * exact one jiffy for requested 1000us on a 1000HZ machine.
+ * This code is to be removed when upgrading to kernel hrtimer.
+ *
+ * Return:
+ * calculated jiffies (max: ULONG_MAX)
+ */
+static unsigned long rounded_tv2jif(const struct timeval *tv)
+{
+ unsigned long sec = tv->tv_sec;
+ unsigned long usec = tv->tv_usec;
+ unsigned long jif;
+
+ if (sec > ULONG_MAX / HZ)
+ return ULONG_MAX;
+
+ /* round up to get at least the requested time */
+ usec += 1000000 / HZ - 1;
+
+ jif = usec / (1000000 / HZ);
+
+ if (sec * HZ > ULONG_MAX - jif)
+ return ULONG_MAX;
+
+ return jif + sec * HZ;
+}
+
+/*
+ * procfs functions
+ */
+static char *bcm_proc_getifname(int ifindex)
+{
+ struct net_device *dev;
+
+ if (!ifindex)
+ return "any";
+
+ /* no usage counting */
+ dev = __dev_get_by_index(&init_net, ifindex);
+ if (dev)
+ return dev->name;
+
+ return "???";
+}
+
+static int bcm_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct sock *sk = (struct sock *)data;
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
+
+ len += snprintf(page + len, PAGE_SIZE - len, ">>> socket %p",
+ sk->sk_socket);
+ len += snprintf(page + len, PAGE_SIZE - len, " / sk %p", sk);
+ len += snprintf(page + len, PAGE_SIZE - len, " / bo %p", bo);
+ len += snprintf(page + len, PAGE_SIZE - len, " / dropped %lu",
+ bo->dropped_usr_msgs);
+ len += snprintf(page + len, PAGE_SIZE - len, " / bound %s",
+ bcm_proc_getifname(bo->ifindex));
+ len += snprintf(page + len, PAGE_SIZE - len, " <<<\n");
+
+ list_for_each_entry(op, &bo->rx_ops, list) {
+
+ unsigned long reduction;
+
+ /* print only active entries & prevent division by zero */
+ if (!op->frames_abs)
+ continue;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "rx_op: %03X %-5s ",
+ op->can_id, bcm_proc_getifname(op->ifindex));
+ len += snprintf(page + len, PAGE_SIZE - len, "[%d]%c ",
+ op->nframes,
+ (op->flags & RX_CHECK_DLC)?'d':' ');
+ if (op->j_ival1)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "timeo=%ld ", op->j_ival1);
+
+ if (op->j_ival2)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "thr=%ld ", op->j_ival2);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "# recv %ld (%ld) => reduction: ",
+ op->frames_filtered, op->frames_abs);
+
+ reduction = 100 - (op->frames_filtered * 100) / op->frames_abs;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s%ld%%\n",
+ (reduction == 100)?"near ":"", reduction);
+
+ if (len > PAGE_SIZE - 200) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len, "(..)\n");
+ break;
+ }
+ }
+
+ list_for_each_entry(op, &bo->tx_ops, list) {
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "tx_op: %03X %s [%d] ",
+ op->can_id, bcm_proc_getifname(op->ifindex),
+ op->nframes);
+
+ if (op->j_ival1)
+ len += snprintf(page + len, PAGE_SIZE - len, "t1=%ld ",
+ op->j_ival1);
+
+ if (op->j_ival2)
+ len += snprintf(page + len, PAGE_SIZE - len, "t2=%ld ",
+ op->j_ival2);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "# sent %ld\n",
+ op->frames_abs);
+
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len, "(..)\n");
+ break;
+ }
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
+ * of the given bcm tx op
+ */
+static void bcm_can_tx(struct bcm_op *op)
+{
+ struct sk_buff *skb;
+ struct net_device *dev;
+ struct can_frame *cf = &op->frames[op->currframe];
+
+ /* no target device? => exit */
+ if (!op->ifindex)
+ return;
+
+ dev = dev_get_by_index(&init_net, op->ifindex);
+ if (!dev) {
+ /* RFC: should this bcm_op remove itself here? */
+ return;
+ }
+
+ skb = alloc_skb(CFSIZ, gfp_any());
+ if (!skb)
+ goto out;
+
+ memcpy(skb_put(skb, CFSIZ), cf, CFSIZ);
+
+ /* send with loopback */
+ skb->dev = dev;
+ skb->sk = op->sk;
+ can_send(skb, 1);
+
+ /* update statistics */
+ op->currframe++;
+ op->frames_abs++;
+
+ /* reached last frame? */
+ if (op->currframe >= op->nframes)
+ op->currframe = 0;
+ out:
+ dev_put(dev);
+}
+
+/*
+ * bcm_send_to_user - send a BCM message to the userspace
+ * (consisting of bcm_msg_head + x CAN frames)
+ */
+static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
+ struct can_frame *frames, int has_timestamp)
+{
+ struct sk_buff *skb;
+ struct can_frame *firstframe;
+ struct sockaddr_can *addr;
+ struct sock *sk = op->sk;
+ int datalen = head->nframes * CFSIZ;
+ int err;
+
+ skb = alloc_skb(sizeof(*head) + datalen, gfp_any());
+ if (!skb)
+ return;
+
+ memcpy(skb_put(skb, sizeof(*head)), head, sizeof(*head));
+
+ if (head->nframes) {
+ /* can_frames starting here */
+ firstframe = (struct can_frame *)skb->tail;
+
+ memcpy(skb_put(skb, datalen), frames, datalen);
+
+ /*
+ * the BCM uses the can_dlc-element of the can_frame
+ * structure for internal purposes. This is only
+ * relevant for updates that are generated by the
+ * BCM, where nframes is 1
+ */
+ if (head->nframes == 1)
+ firstframe->can_dlc &= BCM_CAN_DLC_MASK;
+ }
+
+ if (has_timestamp) {
+ /* restore rx timestamp */
+ skb_set_timestamp(skb, &op->rx_stamp);
+ }
+
+ /*
+ * Put the datagram to the queue so that bcm_recvmsg() can
+ * get it from there. We need to pass the interface index to
+ * bcm_recvmsg(). We pass a whole struct sockaddr_can in skb->cb
+ * containing the interface index.
+ */
+
+ BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can));
+ addr = (struct sockaddr_can *)skb->cb;
+ memset(addr, 0, sizeof(*addr));
+ addr->can_family = AF_CAN;
+ addr->can_ifindex = op->rx_ifindex;
+
+ err = sock_queue_rcv_skb(sk, skb);
+ if (err < 0) {
+ struct bcm_sock *bo = bcm_sk(sk);
+
+ kfree_skb(skb);
+ /* don't care about overflows in this statistic */
+ bo->dropped_usr_msgs++;
+ }
+}
+
+/*
+ * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions
+ */
+static void bcm_tx_timeout_handler(unsigned long data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+
+ if (op->j_ival1 && (op->count > 0)) {
+
+ op->count--;
+ if (!op->count && (op->flags & TX_COUNTEVT)) {
+ struct bcm_msg_head msg_head;
+
+ /* create notification to user */
+ msg_head.opcode = TX_EXPIRED;
+ msg_head.flags = op->flags;
+ msg_head.count = op->count;
+ msg_head.ival1 = op->ival1;
+ msg_head.ival2 = op->ival2;
+ msg_head.can_id = op->can_id;
+ msg_head.nframes = 0;
+
+ bcm_send_to_user(op, &msg_head, NULL, 0);
+ }
+ }
+
+ if (op->j_ival1 && (op->count > 0)) {
+
+ /* send (next) frame */
+ bcm_can_tx(op);
+ mod_timer(&op->timer, jiffies + op->j_ival1);
+
+ } else {
+ if (op->j_ival2) {
+
+ /* send (next) frame */
+ bcm_can_tx(op);
+ mod_timer(&op->timer, jiffies + op->j_ival2);
+ }
+ }
+
+ return;
+}
+
+/*
+ * bcm_rx_changed - create a RX_CHANGED notification due to changed content
+ */
+static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data)
+{
+ struct bcm_msg_head head;
+
+ op->j_lastmsg = jiffies;
+
+ /* update statistics */
+ op->frames_filtered++;
+
+ /* prevent statistics overflow */
+ if (op->frames_filtered > ULONG_MAX/100)
+ op->frames_filtered = op->frames_abs = 0;
+
+ head.opcode = RX_CHANGED;
+ head.flags = op->flags;
+ head.count = op->count;
+ head.ival1 = op->ival1;
+ head.ival2 = op->ival2;
+ head.can_id = op->can_id;
+ head.nframes = 1;
+
+ bcm_send_to_user(op, &head, data, 1);
+}
+
+/*
+ * bcm_rx_update_and_send - process a detected relevant receive content change
+ * 1. update the last received data
+ * 2. send a notification to the user (if possible)
+ */
+static void bcm_rx_update_and_send(struct bcm_op *op,
+ struct can_frame *lastdata,
+ struct can_frame *rxdata)
+{
+ unsigned long nexttx = op->j_lastmsg + op->j_ival2;
+
+ memcpy(lastdata, rxdata, CFSIZ);
+
+ /* mark as used */
+ lastdata->can_dlc |= RX_RECV;
+
+ /* throttle bcm_rx_changed ? */
+ if ((op->thrtimer.expires) ||
+ ((op->j_ival2) && (nexttx > jiffies))) {
+ /* we are already waiting OR we have to start waiting */
+
+ /* mark as 'throttled' */
+ lastdata->can_dlc |= RX_THR;
+
+ if (!(op->thrtimer.expires)) {
+ /* start the timer only the first time */
+ mod_timer(&op->thrtimer, nexttx);
+ }
+
+ } else {
+ /* send RX_CHANGED to the user immediately */
+ bcm_rx_changed(op, rxdata);
+ }
+}
+
+/*
+ * bcm_rx_cmp_to_index - (bit)compares the currently received data to formerly
+ * received data stored in op->last_frames[]
+ */
+static void bcm_rx_cmp_to_index(struct bcm_op *op, int index,
+ struct can_frame *rxdata)
+{
+ /*
+ * no one uses the MSBs of can_dlc for comparation,
+ * so we use it here to detect the first time of reception
+ */
+
+ if (!(op->last_frames[index].can_dlc & RX_RECV)) {
+ /* received data for the first time => send update to user */
+ bcm_rx_update_and_send(op, &op->last_frames[index], rxdata);
+ return;
+ }
+
+ /* do a real check in can_frame data section */
+
+ if ((GET_U64(&op->frames[index]) & GET_U64(rxdata)) !=
+ (GET_U64(&op->frames[index]) & GET_U64(&op->last_frames[index]))) {
+ bcm_rx_update_and_send(op, &op->last_frames[index], rxdata);
+ return;
+ }
+
+ if (op->flags & RX_CHECK_DLC) {
+ /* do a real check in can_frame dlc */
+ if (rxdata->can_dlc != (op->last_frames[index].can_dlc &
+ BCM_CAN_DLC_MASK)) {
+ bcm_rx_update_and_send(op, &op->last_frames[index],
+ rxdata);
+ return;
+ }
+ }
+}
+
+/*
+ * bcm_rx_starttimer - enable timeout monitoring for CAN frame receiption
+ */
+static void bcm_rx_starttimer(struct bcm_op *op)
+{
+ if (op->flags & RX_NO_AUTOTIMER)
+ return;
+
+ if (op->j_ival1)
+ mod_timer(&op->timer, jiffies + op->j_ival1);
+}
+
+/*
+ * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out
+ */
+static void bcm_rx_timeout_handler(unsigned long data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+ struct bcm_msg_head msg_head;
+
+ msg_head.opcode = RX_TIMEOUT;
+ msg_head.flags = op->flags;
+ msg_head.count = op->count;
+ msg_head.ival1 = op->ival1;
+ msg_head.ival2 = op->ival2;
+ msg_head.can_id = op->can_id;
+ msg_head.nframes = 0;
+
+ bcm_send_to_user(op, &msg_head, NULL, 0);
+
+ /* no restart of the timer is done here! */
+
+ /* if user wants to be informed, when cyclic CAN-Messages come back */
+ if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
+ /* clear received can_frames to indicate 'nothing received' */
+ memset(op->last_frames, 0, op->nframes * CFSIZ);
+ }
+}
+
+/*
+ * bcm_rx_thr_flush - Check for throttled data and send it to the userspace
+ */
+static int bcm_rx_thr_flush(struct bcm_op *op)
+{
+ int updated = 0;
+
+ if (op->nframes > 1) {
+ int i;
+
+ /* for MUX filter we start at index 1 */
+ for (i = 1; i < op->nframes; i++) {
+ if ((op->last_frames) &&
+ (op->last_frames[i].can_dlc & RX_THR)) {
+ op->last_frames[i].can_dlc &= ~RX_THR;
+ bcm_rx_changed(op, &op->last_frames[i]);
+ updated++;
+ }
+ }
+
+ } else {
+ /* for RX_FILTER_ID and simple filter */
+ if (op->last_frames && (op->last_frames[0].can_dlc & RX_THR)) {
+ op->last_frames[0].can_dlc &= ~RX_THR;
+ bcm_rx_changed(op, &op->last_frames[0]);
+ updated++;
+ }
+ }
+
+ return updated;
+}
+
+/*
+ * bcm_rx_thr_handler - the time for blocked content updates is over now:
+ * Check for throttled data and send it to the userspace
+ */
+static void bcm_rx_thr_handler(unsigned long data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+
+ if (bcm_rx_thr_flush(op))
+ mod_timer(&op->thrtimer, jiffies + op->j_ival2);
+ else {
+ /* mark disabled / consumed timer */
+ op->thrtimer.expires = 0;
+ }
+}
+
+/*
+ * bcm_rx_handler - handle a CAN frame receiption
+ */
+static void bcm_rx_handler(struct sk_buff *skb, void *data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+ struct can_frame rxframe;
+ int i;
+
+ /* disable timeout */
+ del_timer(&op->timer);
+
+ if (skb->len == sizeof(rxframe)) {
+ memcpy(&rxframe, skb->data, sizeof(rxframe));
+ /* save rx timestamp */
+ skb_get_timestamp(skb, &op->rx_stamp);
+ /* save originator for recvfrom() */
+ op->rx_ifindex = skb->dev->ifindex;
+ /* update statistics */
+ op->frames_abs++;
+ kfree_skb(skb);
+
+ } else {
+ kfree_skb(skb);
+ return;
+ }
+
+ if (op->can_id != rxframe.can_id)
+ return;
+
+ if (op->flags & RX_RTR_FRAME) {
+ /* send reply for RTR-request (placed in op->frames[0]) */
+ bcm_can_tx(op);
+ return;
+ }
+
+ if (op->flags & RX_FILTER_ID) {
+ /* the easiest case */
+ bcm_rx_update_and_send(op, &op->last_frames[0], &rxframe);
+ bcm_rx_starttimer(op);
+ return;
+ }
+
+ if (op->nframes == 1) {
+ /* simple compare with index 0 */
+ bcm_rx_cmp_to_index(op, 0, &rxframe);
+ bcm_rx_starttimer(op);
+ return;
+ }
+
+ if (op->nframes > 1) {
+ /*
+ * multiplex compare
+ *
+ * find the first multiplex mask that fits.
+ * Remark: The MUX-mask is stored in index 0
+ */
+
+ for (i = 1; i < op->nframes; i++) {
+ if ((GET_U64(&op->frames[0]) & GET_U64(&rxframe)) ==
+ (GET_U64(&op->frames[0]) &
+ GET_U64(&op->frames[i]))) {
+ bcm_rx_cmp_to_index(op, i, &rxframe);
+ break;
+ }
+ }
+ bcm_rx_starttimer(op);
+ }
+}
+
+/*
+ * helpers for bcm_op handling: find & delete bcm [rx|tx] op elements
+ */
+static struct bcm_op *bcm_find_op(struct list_head *ops, canid_t can_id,
+ int ifindex)
+{
+ struct bcm_op *op;
+
+ list_for_each_entry(op, ops, list) {
+ if ((op->can_id == can_id) && (op->ifindex == ifindex))
+ return op;
+ }
+
+ return NULL;
+}
+
+static void bcm_remove_op(struct bcm_op *op)
+{
+ del_timer(&op->timer);
+ del_timer(&op->thrtimer);
+
+ if ((op->frames) && (op->frames != &op->sframe))
+ kfree(op->frames);
+
+ if ((op->last_frames) && (op->last_frames != &op->last_sframe))
+ kfree(op->last_frames);
+
+ kfree(op);
+
+ return;
+}
+
+static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
+{
+ if (op->rx_reg_dev == dev) {
+ can_rx_unregister(dev, op->can_id, REGMASK(op->can_id),
+ bcm_rx_handler, op);
+
+ /* mark as removed subscription */
+ op->rx_reg_dev = NULL;
+ } else
+ printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device "
+ "mismatch %p %p\n", op->rx_reg_dev, dev);
+}
+
+/*
+ * bcm_delete_rx_op - find and remove a rx op (returns number of removed ops)
+ */
+static int bcm_delete_rx_op(struct list_head *ops, canid_t can_id, int ifindex)
+{
+ struct bcm_op *op, *n;
+
+ list_for_each_entry_safe(op, n, ops, list) {
+ if ((op->can_id == can_id) && (op->ifindex == ifindex)) {
+
+ /*
+ * Don't care if we're bound or not (due to netdev
+ * problems) can_rx_unregister() is always a save
+ * thing to do here.
+ */
+ if (op->ifindex) {
+ /*
+ * Only remove subscriptions that had not
+ * been removed due to NETDEV_UNREGISTER
+ * in bcm_notifier()
+ */
+ if (op->rx_reg_dev) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net,
+ op->ifindex);
+ if (dev) {
+ bcm_rx_unreg(dev, op);
+ dev_put(dev);
+ }
+ }
+ } else
+ can_rx_unregister(NULL, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op);
+
+ list_del(&op->list);
+ bcm_remove_op(op);
+ return 1; /* done */
+ }
+ }
+
+ return 0; /* not found */
+}
+
+/*
+ * bcm_delete_tx_op - find and remove a tx op (returns number of removed ops)
+ */
+static int bcm_delete_tx_op(struct list_head *ops, canid_t can_id, int ifindex)
+{
+ struct bcm_op *op, *n;
+
+ list_for_each_entry_safe(op, n, ops, list) {
+ if ((op->can_id == can_id) && (op->ifindex == ifindex)) {
+ list_del(&op->list);
+ bcm_remove_op(op);
+ return 1; /* done */
+ }
+ }
+
+ return 0; /* not found */
+}
+
+/*
+ * bcm_read_op - read out a bcm_op and send it to the user (for bcm_sendmsg)
+ */
+static int bcm_read_op(struct list_head *ops, struct bcm_msg_head *msg_head,
+ int ifindex)
+{
+ struct bcm_op *op = bcm_find_op(ops, msg_head->can_id, ifindex);
+
+ if (!op)
+ return -EINVAL;
+
+ /* put current values into msg_head */
+ msg_head->flags = op->flags;
+ msg_head->count = op->count;
+ msg_head->ival1 = op->ival1;
+ msg_head->ival2 = op->ival2;
+ msg_head->nframes = op->nframes;
+
+ bcm_send_to_user(op, msg_head, op->frames, 0);
+
+ return MHSIZ;
+}
+
+/*
+ * bcm_tx_setup - create or update a bcm tx op (for bcm_sendmsg)
+ */
+static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ int ifindex, struct sock *sk)
+{
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
+ int i, err;
+
+ /* we need a real device to send frames */
+ if (!ifindex)
+ return -ENODEV;
+
+ /* we need at least one can_frame */
+ if (msg_head->nframes < 1)
+ return -EINVAL;
+
+ /* check the given can_id */
+ op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex);
+
+ if (op) {
+ /* update existing BCM operation */
+
+ /*
+ * Do we need more space for the can_frames than currently
+ * allocated? -> This is a _really_ unusual use-case and
+ * therefore (complexity / locking) it is not supported.
+ */
+ if (msg_head->nframes > op->nframes)
+ return -E2BIG;
+
+ /* update can_frames content */
+ for (i = 0; i < msg_head->nframes; i++) {
+ err = memcpy_fromiovec((u8 *)&op->frames[i],
+ msg->msg_iov, CFSIZ);
+
+ if (op->frames[i].can_dlc > 8)
+ err = -EINVAL;
+
+ if (err < 0)
+ return err;
+
+ if (msg_head->flags & TX_CP_CAN_ID) {
+ /* copy can_id into frame */
+ op->frames[i].can_id = msg_head->can_id;
+ }
+ }
+
+ } else {
+ /* insert new BCM operation for the given can_id */
+
+ op = kzalloc(OPSIZ, GFP_KERNEL);
+ if (!op)
+ return -ENOMEM;
+
+ op->can_id = msg_head->can_id;
+
+ /* create array for can_frames and copy the data */
+ if (msg_head->nframes > 1) {
+ op->frames = kmalloc(msg_head->nframes * CFSIZ,
+ GFP_KERNEL);
+ if (!op->frames) {
+ kfree(op);
+ return -ENOMEM;
+ }
+ } else
+ op->frames = &op->sframe;
+
+ for (i = 0; i < msg_head->nframes; i++) {
+ err = memcpy_fromiovec((u8 *)&op->frames[i],
+ msg->msg_iov, CFSIZ);
+
+ if (op->frames[i].can_dlc > 8)
+ err = -EINVAL;
+
+ if (err < 0) {
+ if (op->frames != &op->sframe)
+ kfree(op->frames);
+ kfree(op);
+ return err;
+ }
+
+ if (msg_head->flags & TX_CP_CAN_ID) {
+ /* copy can_id into frame */
+ op->frames[i].can_id = msg_head->can_id;
+ }
+ }
+
+ /* tx_ops never compare with previous received messages */
+ op->last_frames = NULL;
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+ op->sk = sk;
+ op->ifindex = ifindex;
+
+ /* initialize uninitialized (kzalloc) structure */
+ setup_timer(&op->timer, bcm_tx_timeout_handler,
+ (unsigned long)op);
+
+ /* currently unused in tx_ops */
+ init_timer(&op->thrtimer);
+
+ /* add this bcm_op to the list of the tx_ops */
+ list_add(&op->list, &bo->tx_ops);
+
+ } /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
+
+ if (op->nframes != msg_head->nframes) {
+ op->nframes = msg_head->nframes;
+ /* start multiple frame transmission with index 0 */
+ op->currframe = 0;
+ }
+
+ /* check flags */
+
+ op->flags = msg_head->flags;
+
+ if (op->flags & TX_RESET_MULTI_IDX) {
+ /* start multiple frame transmission with index 0 */
+ op->currframe = 0;
+ }
+
+ if (op->flags & SETTIMER) {
+ /* set timer values */
+ op->count = msg_head->count;
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+ op->j_ival1 = rounded_tv2jif(&msg_head->ival1);
+ op->j_ival2 = rounded_tv2jif(&msg_head->ival2);
+
+ /* disable an active timer due to zero values? */
+ if (!op->j_ival1 && !op->j_ival2)
+ del_timer(&op->timer);
+ }
+
+ if ((op->flags & STARTTIMER) &&
+ ((op->j_ival1 && op->count) || op->j_ival2)) {
+
+ /* spec: send can_frame when starting timer */
+ op->flags |= TX_ANNOUNCE;
+
+ if (op->j_ival1 && (op->count > 0)) {
+ /* op->count-- is done in bcm_tx_timeout_handler */
+ mod_timer(&op->timer, jiffies + op->j_ival1);
+ } else
+ mod_timer(&op->timer, jiffies + op->j_ival2);
+ }
+
+ if (op->flags & TX_ANNOUNCE)
+ bcm_can_tx(op);
+
+ return msg_head->nframes * CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg)
+ */
+static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ int ifindex, struct sock *sk)
+{
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
+ int do_rx_register;
+ int err = 0;
+
+ if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) {
+ /* be robust against wrong usage ... */
+ msg_head->flags |= RX_FILTER_ID;
+ /* ignore trailing garbage */
+ msg_head->nframes = 0;
+ }
+
+ if ((msg_head->flags & RX_RTR_FRAME) &&
+ ((msg_head->nframes != 1) ||
+ (!(msg_head->can_id & CAN_RTR_FLAG))))
+ return -EINVAL;
+
+ /* check the given can_id */
+ op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex);
+ if (op) {
+ /* update existing BCM operation */
+
+ /*
+ * Do we need more space for the can_frames than currently
+ * allocated? -> This is a _really_ unusual use-case and
+ * therefore (complexity / locking) it is not supported.
+ */
+ if (msg_head->nframes > op->nframes)
+ return -E2BIG;
+
+ if (msg_head->nframes) {
+ /* update can_frames content */
+ err = memcpy_fromiovec((u8 *)op->frames,
+ msg->msg_iov,
+ msg_head->nframes * CFSIZ);
+ if (err < 0)
+ return err;
+
+ /* clear last_frames to indicate 'nothing received' */
+ memset(op->last_frames, 0, msg_head->nframes * CFSIZ);
+ }
+
+ op->nframes = msg_head->nframes;
+
+ /* Only an update -> do not call can_rx_register() */
+ do_rx_register = 0;
+
+ } else {
+ /* insert new BCM operation for the given can_id */
+ op = kzalloc(OPSIZ, GFP_KERNEL);
+ if (!op)
+ return -ENOMEM;
+
+ op->can_id = msg_head->can_id;
+ op->nframes = msg_head->nframes;
+
+ if (msg_head->nframes > 1) {
+ /* create array for can_frames and copy the data */
+ op->frames = kmalloc(msg_head->nframes * CFSIZ,
+ GFP_KERNEL);
+ if (!op->frames) {
+ kfree(op);
+ return -ENOMEM;
+ }
+
+ /* create and init array for received can_frames */
+ op->last_frames = kzalloc(msg_head->nframes * CFSIZ,
+ GFP_KERNEL);
+ if (!op->last_frames) {
+ kfree(op->frames);
+ kfree(op);
+ return -ENOMEM;
+ }
+
+ } else {
+ op->frames = &op->sframe;
+ op->last_frames = &op->last_sframe;
+ }
+
+ if (msg_head->nframes) {
+ err = memcpy_fromiovec((u8 *)op->frames, msg->msg_iov,
+ msg_head->nframes * CFSIZ);
+ if (err < 0) {
+ if (op->frames != &op->sframe)
+ kfree(op->frames);
+ if (op->last_frames != &op->last_sframe)
+ kfree(op->last_frames);
+ kfree(op);
+ return err;
+ }
+ }
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+ op->sk = sk;
+ op->ifindex = ifindex;
+
+ /* initialize uninitialized (kzalloc) structure */
+ setup_timer(&op->timer, bcm_rx_timeout_handler,
+ (unsigned long)op);
+
+ /* init throttle timer for RX_CHANGED */
+ setup_timer(&op->thrtimer, bcm_rx_thr_handler,
+ (unsigned long)op);
+
+ /* mark disabled timer */
+ op->thrtimer.expires = 0;
+
+ /* add this bcm_op to the list of the rx_ops */
+ list_add(&op->list, &bo->rx_ops);
+
+ /* call can_rx_register() */
+ do_rx_register = 1;
+
+ } /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
+
+ /* check flags */
+ op->flags = msg_head->flags;
+
+ if (op->flags & RX_RTR_FRAME) {
+
+ /* no timers in RTR-mode */
+ del_timer(&op->thrtimer);
+ del_timer(&op->timer);
+
+ /*
+ * funny feature in RX(!)_SETUP only for RTR-mode:
+ * copy can_id into frame BUT without RTR-flag to
+ * prevent a full-load-loopback-test ... ;-]
+ */
+ if ((op->flags & TX_CP_CAN_ID) ||
+ (op->frames[0].can_id == op->can_id))
+ op->frames[0].can_id = op->can_id & ~CAN_RTR_FLAG;
+
+ } else {
+ if (op->flags & SETTIMER) {
+
+ /* set timer value */
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+ op->j_ival1 = rounded_tv2jif(&msg_head->ival1);
+ op->j_ival2 = rounded_tv2jif(&msg_head->ival2);
+
+ /* disable an active timer due to zero value? */
+ if (!op->j_ival1)
+ del_timer(&op->timer);
+
+ /*
+ * In any case cancel the throttle timer, flush
+ * potentially blocked msgs and reset throttle handling
+ */
+ del_timer(&op->thrtimer);
+ bcm_rx_thr_flush(op);
+ op->thrtimer.expires = 0;
+ }
+
+ if ((op->flags & STARTTIMER) && op->j_ival1)
+ mod_timer(&op->timer, jiffies + op->j_ival1);
+ }
+
+ /* now we can register for can_ids, if we added a new bcm_op */
+ if (do_rx_register) {
+ if (ifindex) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (dev) {
+ err = can_rx_register(dev, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op,
+ "bcm");
+
+ op->rx_reg_dev = dev;
+ dev_put(dev);
+ }
+
+ } else
+ err = can_rx_register(NULL, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op, "bcm");
+ if (err) {
+ /* this bcm rx op is broken -> remove it */
+ list_del(&op->list);
+ bcm_remove_op(op);
+ return err;
+ }
+ }
+
+ return msg_head->nframes * CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_tx_send - send a single CAN frame to the CAN interface (for bcm_sendmsg)
+ */
+static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk)
+{
+ struct sk_buff *skb;
+ struct net_device *dev;
+ int err;
+
+ /* we need a real device to send frames */
+ if (!ifindex)
+ return -ENODEV;
+
+ skb = alloc_skb(CFSIZ, GFP_KERNEL);
+
+ if (!skb)
+ return -ENOMEM;
+
+ err = memcpy_fromiovec(skb_put(skb, CFSIZ), msg->msg_iov, CFSIZ);
+ if (err < 0) {
+ kfree_skb(skb);
+ return err;
+ }
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (!dev) {
+ kfree_skb(skb);
+ return -ENODEV;
+ }
+
+ skb->dev = dev;
+ skb->sk = sk;
+ err = can_send(skb, 1); /* send with loopback */
+ dev_put(dev);
+
+ if (err)
+ return err;
+
+ return CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_sendmsg - process BCM commands (opcodes) from the userspace
+ */
+static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size)
+{
+ struct sock *sk = sock->sk;
+ struct bcm_sock *bo = bcm_sk(sk);
+ int ifindex = bo->ifindex; /* default ifindex for this bcm_op */
+ struct bcm_msg_head msg_head;
+ int ret; /* read bytes or error codes as return value */
+
+ if (!bo->bound)
+ return -ENOTCONN;
+
+ /* check for valid message length from userspace */
+ if (size < MHSIZ || (size - MHSIZ) % CFSIZ)
+ return -EINVAL;
+
+ /* check for alternative ifindex for this bcm_op */
+
+ if (!ifindex && msg->msg_name) {
+ /* no bound device as default => check msg_name */
+ struct sockaddr_can *addr =
+ (struct sockaddr_can *)msg->msg_name;
+
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+
+ /* ifindex from sendto() */
+ ifindex = addr->can_ifindex;
+
+ if (ifindex) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (!dev)
+ return -ENODEV;
+
+ if (dev->type != ARPHRD_CAN) {
+ dev_put(dev);
+ return -ENODEV;
+ }
+
+ dev_put(dev);
+ }
+ }
+
+ /* read message head information */
+
+ ret = memcpy_fromiovec((u8 *)&msg_head, msg->msg_iov, MHSIZ);
+ if (ret < 0)
+ return ret;
+
+ lock_sock(sk);
+
+ switch (msg_head.opcode) {
+
+ case TX_SETUP:
+ ret = bcm_tx_setup(&msg_head, msg, ifindex, sk);
+ break;
+
+ case RX_SETUP:
+ ret = bcm_rx_setup(&msg_head, msg, ifindex, sk);
+ break;
+
+ case TX_DELETE:
+ if (bcm_delete_tx_op(&bo->tx_ops, msg_head.can_id, ifindex))
+ ret = MHSIZ;
+ else
+ ret = -EINVAL;
+ break;
+
+ case RX_DELETE:
+ if (bcm_delete_rx_op(&bo->rx_ops, msg_head.can_id, ifindex))
+ ret = MHSIZ;
+ else
+ ret = -EINVAL;
+ break;
+
+ case TX_READ:
+ /* reuse msg_head for the reply to TX_READ */
+ msg_head.opcode = TX_STATUS;
+ ret = bcm_read_op(&bo->tx_ops, &msg_head, ifindex);
+ break;
+
+ case RX_READ:
+ /* reuse msg_head for the reply to RX_READ */
+ msg_head.opcode = RX_STATUS;
+ ret = bcm_read_op(&bo->rx_ops, &msg_head, ifindex);
+ break;
+
+ case TX_SEND:
+ /* we need exactly one can_frame behind the msg head */
+ if ((msg_head.nframes != 1) || (size != CFSIZ + MHSIZ))
+ ret = -EINVAL;
+ else
+ ret = bcm_tx_send(msg, ifindex, sk);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ release_sock(sk);
+
+ return ret;
+}
+
+/*
+ * notification handler for netdevice status changes
+ */
+static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
+ void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct bcm_sock *bo = container_of(nb, struct bcm_sock, notifier);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+ struct sock *sk = &bo->sk;
+#else
+ struct sock *sk = bo->sk;
+#endif
+ struct bcm_op *op;
+ int notify_enodev = 0;
+
+ if (dev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+
+ case NETDEV_UNREGISTER:
+ lock_sock(sk);
+
+ /* remove device specific receive entries */
+ list_for_each_entry(op, &bo->rx_ops, list)
+ if (op->rx_reg_dev == dev)
+ bcm_rx_unreg(dev, op);
+
+ /* remove device reference, if this is our bound device */
+ if (bo->bound && bo->ifindex == dev->ifindex) {
+ bo->bound = 0;
+ bo->ifindex = 0;
+ notify_enodev = 1;
+ }
+
+ release_sock(sk);
+
+ if (notify_enodev) {
+ sk->sk_err = ENODEV;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ }
+ break;
+
+ case NETDEV_DOWN:
+ if (bo->bound && bo->ifindex == dev->ifindex) {
+ sk->sk_err = ENETDOWN;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * initial settings for all BCM sockets to be set at socket creation time
+ */
+static int bcm_init(struct sock *sk)
+{
+ struct bcm_sock *bo = bcm_sk(sk);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+ bo->sk = sk;
+#endif
+ bo->bound = 0;
+ bo->ifindex = 0;
+ bo->dropped_usr_msgs = 0;
+ bo->bcm_proc_read = NULL;
+
+ INIT_LIST_HEAD(&bo->tx_ops);
+ INIT_LIST_HEAD(&bo->rx_ops);
+
+ /* set notifier */
+ bo->notifier.notifier_call = bcm_notifier;
+
+ register_netdevice_notifier(&bo->notifier);
+
+ return 0;
+}
+
+/*
+ * standard socket functions
+ */
+static int bcm_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op, *next;
+
+ /* remove bcm_ops, timer, rx_unregister(), etc. */
+
+ unregister_netdevice_notifier(&bo->notifier);
+
+ lock_sock(sk);
+
+ list_for_each_entry_safe(op, next, &bo->tx_ops, list)
+ bcm_remove_op(op);
+
+ list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
+ /*
+ * Don't care if we're bound or not (due to netdev problems)
+ * can_rx_unregister() is always a save thing to do here.
+ */
+ if (op->ifindex) {
+ /*
+ * Only remove subscriptions that had not
+ * been removed due to NETDEV_UNREGISTER
+ * in bcm_notifier()
+ */
+ if (op->rx_reg_dev) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, op->ifindex);
+ if (dev) {
+ bcm_rx_unreg(dev, op);
+ dev_put(dev);
+ }
+ }
+ } else
+ can_rx_unregister(NULL, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op);
+
+ bcm_remove_op(op);
+ }
+
+ /* remove procfs entry */
+ if (proc_dir && bo->bcm_proc_read)
+ remove_proc_entry(bo->procname, proc_dir);
+
+ /* remove device reference */
+ if (bo->bound) {
+ bo->bound = 0;
+ bo->ifindex = 0;
+ }
+
+ release_sock(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
+ int flags)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct sock *sk = sock->sk;
+ struct bcm_sock *bo = bcm_sk(sk);
+
+ if (bo->bound)
+ return -EISCONN;
+
+ /* bind a device to this socket */
+ if (addr->can_ifindex) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, addr->can_ifindex);
+ if (!dev)
+ return -ENODEV;
+
+ if (dev->type != ARPHRD_CAN) {
+ dev_put(dev);
+ return -ENODEV;
+ }
+
+ bo->ifindex = dev->ifindex;
+ dev_put(dev);
+
+ } else {
+ /* no interface reference for ifindex = 0 ('any' CAN device) */
+ bo->ifindex = 0;
+ }
+
+ bo->bound = 1;
+
+ if (proc_dir) {
+ /* unique socket address as filename */
+ sprintf(bo->procname, "%p", sock);
+ bo->bcm_proc_read = create_proc_read_entry(bo->procname, 0644,
+ proc_dir,
+ bcm_read_proc, sk);
+ }
+
+ return 0;
+}
+
+static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int error = 0;
+ int noblock;
+ int err;
+
+ noblock = flags & MSG_DONTWAIT;
+ flags &= ~MSG_DONTWAIT;
+ skb = skb_recv_datagram(sk, flags, noblock, &error);
+ if (!skb)
+ return error;
+
+ if (skb->len < size)
+ size = skb->len;
+
+ err = memcpy_toiovec(msg->msg_iov, skb->data, size);
+ if (err < 0) {
+ skb_free_datagram(sk, skb);
+ return err;
+ }
+
+ sock_recv_timestamp(msg, sk, skb);
+
+ if (msg->msg_name) {
+ msg->msg_namelen = sizeof(struct sockaddr_can);
+ memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+ }
+
+ skb_free_datagram(sk, skb);
+
+ return size;
+}
+
+static struct proto_ops bcm_ops __read_mostly = {
+ .family = PF_CAN,
+ .release = bcm_release,
+ .bind = sock_no_bind,
+ .connect = bcm_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = datagram_poll,
+ .ioctl = NULL, /* use can_ioctl() from af_can.c */
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = bcm_sendmsg,
+ .recvmsg = bcm_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
+static struct proto bcm_proto __read_mostly = {
+ .name = "CAN_BCM",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct bcm_sock),
+ .init = bcm_init,
+};
+
+static struct can_proto bcm_can_proto __read_mostly = {
+ .type = SOCK_DGRAM,
+ .protocol = CAN_BCM,
+ .capability = -1,
+ .ops = &bcm_ops,
+ .prot = &bcm_proto,
+};
+#else
+static struct can_proto bcm_can_proto __read_mostly = {
+ .type = SOCK_DGRAM,
+ .protocol = CAN_BCM,
+ .capability = -1,
+ .ops = &bcm_ops,
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct bcm_sock),
+ .init = bcm_init,
+};
+#endif
+
+static int __init bcm_module_init(void)
+{
+ int err;
+
+ printk(banner);
+
+ err = can_proto_register(&bcm_can_proto);
+ if (err < 0) {
+ printk(KERN_ERR "can: registration of bcm protocol failed\n");
+ return err;
+ }
+
+ /* create /proc/net/can-bcm directory */
+ proc_dir = proc_mkdir("can-bcm", proc_net);
+
+ if (proc_dir)
+ proc_dir->owner = THIS_MODULE;
+
+ return 0;
+}
+
+static void __exit bcm_module_exit(void)
+{
+ can_proto_unregister(&bcm_can_proto);
+
+ if (proc_dir)
+ proc_net_remove("can-bcm");
+}
+
+module_init(bcm_module_init);
+module_exit(bcm_module_exit);
*/
#include <linux/module.h>
+#include <linux/version.h>
#include <linux/init.h>
#include <linux/hrtimer.h>
#include <linux/list.h>
#include <linux/can/core.h>
#include <linux/can/bcm.h>
#include <net/sock.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#include <net/net_namespace.h>
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#include "compat.h"
+#endif
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
/* use of last_frames[index].can_dlc */
#define RX_RECV 0x40 /* received data for this element */
#define BCM_CAN_DLC_MASK 0x0F /* clean private flags in can_dlc by masking */
/* get best masking value for can_rx_register() for a given single can_id */
-#define REGMASK(id) ((id & CAN_RTR_FLAG) | ((id & CAN_EFF_FLAG) ? \
- (CAN_EFF_MASK | CAN_EFF_FLAG) : CAN_SFF_MASK))
+#define REGMASK(id) ((id & CAN_EFF_FLAG) ? \
+ (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \
+ (CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
-#define CAN_BCM_VERSION "20080415"
+#define CAN_BCM_VERSION CAN_VERSION
static __initdata const char banner[] = KERN_INFO
- "can: broadcast manager protocol (rev " CAN_BCM_VERSION ")\n";
+ "can: broadcast manager protocol (rev " CAN_BCM_VERSION " t)\n";
MODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+#error This code only supports Kernel versions 2.6.22+
+#error For older 2.6 Kernels please use bcm-prior-2-6-22.c instead of bcm.c
+#endif
+
/* easy access to can_frame payload */
static inline u64 GET_U64(const struct can_frame *cp)
{
unsigned long frames_abs, frames_filtered;
struct timeval ival1, ival2;
struct hrtimer timer, thrtimer;
+ struct tasklet_struct tsklet, thrtsklet;
ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
int rx_ifindex;
int count;
}
}
-/*
- * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions
- */
-static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
+static void bcm_tx_timeout_tsklet(unsigned long data)
{
- struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
- enum hrtimer_restart ret = HRTIMER_NORESTART;
+ struct bcm_op *op = (struct bcm_op *)data;
+ struct bcm_msg_head msg_head;
if (op->kt_ival1.tv64 && (op->count > 0)) {
op->count--;
if (!op->count && (op->flags & TX_COUNTEVT)) {
- struct bcm_msg_head msg_head;
/* create notification to user */
msg_head.opcode = TX_EXPIRED;
/* send (next) frame */
bcm_can_tx(op);
- hrtimer_forward(hrtimer, ktime_get(), op->kt_ival1);
- ret = HRTIMER_RESTART;
+ hrtimer_start(&op->timer,
+ ktime_add(ktime_get(), op->kt_ival1),
+ HRTIMER_MODE_ABS);
} else {
if (op->kt_ival2.tv64) {
/* send (next) frame */
bcm_can_tx(op);
- hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2);
- ret = HRTIMER_RESTART;
+ hrtimer_start(&op->timer,
+ ktime_add(ktime_get(), op->kt_ival2),
+ HRTIMER_MODE_ABS);
}
}
+}
- return ret;
+/*
+ * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions
+ */
+static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
+{
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+
+ tasklet_schedule(&op->tsklet);
+
+ return HRTIMER_NORESTART;
}
/*
if (op->frames_filtered > ULONG_MAX/100)
op->frames_filtered = op->frames_abs = 0;
+ /* this element is not throttled anymore */
+ data->can_dlc &= (BCM_CAN_DLC_MASK|RX_RECV);
+
head.opcode = RX_CHANGED;
head.flags = op->flags;
head.count = op->count;
bcm_send_to_user(op, &head, data, 1);
}
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,25)
+/* is part of linux/hrtimer.h since 2.6.26 */
+static inline int hrtimer_callback_running(struct hrtimer *timer)
+{
+ return timer->state & HRTIMER_STATE_CALLBACK;
+}
+#endif
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,22)
+static inline s64 ktime_us_delta(const ktime_t later, const ktime_t earlier)
+{
+ return ktime_to_us(ktime_sub(later, earlier));
+}
+#endif
/*
* bcm_rx_update_and_send - process a detected relevant receive content change
* 1. update the last received data
*/
static void bcm_rx_update_and_send(struct bcm_op *op,
struct can_frame *lastdata,
- struct can_frame *rxdata)
+ const struct can_frame *rxdata)
{
memcpy(lastdata, rxdata, CFSIZ);
- /* mark as used */
- lastdata->can_dlc |= RX_RECV;
+ /* mark as used and throttled by default */
+ lastdata->can_dlc |= (RX_RECV|RX_THR);
- /* throtteling mode inactive OR data update already on the run ? */
- if (!op->kt_ival2.tv64 || hrtimer_callback_running(&op->thrtimer)) {
+ /* throtteling mode inactive ? */
+ if (!op->kt_ival2.tv64) {
/* send RX_CHANGED to the user immediately */
- bcm_rx_changed(op, rxdata);
+ bcm_rx_changed(op, lastdata);
return;
}
- if (hrtimer_active(&op->thrtimer)) {
- /* mark as 'throttled' */
- lastdata->can_dlc |= RX_THR;
+ /* with active throttling timer we are just done here */
+ if (hrtimer_active(&op->thrtimer))
return;
- }
- if (!op->kt_lastmsg.tv64) {
- /* send first RX_CHANGED to the user immediately */
- bcm_rx_changed(op, rxdata);
- op->kt_lastmsg = ktime_get();
- return;
- }
+ /* first receiption with enabled throttling mode */
+ if (!op->kt_lastmsg.tv64)
+ goto rx_changed_settime;
+ /* got a second frame inside a potential throttle period? */
if (ktime_us_delta(ktime_get(), op->kt_lastmsg) <
ktime_to_us(op->kt_ival2)) {
- /* mark as 'throttled' and start timer */
- lastdata->can_dlc |= RX_THR;
+ /* do not send the saved data - only start throttle timer */
hrtimer_start(&op->thrtimer,
ktime_add(op->kt_lastmsg, op->kt_ival2),
HRTIMER_MODE_ABS);
}
/* the gap was that big, that throttling was not needed here */
- bcm_rx_changed(op, rxdata);
+rx_changed_settime:
+ bcm_rx_changed(op, lastdata);
op->kt_lastmsg = ktime_get();
}
* received data stored in op->last_frames[]
*/
static void bcm_rx_cmp_to_index(struct bcm_op *op, int index,
- struct can_frame *rxdata)
+ const struct can_frame *rxdata)
{
/*
* no one uses the MSBs of can_dlc for comparation,
hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL);
}
-/*
- * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out
- */
-static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
+static void bcm_rx_timeout_tsklet(unsigned long data)
{
- struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+ struct bcm_op *op = (struct bcm_op *)data;
struct bcm_msg_head msg_head;
+ /* create notification to user */
msg_head.opcode = RX_TIMEOUT;
msg_head.flags = op->flags;
msg_head.count = op->count;
msg_head.nframes = 0;
bcm_send_to_user(op, &msg_head, NULL, 0);
+}
+
+/*
+ * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out
+ */
+static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
+{
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+
+ /* schedule before NET_RX_SOFTIRQ */
+ tasklet_hi_schedule(&op->tsklet);
/* no restart of the timer is done here! */
return HRTIMER_NORESTART;
}
+/*
+ * bcm_rx_do_flush - helper for bcm_rx_thr_flush
+ */
+static inline int bcm_rx_do_flush(struct bcm_op *op, int update, int index)
+{
+ if ((op->last_frames) && (op->last_frames[index].can_dlc & RX_THR)) {
+ if (update)
+ bcm_rx_changed(op, &op->last_frames[index]);
+ return 1;
+ }
+ return 0;
+}
+
/*
* bcm_rx_thr_flush - Check for throttled data and send it to the userspace
+ *
+ * update == 0 : just check if throttled data is available (any irq context)
+ * update == 1 : check and send throttled data to userspace (soft_irq context)
*/
-static int bcm_rx_thr_flush(struct bcm_op *op)
+static int bcm_rx_thr_flush(struct bcm_op *op, int update)
{
int updated = 0;
int i;
/* for MUX filter we start at index 1 */
- for (i = 1; i < op->nframes; i++) {
- if ((op->last_frames) &&
- (op->last_frames[i].can_dlc & RX_THR)) {
- op->last_frames[i].can_dlc &= ~RX_THR;
- bcm_rx_changed(op, &op->last_frames[i]);
- updated++;
- }
- }
+ for (i = 1; i < op->nframes; i++)
+ updated += bcm_rx_do_flush(op, update, i);
} else {
/* for RX_FILTER_ID and simple filter */
- if (op->last_frames && (op->last_frames[0].can_dlc & RX_THR)) {
- op->last_frames[0].can_dlc &= ~RX_THR;
- bcm_rx_changed(op, &op->last_frames[0]);
- updated++;
- }
+ updated += bcm_rx_do_flush(op, update, 0);
}
return updated;
}
+static void bcm_rx_thr_tsklet(unsigned long data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+
+ /* push the changed data to the userspace */
+ bcm_rx_thr_flush(op, 1);
+}
+
/*
* bcm_rx_thr_handler - the time for blocked content updates is over now:
* Check for throttled data and send it to the userspace
{
struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer);
- if (bcm_rx_thr_flush(op)) {
+ tasklet_schedule(&op->thrtsklet);
+
+ if (bcm_rx_thr_flush(op, 0)) {
hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2);
return HRTIMER_RESTART;
} else {
static void bcm_rx_handler(struct sk_buff *skb, void *data)
{
struct bcm_op *op = (struct bcm_op *)data;
- struct can_frame rxframe;
+ const struct can_frame *rxframe = (struct can_frame *)skb->data;
int i;
/* disable timeout */
hrtimer_cancel(&op->timer);
- if (skb->len == sizeof(rxframe)) {
- memcpy(&rxframe, skb->data, sizeof(rxframe));
- /* save rx timestamp */
- op->rx_stamp = skb->tstamp;
- /* save originator for recvfrom() */
- op->rx_ifindex = skb->dev->ifindex;
- /* update statistics */
- op->frames_abs++;
- kfree_skb(skb);
-
- } else {
- kfree_skb(skb);
+ if (op->can_id != rxframe->can_id)
return;
- }
- if (op->can_id != rxframe.can_id)
- return;
+ /* save rx timestamp */
+ op->rx_stamp = skb->tstamp;
+ /* save originator for recvfrom() */
+ op->rx_ifindex = skb->dev->ifindex;
+ /* update statistics */
+ op->frames_abs++;
if (op->flags & RX_RTR_FRAME) {
/* send reply for RTR-request (placed in op->frames[0]) */
if (op->flags & RX_FILTER_ID) {
/* the easiest case */
- bcm_rx_update_and_send(op, &op->last_frames[0], &rxframe);
- bcm_rx_starttimer(op);
- return;
+ bcm_rx_update_and_send(op, &op->last_frames[0], rxframe);
+ goto rx_starttimer;
}
if (op->nframes == 1) {
/* simple compare with index 0 */
- bcm_rx_cmp_to_index(op, 0, &rxframe);
- bcm_rx_starttimer(op);
- return;
+ bcm_rx_cmp_to_index(op, 0, rxframe);
+ goto rx_starttimer;
}
if (op->nframes > 1) {
*/
for (i = 1; i < op->nframes; i++) {
- if ((GET_U64(&op->frames[0]) & GET_U64(&rxframe)) ==
+ if ((GET_U64(&op->frames[0]) & GET_U64(rxframe)) ==
(GET_U64(&op->frames[0]) &
GET_U64(&op->frames[i]))) {
- bcm_rx_cmp_to_index(op, i, &rxframe);
+ bcm_rx_cmp_to_index(op, i, rxframe);
break;
}
}
- bcm_rx_starttimer(op);
}
+
+rx_starttimer:
+ bcm_rx_starttimer(op);
}
/*
hrtimer_cancel(&op->timer);
hrtimer_cancel(&op->thrtimer);
+ if (op->tsklet.func)
+ tasklet_kill(&op->tsklet);
+
+ if (op->thrtsklet.func)
+ tasklet_kill(&op->thrtsklet);
+
if ((op->frames) && (op->frames != &op->sframe))
kfree(op->frames);
hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
op->timer.function = bcm_tx_timeout_handler;
+ /* initialize tasklet for tx countevent notification */
+ tasklet_init(&op->tsklet, bcm_tx_timeout_tsklet,
+ (unsigned long) op);
+
/* currently unused in tx_ops */
hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
op->timer.function = bcm_rx_timeout_handler;
+ /* initialize tasklet for rx timeout notification */
+ tasklet_init(&op->tsklet, bcm_rx_timeout_tsklet,
+ (unsigned long) op);
+
hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
op->thrtimer.function = bcm_rx_thr_handler;
+ /* initialize tasklet for rx throttle handling */
+ tasklet_init(&op->thrtsklet, bcm_rx_thr_tsklet,
+ (unsigned long) op);
+
/* add this bcm_op to the list of the rx_ops */
list_add(&op->list, &bo->rx_ops);
*/
op->kt_lastmsg = ktime_set(0, 0);
hrtimer_cancel(&op->thrtimer);
- bcm_rx_thr_flush(op);
+ bcm_rx_thr_flush(op, 1);
}
if ((op->flags & STARTTIMER) && op->kt_ival1.tv64)
struct bcm_op *op;
int notify_enodev = 0;
- if (dev_net(dev) != &init_net)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ if (!net_eq(dev_net(dev), &init_net))
+ return NOTIFY_DONE;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ if (dev->nd_net != &init_net)
return NOTIFY_DONE;
+#endif
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
}
/* create /proc/net/can-bcm directory */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
proc_dir = proc_mkdir("can-bcm", init_net.proc_net);
+#else
+ proc_dir = proc_mkdir("can-bcm", proc_net);
+#endif
if (proc_dir)
proc_dir->owner = THIS_MODULE;
can_proto_unregister(&bcm_can_proto);
if (proc_dir)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
proc_net_remove(&init_net, "can-bcm");
+#else
+ proc_net_remove("can-bcm");
+#endif
}
module_init(bcm_module_init);
--- /dev/null
+/*
+ * $Id$
+ */
+
+#ifndef CAN_COMPAT_H
+#define CAN_COMPAT_H
+
+#ifndef PF_CAN
+#define PF_CAN 29
+#endif
+
+#ifndef AF_CAN
+#define AF_CAN PF_CAN
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+static inline void *kzalloc(size_t size, unsigned int __nocast flags)
+{
+ void *ret = kmalloc(size, flags);
+ if (ret)
+ memset(ret, 0, size);
+ return ret;
+}
+
+static inline void skb_get_timestamp(const struct sk_buff *skb,
+ struct timeval *stamp)
+{
+ stamp->tv_sec = skb->stamp.tv_sec;
+ stamp->tv_usec = skb->stamp.tv_usec;
+}
+
+static inline void skb_set_timestamp(struct sk_buff *skb,
+ const struct timeval *stamp)
+{
+ skb->stamp.tv_sec = stamp->tv_sec;
+ skb->stamp.tv_usec = stamp->tv_usec;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+#define round_jiffies(j) (j)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+#define dev_get_by_index(ns, ifindex) dev_get_by_index(ifindex)
+#define __dev_get_by_index(ns, ifindex) __dev_get_by_index(ifindex)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+#include <linux/hrtimer.h>
+static inline int hrtimer_callback_running(struct hrtimer *timer)
+{
+ return timer->state & HRTIMER_STATE_CALLBACK;
+}
+#endif
+#endif
--- /dev/null
+/*
+ * isotp.c - ISO 15765-2 CAN transport protocol for protocol family CAN
+ *
+ * WARNING: This is ALPHA code for discussions and first tests that should
+ * not be used in productive environments.
+ *
+ * In the discussion the Socket-API to the userspace or the ISO-TP socket
+ * options or the return values we may change! Current behaviour:
+ *
+ * - no ISO-TP specific return values are provided to the userspace
+ * - when a transfer (tx) is on the run the next write() blocks until it's done
+ * - no support for sending wait frames to the data source in the rx path
+ *
+ * Copyright (c) 2008 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/uio.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <linux/can/isotp.h>
+#include <net/sock.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+#include <net/net_namespace.h>
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#include "compat.h"
+#endif
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
+
+#define CAN_ISOTP_VERSION CAN_VERSION
+static __initdata const char banner[] =
+ KERN_INFO "can: isotp protocol (rev " CAN_ISOTP_VERSION " alpha)\n";
+
+MODULE_DESCRIPTION("PF_CAN isotp 15765-2 protocol");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+#error This modules needs hrtimers (available since Kernel 2.6.22)
+#endif
+
+#define DBG(fmt, args...) (printk( KERN_DEBUG "can-isotp: %s: " fmt, \
+ __func__, ##args))
+#undef DBG
+#define DBG(fmt, args...)
+
+#define SINGLE_MASK(id) ((id & CAN_EFF_FLAG) ? \
+ (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \
+ (CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
+
+/* N_PCI type values in bits 7-4 of N_PCI bytes */
+#define N_PCI_SF 0x00 /* single frame */
+#define N_PCI_FF 0x10 /* first frame */
+#define N_PCI_CF 0x20 /* consecutive frame */
+#define N_PCI_FC 0x30 /* flow control */
+
+/* Flow Status given in FC frame */
+#define ISOTP_FC_CTS 0 /* clear to send */
+#define ISOTP_FC_WT 1 /* wait */
+#define ISOTP_FC_OVFLW 2 /* overflow */
+
+enum {
+ ISOTP_IDLE = 0,
+ ISOTP_WAIT_FIRST_FC,
+ ISOTP_WAIT_FC,
+ ISOTP_WAIT_DATA,
+ ISOTP_SENDING
+};
+
+struct tpcon {
+ int idx;
+ int len;
+ u8 state;
+ u8 bs;
+ u8 sn;
+ u8 buf[4096];
+};
+
+struct isotp_sock {
+ struct sock sk;
+ int bound;
+ int ifindex;
+ canid_t txid;
+ canid_t rxid;
+ ktime_t tx_gap;
+ struct hrtimer rxtimer, txtimer;
+ struct tasklet_struct txtsklet;
+ struct can_isotp_options opt;
+ struct can_isotp_fc_options rxfc, txfc;
+ struct tpcon rx, tx;
+ struct notifier_block notifier;
+ wait_queue_head_t wait;
+};
+
+static inline struct isotp_sock *isotp_sk(const struct sock *sk)
+{
+ return (struct isotp_sock *)sk;
+}
+
+static enum hrtimer_restart isotp_rx_timer_handler(struct hrtimer *hrtimer)
+{
+ struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+ rxtimer);
+ if (so->rx.state == ISOTP_WAIT_DATA) {
+#if 0
+ struct sock *sk = &so->sk;
+
+ /* report 'timeout' */
+ sk->sk_err = E?????;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+#endif
+ DBG("we did not get new data frames in time.\n");
+
+ /* reset tx state */
+ so->rx.state = ISOTP_IDLE;
+ }
+
+ return HRTIMER_NORESTART;
+}
+
+static int isotp_send_fc(struct sock *sk, int ae)
+{
+ struct net_device *dev;
+ struct sk_buff *nskb;
+ struct can_frame *ncf;
+ struct isotp_sock *so = isotp_sk(sk);
+
+ nskb = alloc_skb(sizeof(struct can_frame), gfp_any());
+ if (!nskb)
+ return 1;
+
+ dev = dev_get_by_index(&init_net, so->ifindex);
+ if (!dev) {
+ kfree_skb(nskb);
+ return 1;
+ }
+ nskb->dev = dev;
+ nskb->sk = sk;
+ ncf = (struct can_frame *) nskb->data;
+ skb_put(nskb, sizeof(struct can_frame));
+
+ /* create & send flow control reply */
+ ncf->can_id = so->txid;
+
+ if (so->opt.flags & CAN_ISOTP_RX_PADDING) {
+ memset(ncf->data, so->opt.rxpad_content, 8);
+ ncf->can_dlc = 8;
+ } else
+ ncf->can_dlc = ae+3;
+
+ ncf->data[ae] = N_PCI_FC | ISOTP_FC_CTS;
+ ncf->data[ae+1] = so->rxfc.bs;
+ ncf->data[ae+2] = so->rxfc.stmin;
+
+ if (ae)
+ ncf->data[0] = so->opt.ext_address;
+
+ can_send(nskb, 1);
+ dev_put(dev);
+
+ /* reset blocksize counter */
+ so->rx.bs = 0;
+
+ /* start rx timeout watchdog */
+ hrtimer_start(&so->rxtimer, ktime_set(1,0), HRTIMER_MODE_REL);
+ return 0;
+}
+
+static void isotp_rcv_skb(struct sk_buff *skb, struct sock *sk)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)skb->cb;
+
+ BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can));
+
+ skb->sk = sk;
+
+ memset(addr, 0, sizeof(*addr));
+ addr->can_family = AF_CAN;
+ addr->can_ifindex = skb->dev->ifindex;
+
+ if (sock_queue_rcv_skb(sk, skb) < 0)
+ kfree_skb(skb);
+}
+
+static int check_pad(struct isotp_sock *so, struct can_frame *cf,
+ int start_index, __u8 content)
+{
+ int i;
+
+ /* check datalength code */
+ if ((so->opt.flags & CAN_ISOTP_CHK_PAD_LEN) && cf->can_dlc != 8)
+ return 1;
+
+ /* check padding content */
+ if (so->opt.flags & CAN_ISOTP_CHK_PAD_DATA) {
+ for (i = start_index; i < 8; i++)
+ if (cf->data[i] != content)
+ return 1;
+ }
+ return 0;
+}
+
+static int isotp_rcv_fc(struct isotp_sock *so, struct can_frame *cf, int ae)
+{
+ if (so->tx.state != ISOTP_WAIT_FC &&
+ so->tx.state != ISOTP_WAIT_FIRST_FC)
+ return 0;
+
+ hrtimer_cancel(&so->txtimer);
+
+ if ((so->opt.flags & CAN_ISOTP_TX_PADDING) &&
+ check_pad(so, cf, ae+3, so->opt.txpad_content)) {
+ so->tx.state = ISOTP_IDLE;
+ wake_up_interruptible(&so->wait);
+ return 1;
+ }
+
+ /* get communication parameters only from the first FC frame */
+ if (so->tx.state == ISOTP_WAIT_FIRST_FC) {
+
+ so->txfc.bs = cf->data[ae+1];
+ so->txfc.stmin = cf->data[ae+2];
+
+ /* fix wrong STmin values according spec */
+ if ((so->txfc.stmin > 0x7F) &&
+ ((so->txfc.stmin < 0xF1) || (so->txfc.stmin > 0xF9)))
+ so->txfc.stmin = 0x7F;
+
+ so->tx_gap = ktime_set(0,0);
+ /* add transmission time for CAN frame N_As */
+ so->tx_gap = ktime_add_ns(so->tx_gap, so->opt.frame_txtime);
+ /* add waiting time for consecutive frames N_Cs */
+ if (so->txfc.stmin < 0x80)
+ so->tx_gap = ktime_add_ns(so->tx_gap,
+ so->txfc.stmin * 1000000);
+ else
+ so->tx_gap = ktime_add_ns(so->tx_gap,
+ (so->txfc.stmin - 0xF0)
+ * 100000);
+ so->tx.state = ISOTP_WAIT_FC;
+ }
+
+ DBG("FC frame: FS %d, BS %d, STmin 0x%02X, tx_gap %lld\n",
+ cf->data[ae] & 0x0F & 0x0F, so->txfc.bs, so->txfc.stmin,
+ (long long)so->tx_gap.tv64);
+
+ switch (cf->data[ae] & 0x0F) {
+
+ case ISOTP_FC_CTS:
+ so->tx.bs = 0;
+ so->tx.state = ISOTP_SENDING;
+ DBG("starting txtimer for sending\n");
+ /* start cyclic timer for sending CF frame */
+ hrtimer_start(&so->txtimer, so->tx_gap,
+ HRTIMER_MODE_REL);
+ break;
+
+ case ISOTP_FC_WT:
+ DBG("starting waiting for next FC\n");
+ /* start timer to wait for next FC frame */
+ hrtimer_start(&so->txtimer, ktime_set(1,0),
+ HRTIMER_MODE_REL);
+ break;
+
+ case ISOTP_FC_OVFLW:
+ DBG("overflow in receiver side\n");
+
+ default:
+ /* stop this tx job. TODO: error reporting? */
+ so->tx.state = ISOTP_IDLE;
+ wake_up_interruptible(&so->wait);
+ }
+ return 0;
+}
+
+static int isotp_rcv_sf(struct sock *sk, struct can_frame *cf, int ae,
+ struct sk_buff *skb)
+{
+ struct isotp_sock *so = isotp_sk(sk);
+ int len = cf->data[ae] & 0x0F;
+ struct sk_buff *nskb;
+
+ hrtimer_cancel(&so->rxtimer);
+ so->rx.state = ISOTP_IDLE;
+
+ if (!len || len > 7 || (ae && len > 6))
+ return 1;
+
+ if ((so->opt.flags & CAN_ISOTP_RX_PADDING) &&
+ check_pad(so, cf, 1+ae+len, so->opt.rxpad_content))
+ return 1;
+
+ nskb = alloc_skb(len, gfp_any());
+ if (!nskb)
+ return 1;
+
+ memcpy(skb_put(nskb, len), &cf->data[1+ae], len);
+
+ nskb->tstamp = skb->tstamp;
+ nskb->dev = skb->dev;
+ isotp_rcv_skb(nskb, sk);
+ return 0;
+}
+
+static int isotp_rcv_ff(struct sock *sk, struct can_frame *cf, int ae)
+{
+ struct isotp_sock *so = isotp_sk(sk);
+ int i;
+
+ hrtimer_cancel(&so->rxtimer);
+ so->rx.state = ISOTP_IDLE;
+
+ if (cf->can_dlc != 8)
+ return 1;
+
+ so->rx.len = (cf->data[ae] & 0x0F) << 8;
+ so->rx.len += cf->data[ae+1];
+
+ if (so->rx.len + ae < 8)
+ return 1;
+
+ /* copy the first received data bytes */
+ so->rx.idx = 0;
+ for (i = ae+2; i < 8; i++)
+ so->rx.buf[so->rx.idx++] = cf->data[i];
+
+ /* initial setup for this pdu receiption */
+ so->rx.sn = 1;
+ so->rx.state = ISOTP_WAIT_DATA;
+
+ /* no creation of flow control frames */
+ if (so->opt.flags & CAN_ISOTP_LISTEN_MODE)
+ return 0;
+
+ /* send our first FC frame */
+ isotp_send_fc(sk, ae);
+ return 0;
+}
+
+static int isotp_rcv_cf(struct sock *sk, struct can_frame *cf, int ae,
+ struct sk_buff *skb)
+{
+ struct isotp_sock *so = isotp_sk(sk);
+ struct sk_buff *nskb;
+ int i;
+
+ if (so->rx.state != ISOTP_WAIT_DATA)
+ return 0;
+
+ hrtimer_cancel(&so->rxtimer);
+
+ if ((cf->data[ae] & 0x0F) != so->rx.sn) {
+ DBG("wrong sn %d. expected %d.\n",
+ cf->data[ae] & 0x0F, so->rx.sn);
+ /* some error reporting? */
+ so->rx.state = ISOTP_IDLE;
+ return 1;
+ }
+ so->rx.sn++;
+ so->rx.sn %= 16;
+
+ for (i = ae+1; i < 8; i++) {
+ so->rx.buf[so->rx.idx++] = cf->data[i];
+ if (so->rx.idx >= so->rx.len)
+ break;
+ }
+
+ if (so->rx.idx >= so->rx.len) {
+
+ /* we are done */
+ so->rx.state = ISOTP_IDLE;
+
+ if ((so->opt.flags & CAN_ISOTP_RX_PADDING) &&
+ check_pad(so, cf, i+1, so->opt.rxpad_content))
+ return 1;
+
+ nskb = alloc_skb(so->rx.len, gfp_any());
+ if (!nskb)
+ return 1;
+
+ memcpy(skb_put(nskb, so->rx.len), so->rx.buf,
+ so->rx.len);
+
+ nskb->tstamp = skb->tstamp;
+ nskb->dev = skb->dev;
+ isotp_rcv_skb(nskb, sk);
+ return 0;
+ }
+
+ /* no creation of flow control frames */
+ if (so->opt.flags & CAN_ISOTP_LISTEN_MODE)
+ return 0;
+
+ /* perform blocksize handling, if enabled */
+ if (!so->rxfc.bs || ++so->rx.bs < so->rxfc.bs) {
+
+ /* start rx timeout watchdog */
+ hrtimer_start(&so->rxtimer, ktime_set(1,0),
+ HRTIMER_MODE_REL);
+ return 0;
+ }
+
+ /* we reached the specified blocksize so->rxfc.bs */
+ isotp_send_fc(sk, ae);
+ return 0;
+}
+
+static void isotp_rcv(struct sk_buff *skb, void *data)
+{
+ struct sock *sk = (struct sock *)data;
+ struct isotp_sock *so = isotp_sk(sk);
+ struct can_frame *cf;
+ int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR)? 1:0;
+ u8 n_pci_type;
+
+ /* read CAN frame and free skbuff */
+ BUG_ON(skb->len != sizeof(struct can_frame));
+ cf = (struct can_frame *) skb->data;
+
+ /* if enabled: check receiption of my configured extended address */
+ if (ae && cf->data[0] != so->opt.ext_address)
+ return;
+
+ n_pci_type = cf->data[ae] & 0xF0;
+
+ if (so->opt.flags & CAN_ISOTP_HALF_DUPLEX) {
+ /* check rx/tx path half duplex expectations */
+ if ((so->tx.state != ISOTP_IDLE && n_pci_type != N_PCI_FC) ||
+ (so->rx.state != ISOTP_IDLE && n_pci_type == N_PCI_FC))
+ return;
+ }
+
+ switch (n_pci_type) {
+ case N_PCI_FC:
+ /* tx path: flow control frame containing the FC parameters */
+ isotp_rcv_fc(so, cf, ae);
+ break;
+
+ case N_PCI_SF:
+ /* rx path: single frame */
+ isotp_rcv_sf(sk, cf, ae, skb);
+ break;
+
+ case N_PCI_FF:
+ /* rx path: first frame */
+ isotp_rcv_ff(sk, cf, ae);
+ break;
+
+ case N_PCI_CF:
+ /* rx path: consecutive frame */
+ isotp_rcv_cf(sk, cf, ae, skb);
+ break;
+ }
+}
+
+static void isotp_fill_dataframe(struct can_frame *cf, struct isotp_sock *so,
+ int ae)
+{
+ unsigned char space = 7 - ae;
+ int num = min_t(int, so->tx.len - so->tx.idx, space);
+ int i;
+
+ cf->can_id = so->txid;
+
+ if (so->opt.flags & CAN_ISOTP_TX_PADDING) {
+ if (num < space)
+ memset(cf->data, so->opt.txpad_content, 8);
+
+ cf->can_dlc = 8;
+ } else
+ cf->can_dlc = num + 1 + ae;
+
+
+ for (i = 0; i < num; i++)
+ cf->data[i+ae+1] = so->tx.buf[so->tx.idx++];
+
+ if (ae)
+ cf->data[0] = so->opt.ext_address;
+}
+
+static void isotp_create_fframe(struct can_frame *cf, struct isotp_sock *so,
+ int ae)
+{
+ int i;
+
+ cf->can_id = so->txid;
+ cf->can_dlc = 8;
+ if (ae)
+ cf->data[0] = so->opt.ext_address;
+
+ /* N_PCI bytes with FF_DL data length */
+ cf->data[ae] = (u8) (so->tx.len>>8) | N_PCI_FF;
+ cf->data[ae+1] = (u8) so->tx.len & 0xFFU;
+
+ /* add first 5 or 6 data bytes depending on ae */
+ for (i = ae+2; i < 8; i++)
+ cf->data[i] = so->tx.buf[so->tx.idx++];
+
+ so->tx.sn = 1;
+ so->tx.state = ISOTP_WAIT_FIRST_FC;
+}
+
+static void isotp_tx_timer_tsklet(unsigned long data)
+{
+ struct isotp_sock *so = (struct isotp_sock *)data;
+ struct sock *sk = &so->sk;
+ struct sk_buff *skb;
+ struct net_device *dev;
+ struct can_frame *cf;
+ int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR)? 1:0;
+
+ switch (so->tx.state) {
+
+ case ISOTP_WAIT_FC:
+ case ISOTP_WAIT_FIRST_FC:
+
+ /* we did not get any flow control frame in time */
+
+ DBG("we did not get FC frame in time.\n");
+
+#if 0
+ /* report 'communication error on send' */
+ sk->sk_err = ECOMM;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+#endif
+ /* reset tx state */
+ so->tx.state = ISOTP_IDLE;
+ wake_up_interruptible(&so->wait);
+ break;
+
+ case ISOTP_SENDING:
+
+ /* push out the next segmented pdu */
+
+ DBG("next pdu to send.\n");
+
+ dev = dev_get_by_index(&init_net, so->ifindex);
+ if (!dev)
+ break;
+
+ skb = alloc_skb(sizeof(*cf), gfp_any());
+ if (!skb) {
+ dev_put(dev);
+ break;
+ }
+
+ cf = (struct can_frame *)skb->data;
+ skb_put(skb, sizeof(*cf));
+
+ /* create consecutive frame */
+ isotp_fill_dataframe(cf, so, ae);
+
+ /* place consecutive frame N_PCI in appropriate index */
+ cf->data[ae] = N_PCI_CF | so->tx.sn++;
+ so->tx.sn %= 16;
+ so->tx.bs++;
+
+ skb->dev = dev;
+ skb->sk = sk;
+ can_send(skb, 1);
+ dev_put(dev);
+
+ if (so->tx.idx >= so->tx.len) {
+ /* we are done */
+ DBG("we are done\n");
+ so->tx.state = ISOTP_IDLE;
+ wake_up_interruptible(&so->wait);
+ break;
+ }
+
+ if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
+ /* stop and wait for FC */
+ DBG("BS stop and wait for FC\n");
+ so->tx.state = ISOTP_WAIT_FC;
+ hrtimer_start(&so->txtimer,
+ ktime_add(ktime_get(), ktime_set(1,0)),
+ HRTIMER_MODE_ABS);
+ } else
+ hrtimer_start(&so->txtimer,
+ ktime_add(ktime_get(), so->tx_gap),
+ HRTIMER_MODE_ABS);
+ break;
+
+ default:
+ BUG_ON(1);
+ }
+}
+
+static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
+{
+ struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+ txtimer);
+ tasklet_schedule(&so->txtsklet);
+
+ return HRTIMER_NORESTART;
+}
+
+static int isotp_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size)
+{
+ struct sock *sk = sock->sk;
+ struct isotp_sock *so = isotp_sk(sk);
+ struct sk_buff *skb;
+ struct net_device *dev;
+ struct can_frame *cf;
+ int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR)? 1:0;
+ int err;
+
+ if (!so->bound)
+ return -EADDRNOTAVAIL;
+
+ /* we do not support multiple buffers - for now */
+ if (so->tx.state != ISOTP_IDLE) {
+ if (msg->msg_flags & MSG_DONTWAIT)
+ return -EAGAIN;
+
+ /* wait for complete transmission of current pdu */
+ wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
+ }
+
+ if (!size || size > 4095)
+ return -EINVAL;
+
+ err = memcpy_fromiovec(so->tx.buf, msg->msg_iov, size);
+ if (err < 0)
+ return err;
+
+ dev = dev_get_by_index(&init_net, so->ifindex);
+ if (!dev)
+ return -ENXIO;
+
+ skb = sock_alloc_send_skb(sk, sizeof(*cf),
+ msg->msg_flags & MSG_DONTWAIT, &err);
+ if (!skb) {
+ dev_put(dev);
+ return err;
+ }
+
+ so->tx.state = ISOTP_SENDING;
+ so->tx.len = size;
+ so->tx.idx = 0;
+
+ cf = (struct can_frame *)skb->data;
+ skb_put(skb, sizeof(*cf));
+
+ /* check for single frame transmission */
+ if (size <= 7 - ae) {
+
+ isotp_fill_dataframe(cf, so, ae);
+
+ /* place single frame N_PCI in appropriate index */
+ cf->data[ae] = size | N_PCI_SF;
+
+ so->tx.state = ISOTP_IDLE;
+ wake_up_interruptible(&so->wait);
+ } else {
+ /* send first frame and wait for FC */
+
+ isotp_create_fframe(cf, so, ae);
+
+ DBG("starting txtimer for fc\n");
+ /* start timeout for FC */
+ hrtimer_start(&so->txtimer, ktime_set(1,0), HRTIMER_MODE_REL);
+ }
+
+ /* send the first or only CAN frame */
+ skb->dev = dev;
+ skb->sk = sk;
+ err = can_send(skb, 1);
+ dev_put(dev);
+ if (err)
+ return err;
+
+ return size;
+}
+
+static int isotp_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int err = 0;
+ int noblock;
+
+ noblock = flags & MSG_DONTWAIT;
+ flags &= ~MSG_DONTWAIT;
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ return err;
+
+ if (size < skb->len)
+ msg->msg_flags |= MSG_TRUNC;
+ else
+ size = skb->len;
+
+ err = memcpy_toiovec(msg->msg_iov, skb->data, size);
+ if (err < 0) {
+ skb_free_datagram(sk, skb);
+ return err;
+ }
+
+ sock_recv_timestamp(msg, sk, skb);
+
+ if (msg->msg_name) {
+ msg->msg_namelen = sizeof(struct sockaddr_can);
+ memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+ }
+
+ skb_free_datagram(sk, skb);
+
+ return size;
+}
+
+static int isotp_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct isotp_sock *so = isotp_sk(sk);
+
+ /* wait for complete transmission of current pdu */
+ wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
+
+ unregister_netdevice_notifier(&so->notifier);
+
+ lock_sock(sk);
+
+ hrtimer_cancel(&so->txtimer);
+ hrtimer_cancel(&so->rxtimer);
+ tasklet_kill(&so->txtsklet);
+
+ /* remove current filters & unregister */
+ if (so->bound) {
+ if (so->ifindex) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, so->ifindex);
+ if (dev) {
+ can_rx_unregister(dev, so->rxid,
+ SINGLE_MASK(so->rxid),
+ isotp_rcv, sk);
+ dev_put(dev);
+ }
+ }
+ }
+
+ so->ifindex = 0;
+ so->bound = 0;
+
+ release_sock(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct sock *sk = sock->sk;
+ struct isotp_sock *so = isotp_sk(sk);
+ int ifindex;
+ struct net_device *dev;
+ int err = 0;
+ int notify_enetdown = 0;
+
+ if (len < sizeof(*addr))
+ return -EINVAL;
+
+ if (addr->can_addr.tp.rx_id == addr->can_addr.tp.tx_id)
+ return -EADDRNOTAVAIL;
+
+ if ((addr->can_addr.tp.rx_id | addr->can_addr.tp.tx_id) &
+ (CAN_ERR_FLAG | CAN_RTR_FLAG))
+ return -EADDRNOTAVAIL;
+
+ if (!addr->can_ifindex)
+ return -ENODEV;
+
+ lock_sock(sk);
+
+ if (so->bound && addr->can_ifindex == so->ifindex &&
+ addr->can_addr.tp.rx_id == so->rxid &&
+ addr->can_addr.tp.tx_id == so->txid)
+ goto out;
+
+ dev = dev_get_by_index(&init_net, addr->can_ifindex);
+ if (!dev) {
+ err = -ENODEV;
+ goto out;
+ }
+ if (dev->type != ARPHRD_CAN) {
+ dev_put(dev);
+ err = -ENODEV;
+ goto out;
+ }
+ if (!(dev->flags & IFF_UP))
+ notify_enetdown = 1;
+
+ ifindex = dev->ifindex;
+
+ can_rx_register(dev, addr->can_addr.tp.rx_id,
+ SINGLE_MASK(addr->can_addr.tp.rx_id),
+ isotp_rcv, sk, "isotp");
+ dev_put(dev);
+
+ if (so->bound) {
+ /* unregister old filter */
+ if (so->ifindex) {
+ dev = dev_get_by_index(&init_net, so->ifindex);
+ if (dev) {
+ can_rx_unregister(dev, so->rxid,
+ SINGLE_MASK(so->rxid),
+ isotp_rcv, sk);
+ dev_put(dev);
+ }
+ }
+ }
+
+ /* switch to new settings */
+ so->ifindex = ifindex;
+ so->rxid = addr->can_addr.tp.rx_id;
+ so->txid = addr->can_addr.tp.tx_id;
+ so->bound = 1;
+
+ out:
+ release_sock(sk);
+
+ if (notify_enetdown) {
+ sk->sk_err = ENETDOWN;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ }
+
+ return err;
+}
+
+static int isotp_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *len, int peer)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct sock *sk = sock->sk;
+ struct isotp_sock *so = isotp_sk(sk);
+
+ if (peer)
+ return -EOPNOTSUPP;
+
+ addr->can_family = AF_CAN;
+ addr->can_ifindex = so->ifindex;
+
+ *len = sizeof(*addr);
+
+ return 0;
+}
+
+static int isotp_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int optlen)
+{
+ struct sock *sk = sock->sk;
+ struct isotp_sock *so = isotp_sk(sk);
+ int ret = 0;
+
+ if (level != SOL_CAN_ISOTP)
+ return -EINVAL;
+ if (optlen < 0)
+ return -EINVAL;
+
+ switch (optname) {
+
+ case CAN_ISOTP_OPTS:
+ if (optlen != sizeof(struct can_isotp_options))
+ return -EINVAL;
+
+ if (copy_from_user(&so->opt, optval, optlen))
+ return -EFAULT;
+ break;
+
+ case CAN_ISOTP_RECV_FC:
+ if (optlen != sizeof(struct can_isotp_fc_options))
+ return -EINVAL;
+
+ if (copy_from_user(&so->rxfc, optval, optlen))
+ return -EFAULT;
+ break;
+
+ default:
+ ret = -ENOPROTOOPT;
+ }
+
+ return ret;
+}
+
+static int isotp_getsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ struct sock *sk = sock->sk;
+ struct isotp_sock *so = isotp_sk(sk);
+ int len;
+ void *val;
+
+ if (level != SOL_CAN_ISOTP)
+ return -EINVAL;
+ if (get_user(len, optlen))
+ return -EFAULT;
+ if (len < 0)
+ return -EINVAL;
+
+ switch (optname) {
+
+ case CAN_ISOTP_OPTS:
+ len = min_t(int, len, sizeof(struct can_isotp_options));
+ val = &so->opt;
+ break;
+
+ case CAN_ISOTP_RECV_FC:
+ len = min_t(int, len, sizeof(struct can_isotp_fc_options));
+ val = &so->rxfc;
+ break;
+
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, val, len))
+ return -EFAULT;
+ return 0;
+}
+
+
+static int isotp_notifier(struct notifier_block *nb,
+ unsigned long msg, void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct isotp_sock *so = container_of(nb, struct isotp_sock, notifier);
+ struct sock *sk = &so->sk;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ if (dev_net(dev) != &init_net)
+ return NOTIFY_DONE;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ if (dev->nd_net != &init_net)
+ return NOTIFY_DONE;
+#endif
+
+ if (dev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ if (so->ifindex != dev->ifindex)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+
+ case NETDEV_UNREGISTER:
+ lock_sock(sk);
+ /* remove current filters & unregister */
+ if (so->bound)
+ can_rx_unregister(dev, so->rxid, SINGLE_MASK(so->rxid),
+ isotp_rcv, sk);
+
+ so->ifindex = 0;
+ so->bound = 0;
+ release_sock(sk);
+
+ sk->sk_err = ENODEV;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ break;
+
+ case NETDEV_DOWN:
+ sk->sk_err = ENETDOWN;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+
+static int isotp_init(struct sock *sk)
+{
+ struct isotp_sock *so = isotp_sk(sk);
+
+ so->ifindex = 0;
+ so->bound = 0;
+
+ so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS;
+ so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS;
+ so->opt.rxpad_content = CAN_ISOTP_DEFAULT_RXPAD_CONTENT;
+ so->opt.txpad_content = CAN_ISOTP_DEFAULT_TXPAD_CONTENT;
+ so->opt.frame_txtime = CAN_ISOTP_DEFAULT_FRAME_TXTIME;
+ so->rxfc.bs = CAN_ISOTP_DEFAULT_RECV_BS;
+ so->rxfc.stmin = CAN_ISOTP_DEFAULT_RECV_STMIN;
+ so->rxfc.wftmax = CAN_ISOTP_DEFAULT_RECV_WFTMAX;
+
+ so->rx.state = ISOTP_IDLE;
+ so->tx.state = ISOTP_IDLE;
+
+ hrtimer_init(&so->rxtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ so->rxtimer.function = isotp_rx_timer_handler;
+ hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ so->txtimer.function = isotp_tx_timer_handler;
+
+ tasklet_init(&so->txtsklet, isotp_tx_timer_tsklet, (unsigned long)so);
+
+ init_waitqueue_head(&so->wait);
+
+ so->notifier.notifier_call = isotp_notifier;
+ register_netdevice_notifier(&so->notifier);
+
+ return 0;
+}
+
+
+static struct proto_ops isotp_ops __read_mostly = {
+ .family = PF_CAN,
+ .release = isotp_release,
+ .bind = isotp_bind,
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = isotp_getname,
+ .poll = datagram_poll,
+ .ioctl = NULL, /* use can_ioctl() from af_can.c */
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = isotp_setsockopt,
+ .getsockopt = isotp_getsockopt,
+ .sendmsg = isotp_sendmsg,
+ .recvmsg = isotp_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static struct proto isotp_proto __read_mostly = {
+ .name = "CAN_ISOTP",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct isotp_sock),
+ .init = isotp_init,
+};
+
+static struct can_proto isotp_can_proto __read_mostly = {
+ .type = SOCK_DGRAM,
+ .protocol = CAN_ISOTP,
+ .capability = -1,
+ .ops = &isotp_ops,
+ .prot = &isotp_proto,
+};
+
+static __init int isotp_module_init(void)
+{
+ int err;
+
+ printk(banner);
+
+ err = can_proto_register(&isotp_can_proto);
+ if (err < 0)
+ printk(KERN_ERR "can: registration of isotp protocol failed\n");
+
+ return err;
+}
+
+static __exit void isotp_module_exit(void)
+{
+ can_proto_unregister(&isotp_can_proto);
+}
+
+module_init(isotp_module_init);
+module_exit(isotp_module_exit);
*/
#include <linux/module.h>
+#include <linux/version.h>
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <linux/rcupdate.h>
#include <linux/can/core.h>
#include "af_can.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#include "compat.h"
+#endif
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
/*
* proc filenames for the PF_CAN core
void can_init_proc(void)
{
/* create /proc/net/can directory */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
can_dir = proc_mkdir("can", init_net.proc_net);
+#else
+ can_dir = proc_mkdir("can", proc_net);
+#endif
if (!can_dir) {
printk(KERN_INFO "can: failed to create /proc/net/can . "
can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF);
if (can_dir)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
proc_net_remove(&init_net, "can");
+#else
+ proc_net_remove("can");
+#endif
}
*/
#include <linux/module.h>
+#include <linux/version.h>
#include <linux/init.h>
#include <linux/uio.h>
#include <linux/net.h>
#include <linux/can/core.h>
#include <linux/can/raw.h>
#include <net/sock.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#include <net/net_namespace.h>
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#include "compat.h"
+#endif
+
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+RCSID("$Id$");
#define CAN_RAW_VERSION CAN_VERSION
static __initdata const char banner[] =
*/
struct raw_sock {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
struct sock sk;
+#else
+ struct sock *sk;
+#endif
int bound;
int ifindex;
struct notifier_block notifier;
static inline struct raw_sock *raw_sk(const struct sock *sk)
{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
return (struct raw_sock *)sk;
+#else
+ return (struct raw_sock *)sk->sk_protinfo;
+#endif
}
static void raw_rcv(struct sk_buff *skb, void *data)
struct raw_sock *ro = raw_sk(sk);
struct sockaddr_can *addr;
- if (!ro->recv_own_msgs) {
- /* check the received tx sock reference */
- if (skb->sk == sk) {
- kfree_skb(skb);
- return;
- }
- }
+ /* check the received tx sock reference */
+ if (!ro->recv_own_msgs && skb->sk == sk)
+ return;
+
+ /* clone the given skb to be able to enqueue it into the rcv queue */
+ skb = skb_clone(skb, GFP_ATOMIC);
+ if (!skb)
+ return;
/*
* Put the datagram to the queue so that raw_recvmsg() can
{
struct net_device *dev = (struct net_device *)data;
struct raw_sock *ro = container_of(nb, struct raw_sock, notifier);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
struct sock *sk = &ro->sk;
+#else
+ struct sock *sk = ro->sk;
+#endif
- if (dev_net(dev) != &init_net)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ if (!net_eq(dev_net(dev), &init_net))
+ return NOTIFY_DONE;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ if (dev->nd_net != &init_net)
return NOTIFY_DONE;
+#endif
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
{
struct raw_sock *ro = raw_sk(sk);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+ ro->sk = sk;
+#endif
ro->bound = 0;
ro->ifindex = 0;
skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT,
&err);
- if (!skb) {
- dev_put(dev);
- return err;
- }
+ if (!skb)
+ goto put_dev;
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
- if (err < 0) {
- kfree_skb(skb);
- dev_put(dev);
- return err;
- }
+ if (err < 0)
+ goto free_skb;
skb->dev = dev;
skb->sk = sk;
dev_put(dev);
if (err)
- return err;
+ goto send_failed;
return size;
+
+free_skb:
+ kfree_skb(skb);
+put_dev:
+ dev_put(dev);
+send_failed:
+ return err;
}
static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
.sendpage = sock_no_sendpage,
};
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
static struct proto raw_proto __read_mostly = {
.name = "CAN_RAW",
.owner = THIS_MODULE,
.ops = &raw_ops,
.prot = &raw_proto,
};
+#else
+static struct can_proto raw_can_proto __read_mostly = {
+ .type = SOCK_RAW,
+ .protocol = CAN_RAW,
+ .capability = -1,
+ .ops = &raw_ops,
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct raw_sock),
+ .init = raw_init,
+};
+#endif
static __init int raw_module_init(void)
{