]> rtime.felk.cvut.cz Git - lisovros/linux_canprio.git/commitdiff
Added socketcan from SVN r903
authorMichal Sojka <sojkam1@fel.cvut.cz>
Fri, 16 Jan 2009 11:35:00 +0000 (12:35 +0100)
committerMichal Sojka <sojkam1@fel.cvut.cz>
Fri, 16 Jan 2009 11:35:00 +0000 (12:35 +0100)
74 files changed:
Documentation/networking/can.txt
drivers/net/can/Kconfig
drivers/net/can/Makefile
drivers/net/can/dev.c [new file with mode: 0644]
drivers/net/can/mcp251x.c [new file with mode: 0644]
drivers/net/can/mscan/Makefile [new file with mode: 0644]
drivers/net/can/mscan/mpc52xx_can.c [new file with mode: 0644]
drivers/net/can/mscan/mscan.c [new file with mode: 0644]
drivers/net/can/mscan/mscan.h [new file with mode: 0644]
drivers/net/can/old/Kconfig [new file with mode: 0644]
drivers/net/can/old/ccan/Makefile [new file with mode: 0644]
drivers/net/can/old/ccan/ccan.c [new file with mode: 0644]
drivers/net/can/old/ccan/ccan.h [new file with mode: 0644]
drivers/net/can/old/ccan/h7202_can.c [new file with mode: 0644]
drivers/net/can/old/hal/c200.c [new file with mode: 0644]
drivers/net/can/old/hal/esdio.c [new file with mode: 0644]
drivers/net/can/old/hal/gw2.c [new file with mode: 0644]
drivers/net/can/old/hal/hal.h [new file with mode: 0644]
drivers/net/can/old/hal/io.c [new file with mode: 0644]
drivers/net/can/old/hal/iomem.c [new file with mode: 0644]
drivers/net/can/old/hal/iomux.c [new file with mode: 0644]
drivers/net/can/old/hal/pc7io.c [new file with mode: 0644]
drivers/net/can/old/i82527/Makefile [new file with mode: 0644]
drivers/net/can/old/i82527/i82527.c [new file with mode: 0644]
drivers/net/can/old/i82527/i82527.h [new file with mode: 0644]
drivers/net/can/old/i82527/proc.c [new file with mode: 0644]
drivers/net/can/old/mscan/Makefile [new file with mode: 0644]
drivers/net/can/old/mscan/mpc52xx_can.c [new file with mode: 0644]
drivers/net/can/old/mscan/mscan.c [new file with mode: 0644]
drivers/net/can/old/mscan/mscan.h [new file with mode: 0644]
drivers/net/can/old/sja1000/Makefile [new file with mode: 0644]
drivers/net/can/old/sja1000/proc.c [new file with mode: 0644]
drivers/net/can/old/sja1000/sja1000.c [new file with mode: 0644]
drivers/net/can/old/sja1000/sja1000.h [new file with mode: 0644]
drivers/net/can/sja1000/Makefile [new file with mode: 0644]
drivers/net/can/sja1000/ems_pci.c [new file with mode: 0644]
drivers/net/can/sja1000/ems_pcmcia.c [new file with mode: 0644]
drivers/net/can/sja1000/ixxat_pci.c [new file with mode: 0644]
drivers/net/can/sja1000/kvaser_pci.c [new file with mode: 0644]
drivers/net/can/sja1000/peak_pci.c [new file with mode: 0644]
drivers/net/can/sja1000/pipcan.c [new file with mode: 0644]
drivers/net/can/sja1000/sja1000.c [new file with mode: 0644]
drivers/net/can/sja1000/sja1000.h [new file with mode: 0644]
drivers/net/can/sja1000/sja1000_platform.c [new file with mode: 0644]
drivers/net/can/slcan.c [new file with mode: 0644]
drivers/net/can/softing/Makefile [new file with mode: 0644]
drivers/net/can/softing/softing.h [new file with mode: 0644]
drivers/net/can/softing/softing_cs.c [new file with mode: 0644]
drivers/net/can/softing/softing_fw.c [new file with mode: 0644]
drivers/net/can/softing/softing_main.c [new file with mode: 0644]
drivers/net/can/sysfs.c [new file with mode: 0644]
drivers/net/can/sysfs.h [new file with mode: 0644]
drivers/net/can/vcan.c
include/linux/can.h
include/linux/can/bcm.h
include/linux/can/core.h
include/linux/can/dev.h [new file with mode: 0644]
include/linux/can/error.h
include/linux/can/ioctl.h [new file with mode: 0644]
include/linux/can/isotp.h [new file with mode: 0644]
include/linux/can/platform/mcp251x.h [new file with mode: 0644]
include/linux/can/platform/sja1000.h [new file with mode: 0644]
include/linux/can/raw.h
include/linux/can/version.h [new file with mode: 0644]
net/can/Kconfig
net/can/Makefile
net/can/af_can.c
net/can/af_can.h
net/can/bcm-prior-2-6-22.c [new file with mode: 0644]
net/can/bcm.c
net/can/compat.h [new file with mode: 0644]
net/can/isotp.c [new file with mode: 0644]
net/can/proc.c
net/can/raw.c

index 641d2afacffa33094a5ee7a52e6646ec7c089220..2035bc4932f224685992ca57b29e64de5da4934e 100644 (file)
@@ -35,8 +35,9 @@ This file contains
     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
 
@@ -186,7 +187,7 @@ solution for a couple of reasons:
 
   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
@@ -481,7 +482,7 @@ solution for a couple of reasons:
   - 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)
 
@@ -584,7 +585,42 @@ solution for a couple of reasons:
   @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:
@@ -603,7 +639,7 @@ solution for a couple of reasons:
 
   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
index 57def0d573716941b0f86b8aeeb0b2f216031434..2d4a8c5e5f1f9c6205d4876eedfef1b475059966 100644 (file)
@@ -3,15 +3,155 @@ menu "CAN Device Drivers"
 
 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
index c4bead705cd958877231557d67a7f105b2730aa8..7656d196e2c68328878b36c41e46134029b5990d 100644 (file)
@@ -1,5 +1,14 @@
-#
-#  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
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
new file mode 100644 (file)
index 0000000..def7b18
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * $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);
diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c
new file mode 100644 (file)
index 0000000..3600efd
--- /dev/null
@@ -0,0 +1,1244 @@
+/*
+ *
+ * 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");
diff --git a/drivers/net/can/mscan/Makefile b/drivers/net/can/mscan/Makefile
new file mode 100644 (file)
index 0000000..7eacddf
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_CAN_MPC52XX)      += mscan-mpc52xx.o
+
+mscan-mpc52xx-objs     := mscan.o mpc52xx_can.o
diff --git a/drivers/net/can/mscan/mpc52xx_can.c b/drivers/net/can/mscan/mpc52xx_can.c
new file mode 100644 (file)
index 0000000..7762f26
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * 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(&regs->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");
diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c
new file mode 100644 (file)
index 0000000..af72e78
--- /dev/null
@@ -0,0 +1,775 @@
+/*
+ * 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(&regs->cantier, 0);
+                       out_8(&regs->cantarq, priv->tx_active);
+                       out_8(&regs->cantier, priv->tx_active);
+               }
+
+               canctl1 = in_8(&regs->canctl1);
+               if ((mode & MSCAN_SLPRQ) && (canctl1 & MSCAN_SLPAK) == 0) {
+                       out_8(&regs->canctl0,
+                             in_8(&regs->canctl0) | MSCAN_SLPRQ);
+                       for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+                               if (in_8(&regs->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(&regs->canctl0,
+                             in_8(&regs->canctl0) | MSCAN_INITRQ);
+                       for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+                               if (in_8(&regs->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(&regs->canctl0,
+                             in_8(&regs->canctl0) | MSCAN_CSWAI);
+
+       } else {
+               canctl1 = in_8(&regs->canctl1);
+               if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) {
+                       out_8(&regs->canctl0, in_8(&regs->canctl0) &
+                             ~(MSCAN_SLPRQ | MSCAN_INITRQ));
+                       for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+                               canctl1 = in_8(&regs->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(&regs->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(&regs->canrflg);
+       priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+       priv->can.state = state_map[max(MSCAN_STATE_RX(canrflg),
+                                   MSCAN_STATE_TX(canrflg))];
+       out_8(&regs->cantier, 0);
+
+       /* Enable receive interrupts. */
+       out_8(&regs->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(&regs->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(&regs->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(&regs->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(&regs->tx.idr1_0, can_id);
+
+       if (!rtr) {
+               volatile void __iomem *data = &regs->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(&regs->tx.dlr, frame->can_dlc);
+       out_8(&regs->tx.tbpr, priv->cur_pri);
+
+       /* Start transmission. */
+       out_8(&regs->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(&regs->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(&regs->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(&regs->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(&regs->rx.idr1_0);
+                       if (can_id & (1 << 3)) {
+                               frame->can_id = CAN_EFF_FLAG;
+                               can_id = ((can_id << 16) |
+                                         in_be16(&regs->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(&regs->rx.dlr) & 0xf;
+
+                       if (!(frame->can_id & CAN_RTR_FLAG)) {
+                               volatile void __iomem *data = &regs->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(&regs->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(&regs->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(&regs->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(&regs->canrier,
+                     in_8(&regs->canrier) | MSCAN_ERR_IF | MSCAN_RXFIE);
+#else
+               if (priv->can.state < CAN_STATE_BUS_OFF)
+                       out_8(&regs->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(&regs->cantier) & MSCAN_TXE;
+       cantflg = in_8(&regs->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(&regs->cantbsel, mask);
+                       stats->tx_bytes += in_8(&regs->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(&regs->cantier, priv->tx_active);
+               ret = IRQ_HANDLED;
+       }
+
+       canrflg = in_8(&regs->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(&regs->canrier));
+#endif
+#if 0
+               if (check_set_state(dev, canrflg)) {
+                       out_8(&regs->canrflg, MSCAN_CSCIF);
+                       ret = IRQ_HANDLED;
+               }
+#endif
+               if (canrflg & ~MSCAN_STAT_MSK) {
+                       priv->shadow_canrier = in_8(&regs->canrier);
+                       out_8(&regs->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(&regs->canbtr0, btr0);
+       out_8(&regs->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(&regs->canctl1, in_8(&regs->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(&regs->cantier, 0);
+       out_8(&regs->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(&regs->canctl1);
+       if (clock_src)
+               ctl1 |= MSCAN_CLKSRC;
+       else
+               ctl1 &= ~MSCAN_CLKSRC;
+
+       ctl1 |= MSCAN_CANE;
+       out_8(&regs->canctl1, ctl1);
+       udelay(100);
+
+       /* acceptance mask/acceptance code (accept everything) */
+       out_be16(&regs->canidar1_0, 0);
+       out_be16(&regs->canidar3_2, 0);
+       out_be16(&regs->canidar5_4, 0);
+       out_be16(&regs->canidar7_6, 0);
+
+       out_be16(&regs->canidmr1_0, 0xffff);
+       out_be16(&regs->canidmr3_2, 0xffff);
+       out_be16(&regs->canidmr5_4, 0xffff);
+       out_be16(&regs->canidmr7_6, 0xffff);
+       /* Two 32 bit Acceptance Filters */
+       out_8(&regs->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(&regs->canctl1, in_8(&regs->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");
diff --git a/drivers/net/can/mscan/mscan.h b/drivers/net/can/mscan/mscan.h
new file mode 100644 (file)
index 0000000..b7f6e2c
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * $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__ */
diff --git a/drivers/net/can/old/Kconfig b/drivers/net/can/old/Kconfig
new file mode 100644 (file)
index 0000000..4b8567a
--- /dev/null
@@ -0,0 +1,67 @@
+
+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.
+
diff --git a/drivers/net/can/old/ccan/Makefile b/drivers/net/can/old/ccan/Makefile
new file mode 100644 (file)
index 0000000..8c44d9f
--- /dev/null
@@ -0,0 +1,21 @@
+#
+#  $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
diff --git a/drivers/net/can/old/ccan/ccan.c b/drivers/net/can/old/ccan/ccan.c
new file mode 100644 (file)
index 0000000..bf8f9db
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ * 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");
diff --git a/drivers/net/can/old/ccan/ccan.h b/drivers/net/can/old/ccan/ccan.h
new file mode 100644 (file)
index 0000000..b7a5460
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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__ */
diff --git a/drivers/net/can/old/ccan/h7202_can.c b/drivers/net/can/old/ccan/h7202_can.c
new file mode 100644 (file)
index 0000000..5d6fa5a
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * 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");
diff --git a/drivers/net/can/old/hal/c200.c b/drivers/net/can/old/hal/c200.c
new file mode 100644 (file)
index 0000000..2ace5fc
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * 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; }
diff --git a/drivers/net/can/old/hal/esdio.c b/drivers/net/can/old/hal/esdio.c
new file mode 100644 (file)
index 0000000..f4db50c
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * 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; 
+}
diff --git a/drivers/net/can/old/hal/gw2.c b/drivers/net/can/old/hal/gw2.c
new file mode 100644 (file)
index 0000000..9fcb90b
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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; }
+
diff --git a/drivers/net/can/old/hal/hal.h b/drivers/net/can/old/hal/hal.h
new file mode 100644 (file)
index 0000000..1c59b54
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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 */
diff --git a/drivers/net/can/old/hal/io.c b/drivers/net/can/old/hal/io.c
new file mode 100644 (file)
index 0000000..aab1aa8
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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; }
+
diff --git a/drivers/net/can/old/hal/iomem.c b/drivers/net/can/old/hal/iomem.c
new file mode 100644 (file)
index 0000000..b4a7fdd
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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; }
+
diff --git a/drivers/net/can/old/hal/iomux.c b/drivers/net/can/old/hal/iomux.c
new file mode 100644 (file)
index 0000000..71bd00f
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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; }
+
diff --git a/drivers/net/can/old/hal/pc7io.c b/drivers/net/can/old/hal/pc7io.c
new file mode 100644 (file)
index 0000000..567de15
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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; }
+
diff --git a/drivers/net/can/old/i82527/Makefile b/drivers/net/can/old/i82527/Makefile
new file mode 100644 (file)
index 0000000..cf1a839
--- /dev/null
@@ -0,0 +1,25 @@
+#
+#  $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
diff --git a/drivers/net/can/old/i82527/i82527.c b/drivers/net/can/old/i82527/i82527.c
new file mode 100644 (file)
index 0000000..16d585e
--- /dev/null
@@ -0,0 +1,1308 @@
+/*
+ * 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);
+
diff --git a/drivers/net/can/old/i82527/i82527.h b/drivers/net/can/old/i82527/i82527.h
new file mode 100644 (file)
index 0000000..e244e15
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * $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 */
diff --git a/drivers/net/can/old/i82527/proc.c b/drivers/net/can/old/i82527/proc.c
new file mode 100644 (file)
index 0000000..6028e96
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * 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);
+       }
+}
diff --git a/drivers/net/can/old/mscan/Makefile b/drivers/net/can/old/mscan/Makefile
new file mode 100644 (file)
index 0000000..a2a8c11
--- /dev/null
@@ -0,0 +1,22 @@
+#
+#  $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
diff --git a/drivers/net/can/old/mscan/mpc52xx_can.c b/drivers/net/can/old/mscan/mpc52xx_can.c
new file mode 100644 (file)
index 0000000..6da9572
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * 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(&regs->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");
diff --git a/drivers/net/can/old/mscan/mscan.c b/drivers/net/can/old/mscan/mscan.c
new file mode 100644 (file)
index 0000000..2d6e13d
--- /dev/null
@@ -0,0 +1,752 @@
+/*
+ * 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(&regs->canctl1);
+               if ((mode & MSCAN_SLPRQ) && (canctl1 & MSCAN_SLPAK) == 0) {
+                       out_8(&regs->canctl0,
+                             in_8(&regs->canctl0) | MSCAN_SLPRQ);
+                       for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+                               if (in_8(&regs->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(&regs->canctl0,
+                             in_8(&regs->canctl0) | MSCAN_INITRQ);
+                       for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+                               if (in_8(&regs->canctl1) & MSCAN_INITAK)
+                                       break;
+                       }
+                       if (i >= MSCAN_SET_MODE_RETRIES)
+                               ret = -ENODEV;
+               }
+
+               if (!ret && (mode & MSCAN_CSWAI))
+                       out_8(&regs->canctl0,
+                             in_8(&regs->canctl0) | MSCAN_CSWAI);
+
+       } else {
+               canctl1 = in_8(&regs->canctl1);
+               if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) {
+                       out_8(&regs->canctl0, in_8(&regs->canctl0) &
+                             ~(MSCAN_SLPRQ | MSCAN_INITRQ));
+                       for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+                               canctl1 = in_8(&regs->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(&regs->canctl0) & (MSCAN_SLPRQ | MSCAN_INITRQ |
+                                             MSCAN_CSWAI);
+       state->canrier = in_8(&regs->canrier);
+       state->cantier = in_8(&regs->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(&regs->canrier, state->canrier);
+               out_8(&regs->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(&regs->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(&regs->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(&regs->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(&regs->tx.idr1_0, can_id);
+
+       if (!rtr) {
+               volatile void __iomem *data = &regs->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(&regs->tx.dlr, frame->can_dlc);
+       out_8(&regs->tx.tbpr, priv->cur_pri);
+
+       /* Start transmission. */
+       out_8(&regs->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(&regs->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(&regs->cantier, 0);
+
+       mask = list_entry(priv->tx_head.next, tx_queue_entry_t, list)->mask;
+       dev->trans_start = jiffies;
+       out_8(&regs->cantarq, mask);
+       out_8(&regs->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(&regs->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(&regs->canrflg, canrflg);
+                       continue;
+               }
+
+               frame = (struct can_frame *)skb_put(skb,
+                                                   sizeof(struct can_frame));
+
+               if (canrflg & MSCAN_RXF) {
+                       can_id = in_be16(&regs->rx.idr1_0);
+                       if (can_id & (1 << 3)) {
+                               frame->can_id = CAN_EFF_FLAG;
+                               can_id = ((can_id << 16) |
+                                         in_be16(&regs->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(&regs->rx.dlr) & 0xf;
+
+                       if (!(frame->can_id & CAN_RTR_FLAG)) {
+                               volatile void __iomem *data = &regs->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(&regs->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(&regs->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(&regs->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(&regs->canrier,
+                     in_8(&regs->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(&regs->cantier) & MSCAN_TXE) &&
+           (cantflg = in_8(&regs->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(&regs->cantaak) & mask) {
+                               stats->tx_dropped++;
+                               stats->tx_aborted_errors++;
+                       } else {
+                               out_8(&regs->cantbsel, mask);
+                               stats->tx_bytes +=
+                                   in_8(&regs->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(&regs->cantier, priv->tx_active);
+               ret = IRQ_HANDLED;
+       }
+
+       if ((((canrflg = in_8(&regs->canrflg)) & ~MSCAN_STAT_MSK)) &&
+           !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) {
+               if (check_set_state(dev, canrflg)) {
+                       out_8(&regs->canrflg, MSCAN_CSCIF);
+                       ret = IRQ_HANDLED;
+               }
+               if (canrflg & ~MSCAN_STAT_MSK) {
+                       priv->shadow_canrier = in_8(&regs->canrier);
+                       out_8(&regs->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(&regs->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(&regs->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(&regs->canidar1_0, 0);
+       out_be16(&regs->canidar3_2, 0);
+       out_be16(&regs->canidar5_4, 0);
+       out_be16(&regs->canidar7_6, 0);
+
+       out_be16(&regs->canidmr1_0, 0xffff);
+       out_be16(&regs->canidmr3_2, 0xffff);
+       out_be16(&regs->canidmr5_4, 0xffff);
+       out_be16(&regs->canidmr7_6, 0xffff);
+       /* Two 32 bit Acceptance Filters */
+       out_8(&regs->canidac, MSCAN_AF_32BIT);
+
+       out_8(&regs->canctl1, in_8(&regs->canctl1) & ~MSCAN_LISTEN);
+       mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+
+       priv->shadow_statflg = in_8(&regs->canrflg) & MSCAN_STAT_MSK;
+       priv->cur_pri = 0;
+       priv->tx_active = 0;
+
+       out_8(&regs->cantier, 0);
+       /* Enable receive interrupts. */
+       out_8(&regs->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(&regs->cantier, 0);
+       out_8(&regs->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(&regs->canctl1);
+       if (clock_src)
+               ctl1 |= MSCAN_CLKSRC;
+       else
+               ctl1 &= ~MSCAN_CLKSRC;
+
+       ctl1 |= MSCAN_CANE;
+       out_8(&regs->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(&regs->canctl1, in_8(&regs->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");
diff --git a/drivers/net/can/old/mscan/mscan.h b/drivers/net/can/old/mscan/mscan.h
new file mode 100644 (file)
index 0000000..7fdd397
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * $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__ */
diff --git a/drivers/net/can/old/sja1000/Makefile b/drivers/net/can/old/sja1000/Makefile
new file mode 100644 (file)
index 0000000..e56f801
--- /dev/null
@@ -0,0 +1,28 @@
+#
+#  $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
diff --git a/drivers/net/can/old/sja1000/proc.c b/drivers/net/can/old/sja1000/proc.c
new file mode 100644 (file)
index 0000000..86a81e8
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * 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);
+       }
+}
diff --git a/drivers/net/can/old/sja1000/sja1000.c b/drivers/net/can/old/sja1000/sja1000.c
new file mode 100644 (file)
index 0000000..c8876f3
--- /dev/null
@@ -0,0 +1,1198 @@
+/*
+ * 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);
+
diff --git a/drivers/net/can/old/sja1000/sja1000.h b/drivers/net/can/old/sja1000/sja1000.h
new file mode 100644 (file)
index 0000000..d6d48fb
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * $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 */
diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile
new file mode 100644 (file)
index 0000000..bd1f0ab
--- /dev/null
@@ -0,0 +1,28 @@
+#
+#  $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
diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c
new file mode 100644 (file)
index 0000000..d759200
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * 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);
+
diff --git a/drivers/net/can/sja1000/ems_pcmcia.c b/drivers/net/can/sja1000/ems_pcmcia.c
new file mode 100644 (file)
index 0000000..0f6219d
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * 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);
+
diff --git a/drivers/net/can/sja1000/ixxat_pci.c b/drivers/net/can/sja1000/ixxat_pci.c
new file mode 100644 (file)
index 0000000..8f9ceb6
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * 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);
diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c
new file mode 100644 (file)
index 0000000..d8fb759
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * 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);
diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c
new file mode 100644 (file)
index 0000000..22d69e4
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * 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);
diff --git a/drivers/net/can/sja1000/pipcan.c b/drivers/net/can/sja1000/pipcan.c
new file mode 100644 (file)
index 0000000..b7414b2
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * 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);
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c
new file mode 100644 (file)
index 0000000..43775f8
--- /dev/null
@@ -0,0 +1,824 @@
+/*
+ * 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);
diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h
new file mode 100644 (file)
index 0000000..29993b9
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * $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 */
diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c
new file mode 100644 (file)
index 0000000..5157c17
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * 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);
diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c
new file mode 100644 (file)
index 0000000..53f15b3
--- /dev/null
@@ -0,0 +1,999 @@
+/*
+ * 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);
diff --git a/drivers/net/can/softing/Makefile b/drivers/net/can/softing/Makefile
new file mode 100644 (file)
index 0000000..df3fcec
--- /dev/null
@@ -0,0 +1,20 @@
+# 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
diff --git a/drivers/net/can/softing/softing.h b/drivers/net/can/softing/softing.h
new file mode 100644 (file)
index 0000000..e43c7f1
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * 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); \
+       }
+
diff --git a/drivers/net/can/softing/softing_cs.c b/drivers/net/can/softing/softing_cs.c
new file mode 100644 (file)
index 0000000..e33b614
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+* 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, &reg);
+}
+
+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(&reg, 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, &reg);
+       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");
+
diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c
new file mode 100644 (file)
index 0000000..4d20774
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+* 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;
+       }
+}
+
+
diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c
new file mode 100644 (file)
index 0000000..19ec72e
--- /dev/null
@@ -0,0 +1,1062 @@
+/*
+* 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");
diff --git a/drivers/net/can/sysfs.c b/drivers/net/can/sysfs.c
new file mode 100644 (file)
index 0000000..407bf25
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * $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 */
+
+
+
diff --git a/drivers/net/can/sysfs.h b/drivers/net/can/sysfs.h
new file mode 100644 (file)
index 0000000..059ec4c
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * $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 */
index 103f0f1df28065e9b30fab934f1f43184f654293..bb0ff84d29d476fa7cc31342e53ab9fabcb85d6c 100644 (file)
 
 #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";
@@ -56,6 +62,23 @@ MODULE_DESCRIPTION("virtual CAN interface");
 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:
@@ -67,10 +90,23 @@ static int echo; /* echo testing. Default: 0 (Off) */
 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;
@@ -85,7 +121,11 @@ static void vcan_rx(struct sk_buff *skb, struct net_device *dev)
 
 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++;
@@ -128,26 +168,53 @@ static int vcan_tx(struct sk_buff *skb, struct net_device *dev)
        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)
@@ -164,6 +231,75 @@ static __exit void vcan_cleanup_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);
index d18333302cbd0a65fcb62caa13e3505f07aa6dc7..8981ba11bf05d46e61eab7117eebb3d0a566faa4 100644 (file)
@@ -3,6 +3,8 @@
  *
  * 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
@@ -15,6 +17,7 @@
 #ifndef CAN_H
 #define CAN_H
 
+#include <linux/version.h>
 #include <linux/types.h>
 #include <linux/socket.h>
 
index 7f293273c444a66101b7b66f9394c3c1c16b6bce..0a32364b4c37b5f1578fa8ef6e3561935bd66927 100644 (file)
@@ -3,6 +3,8 @@
  *
  * 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.
index e9ca210ffa5b6cf9fd9c0c5f577bbca752fc04d1..99f9607179ecb897123435ec39b9cf9cf8c536ee 100644 (file)
@@ -3,6 +3,8 @@
  *
  * 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
@@ -19,7 +21,7 @@
 #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"
@@ -41,7 +43,13 @@ struct can_proto {
        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) */
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
new file mode 100644 (file)
index 0000000..5db4c2e
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * 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 */
index d4127fd9e6810c5005197488d12112ff4e4afb46..918a6bfdc04a3075342a2be47a8bfc94e760fb09 100644 (file)
@@ -3,6 +3,8 @@
  *
  * 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.
diff --git a/include/linux/can/ioctl.h b/include/linux/can/ioctl.h
new file mode 100644 (file)
index 0000000..f9ba439
--- /dev/null
@@ -0,0 +1,99 @@
+
+/*
+ * 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 */
diff --git a/include/linux/can/isotp.h b/include/linux/can/isotp.h
new file mode 100644 (file)
index 0000000..b91337a
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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
diff --git a/include/linux/can/platform/mcp251x.h b/include/linux/can/platform/mcp251x.h
new file mode 100644 (file)
index 0000000..d217ffa
--- /dev/null
@@ -0,0 +1,34 @@
+#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__ */
diff --git a/include/linux/can/platform/sja1000.h b/include/linux/can/platform/sja1000.h
new file mode 100644 (file)
index 0000000..37966e6
--- /dev/null
@@ -0,0 +1,32 @@
+#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_ */
index b2a0f87492c50da809b79481c06383cfdcb034e3..cb61f105f9ceba24baa3e64497ab2f8b374f4c8a 100644 (file)
@@ -3,6 +3,8 @@
  *
  * 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
diff --git a/include/linux/can/version.h b/include/linux/can/version.h
new file mode 100644 (file)
index 0000000..6a62680
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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 */
index 89395b2c8bcaf6d41b4f84b37d02beec643f2fee..9141bc011e23b63c0224c90e6b71acddaa059751 100644 (file)
@@ -40,5 +40,21 @@ config CAN_BCM
          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"
index 9cd3c4b3abda884901aad20640215c2b50fc01ea..8cfea699932dc79dfee7711dd87a3129d35302a2 100644 (file)
@@ -1,7 +1,3 @@
-#
-#  Makefile for the Linux Controller Area Network core.
-#
-
 obj-$(CONFIG_CAN)      += can.o
 can-objs               := af_can.o proc.o
 
@@ -10,3 +6,6 @@ can-raw-objs           := raw.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
index 484bbf6dd0322790f6f42cee71c3e1f86f6ace41..66b7079492afce23a7eefb216b8c8d97af753afa 100644 (file)
  */
 
 #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";
@@ -81,7 +94,11 @@ HLIST_HEAD(can_rx_dev_list);
 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;
@@ -105,16 +122,28 @@ static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                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;
@@ -125,8 +154,10 @@ static int can_create(struct net *net, struct socket *sock, int protocol)
        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 */
@@ -146,8 +177,13 @@ static int can_create(struct net *net, struct socket *sock, int protocol)
 
        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 */
@@ -167,17 +203,40 @@ static int can_create(struct net *net, struct socket *sock, int protocol)
 
        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 */
@@ -186,7 +245,11 @@ static int can_create(struct net *net, struct socket *sock, int protocol)
        }
 
  errout:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
        module_put(cp->prot->owner);
+#else
+       module_put(cp->owner);
+#endif
        return err;
 }
 
@@ -229,8 +292,13 @@ int can_send(struct sk_buff *skb, int loop)
        }
 
        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 */
@@ -247,6 +315,9 @@ int can_send(struct sk_buff *skb, int loop)
                 * 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
@@ -319,23 +390,52 @@ static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
        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;
@@ -348,15 +448,19 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *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 */
@@ -381,6 +485,12 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *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
@@ -536,13 +646,8 @@ EXPORT_SYMBOL(can_rx_unregister);
 
 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)
@@ -589,7 +694,10 @@ 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) {
@@ -608,14 +716,25 @@ static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
        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;
        }
@@ -674,9 +793,11 @@ int can_proto_register(struct can_proto *cp)
                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]) {
@@ -692,8 +813,10 @@ int can_proto_register(struct can_proto *cp)
        }
        spin_unlock(&proto_tab_lock);
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
        if (err < 0)
                proto_unregister(cp->prot);
+#endif
 
        return err;
 }
@@ -715,7 +838,9 @@ void can_proto_unregister(struct can_proto *cp)
        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);
 
@@ -728,8 +853,13 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg,
        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;
@@ -811,8 +941,13 @@ static __init int can_init(void)
 {
        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;
 
index 18f91e37cc30656308f84f109f6b7bff00771d92..d1de572ee84b3c46d927499352ee511c0ae7b346 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
  * All rights reserved.
  *
diff --git a/net/can/bcm-prior-2-6-22.c b/net/can/bcm-prior-2-6-22.c
new file mode 100644 (file)
index 0000000..6e43511
--- /dev/null
@@ -0,0 +1,1624 @@
+/*
+ * 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);
index 72c2ce904f83ab9f92109e33c6ed34f947a80a73..28742e614ca6f1ff037f92345125f898faf92277 100644 (file)
@@ -42,6 +42,7 @@
  */
 
 #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)
 {
@@ -89,6 +104,7 @@ struct bcm_op {
        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;
@@ -340,19 +356,15 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
        }
 }
 
-/*
- * 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;
@@ -371,20 +383,32 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
 
                /* 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;
 }
 
 /*
@@ -401,6 +425,9 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data)
        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;
@@ -412,6 +439,19 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data)
        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
@@ -419,37 +459,32 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *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);
@@ -457,7 +492,8 @@ static void bcm_rx_update_and_send(struct bcm_op *op,
        }
 
        /* 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();
 }
 
@@ -466,7 +502,7 @@ static void bcm_rx_update_and_send(struct bcm_op *op,
  *                       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,
@@ -510,14 +546,12 @@ static void bcm_rx_starttimer(struct bcm_op *op)
                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;
@@ -527,6 +561,17 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
        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! */
 
@@ -539,10 +584,26 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
        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;
 
@@ -550,27 +611,25 @@ static int bcm_rx_thr_flush(struct bcm_op *op)
                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
@@ -579,7 +638,9 @@ static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
 {
        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 {
@@ -595,29 +656,21 @@ static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
 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]) */
@@ -627,16 +680,14 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
 
        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) {
@@ -648,15 +699,17 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
                 */
 
                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);
 }
 
 /*
@@ -680,6 +733,12 @@ static void bcm_remove_op(struct bcm_op *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);
 
@@ -890,6 +949,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
                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);
 
@@ -1053,9 +1116,17 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
                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);
 
@@ -1101,7 +1172,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
                         */
                        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)
@@ -1303,8 +1374,13 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
        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;
@@ -1559,7 +1635,11 @@ static int __init bcm_module_init(void)
        }
 
        /* 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;
@@ -1572,7 +1652,11 @@ static void __exit bcm_module_exit(void)
        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);
diff --git a/net/can/compat.h b/net/can/compat.h
new file mode 100644 (file)
index 0000000..8f7260f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * $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
diff --git a/net/can/isotp.c b/net/can/isotp.c
new file mode 100644 (file)
index 0000000..6c81a44
--- /dev/null
@@ -0,0 +1,1119 @@
+/*
+ * 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);
index 520fef5e5398aa867a267ae32bbffbba557795c2..3b34d8980acba3dc450507b0b60389357031e99a 100644 (file)
  */
 
 #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
@@ -465,7 +472,11 @@ static void can_remove_proc_readentry(const char *name)
 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 . "
@@ -529,5 +540,9 @@ void can_remove_proc(void)
                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
 }
index 3e46ee36a1aaf819b8ed4a8d7c6afcb7b8614948..718c8469f4ff77311f659637c7fcbed58daa3e43 100644 (file)
@@ -42,6 +42,7 @@
  */
 
 #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[] =
@@ -76,7 +85,11 @@ MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
  */
 
 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;
@@ -90,7 +103,11 @@ struct raw_sock {
 
 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)
@@ -99,13 +116,14 @@ 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
@@ -208,10 +226,19 @@ static int raw_notifier(struct notifier_block *nb,
 {
        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;
@@ -254,6 +281,9 @@ static int raw_init(struct sock *sk)
 {
        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;
 
@@ -641,17 +671,12 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
 
        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;
 
@@ -660,9 +685,16 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
        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,
@@ -723,6 +755,7 @@ static struct proto_ops raw_ops __read_mostly = {
        .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,
@@ -737,6 +770,17 @@ static struct can_proto raw_can_proto __read_mostly = {
        .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)
 {