--- /dev/null
+/****************************************************************************/
+// Ingenieria Almudi (www.almudi.com)
+// Ported to LinCAN by Jose Pascual Ramírez (josepascual@almudi.com)
+//
+//
+// Copyright (C) 2001,2002,2003,2004 PEAK System-Technik GmbH
+//
+// linux@peak-system.com
+// www.peak-system.com
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// Maintainer(s): Klaus Hitschler (klaus.hitschler@gmx.de)
+//
+// Contributions: Marcel Offermans (marcel.offermans@luminis.nl)
+// Philipp Baer (philipp.baer@informatik.uni-ulm.de)
+/****************************************************************************/
+
+/****************************************************************************/
+//
+// all parts to handle the interface specific parts of pcan-dongle
+//
+// Revision 1.38 2004/07/28 22:03:29 jose pascual
+// ported to LinCAN
+//
+// Revision 1.37 2004/04/11 22:03:29 klaus
+// cosmetic changes
+//
+// Revision 1.36 2004/04/10 12:25:39 klaus
+// merge polished between HEAD and kernel-2.6 branch
+//
+// Revision 1.35 2004/04/10 08:57:26 klaus
+// merge finished between HEAD and kernel-2.6 branch
+//
+// Revision 1.32.2.1 2004/03/21 12:09:09 klaus
+// first commit for branch to kernel 2.6 code
+//
+// Revision 1.34 2004/03/27 16:57:06 klaus
+// modified for use with kernels <= 2.2.14
+//
+// Revision 1.33 2004/03/27 15:10:54 klaus
+// prepared for use with gcc 3.x, modified for use with kernels < 2.2.4
+//
+// Revision 1.32 2004/03/04 18:50:08 klaus
+// renamed PA,PB,PC to _PA_, ... to support (partially) cross-compiling for MIPS
+//
+// Revision 1.31 2003/06/22 15:34:50 klaus
+// added parts to support devfs provided by Philipp Baer (partially untested)
+//
+// Revision 1.30 2003/06/04 19:26:15 klaus
+// adapted to kernel 2.5.69 using GCC 3.2.3 (marcel), released release_20030604_x
+//
+// Revision 1.28 2003/03/02 10:58:07 klaus
+// merged USB thread into main path
+//
+// Revision 1.27 2003/03/02 10:58:07 klaus
+// merged USB thread into main path
+//
+// Revision 1.26.2.5 2003/01/29 20:34:20 klaus
+// release_20030129_a and release_20030129_u released
+//
+// Revision 1.26.2.4 2003/01/29 20:34:19 klaus
+// release_20030129_a and release_20030129_u released
+//
+// Revision 1.26.2.3 2003/01/28 23:28:26 klaus
+// reorderd pcan_usb.c and pcan_usb_kernel.c, tidied up
+//
+// Revision 1.26.2.2 2003/01/14 20:31:53 klaus
+// read/write/minor assigment is working
+//
+/****************************************************************************/
+
+/****************************************************************************/
+// INCLUDES
+
+
+#include "../include/can.h"
+#include "../include/can_sysdep.h"
+#include "../include/main.h"
+#include "../include/sja1000p.h"
+
+#include <linux/parport.h>
+
+#include "../include/pcan_dongle.h"
+
+
+
+/****************************************************************************/
+// DEFINES
+#define PCAN_DNG_SP_MINOR_BASE 16 // starting point of minors for SP devices
+#define PCAN_DNG_EPP_MINOR_BASE 24 // starting point of minors for EPP devices
+#define DNG_PORT_SIZE 4 // the address range of the dongle-port
+#define ECR_PORT_SIZE 1 // size of the associated ECR register
+#define DNG_DEFAULT_COUNT 4 // count of defaults for init
+
+typedef void (*PARPORT_IRQ_HANLDER)(int, void *, struct pt_regs *);
+
+/****************************************************************************/
+// GLOBALS
+spinlock_t pcan_lock = SPIN_LOCK_UNLOCKED;
+
+/****************************************************************************/
+// LOCALS
+static u16 dng_ports[] = {0x378, 0x278, 0x3bc, 0x2bc};
+static u8 dng_irqs[] = {7, 5, 7, 5};
+static u16 dng_devices = 0; // the number of accepted dng_devices
+static u16 epp_devices = 0; // ... epp_devices
+static u16 sp_devices = 0; // ... sp_devices
+
+static unsigned char nibble_decode[32] =
+{
+ 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+ 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
+ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7
+};
+
+struct DONGLE_PORT dongle_port;
+
+char dongle_type[] = "epp_dongle";
+//char dongle_type[] = "sp_dongle";
+
+/****************************************************************************/
+// CODE
+
+//----------------------------------------------------------------------------
+// enable and disable irqs
+static void _parport_disable_irq(struct DONGLE_PORT *dng)
+{
+ u16 _PC_ = (u16)dng->dwPort + 2;
+ outb(inb(_PC_) & ~0x10, _PC_);
+}
+
+static void _parport_enable_irq(struct DONGLE_PORT *dng)
+{
+ u16 _PC_ = (u16)dng->dwPort + 2;
+ outb(inb(_PC_) | 0x10, _PC_);
+}
+
+
+// functions for SP port
+static u8 pcan_dongle_sp_readreg(struct DONGLE_PORT *dng, u8 port) // read a register
+{
+ u16 _PA_ = (u16)dng->dwPort;
+ u16 _PB_ = _PA_ + 1;
+ u16 _PC_ = _PB_ + 1;
+ u8 b0, b1 ;
+ u8 irqEnable = inb(_PC_) & 0x10; // don't influence irqEnable
+ unsigned long flags;
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
+ save_flags(flags);
+ cli();
+ #else
+ spin_lock_irqsave(&pcan_lock, flags);
+ #endif
+
+ outb((0x0B ^ 0x0D) | irqEnable, _PC_);
+ outb((port & 0x1F) | 0x80, _PA_);
+ outb((0x0B ^ 0x0C) | irqEnable, _PC_);
+ b1=nibble_decode[inb(_PB_)>>3];
+ outb(0x40, _PA_);
+ b0=nibble_decode[inb(_PB_)>>3];
+ outb((0x0B ^ 0x0D) | irqEnable, _PC_);
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
+ restore_flags(flags);
+ #else
+ spin_unlock_irqrestore(&pcan_lock, flags);
+ #endif
+
+ return (b1 << 4) | b0 ;
+}
+
+static void pcan_dongle_writereg(struct DONGLE_PORT *dng, u8 port, u8 data) // write a register
+{
+ u16 _PA_ = (u16)dng->dwPort;
+ u16 _PC_ = _PA_ + 2;
+ u8 irqEnable = inb(_PC_) & 0x10; // don't influence irqEnable
+ unsigned long flags;
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
+ save_flags(flags);
+ cli();
+ #else
+ spin_lock_irqsave(&pcan_lock, flags);
+ #endif
+
+ outb((0x0B ^ 0x0D) | irqEnable, _PC_);
+ outb(port & 0x1F, _PA_);
+ outb((0x0B ^ 0x0C) | irqEnable, _PC_);
+ outb(data, _PA_);
+ outb((0x0B ^ 0x0D) | irqEnable, _PC_);
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
+ restore_flags(flags);
+ #else
+ spin_unlock_irqrestore(&pcan_lock, flags);
+ #endif
+}
+
+// functions for EPP port
+static u8 pcan_dongle_epp_readreg(struct DONGLE_PORT *dng, u8 port) // read a register
+{
+ u16 _PA_ = (u16)dng->dwPort;
+ u16 _PC_ = _PA_ + 2;
+ u8 wert;
+ u8 irqEnable = inb(_PC_) & 0x10; // don't influence irqEnable
+ unsigned long flags;
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
+ save_flags(flags);
+ cli();
+ #else
+ spin_lock_irqsave(&pcan_lock, flags);
+ #endif
+
+ outb((0x0B ^ 0x0F) | irqEnable, _PC_);
+ outb((port & 0x1F) | 0x80, _PA_);
+ outb((0x0B ^ 0x2E) | irqEnable, _PC_);
+ wert = inb(_PA_);
+ outb((0x0B ^ 0x0F) | irqEnable, _PC_);
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
+ restore_flags(flags);
+ #else
+ spin_unlock_irqrestore(&pcan_lock, flags);
+ #endif
+
+ return wert;
+}
+
+static int pcan_dongle_req_irq(struct DONGLE_PORT *dng)
+{
+ if (dng->wInitStep == 3)
+ {
+ dng->wInitStep++;
+ }
+
+ return 0;
+}
+
+static void pcan_dongle_free_irq(struct DONGLE_PORT *dng)
+{
+ if (dng->wInitStep == 4)
+ {
+ dng->wInitStep--;
+ }
+}
+
+// release and probe functions
+static int pcan_dongle_cleanup(struct DONGLE_PORT *dng)
+{
+ DEBUGMSG("%s: pcan_dongle_cleanup()\n", DEVICE_NAME);
+
+ switch(dng->wInitStep)
+ {
+ case 4: pcan_dongle_free_irq(dng);
+ case 3: if (dng->wType == HW_DONGLE_SJA)
+ sp_devices--;
+ else
+ epp_devices--;
+ dng_devices = sp_devices + epp_devices;
+ case 2:
+ case 1:
+ parport_unregister_device(dng->pardev);
+ case 0: dng->wInitStep = 0;
+ }
+
+ return 0;
+}
+
+// to switch epp on or restore register
+static void setECR(struct DONGLE_PORT *dng)
+{
+ u16 wEcr = dng->wEcr;
+
+ dng->ucOldECRContent = inb(wEcr);
+ outb((dng->ucOldECRContent & 0x1F) | 0x20, wEcr);
+
+ if (dng->ucOldECRContent == 0xff)
+ DEBUGMSG("%s: realy ECP mode configured?\n", DEVICE_NAME);
+}
+
+static void restoreECR(struct DONGLE_PORT *dng)
+{
+ u16 wEcr = dng->wEcr;
+
+ outb(dng->ucOldECRContent, wEcr);
+
+ DEBUGMSG("%s: restore ECR\n", DEVICE_NAME);
+}
+
+static int pcan_dongle_probe(struct DONGLE_PORT *dng) // probe for type
+{
+ struct parport *p;
+
+ DEBUGMSG("%s: pcan_dongle_probe() - PARPORT_SUBSYSTEM\n", DEVICE_NAME);
+
+ // probe does not probe for the sja1000 device here - this is done at sja1000_open()
+ p = parport_find_base(dng->dwPort);
+ if (!p)
+ {
+ DEBUGMSG("found no parport\n");
+ return -ENXIO;
+ }
+ else
+ {
+ dng->pardev = parport_register_device(p, "can", NULL, NULL,
+ (PARPORT_IRQ_HANLDER)dng->chip->chipspecops->irq_handler,
+ 0, (void *)dng->chip);
+
+// DEBUGMSG("datos IRQ: irq_handler=0x%x p=0x%x dng->chip=0x%x dng->pardev->port->irq=0x%x irq_handler2=0x%x\n",
+// dng->chip->chipspecops->irq_handler,
+// p,dng->chip,dng->pardev->port->irq, &sja1000p_irq_handler);
+
+ if (!dng->pardev)
+ {
+ DEBUGMSG("found no parport device\n");
+ return -ENODEV;
+ }
+
+ }
+
+ return 0;
+}
+
+// interface depended open and close
+static int pcan_dongle_open(struct DONGLE_PORT *dng)
+{
+ int result = 0;
+ u16 wPort;
+
+ DEBUGMSG("%s: pcan_dongle_open()\n", DEVICE_NAME);
+
+ result = parport_claim(dng->pardev);
+
+ if (!result)
+ {
+ if (dng->pardev->port->irq == PARPORT_IRQ_NONE)
+ {
+ DEBUGMSG(KERN_ERR "%s: no irq associated to parport.\n", DEVICE_NAME);
+ result = -ENXIO;
+ }
+ }
+ else
+ DEBUGMSG(KERN_ERR "%s: can't claim parport.\n", DEVICE_NAME);
+
+ // save port state
+ if (!result)
+ {
+ wPort = (u16)dng->dwPort;
+
+ // save old port contents
+ dng->ucOldDataContent = inb(wPort);
+ dng->ucOldControlContent = inb(wPort + 2);
+
+ // switch to epp mode if possible
+ if (dng->wType == HW_DONGLE_SJA_EPP)
+ setECR(dng);
+
+ // enable irqs
+ _parport_enable_irq(dng); // parport_enable_irq(dng->pardev->port); not working since 2.4.18
+ }
+
+ return result;
+}
+
+static int pcan_dongle_release(struct DONGLE_PORT *dng)
+{
+ u16 wPort = (u16)dng->dwPort;
+
+ DEBUGMSG("%s: pcan_dongle_release()\n", DEVICE_NAME);
+
+ // disable irqs
+ _parport_disable_irq(dng); // parport_disable_irq(dng->pardev->port); not working since 2.4.18
+
+ if (dng->wType == HW_DONGLE_SJA_EPP)
+ restoreECR(dng);
+
+ // restore port state
+ outb(dng->ucOldDataContent, wPort);
+ outb(dng->ucOldControlContent, wPort + 2);
+
+ parport_release(dng->pardev);
+
+ return 0;
+}
+
+int pcan_dongle_init(struct DONGLE_PORT *dng, u32 dwPort, u16 wIrq, char *type)
+{
+ int err;
+
+ DEBUGMSG("%s: pcan_dongle_init(), dng_devices = %d\n", DEVICE_NAME, dng_devices);
+
+ dng->type = type;
+
+ dng->wType = (!strncmp(dongle_type, "sp", 4)) ? HW_DONGLE_SJA : HW_DONGLE_SJA_EPP;
+
+ // set this before any instructions, fill struct pcandev, part 1
+ dng->wInitStep = 0;
+
+ // fill struct pcandev, 1st check if a default is set
+ if (!dwPort)
+ {
+ // there's no default available
+ if (dng_devices >= DNG_DEFAULT_COUNT)
+ return -ENODEV;
+
+ dng->dwPort = dng_ports[dng_devices];
+ }
+ else
+ dng->dwPort = dwPort;
+
+ if (!wIrq)
+ {
+ if (dng_devices >= DNG_DEFAULT_COUNT)
+ return -ENODEV;
+
+ dng->wIrq = dng_irqs[dng_devices];
+ }
+ else
+ dng->wIrq = wIrq;
+
+ if (dng->wType == HW_DONGLE_SJA)
+ {
+ dng->nMinor = PCAN_DNG_SP_MINOR_BASE + sp_devices;
+ dng->wEcr = 0; // set to anything
+ }
+ else
+ {
+ dng->nMinor = PCAN_DNG_EPP_MINOR_BASE + epp_devices;
+ dng->wEcr = (u16)dng->dwPort + 0x402;
+ }
+
+ // is the device really available?
+ if ((err = pcan_dongle_probe(dng)) < 0)
+ return err;
+
+ if (dng->wType == HW_DONGLE_SJA)
+ sp_devices++;
+ else
+ epp_devices++;
+
+ dng_devices = sp_devices + epp_devices;
+
+ dng->wInitStep = 3;
+
+ DEBUGMSG(KERN_INFO "%s: %s device minor %d prepared (io=0x%04x,irq=%d)\n", DEVICE_NAME,
+ dng->type, dng->nMinor, dng->dwPort, dng->wIrq);
+
+ return 0;
+}
+
+
+
+
+/**
+ * template_request_io: - reserve io or memory range for can board
+ * @candev: pointer to candevice/board which asks for io. Field @io_addr
+ * of @candev is used in most cases to define start of the range
+ *
+ * The function template_request_io() is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * %IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int pcan_dongle_request_io(struct candevice_t *candev)
+{
+ int res_init;
+
+ dongle_port.chip = candev->chip[0];
+
+ res_init = pcan_dongle_init(&dongle_port, 0, 0, dongle_type);
+
+ return res_init;
+}
+
+/**
+ * template_elease_io - free reserved io memory range
+ * @candev: pointer to candevice/board which releases io
+ *
+ * The function template_release_io() is used to free reserved io-memory.
+ * In case you have reserved more io memory, don't forget to free it here.
+ * IO_RANGE is the io-memory range that gets released, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int pcan_dongle_release_io(struct candevice_t *candev)
+{
+ /* release I/O port */
+ pcan_dongle_release(&dongle_port);
+
+ pcan_dongle_cleanup(&dongle_port);
+
+ return 0;
+}
+
+/**
+ * template_reset - hardware reset routine
+ * @candev: Pointer to candevice/board structure
+ *
+ * The function template_reset() is used to give a hardware reset. This is
+ * rather hardware specific so I haven't included example code. Don't forget to
+ * check the reset status of the chip before returning.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int pcan_dongle_reset(struct candevice_t *candev)
+{
+ int i=0;
+ struct chip_t *chip;
+ int chipnr;
+ unsigned cdr;
+
+ DEBUGMSG("Resetting pcan_dongle hardware ...\n");
+ for(chipnr=0;chipnr<candev->nr_sja1000_chips;chipnr++) {
+ chip=candev->chip[chipnr];
+
+ pcan_dongle_write_register(sjaMOD_RM, chip->chip_base_addr+SJAMOD);
+ udelay(1000);
+
+ cdr=pcan_dongle_read_register(chip->chip_base_addr+SJACDR);
+ pcan_dongle_write_register(cdr|sjaCDR_PELICAN, chip->chip_base_addr+SJACDR);
+
+ pcan_dongle_write_register(0, chip->chip_base_addr+SJAIER);
+
+ i=20;
+ pcan_dongle_write_register(0, chip->chip_base_addr+SJAMOD);
+ while (pcan_dongle_read_register(chip->chip_base_addr+SJAMOD)&sjaMOD_RM) {
+ if(!i--) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ udelay(1000);
+ pcan_dongle_write_register(0, chip->chip_base_addr+SJAMOD);
+ }
+
+ cdr = pcan_dongle_read_register(chip->chip_base_addr+SJACDR);
+ pcan_dongle_write_register(cdr|sjaCDR_PELICAN, chip->chip_base_addr+SJACDR);
+
+ pcan_dongle_write_register(0, chip->chip_base_addr+SJAIER);
+ }
+ return 0;
+}
+
+#define RESET_ADDR 0x0
+#define NR_82527 0
+#define NR_SJA1000 1
+
+/**
+ * template_init_hw_data - Initialize hardware cards
+ * @candev: Pointer to candevice/board structure
+ *
+ * The function template_init_hw_data() is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * %RESET_ADDR represents the io-address of the hardware reset register.
+ * %NR_82527 represents the number of intel 82527 chips on the board.
+ * %NR_SJA1000 represents the number of philips sja1000 chips on the board.
+ * The flags entry can currently only be %CANDEV_PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int pcan_dongle_init_hw_data(struct candevice_t *candev)
+{
+ candev->res_addr=RESET_ADDR;
+ candev->nr_82527_chips=NR_82527;
+ candev->nr_sja1000_chips=NR_SJA1000;
+ candev->nr_all_chips=NR_82527+NR_SJA1000;
+// candev->flags &= ~CANDEV_PROGRAMMABLE_IRQ;
+ candev->flags |= CANDEV_PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+#define CHIP_TYPE "sja1000p"
+/**
+ * template_init_chip_data - Initialize chips
+ * @candev: Pointer to candevice/board structure
+ * @chipnr: Number of the CAN chip on the hardware card
+ *
+ * The function template_init_chip_data() is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The @chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the @io_addr
+ * argument supplied at module loading time.
+ * The @clock entry holds the chip clock value in Hz.
+ * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
+ * register. Options defined in the %sja1000.h file:
+ * %CDR_CLKOUT_MASK, %CDR_CLK_OFF, %CDR_RXINPEN, %CDR_CBP, %CDR_PELICAN
+ * The entry @sja_ocr_reg holds hardware specific options for the Output Control
+ * register. Options defined in the %sja1000.h file:
+ * %OCR_MODE_BIPHASE, %OCR_MODE_TEST, %OCR_MODE_NORMAL, %OCR_MODE_CLOCK,
+ * %OCR_TX0_LH, %OCR_TX1_ZZ.
+ * The entry @int_clk_reg holds hardware specific options for the Clock Out
+ * register. Options defined in the %i82527.h file:
+ * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
+ * The entry @int_bus_reg holds hardware specific options for the Bus
+ * Configuration register. Options defined in the %i82527.h file:
+ * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int pcan_dongle_init_chip_data(struct candevice_t *candev, int chipnr)
+{
+// if (chipnr == 0)
+ {
+ /* initialize common routines for the SJA1000 chip */
+ sja1000p_fill_chipspecops(candev->chip[chipnr]);
+
+
+ candev->chip[chipnr]->chip_type=CHIP_TYPE;
+ candev->chip[chipnr]->chip_base_addr=candev->io_addr;
+ candev->chip[chipnr]->clock = 16000000;
+ candev->chip[chipnr]->int_clk_reg = 0x0;
+ candev->chip[chipnr]->int_bus_reg = 0x0;
+ candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF;
+ candev->chip[chipnr]->sja_ocr_reg = sjaOCR_MODE_NORMAL | sjaOCR_TX0_LH;
+
+ candev->chip[chipnr]->flags |= CHIP_IRQ_VME; // I don't want setup call request_irq
+ // I'm going to do it through parport_register_device
+
+ }
+
+ return 0;
+}
+
+/**
+ * template_init_obj_data - Initialize message buffers
+ * @chip: Pointer to chip specific structure
+ * @objnr: Number of the message buffer
+ *
+ * The function template_init_obj_data() is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry @obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int pcan_dongle_init_obj_data(struct chip_t *chip, int objnr)
+{
+ chip->msgobj[objnr]->obj_base_addr=chip->chip_base_addr;
+ chip->msgobj[objnr]->obj_flags=0;
+
+ return 0;
+}
+
+/**
+ * template_program_irq - program interrupts
+ * @candev: Pointer to candevice/board structure
+ *
+ * The function template_program_irq() is used for hardware that uses
+ * programmable interrupts. If your hardware doesn't use programmable interrupts
+ * you should not set the @candevices_t->flags entry to %CANDEV_PROGRAMMABLE_IRQ and
+ * leave this function unedited. Again this function is hardware specific so
+ * there's no example code.
+ * Return value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int pcan_dongle_program_irq(struct candevice_t *candev)
+{
+ int ret_open;
+
+ pcan_dongle_req_irq(&dongle_port);
+ ret_open = pcan_dongle_open(&dongle_port);
+
+ return ret_open;
+}
+
+
+/**
+ * template_write_register - Low level write register routine
+ * @data: data to be written
+ * @address: memory address to write to
+ *
+ * The function template_write_register() is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ * Return Value: The function does not return a value
+ * File: src/template.c
+ */
+void pcan_dongle_write_register(unsigned data, unsigned long address)
+{
+ address -= dongle_port.chip->chip_base_addr; // it's in mutiplexed mode
+
+ pcan_dongle_writereg(&dongle_port, (u8) address, (u8) data); // write a register
+
+// DEBUGMSG("Write Reg at: 0x%lx data 0x%x \n", address, (unsigned) data);
+}
+
+/**
+ * template_read_register - Low level read register routine
+ * @address: memory address to read from
+ *
+ * The function template_read_register() is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ * Return Value: The function returns the value stored in @address
+ * File: src/template.c
+ */
+unsigned pcan_dongle_read_register(unsigned long address)
+{
+ u8 val;
+
+ address -= dongle_port.chip->chip_base_addr; // it's in mutiplexed mode
+
+ if (dongle_port.wType == HW_DONGLE_SJA)
+ val = pcan_dongle_sp_readreg(&dongle_port, (u8) address); // functions for SP port
+ else
+ val = pcan_dongle_epp_readreg(&dongle_port, (u8) address); // functions for EPP port
+
+// DEBUGMSG("Read Reg at: 0x%lx data 0x%x \n", address, val);
+
+ return ((unsigned)val);
+}
+
+/* !!! Don't change this function !!! */
+int pcan_dongle_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = pcan_dongle_request_io;
+ hwspecops->release_io = pcan_dongle_release_io;
+ hwspecops->reset = pcan_dongle_reset;
+ hwspecops->init_hw_data = pcan_dongle_init_hw_data;
+ hwspecops->init_chip_data = pcan_dongle_init_chip_data;
+ hwspecops->init_obj_data = pcan_dongle_init_obj_data;
+ hwspecops->write_register = pcan_dongle_write_register;
+ hwspecops->read_register = pcan_dongle_read_register;
+ hwspecops->program_irq = pcan_dongle_program_irq;
+ return 0;
+}
+
+