📄 pcan_dongle.c
字号:
//****************************************************************************// Copyright (C) 2001,2002,2003 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)//****************************************************************************//****************************************************************************//// all parts to handle the interface specific parts of pcan-dongle//// $Log: pcan_dongle.c,v $// 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 <src/pcan_common.h> // must always be the 1st include#include <linux/errno.h>#include <linux/ioport.h>#include <asm/io.h> #include <linux/sched.h>#ifdef PARPORT_SUBSYSTEM#include <linux/parport.h>#endif#include <linux/delay.h>#include <src/pcan_dongle.h>#include <src/pcan_sja1000.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 //****************************************************************************// GLOBALS//****************************************************************************// LOCALSstatic 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_devicesstatic u16 epp_devices = 0; // ... epp_devicesstatic u16 sp_devices = 0; // ... sp_devicesstatic 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 };//****************************************************************************// CODE //----------------------------------------------------------------------------// enable and disable irqsstatic void _parport_disable_irq(struct pcandev *dev){ u16 PC = (u16)dev->port.dng.dwPort + 2; outb(inb(PC) & ~0x10, PC);}static void _parport_enable_irq(struct pcandev *dev){ u16 PC = (u16)dev->port.dng.dwPort + 2; outb(inb(PC) | 0x10, PC);}// functions for SP portstatic u8 pcan_dongle_sp_readreg(struct pcandev *dev, u8 port) // read a register{ u16 PA = (u16)dev->port.dng.dwPort; u16 PB = PA + 1; u16 PC = PB + 1; u8 b0, b1 ; u8 irqEnable = inb(PC) & 0x10; // don't influence irqEnable u32 flags; save_flags(flags); cli(); 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); restore_flags(flags); return (b1 << 4) | b0 ;}static void pcan_dongle_writereg(struct pcandev *dev, u8 port, u8 data) // write a register{ u16 PA = (u16)dev->port.dng.dwPort; u16 PC = PA + 2; u8 irqEnable = inb(PC) & 0x10; // don't influence irqEnable u32 flags; save_flags(flags); cli(); outb((0x0B ^ 0x0D) | irqEnable, PC); outb(port & 0x1F, PA); outb((0x0B ^ 0x0C) | irqEnable, PC); outb(data, PA); outb((0x0B ^ 0x0D) | irqEnable, PC); restore_flags(flags);}// functions for EPP portstatic u8 pcan_dongle_epp_readreg(struct pcandev *dev, u8 port) // read a register{ u16 PA = (u16)dev->port.dng.dwPort; u16 PC = PA + 2; u8 wert; u8 irqEnable = inb(PC) & 0x10; // don't influence irqEnable u32 flags; save_flags(flags); cli(); outb((0x0B ^ 0x0F) | irqEnable, PC); outb((port & 0x1F) | 0x80, PA); outb((0x0B ^ 0x2E) | irqEnable, PC); wert = inb(PA); outb((0x0B ^ 0x0F) | irqEnable, PC); restore_flags(flags); return wert;}static int pcan_dongle_req_irq(struct pcandev *dev){ if (dev->wInitStep == 3) { #ifndef PARPORT_SUBSYSTEM int err; if ((err = request_irq(dev->port.dng.wIrq, sja1000_irqhandler, SA_INTERRUPT | SA_SHIRQ, "pcan", dev))) return err; #endif dev->wInitStep++; } return 0;}static void pcan_dongle_free_irq(struct pcandev *dev){ if (dev->wInitStep == 4) { #ifndef PARPORT_SUBSYSTEM free_irq(dev->port.dng.wIrq, dev); #endif dev->wInitStep--; }}// release and probe functionsstatic int pcan_dongle_cleanup(struct pcandev *dev){ DPRINTK(KERN_DEBUG "%s: pcan_dongle_cleanup()\n", DEVICE_NAME); switch(dev->wInitStep) { case 4: pcan_dongle_free_irq(dev); case 3: if (dev->wType == HW_DONGLE_SJA) sp_devices--; else epp_devices--; dng_devices = sp_devices + epp_devices; case 2: #ifndef PARPORT_SUBSYSTEM if (dev->wType == HW_DONGLE_SJA_EPP) release_region(dev->port.dng.wEcr, ECR_PORT_SIZE); #endif case 1: #ifdef PARPORT_SUBSYSTEM parport_unregister_device(dev->port.dng.pardev); #else release_region(dev->port.dng.dwPort, DNG_PORT_SIZE); #endif case 0: dev->wInitStep = 0; } return 0;}// to switch epp on or restore registerstatic void setECR(struct pcandev *dev){ u16 wEcr = dev->port.dng.wEcr; dev->port.dng.ucOldECRContent = inb(wEcr); outb((dev->port.dng.ucOldECRContent & 0x1F) | 0x20, wEcr); if (dev->port.dng.ucOldECRContent == 0xff) printk(KERN_DEBUG "%s: realy ECP mode configured?\n", DEVICE_NAME);}static void restoreECR(struct pcandev *dev){ u16 wEcr = dev->port.dng.wEcr; outb(dev->port.dng.ucOldECRContent, wEcr); DPRINTK(KERN_DEBUG "%s: restore ECR\n", DEVICE_NAME); }#ifdef PARPORT_SUBSYSTEM#ifdef LINUX_22// replacement for kernel 2.4 parport functionsstatic struct parport *parport_find_base(u32 dwPort){ struct parport *p = NULL; p = parport_enumerate(); while ((p != NULL) && (p->base != dwPort)) p = p->next; return p; }static void parport_enable_irq(struct parport *p){ p->ops->enable_irq(p);}static void parport_disable_irq(struct parport *p){ p->ops->disable_irq(p);}#endifstatic int pcan_dongle_probe(struct pcandev *dev) // probe for type{ struct parport *p; DPRINTK(KERN_DEBUG "%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(dev->port.dng.dwPort); if (!p) { DPRINTK(KERN_DEBUG "found no parport\n"); return -ENXIO; } else { // register my device at the parport dev->port.dng.pardev = parport_register_device(p, "pcan", NULL, NULL, sja1000_irqhandler, 0, (void *)dev); if (!dev->port.dng.pardev) { DPRINTK(KERN_DEBUG "found no parport device\n"); return -ENODEV; } } return 0;}#elsestatic int pcan_dongle_probe(struct pcandev *dev) // probe for type{ int result; DPRINTK(KERN_DEBUG "%s: pcan_dongle_probe()\n", DEVICE_NAME); result = check_region(dev->port.dng.dwPort, DNG_PORT_SIZE) ? -EBUSY : 0; if (!result) { request_region(dev->port.dng.dwPort, DNG_PORT_SIZE, DEVICE_NAME); dev->wInitStep = 1; if (dev->wType == HW_DONGLE_SJA_EPP) { result = check_region(dev->port.dng.wEcr, ECR_PORT_SIZE) ? -EBUSY : 0; if (!result) { request_region(dev->port.dng.wEcr, ECR_PORT_SIZE, DEVICE_NAME); dev->wInitStep = 2; } } } return result;}#endif // PARPORT_SUBSYSTEM// interface depended open and closestatic int pcan_dongle_open(struct pcandev *dev){ int result = 0; u16 wPort; DPRINTK(KERN_DEBUG "%s: pcan_dongle_open()\n", DEVICE_NAME); #ifdef PARPORT_SUBSYSTEM result = parport_claim(dev->port.dng.pardev); if (!result) { if (dev->port.dng.pardev->port->irq == PARPORT_IRQ_NONE) { printk(KERN_ERR "%s: no irq associated to parport.\n", DEVICE_NAME); result = -ENXIO; } } else printk(KERN_ERR "%s: can't claim parport.\n", DEVICE_NAME); #endif // PARPORT_SUBSYSTEM // save port state if (!result) { wPort = (u16)dev->port.dng.dwPort; // save old port contents dev->port.dng.ucOldDataContent = inb(wPort); dev->port.dng.ucOldControlContent = inb(wPort + 2); // switch to epp mode if possible if (dev->wType == HW_DONGLE_SJA_EPP) setECR(dev); // enable irqs #ifdef PARPORT_SUBSYSTEM _parport_enable_irq(dev); // parport_enable_irq(dev->port.dng.pardev->port); not working since 2.4.18 #else _parport_enable_irq(dev); #endif } return result;}static int pcan_dongle_release(struct pcandev *dev){ u16 wPort = (u16)dev->port.dng.dwPort; DPRINTK(KERN_DEBUG "%s: pcan_dongle_release()\n", DEVICE_NAME); // disable irqs #ifdef PARPORT_SUBSYSTEM _parport_disable_irq(dev); // parport_disable_irq(dev->port.dng.pardev->port); not working since 2.4.18 #else _parport_disable_irq(dev); #endif if (dev->wType == HW_DONGLE_SJA_EPP) restoreECR(dev); // restore port state outb(dev->port.dng.ucOldDataContent, wPort); outb(dev->port.dng.ucOldControlContent, wPort + 2); #ifdef PARPORT_SUBSYSTEM parport_release(dev->port.dng.pardev); #endif return 0;}int pcan_dongle_init(struct pcandev *dev, u32 dwPort, u16 wIrq, char *type){ int err; DPRINTK(KERN_DEBUG "%s: pcan_dongle_init(), dng_devices = %d\n", DEVICE_NAME, dng_devices); // init process wait queues init_waitqueue_head(&dev->read_queue); init_waitqueue_head(&dev->write_queue); // set this before any instructions, fill struct pcandev, part 1 dev->wInitStep = 0; dev->cleanup = pcan_dongle_cleanup; dev->req_irq = pcan_dongle_req_irq; dev->free_irq = pcan_dongle_free_irq; dev->open = pcan_dongle_open; dev->release = pcan_dongle_release; // 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; dev->port.dng.dwPort = dng_ports[dng_devices]; } else dev->port.dng.dwPort = dwPort; if (!wIrq) { if (dng_devices >= DNG_DEFAULT_COUNT) return -ENODEV; dev->port.dng.wIrq = dng_irqs[dng_devices]; } else dev->port.dng.wIrq = wIrq; if (dev->wType == HW_DONGLE_SJA) { dev->nMinor = PCAN_DNG_SP_MINOR_BASE + sp_devices; dev->readreg = pcan_dongle_sp_readreg; dev->writereg = pcan_dongle_writereg; dev->port.dng.wEcr = 0; // set to anything } else { dev->nMinor = PCAN_DNG_EPP_MINOR_BASE + epp_devices; dev->readreg = pcan_dongle_epp_readreg; dev->writereg = pcan_dongle_writereg; dev->port.dng.wEcr = (u16)dev->port.dng.dwPort + 0x402; } // is the device really available? if ((err = pcan_dongle_probe(dev)) < 0) return err; dev->ucPhysicallyInstalled = 1; if (dev->wType == HW_DONGLE_SJA) sp_devices++; else epp_devices++; dng_devices = sp_devices + epp_devices; dev->wInitStep = 3; printk(KERN_INFO "%s: %s-dongle device minor %d prepared (io=0x%04x,irq=%d)\n", DEVICE_NAME, dev->type, dev->nMinor, dev->port.dng.dwPort, dev->port.dng.wIrq); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -