ircard_cs.c

来自「linux操作系统下的红外驱动的测试程序」· C语言 代码 · 共 1,368 行 · 第 1/3 页

C
1,368
字号
/********************************************************************* *                 * Filename:      ircard_cs.c * Version:       0.8 * Description:   IrDA FIR device driver for the IRDATA IRCARD which is *                using the IBM 31T1502 controller chip * Status:        Experimental. * Author:        Dag Brattli <dagb@cs.uit.no> * Created at:    Fri Apr  2 00:01:11 1999 * Modified at:   Fri Aug 27 12:30:00 1999 * Modified by:   Dag Brattli <dagb@cs.uit.no> *  *     Copyright (c) 1999 Dag Brattli, All Rights Reserved. *      *     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., 59 Temple Place, Suite 330, Boston,  *     MA 02111-1307 USA *      ********************************************************************/#include <pcmcia/config.h>#include <pcmcia/k_compat.h>#include <linux/autoconf.h> /* Very important! */#include <linux/kernel.h>#include <linux/sched.h>#include <linux/ptrace.h>#include <linux/malloc.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/delay.h>#include <linux/ioport.h>#include <asm/io.h>#include <asm/system.h>#include <net/irda/irda.h>#include <net/irda/irmod.h>#include <net/irda/irda_device.h>#include <net/irda/irport.h>#include <net/irda/wrapper.h>#include <pcmcia/version.h>#include <pcmcia/cs_types.h>#include <pcmcia/cs.h>#include <pcmcia/cistpl.h>#include <pcmcia/cisreg.h>#include <pcmcia/ds.h>#include <pcmcia/mem_op.h>#include "ircard_cs.h"#if LINUX_VERSION_CODE < VERSION(2,3,14)#define net_device device#endifstatic char *version = "ircard_cs.c 0.1 Fri Apr 2 11:21:27 1999 (Dag Brattli)";/* Parameters that can be set with 'insmod' *//* The old way: bit map of interrupts to choose from *//* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */static u_int irq_mask = 0xdeb8;/* Newer, simpler way of listing specific interrupts */static int irq_list[4] = { -1 };/* The IBM 31T1100A tranceiver needs at least 120 us */static int qos_mtt_bits = 0x0f; /* We have to set it to 500 us */MODULE_PARM(irq_mask, "i");MODULE_PARM(irq_list, "1-4i");MODULE_PARM(qos_mtt_bits, "i");static void ircard_config(dev_link_t *link);static void ircard_release(u_long arg);static int  ircard_event(event_t event, int priority, 			 event_callback_args_t *args);static dev_link_t *ircard_attach(void);static void ircard_detach(dev_link_t *);static int  ircard_probe(struct irda_device *idev);static void ircard_change_speed(struct irda_device *idev, __u32 speed);static int  ircard_hard_xmit(struct sk_buff *skb, struct net_device *dev);static void ircard_dma_write(struct irda_device *idev, int iobase);static int  ircard_receive(struct irda_device *idev, int iobase);static void ircard_interrupt(int irq, void *dev_id, struct pt_regs *regs);static int  ircard_is_receiving(struct irda_device *idev);static void ircard_wait_until_sent(struct irda_device *idev);static int  ircard_net_close(struct net_device *dev);static int  ircard_net_open(struct net_device *dev);static int  ircard_net_init(struct net_device *dev);static dev_info_t dev_info = "ircard_cs";static dev_link_t *dev_list = NULL;static void cs_error(client_handle_t handle, int func, int ret){	error_info_t err = { func, ret };	CardServices(ReportError, handle, &err);}/* * Function ircard_attach () * *    creates an "instance" of the driver, allocating local data structures *    for one device. The device is registered with Card Services. *  */static dev_link_t *ircard_attach(void){        client_reg_t client_reg;	dev_link_t *link;	struct ircard_cb *self;	int ret, i;		DEBUG(0, __FUNCTION__ "()\n");		/* Initialize the dev_link_t structure */	link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);	memset(link, 0, sizeof(struct dev_link_t));	link->release.function = &ircard_release;	link->release.data = (u_long)link;		/* Interrupt setup */	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE|IRQ_HANDLE_PRESENT;	link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;	if (irq_list[0] == -1)	     link->irq.IRQInfo2 = irq_mask;	else	     for (i = 0; i < 4; i++)		  link->irq.IRQInfo2 |= 1 << irq_list[i];	link->irq.Handler = &ircard_interrupt;		/* 	 * General socket configuration defaults can go here.  In this	 * client, we assume very little, and rely on the CIS for almost	 * everything.  In most clients, many details (i.e., number, sizes,	 * and attributes of IO windows) are fixed by the nature of the	 * device, and can be hard-wired here.  	 */	link->conf.Attributes = 0;	link->conf.Vcc = 50;	link->conf.IntType = INT_MEMORY_AND_IO;		/* Allocate space for private device-specific data */	self = kmalloc(sizeof(struct ircard_cb), GFP_KERNEL);	memset(self, 0, sizeof(struct ircard_cb));	self->magic = IRCARD_MAGIC;	link->priv = self;	link->irq.Instance = &self->idev;	/* Register with Card Services */	link->next = dev_list;	dev_list = link;	client_reg.dev_info = &dev_info;	client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;	client_reg.EventMask =	     CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |	     CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |	     CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;	client_reg.event_handler = &ircard_event;	client_reg.Version = 0x0210;	client_reg.event_callback_args.client_data = link;	ret = CardServices(RegisterClient, &link->handle, &client_reg);	if (ret != 0) {		cs_error(link->handle, RegisterClient, ret);		ircard_detach(link);		return NULL;	}		return link;}/* * Function ircard_detach (link) * *    This deletes a driver "instance". The device is de-registered with *    Card Services. If it has been released, all local data structures are *    freed.  Otherwise, the structures will be freed when the device is *    released. *  */static void ircard_detach(dev_link_t *link){	   dev_link_t **linkp;	   struct ircard_cb *self;	   struct irda_device *idev;		   DEBUG(0, __FUNCTION__ "(0x%p)\n", link);	   self = (struct ircard_cb *) link->priv;	   idev = &self->idev;	   	   /* Locate device structure */	   for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)		   if (*linkp == link) break;	   if (*linkp == NULL)		   return;	   	   /*	    * If the device is currently configured and active, we won't	    * actually delete it yet.  Instead, it is marked so that when	    * the release() function is called, that will trigger a proper	    * detach().	    */	   if (link->state & DEV_CONFIG) {#ifdef PCMCIA_DEBUG		   printk(KERN_DEBUG "ircard_cs: detach postponed, '%s' "			  "still locked\n", link->dev->dev_name);#endif		   link->state |= DEV_STALE_LINK;		   return;	   }	   	   /* Break the link with Card Services */	   if (link->handle)		   CardServices(DeregisterClient, link->handle);	   	   /* Unlink device structure, free pieces */	   *linkp = link->next;	   if (self) {		   		   /* Close irport */		   irport_stop(idev, idev->io.iobase2);		   		   irda_device_close(idev);		   		   kfree_s(self, sizeof(struct ircard_cb));	   }	   kfree_s(link, sizeof(struct dev_link_t));   }#define CS_CHECK(fn, args...) \while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed#define CFG_CHECK(fn, args...) \if (CardServices(fn, args) != 0) goto next_entry/* * Function ircard_config (link) * *    Configure the card and prepare for IO * */static void ircard_config(dev_link_t *link){	struct irda_device *idev;	struct ircard_cb *self;	client_handle_t handle;	tuple_t tuple;	cisparse_t parse;	int last_fn, last_ret;	u_char buf[64];	win_req_t req;	memreq_t map;		handle = link->handle;	self = (struct ircard_cb *) link->priv;	ASSERT(self != NULL, return;);	ASSERT(self->magic == IRCARD_MAGIC, return;);		DEBUG(0, __FUNCTION__ "(0x%p)\n", link);		/*	 * This reads the card's CONFIG tuple to find its configuration	 * registers.	 */	tuple.DesiredTuple = CISTPL_CONFIG;	tuple.Attributes = 0;	tuple.TupleData = buf;	tuple.TupleDataMax = sizeof(buf);	tuple.TupleOffset = 0;	CS_CHECK(GetFirstTuple, handle, &tuple);	CS_CHECK(GetTupleData, handle, &tuple);	CS_CHECK(ParseTuple, handle, &tuple, &parse);	link->conf.ConfigBase = parse.config.base;	link->conf.Present = parse.config.rmask[0];	DEBUG(0, __FUNCTION__ "() present=%x\n", link->conf.Present);	/* Configure card */	link->state |= DEV_CONFIG;		/* 	 * In this loop, we scan the CIS for configuration table entries,	 * each of which describes a valid card configuration, including	 * voltage, IO window, memory window, and interrupt settings.	 * 	 * We make no assumptions about the card to be configured: we use	 * just the information available in the CIS.  In an ideal world,	 * this would work for any PCMCIA card, but it requires a complete	 * and accurate CIS.  In practice, a driver usually "knows" most of	 * these things without consulting the CIS, and most client drivers	 * will only use the CIS to fill in implementation-defined details.	 * 	 */	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;	CS_CHECK(GetFirstTuple, handle, &tuple);	while (1) {		cistpl_cftable_entry_t dflt = { 0 };		cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);		CFG_CHECK(GetTupleData, handle, &tuple);		CFG_CHECK(ParseTuple, handle, &tuple, &parse);				if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;		if (cfg->index == 0) goto next_entry;		link->conf.ConfigIndex = cfg->index;				/* Does this card need audio output? */		if (cfg->flags & CISTPL_CFTABLE_AUDIO) {			link->conf.Attributes |= CONF_ENABLE_SPKR;			link->conf.Status = CCSR_AUDIO_ENA;		}		/* 		 * Use power settings for Vcc and Vpp if present 		 *		 * Note that the CIS values need to be rescaled 		 */		if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM))			link->conf.Vcc = cfg->vcc.param[CISTPL_POWER_VNOM]/10000;		else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM))			link->conf.Vcc = dflt.vcc.param[CISTPL_POWER_VNOM]/10000;				if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))			link->conf.Vpp1 = link->conf.Vpp2 =				cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;		else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))			link->conf.Vpp1 = link->conf.Vpp2 =				dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;				/* Do we need to allocate an interrupt? */		if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)			link->conf.Attributes |= CONF_ENABLE_IRQ;				/* IO window settings */		link->io.NumPorts1 = link->io.NumPorts2 = 0;		if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {			cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;			link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;			if (!(io->flags & CISTPL_IO_8BIT))				link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;			if (!(io->flags & CISTPL_IO_16BIT))				link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;			link->io.BasePort1 = io->win[0].base;			link->io.NumPorts1 = io->win[0].len;			if (io->nwin > 1) {				link->io.Attributes2 = link->io.Attributes1;				link->io.BasePort2 = io->win[1].base;				link->io.NumPorts2 = io->win[1].len;			}		}				/* This reserves IO space but doesn't actually enable it */		CFG_CHECK(RequestIO, link->handle, &link->io);				/*  		 * Now set up a common memory window, if needed. There is		 * room in the dev_link_t structure for one memory window		 * handle, but if the base addresses need to be saved, or		 * if multiple windows are needed, the info should go in		 * the private data structure for this device.		 * 		 * Note that the memory window base is a physical address,		 * and needs to be mapped to virtual space with ioremap()		 * before it is used.  		 */		if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {			cistpl_mem_t *mem =				(cfg->mem.nwin) ? &cfg->mem : &dflt.mem;			req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|				WIN_ENABLE/*|WIN_USE_WAIT*/|WIN_MAP_BELOW_1MB;			req.Base = 0; /*mem->win[0].host_addr;*/			req.Size = mem->win[0].len;			req.Size = 0x8000;			req.AccessSpeed = 0;			link->win = (window_handle_t)link->handle;			CFG_CHECK(RequestWindow, &link->win, &req);			map.Page = 0; map.CardOffset = mem->win[0].card_addr;			CFG_CHECK(MapMemPage, link->win, &map);		}		/* If we got this far, we're cool! */		break;			next_entry:		CS_CHECK(GetNextTuple, handle, &tuple);	}		/*	 * Allocate an interrupt line.  Note that this does not assign a	 * handler to the interrupt, unless the 'Handler' member of the	 * irq structure is initialized.	 */	if (link->conf.Attributes & CONF_ENABLE_IRQ)		CS_CHECK(RequestIRQ, link->handle, &link->irq);		/*	 * This actually configures the PCMCIA socket -- setting up	 * the I/O windows and the interrupt mapping, and putting the	 * card and host interface into "Memory and IO" mode.	 */	CS_CHECK(RequestConfiguration, link->handle, &link->conf);		/*	 * At this point, the dev_node_t structure(s) need to be	 * initialized and arranged in a linked list at link->dev.	 */	sprintf(self->node.dev_name, "ircard0");	self->node.major = self->node.minor = 0;	link->dev = &self->node;		/* Finally, report what we've done */	printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",	       self->node.dev_name, link->conf.ConfigIndex,	       link->conf.Vcc/10, link->conf.Vcc%10);	if (link->conf.Vpp1)		printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);	if (link->conf.Attributes & CONF_ENABLE_IRQ)		printk(", irq %d", link->irq.AssignedIRQ);	if (link->io.NumPorts1)		printk(", io 0x%04x-0x%04x", link->io.BasePort1,		       link->io.BasePort1+link->io.NumPorts1-1);	if (link->io.NumPorts2)		printk(" & 0x%04x-0x%04x", link->io.BasePort2,		       link->io.BasePort2+link->io.NumPorts2-1);	if (link->win)		printk(", mem 0x%06lx-0x%06lx", req.Base, req.Base+req.Size-1);	printk("\n");	/* Look up the irda device */	idev = &self->idev;	/* Initialize IO */	idev->io.iobase    = link->io.BasePort1+8;        idev->io.iobase2   = link->io.BasePort1; /* Used by irport */        idev->io.irq       = link->irq.AssignedIRQ;        idev->io.io_ext    = 8;        idev->io.io_ext2   = 8;       /* Used by irport */	idev->io.membase   = (int) ioremap(req.Base, req.Size);        idev->io.fifo_size = 16;	idev->io.baudrate  = 9600;	DEBUG(0, __FUNCTION__ "(), membase=%08x\n", idev->io.membase);	/* Reset Tx queue info */	self->tx_q.len = self->tx_q.ptr = self->tx_q.free = 0;

⌨️ 快捷键说明

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