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

📄 amplc_pc236.c

📁 rtlinux-3.2-pre3.tar.bz2 rtlinux3.2-pre3的源代码
💻 C
字号:
/*    comedi/drivers/amplc_pc236.c    Driver for Amplicon PC36AT and PCI236 DIO boards.    Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>    COMEDI - Linux Control and Measurement Device Interface    Copyright (C) 2000 David A. Schleef <ds@schleef.org>    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.*//*Driver: amplc_pc236.oDescription: Driver for Amplicon PC36AT and PCI236 DIO boardsAuthor: Ian Abbott <abbotti@mev.co.uk>Devices: [Amplicon] PC36AT (pc36at), PCI236 (pci236)Updated: Fri, 23 Aug 2002 11:41:11 +0100Status: worksConfiguration options - PC36AT:  [0] - I/O port base address  [1] - IRQ (optional)Configuration options - PCI236:  [0] - PCI bus of device (optional)  [1] - PCI slot of device (optional)  If bus/slot is not specified, the first available PCI device will be  used.The PC36AT ISA board and PCI236 PCI board have a single 8255 appearingas subdevice 0.Subdevice 1 pretends to be a digital input device, but it always returns0 when read. However, if you run a command with scan_begin_src=TRIG_EXT,a rising edge on port C bit 7 acts as an external trigger, which can beused to wake up tasks.  This is like the comedi_parport device, but theonly way to physically disable the interrupt on the PC36AT is to removethe IRQ jumper.  If no interrupt is connected, then subdevice 1 isunused.*/#include <linux/comedidev.h>#include <linux/pci.h>#include "8255.h"#include "plx9052.h"#define PC236_DRIVER_NAME	"amplc_pc236"/* PCI236 PCI configuration register information */#define PCI_VENDOR_ID_AMPLICON 0x14dc#define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009/* PC36AT / PCI236 registers */#define PC236_IO_SIZE		4#define PC236_LCR_IO_SIZE	128/* * Board descriptions for Amplicon PC36AT and PCI236. */enum pc236_bustype {isa_bustype, pci_bustype};enum pc236_model {pc36at_model, pci236_model};typedef struct pc236_board_struct{	char *name;	char *fancy_name;	enum pc236_bustype bustype;	enum pc236_model model;}pc236_board;static pc236_board pc236_boards[] = {	{	name:		"pc36at",	fancy_name:	"PC36AT",	bustype:	isa_bustype,	model:		pc36at_model,	},	{	name:		"pci236",	fancy_name:	"PCI236",	bustype:	pci_bustype,	model:		pci236_model,	},};static struct pci_device_id pc236_pci_table[] __devinitdata = {	{ PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pci236_model },	{ 0 }};MODULE_DEVICE_TABLE(pci, pc236_pci_table);/* * Useful for shorthand access to the particular board structure */#define thisboard ((pc236_board *)dev->board_ptr)/* this structure is for data unique to this hardware driver.  If   several hardware drivers keep similar information in this structure,   feel free to suggest moving the variable to the comedi_device struct.  */typedef struct{	/* PCI device */	struct pci_dev *pci_dev;	int lcr_iobase;	/* PLX PCI9052 config registers in PCIBAR1 */	int enable_irq;	int share_irq;}pc236_private;#define devpriv ((pc236_private *)dev->private)/* * The comedi_driver structure tells the Comedi core module * which functions to call to configure/deconfigure (attach/detach) * the board, and also about the kernel module that contains * the device code. */static int pc236_attach(comedi_device *dev,comedi_devconfig *it);static int pc236_detach(comedi_device *dev);static comedi_driver driver_amplc_pc236={	driver_name:	PC236_DRIVER_NAME,	module:		THIS_MODULE,	attach:		pc236_attach,	detach:		pc236_detach,	board_name:	pc236_boards,	offset:		sizeof(pc236_board),	num_names:	sizeof(pc236_boards) / sizeof(pc236_board),};COMEDI_INITCLEANUP(driver_amplc_pc236);static int pc236_request_region(unsigned long from, unsigned long extent);static void pc236_intr_disable(comedi_device *dev);static void pc236_intr_enable(comedi_device *dev);static int pc236_intr_check(comedi_device *dev);static int pc236_intr_insn(comedi_device *dev,comedi_subdevice *s,	comedi_insn *insn,lsampl_t *data);static int pc236_intr_cmdtest(comedi_device *dev,comedi_subdevice *s,	comedi_cmd *cmd);static int pc236_intr_cmd(comedi_device *dev,comedi_subdevice *s);static int pc236_intr_cancel(comedi_device *dev,comedi_subdevice *s);static void pc236_interrupt(int irq,void *d,struct pt_regs *regs);/* * Attach is called by the Comedi core to configure the driver * for a particular board.  If you specified a board_name array * in the driver structure, dev->board_ptr contains that * address. */static int pc236_attach(comedi_device *dev,comedi_devconfig *it){	comedi_subdevice *s;	struct pci_dev *pci_dev = NULL;	int iobase = 0, irq = 0;	int lcr_iobase = 0;	int bus = 0, slot = 0;	struct pci_device_id *pci_id;	int share_irq = 0;	int ret;	printk("comedi%d: %s: ",dev->minor, PC236_DRIVER_NAME);	/* Get card bus position and base address. */	switch (thisboard->bustype) {	case isa_bustype:		iobase = it->options[0];		irq = it->options[1];		share_irq = 0;		break;	case pci_bustype:		bus = it->options[0];		slot = it->options[1];		share_irq = 1;		/* Look for PCI table entry for this model. */		for (pci_id = pc236_pci_table; pci_id->vendor != 0; pci_id++) {			if (pci_id->driver_data == thisboard->model)				break;		}		if (pci_id->driver_data != thisboard->model) {			printk("bug! cannot determine board type!\n");			return -EINVAL;		}		/* Look for matching PCI device. */		pci_for_each_dev(pci_dev) {			/* If bus/slot specified, check them. */			if (bus || slot) {				if (bus != pci_dev->bus->number						|| slot != PCI_SLOT(pci_dev->devfn))					continue;			}			if (pci_dev->vendor != pci_id->vendor)				continue;			if (pci_dev->device != pci_id->device)				continue;#if 0			if (pci_id->subvendor != PCI_ANY_ID) {				if (pci_dev->subsystem_vendor != pci_id->subvendor)					continue;			}			if (pci_id->subdevice != PCI_ANY_ID) {				if (pci_dev->subsystem_device != pci_id->subdevice)					continue;			}#endif			if (((pci_dev->class ^ pci_id->class) & pci_id->class_mask) != 0)				continue;			/* Found a match. */			break;		}		if (!pci_dev) {			printk("no %s found!\n", thisboard->fancy_name);			return -EIO;		}		if ((ret=pci_enable_device(pci_dev)) < 0) {			printk("error enabling PCI device!\n");			return ret;		}		lcr_iobase = pci_resource_start(pci_dev, 1);		iobase = pci_resource_start(pci_dev, 2);		irq = pci_dev->irq;		break;	default:		printk("bug! cannot determine board type!\n");		return -EINVAL;		break;	}/* * Initialize dev->board_name. */	dev->board_name = thisboard->name;	printk("%s ", dev->board_name);/* * Allocate the private structure area.  alloc_private() is a * convenient macro defined in comedidev.h. */	if ((ret=alloc_private(dev,sizeof(pc236_private))) < 0) {		printk("out of memory!\n");		return ret; 	}	devpriv->pci_dev = pci_dev;	devpriv->share_irq = share_irq;	/* Reserve I/O spaces. */	if (lcr_iobase) {		if ((ret=pc236_request_region(lcr_iobase, PC236_LCR_IO_SIZE)) < 0) {			return ret;		}		devpriv->lcr_iobase = lcr_iobase;	}	if ((ret=pc236_request_region(iobase, PC236_IO_SIZE)) < 0) {		return ret;	}	dev->iobase = iobase;/* * Allocate the subdevice structures.  alloc_subdevice() is a * convenient macro defined in comedidev.h. */	if ((ret=alloc_subdevices(dev, 2)) < 0) {		printk("out of memory!\n");		return ret;	}	s = dev->subdevices+0;	/* digital i/o subdevice (8255) */	if ((ret=subdev_8255_init(dev, s, NULL, iobase)) < 0) {		printk("out of memory!\n");		return ret;	}	s = dev->subdevices+1;	dev->read_subdev = s;	s->type = COMEDI_SUBD_UNUSED;	pc236_intr_disable(dev);	if (irq) {		unsigned long flags = share_irq ? SA_SHIRQ : 0;		if (comedi_request_irq(irq, pc236_interrupt, flags,					PC236_DRIVER_NAME, dev) >= 0) {			dev->irq = irq;			s->type = COMEDI_SUBD_DI;			s->subdev_flags = SDF_READABLE;			s->n_chan = 1;			s->maxdata = 1;			s->range_table = &range_digital;			s->insn_bits = pc236_intr_insn;			s->do_cmdtest = pc236_intr_cmdtest;			s->do_cmd = pc236_intr_cmd;			s->cancel = pc236_intr_cancel;		}	}	if (thisboard->bustype == isa_bustype) {		printk("(base %#x) ", iobase);	} else {		printk("(pci %02x:%02x.%x) ", pci_dev->bus->number,				PCI_SLOT(pci_dev->devfn),				PCI_FUNC(pci_dev->devfn));	}	if (irq) {		printk("(irq %d%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));	} else {		printk("(no irq) ");	}		printk("attached\n");	return 1;}/* * _detach is called to deconfigure a device.  It should deallocate * resources.   * This function is also called when _attach() fails, so it should be * careful not to release resources that were not necessarily * allocated by _attach().  dev->private and dev->subdevices are * deallocated automatically by the core. */static int pc236_detach(comedi_device *dev){	printk("comedi%d: %s: remove\n", dev->minor, PC236_DRIVER_NAME);	if (devpriv) {		pc236_intr_disable(dev);	}	if (dev->irq) comedi_free_irq(dev->irq, dev);	if (dev->subdevices) {		subdev_8255_cleanup(dev, dev->subdevices+0);	}	if (devpriv) {		if (devpriv->lcr_iobase)			release_region(devpriv->lcr_iobase, PC236_LCR_IO_SIZE);	}	if (dev->iobase) release_region(dev->iobase, PC236_IO_SIZE);		return 0;}/* * This function checks and requests an I/O region, reporting an error * if there is a conflict. */static int pc236_request_region(unsigned long from, unsigned long extent){	if (check_region(from, extent) < 0) {		printk("I/O port conflict (%#lx,%lu)!\n", from, extent);		return -EIO;	}	request_region(from, extent, PC236_DRIVER_NAME);	return 0;}/* * This function is called to mark the interrupt as disabled (no command * configured on subdevice 1) and to physically disable the interrupt * (not possible on the PC36AT, except by removing the IRQ jumper!). */static void pc236_intr_disable(comedi_device *dev){	unsigned long flags;	comedi_spin_lock_irqsave(&dev->spinlock, flags);	devpriv->enable_irq = 0;	if (devpriv->lcr_iobase)		outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase+PLX9052_INTCSR);	comedi_spin_unlock_irqrestore(&dev->spinlock, flags);}/* * This function is called to mark the interrupt as enabled (a command * configured on subdevice 1) and to physically enable the interrupt * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!). */static void pc236_intr_enable(comedi_device *dev){	unsigned long flags;	comedi_spin_lock_irqsave(&dev->spinlock, flags);	devpriv->enable_irq = 1;	if (devpriv->lcr_iobase)		outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase+PLX9052_INTCSR);	comedi_spin_unlock_irqrestore(&dev->spinlock, flags);}/* * This function is called when an interrupt occurs to check whether * the interrupt has been marked as enabled and was generated by the * board.  If so, the function prepares the hardware for the next * interrupt. * Returns 0 if the interrupt should be ignored. */static int pc236_intr_check(comedi_device *dev){	int retval = 0;	unsigned long flags;	comedi_spin_lock_irqsave(&dev->spinlock, flags);	if (devpriv->enable_irq) {		retval = 1;		if (devpriv->lcr_iobase) {			if ((inl(devpriv->lcr_iobase+PLX9052_INTCSR)						& PLX9052_INTCSR_LI1STAT_MASK)					== PLX9052_INTCSR_LI1STAT_SHIFTED_INACTIVE) {				retval = 0;			} else {				/* Clear interrupt and keep it enabled. */				outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase+PLX9052_INTCSR);			}		}	}	comedi_spin_unlock_irqrestore(&dev->spinlock, flags);	return retval;}/* * Input from subdevice 1. * Copied from the comedi_parport driver. */static int pc236_intr_insn(comedi_device *dev,comedi_subdevice *s,	comedi_insn *insn,lsampl_t *data){	data[1] = 0;	return 2;}/* * Subdevice 1 command test. * Copied from the comedi_parport driver. */static int pc236_intr_cmdtest(comedi_device *dev,comedi_subdevice *s,	comedi_cmd *cmd){	int err=0;	int tmp;	/* step 1 */	tmp=cmd->start_src;	cmd->start_src &= TRIG_NOW;	if(!cmd->start_src || tmp!=cmd->start_src)err++;	tmp=cmd->scan_begin_src;	cmd->scan_begin_src &= TRIG_EXT;	if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;	tmp=cmd->convert_src;	cmd->convert_src &= TRIG_FOLLOW;	if(!cmd->convert_src || tmp!=cmd->convert_src)err++;	tmp=cmd->scan_end_src;	cmd->scan_end_src &= TRIG_COUNT;	if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;	tmp=cmd->stop_src;	cmd->stop_src &= TRIG_NONE;	if(!cmd->stop_src || tmp!=cmd->stop_src)err++;	if(err)return 1;	/* step 2: ignored */	if(err)return 2;	/* step 3: */	if(cmd->start_arg!=0){		cmd->start_arg = 0;		err++;	}	if(cmd->scan_begin_arg!=0){		cmd->scan_begin_arg = 0;		err++;	}	if(cmd->convert_arg!=0){		cmd->convert_arg = 0;		err++;	}	if(cmd->scan_end_arg!=1){		cmd->scan_end_arg = 1;		err++;	}	if(cmd->stop_arg!=0){		cmd->stop_arg = 0;		err++;	}	if(err)return 3;	/* step 4: ignored */	if(err)return 4;	return 0;}/* * Subdevice 1 command. */static int pc236_intr_cmd(comedi_device *dev,comedi_subdevice *s){	pc236_intr_enable(dev);	return 0;}/* * Subdevice 1 cancel command. */static int pc236_intr_cancel(comedi_device *dev,comedi_subdevice *s){	pc236_intr_disable(dev);	return 0;}/* * Interrupt service routine. * Based on the comedi_parport driver. */static void pc236_interrupt(int irq,void *d,struct pt_regs *regs){	comedi_device *dev=d;	comedi_subdevice *s=dev->subdevices+1;	if(!pc236_intr_check(dev)) 		return;	comedi_buf_put(s->async,0);	s->async->events |= COMEDI_CB_EOS;		comedi_event(dev,s,s->async->events);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -