1 /****************************************************************************/
2 // Ingenieria Almudi (www.almudi.com)
3 // Ported to LinCAN by Jose Pascual RamÃrez (josepascual@almudi.com)
6 // Copyright (C) 2001,2002,2003,2004 PEAK System-Technik GmbH
8 // linux@peak-system.com
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 // Maintainer(s): Klaus Hitschler (klaus.hitschler@gmx.de)
27 // Contributions: Marcel Offermans (marcel.offermans@luminis.nl)
28 // Philipp Baer (philipp.baer@informatik.uni-ulm.de)
29 /****************************************************************************/
31 /****************************************************************************/
33 // all parts to handle the interface specific parts of pcan-dongle
35 // Revision 1.38 2004/07/28 22:03:29 jose pascual
38 // Revision 1.37 2004/04/11 22:03:29 klaus
41 // Revision 1.36 2004/04/10 12:25:39 klaus
42 // merge polished between HEAD and kernel-2.6 branch
44 // Revision 1.35 2004/04/10 08:57:26 klaus
45 // merge finished between HEAD and kernel-2.6 branch
47 // Revision 1.32.2.1 2004/03/21 12:09:09 klaus
48 // first commit for branch to kernel 2.6 code
50 // Revision 1.34 2004/03/27 16:57:06 klaus
51 // modified for use with kernels <= 2.2.14
53 // Revision 1.33 2004/03/27 15:10:54 klaus
54 // prepared for use with gcc 3.x, modified for use with kernels < 2.2.4
56 // Revision 1.32 2004/03/04 18:50:08 klaus
57 // renamed PA,PB,PC to _PA_, ... to support (partially) cross-compiling for MIPS
59 // Revision 1.31 2003/06/22 15:34:50 klaus
60 // added parts to support devfs provided by Philipp Baer (partially untested)
62 // Revision 1.30 2003/06/04 19:26:15 klaus
63 // adapted to kernel 2.5.69 using GCC 3.2.3 (marcel), released release_20030604_x
65 // Revision 1.28 2003/03/02 10:58:07 klaus
66 // merged USB thread into main path
68 // Revision 1.27 2003/03/02 10:58:07 klaus
69 // merged USB thread into main path
71 // Revision 1.26.2.5 2003/01/29 20:34:20 klaus
72 // release_20030129_a and release_20030129_u released
74 // Revision 1.26.2.4 2003/01/29 20:34:19 klaus
75 // release_20030129_a and release_20030129_u released
77 // Revision 1.26.2.3 2003/01/28 23:28:26 klaus
78 // reorderd pcan_usb.c and pcan_usb_kernel.c, tidied up
80 // Revision 1.26.2.2 2003/01/14 20:31:53 klaus
81 // read/write/minor assigment is working
83 /****************************************************************************/
85 /****************************************************************************/
89 #include "../include/can.h"
90 #include "../include/can_sysdep.h"
91 #include "../include/main.h"
92 #include "../include/sja1000p.h"
94 #include <linux/parport.h>
96 #include "../include/pcan_dongle.h"
100 /****************************************************************************/
102 #define PCAN_DNG_SP_MINOR_BASE 16 // starting point of minors for SP devices
103 #define PCAN_DNG_EPP_MINOR_BASE 24 // starting point of minors for EPP devices
104 #define DNG_PORT_SIZE 4 // the address range of the dongle-port
105 #define ECR_PORT_SIZE 1 // size of the associated ECR register
106 #define DNG_DEFAULT_COUNT 4 // count of defaults for init
108 typedef void (*PARPORT_IRQ_HANLDER)(int, void *, struct pt_regs *);
110 /****************************************************************************/
112 spinlock_t pcan_lock = SPIN_LOCK_UNLOCKED;
114 /****************************************************************************/
116 static u16 dng_ports[] = {0x378, 0x278, 0x3bc, 0x2bc};
117 static u8 dng_irqs[] = {7, 5, 7, 5};
118 static u16 dng_devices = 0; // the number of accepted dng_devices
119 static u16 epp_devices = 0; // ... epp_devices
120 static u16 sp_devices = 0; // ... sp_devices
122 static unsigned char nibble_decode[32] =
124 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
125 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
126 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
127 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7
130 struct DONGLE_PORT dongle_port;
132 char dongle_type[] = "epp_dongle";
133 //char dongle_type[] = "sp_dongle";
135 /****************************************************************************/
138 //----------------------------------------------------------------------------
139 // enable and disable irqs
140 static void _parport_disable_irq(struct DONGLE_PORT *dng)
142 u16 _PC_ = (u16)dng->dwPort + 2;
143 outb(inb(_PC_) & ~0x10, _PC_);
146 static void _parport_enable_irq(struct DONGLE_PORT *dng)
148 u16 _PC_ = (u16)dng->dwPort + 2;
149 outb(inb(_PC_) | 0x10, _PC_);
153 // functions for SP port
154 static u8 pcan_dongle_sp_readreg(struct DONGLE_PORT *dng, u8 port) // read a register
156 u16 _PA_ = (u16)dng->dwPort;
160 u8 irqEnable = inb(_PC_) & 0x10; // don't influence irqEnable
163 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
167 spin_lock_irqsave(&pcan_lock, flags);
170 outb((0x0B ^ 0x0D) | irqEnable, _PC_);
171 outb((port & 0x1F) | 0x80, _PA_);
172 outb((0x0B ^ 0x0C) | irqEnable, _PC_);
173 b1=nibble_decode[inb(_PB_)>>3];
175 b0=nibble_decode[inb(_PB_)>>3];
176 outb((0x0B ^ 0x0D) | irqEnable, _PC_);
178 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
179 restore_flags(flags);
181 spin_unlock_irqrestore(&pcan_lock, flags);
184 return (b1 << 4) | b0 ;
187 static void pcan_dongle_writereg(struct DONGLE_PORT *dng, u8 port, u8 data) // write a register
189 u16 _PA_ = (u16)dng->dwPort;
191 u8 irqEnable = inb(_PC_) & 0x10; // don't influence irqEnable
194 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
198 spin_lock_irqsave(&pcan_lock, flags);
201 outb((0x0B ^ 0x0D) | irqEnable, _PC_);
202 outb(port & 0x1F, _PA_);
203 outb((0x0B ^ 0x0C) | irqEnable, _PC_);
205 outb((0x0B ^ 0x0D) | irqEnable, _PC_);
207 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
208 restore_flags(flags);
210 spin_unlock_irqrestore(&pcan_lock, flags);
214 // functions for EPP port
215 static u8 pcan_dongle_epp_readreg(struct DONGLE_PORT *dng, u8 port) // read a register
217 u16 _PA_ = (u16)dng->dwPort;
220 u8 irqEnable = inb(_PC_) & 0x10; // don't influence irqEnable
223 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
227 spin_lock_irqsave(&pcan_lock, flags);
230 outb((0x0B ^ 0x0F) | irqEnable, _PC_);
231 outb((port & 0x1F) | 0x80, _PA_);
232 outb((0x0B ^ 0x2E) | irqEnable, _PC_);
234 outb((0x0B ^ 0x0F) | irqEnable, _PC_);
236 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
237 restore_flags(flags);
239 spin_unlock_irqrestore(&pcan_lock, flags);
245 static int pcan_dongle_req_irq(struct DONGLE_PORT *dng)
247 if (dng->wInitStep == 3)
255 static void pcan_dongle_free_irq(struct DONGLE_PORT *dng)
257 if (dng->wInitStep == 4)
263 // release and probe functions
264 static int pcan_dongle_cleanup(struct DONGLE_PORT *dng)
266 DEBUGMSG("%s: pcan_dongle_cleanup()\n", DEVICE_NAME);
268 switch(dng->wInitStep)
270 case 4: pcan_dongle_free_irq(dng);
271 case 3: if (dng->wType == HW_DONGLE_SJA)
275 dng_devices = sp_devices + epp_devices;
278 parport_unregister_device(dng->pardev);
279 case 0: dng->wInitStep = 0;
285 // to switch epp on or restore register
286 static void setECR(struct DONGLE_PORT *dng)
288 u16 wEcr = dng->wEcr;
290 dng->ucOldECRContent = inb(wEcr);
291 outb((dng->ucOldECRContent & 0x1F) | 0x20, wEcr);
293 if (dng->ucOldECRContent == 0xff)
294 DEBUGMSG("%s: realy ECP mode configured?\n", DEVICE_NAME);
297 static void restoreECR(struct DONGLE_PORT *dng)
299 u16 wEcr = dng->wEcr;
301 outb(dng->ucOldECRContent, wEcr);
303 DEBUGMSG("%s: restore ECR\n", DEVICE_NAME);
306 static int pcan_dongle_probe(struct DONGLE_PORT *dng) // probe for type
310 DEBUGMSG("%s: pcan_dongle_probe() - PARPORT_SUBSYSTEM\n", DEVICE_NAME);
312 // probe does not probe for the sja1000 device here - this is done at sja1000_open()
313 p = parport_find_base(dng->dwPort);
316 DEBUGMSG("found no parport\n");
321 dng->pardev = parport_register_device(p, "can", NULL, NULL,
322 (PARPORT_IRQ_HANLDER)dng->chip->chipspecops->irq_handler,
323 0, (void *)dng->chip);
325 // 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",
326 // dng->chip->chipspecops->irq_handler,
327 // p,dng->chip,dng->pardev->port->irq, &sja1000p_irq_handler);
331 DEBUGMSG("found no parport device\n");
340 // interface depended open and close
341 static int pcan_dongle_open(struct DONGLE_PORT *dng)
346 DEBUGMSG("%s: pcan_dongle_open()\n", DEVICE_NAME);
348 result = parport_claim(dng->pardev);
352 if (dng->pardev->port->irq == PARPORT_IRQ_NONE)
354 DEBUGMSG(KERN_ERR "%s: no irq associated to parport.\n", DEVICE_NAME);
359 DEBUGMSG(KERN_ERR "%s: can't claim parport.\n", DEVICE_NAME);
364 wPort = (u16)dng->dwPort;
366 // save old port contents
367 dng->ucOldDataContent = inb(wPort);
368 dng->ucOldControlContent = inb(wPort + 2);
370 // switch to epp mode if possible
371 if (dng->wType == HW_DONGLE_SJA_EPP)
375 _parport_enable_irq(dng); // parport_enable_irq(dng->pardev->port); not working since 2.4.18
381 static int pcan_dongle_release(struct DONGLE_PORT *dng)
383 u16 wPort = (u16)dng->dwPort;
385 DEBUGMSG("%s: pcan_dongle_release()\n", DEVICE_NAME);
388 _parport_disable_irq(dng); // parport_disable_irq(dng->pardev->port); not working since 2.4.18
390 if (dng->wType == HW_DONGLE_SJA_EPP)
393 // restore port state
394 outb(dng->ucOldDataContent, wPort);
395 outb(dng->ucOldControlContent, wPort + 2);
397 parport_release(dng->pardev);
402 int pcan_dongle_init(struct DONGLE_PORT *dng, u32 dwPort, u16 wIrq, char *type)
406 DEBUGMSG("%s: pcan_dongle_init(), dng_devices = %d\n", DEVICE_NAME, dng_devices);
410 dng->wType = (!strncmp(dongle_type, "sp", 4)) ? HW_DONGLE_SJA : HW_DONGLE_SJA_EPP;
412 // set this before any instructions, fill struct pcandev, part 1
415 // fill struct pcandev, 1st check if a default is set
418 // there's no default available
419 if (dng_devices >= DNG_DEFAULT_COUNT)
422 dng->dwPort = dng_ports[dng_devices];
425 dng->dwPort = dwPort;
429 if (dng_devices >= DNG_DEFAULT_COUNT)
432 dng->wIrq = dng_irqs[dng_devices];
437 if (dng->wType == HW_DONGLE_SJA)
439 dng->nMinor = PCAN_DNG_SP_MINOR_BASE + sp_devices;
440 dng->wEcr = 0; // set to anything
444 dng->nMinor = PCAN_DNG_EPP_MINOR_BASE + epp_devices;
445 dng->wEcr = (u16)dng->dwPort + 0x402;
448 // is the device really available?
449 if ((err = pcan_dongle_probe(dng)) < 0)
452 if (dng->wType == HW_DONGLE_SJA)
457 dng_devices = sp_devices + epp_devices;
461 DEBUGMSG(KERN_INFO "%s: %s device minor %d prepared (io=0x%04x,irq=%d)\n", DEVICE_NAME,
462 dng->type, dng->nMinor, dng->dwPort, dng->wIrq);
471 * template_request_io: - reserve io or memory range for can board
472 * @candev: pointer to candevice/board which asks for io. Field @io_addr
473 * of @candev is used in most cases to define start of the range
475 * The function template_request_io() is used to reserve the io-memory. If your
476 * hardware uses a dedicated memory range as hardware control registers you
477 * will have to add the code to reserve this memory as well.
478 * %IO_RANGE is the io-memory range that gets reserved, please adjust according
479 * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
480 * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
481 * Return Value: The function returns zero on success or %-ENODEV on failure
482 * File: src/template.c
484 int pcan_dongle_request_io(struct candevice_t *candev)
488 dongle_port.chip = candev->chip[0];
490 res_init = pcan_dongle_init(&dongle_port, 0, 0, dongle_type);
496 * template_elease_io - free reserved io memory range
497 * @candev: pointer to candevice/board which releases io
499 * The function template_release_io() is used to free reserved io-memory.
500 * In case you have reserved more io memory, don't forget to free it here.
501 * IO_RANGE is the io-memory range that gets released, please adjust according
502 * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
503 * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
504 * Return Value: The function always returns zero
505 * File: src/template.c
507 int pcan_dongle_release_io(struct candevice_t *candev)
509 /* release I/O port */
510 pcan_dongle_release(&dongle_port);
512 pcan_dongle_cleanup(&dongle_port);
518 * template_reset - hardware reset routine
519 * @candev: Pointer to candevice/board structure
521 * The function template_reset() is used to give a hardware reset. This is
522 * rather hardware specific so I haven't included example code. Don't forget to
523 * check the reset status of the chip before returning.
524 * Return Value: The function returns zero on success or %-ENODEV on failure
525 * File: src/template.c
527 int pcan_dongle_reset(struct candevice_t *candev)
530 struct canchip_t *chip;
534 DEBUGMSG("Resetting pcan_dongle hardware ...\n");
535 for(chipnr=0;chipnr<candev->nr_sja1000_chips;chipnr++) {
536 chip=candev->chip[chipnr];
538 pcan_dongle_write_register(sjaMOD_RM, chip->chip_base_addr+SJAMOD);
541 cdr=pcan_dongle_read_register(chip->chip_base_addr+SJACDR);
542 pcan_dongle_write_register(cdr|sjaCDR_PELICAN, chip->chip_base_addr+SJACDR);
544 pcan_dongle_write_register(0, chip->chip_base_addr+SJAIER);
547 pcan_dongle_write_register(0, chip->chip_base_addr+SJAMOD);
548 while (pcan_dongle_read_register(chip->chip_base_addr+SJAMOD)&sjaMOD_RM) {
550 CANMSG("Reset status timeout!\n");
551 CANMSG("Please check your hardware.\n");
555 pcan_dongle_write_register(0, chip->chip_base_addr+SJAMOD);
558 cdr = pcan_dongle_read_register(chip->chip_base_addr+SJACDR);
559 pcan_dongle_write_register(cdr|sjaCDR_PELICAN, chip->chip_base_addr+SJACDR);
561 pcan_dongle_write_register(0, chip->chip_base_addr+SJAIER);
566 #define RESET_ADDR 0x0
571 * template_init_hw_data - Initialize hardware cards
572 * @candev: Pointer to candevice/board structure
574 * The function template_init_hw_data() is used to initialize the hardware
575 * structure containing information about the installed CAN-board.
576 * %RESET_ADDR represents the io-address of the hardware reset register.
577 * %NR_82527 represents the number of intel 82527 chips on the board.
578 * %NR_SJA1000 represents the number of philips sja1000 chips on the board.
579 * The flags entry can currently only be %CANDEV_PROGRAMMABLE_IRQ to indicate that
580 * the hardware uses programmable interrupts.
581 * Return Value: The function always returns zero
582 * File: src/template.c
584 int pcan_dongle_init_hw_data(struct candevice_t *candev)
586 candev->res_addr=RESET_ADDR;
587 candev->nr_82527_chips=NR_82527;
588 candev->nr_sja1000_chips=NR_SJA1000;
589 candev->nr_all_chips=NR_82527+NR_SJA1000;
590 // candev->flags &= ~CANDEV_PROGRAMMABLE_IRQ;
591 candev->flags |= CANDEV_PROGRAMMABLE_IRQ;
596 #define CHIP_TYPE "sja1000p"
598 * template_init_chip_data - Initialize chips
599 * @candev: Pointer to candevice/board structure
600 * @chipnr: Number of the CAN chip on the hardware card
602 * The function template_init_chip_data() is used to initialize the hardware
603 * structure containing information about the CAN chips.
604 * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
606 * The @chip_base_addr entry represents the start of the 'official' memory map
607 * of the installed chip. It's likely that this is the same as the @io_addr
608 * argument supplied at module loading time.
609 * The @clock entry holds the chip clock value in Hz.
610 * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
611 * register. Options defined in the %sja1000.h file:
612 * %CDR_CLKOUT_MASK, %CDR_CLK_OFF, %CDR_RXINPEN, %CDR_CBP, %CDR_PELICAN
613 * The entry @sja_ocr_reg holds hardware specific options for the Output Control
614 * register. Options defined in the %sja1000.h file:
615 * %OCR_MODE_BIPHASE, %OCR_MODE_TEST, %OCR_MODE_NORMAL, %OCR_MODE_CLOCK,
616 * %OCR_TX0_LH, %OCR_TX1_ZZ.
617 * The entry @int_clk_reg holds hardware specific options for the Clock Out
618 * register. Options defined in the %i82527.h file:
619 * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
620 * The entry @int_bus_reg holds hardware specific options for the Bus
621 * Configuration register. Options defined in the %i82527.h file:
622 * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
623 * Return Value: The function always returns zero
624 * File: src/template.c
626 int pcan_dongle_init_chip_data(struct candevice_t *candev, int chipnr)
630 /* initialize common routines for the SJA1000 chip */
631 sja1000p_fill_chipspecops(candev->chip[chipnr]);
634 candev->chip[chipnr]->chip_type=CHIP_TYPE;
635 candev->chip[chipnr]->chip_base_addr=candev->io_addr;
636 candev->chip[chipnr]->clock = 16000000;
637 candev->chip[chipnr]->int_clk_reg = 0x0;
638 candev->chip[chipnr]->int_bus_reg = 0x0;
639 candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF;
640 candev->chip[chipnr]->sja_ocr_reg = sjaOCR_MODE_NORMAL | sjaOCR_TX0_LH;
642 candev->chip[chipnr]->flags |= CHIP_IRQ_CUSTOM; // I don't want setup call request_irq
643 // I'm going to do it through parport_register_device
651 * template_init_obj_data - Initialize message buffers
652 * @chip: Pointer to chip specific structure
653 * @objnr: Number of the message buffer
655 * The function template_init_obj_data() is used to initialize the hardware
656 * structure containing information about the different message objects on the
657 * CAN chip. In case of the sja1000 there's only one message object but on the
658 * i82527 chip there are 15.
659 * The code below is for a i82527 chip and initializes the object base addresses
660 * The entry @obj_base_addr represents the first memory address of the message
661 * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
663 * Unless the hardware uses a segmented memory map, flags can be set zero.
664 * Return Value: The function always returns zero
665 * File: src/template.c
667 int pcan_dongle_init_obj_data(struct canchip_t *chip, int objnr)
669 chip->msgobj[objnr]->obj_base_addr=chip->chip_base_addr;
670 chip->msgobj[objnr]->obj_flags=0;
676 * template_program_irq - program interrupts
677 * @candev: Pointer to candevice/board structure
679 * The function template_program_irq() is used for hardware that uses
680 * programmable interrupts. If your hardware doesn't use programmable interrupts
681 * you should not set the @candevices_t->flags entry to %CANDEV_PROGRAMMABLE_IRQ and
682 * leave this function unedited. Again this function is hardware specific so
683 * there's no example code.
684 * Return value: The function returns zero on success or %-ENODEV on failure
685 * File: src/template.c
687 int pcan_dongle_program_irq(struct candevice_t *candev)
691 pcan_dongle_req_irq(&dongle_port);
692 ret_open = pcan_dongle_open(&dongle_port);
699 * template_write_register - Low level write register routine
700 * @data: data to be written
701 * @address: memory address to write to
703 * The function template_write_register() is used to write to hardware registers
704 * on the CAN chip. You should only have to edit this function if your hardware
705 * uses some specific write process.
706 * Return Value: The function does not return a value
707 * File: src/template.c
709 void pcan_dongle_write_register(unsigned data, unsigned long address)
711 address -= dongle_port.chip->chip_base_addr; // it's in mutiplexed mode
713 pcan_dongle_writereg(&dongle_port, (u8) address, (u8) data); // write a register
715 // DEBUGMSG("Write Reg at: 0x%lx data 0x%x \n", address, (unsigned) data);
719 * template_read_register - Low level read register routine
720 * @address: memory address to read from
722 * The function template_read_register() is used to read from hardware registers
723 * on the CAN chip. You should only have to edit this function if your hardware
724 * uses some specific read process.
725 * Return Value: The function returns the value stored in @address
726 * File: src/template.c
728 unsigned pcan_dongle_read_register(unsigned long address)
732 address -= dongle_port.chip->chip_base_addr; // it's in mutiplexed mode
734 if (dongle_port.wType == HW_DONGLE_SJA)
735 val = pcan_dongle_sp_readreg(&dongle_port, (u8) address); // functions for SP port
737 val = pcan_dongle_epp_readreg(&dongle_port, (u8) address); // functions for EPP port
739 // DEBUGMSG("Read Reg at: 0x%lx data 0x%x \n", address, val);
741 return ((unsigned)val);
744 /* !!! Don't change this function !!! */
745 int pcan_dongle_register(struct hwspecops_t *hwspecops)
747 hwspecops->request_io = pcan_dongle_request_io;
748 hwspecops->release_io = pcan_dongle_release_io;
749 hwspecops->reset = pcan_dongle_reset;
750 hwspecops->init_hw_data = pcan_dongle_init_hw_data;
751 hwspecops->init_chip_data = pcan_dongle_init_chip_data;
752 hwspecops->init_obj_data = pcan_dongle_init_obj_data;
753 hwspecops->write_register = pcan_dongle_write_register;
754 hwspecops->read_register = pcan_dongle_read_register;
755 hwspecops->program_irq = pcan_dongle_program_irq;