⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcan_dongle.c

📁 CAN 驱动编程
💻 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 + -