ircard_cs.c

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

C
1,368
字号
	self->tx_q.tail = SHMEM_TX_START + idev->io.membase;	ircard_probe(idev);	/* Initialize QoS for this device */	irda_init_max_qos_capabilies(&idev->qos);		/* The only value we must override it the baudrate */	idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|		IR_115200|IR_576000|IR_1152000 |(IR_4000000 << 8);	idev->qos.min_turn_time.bits = qos_mtt_bits;	irda_qos_bits_to_value(&idev->qos);	idev->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_SHM|IFF_PIO;	/* Specify which buffer allocation policy we need */	idev->rx_buff.flags = GFP_KERNEL;	idev->tx_buff.flags = GFP_KERNEL;	/* We still need the SIR buffer since we don't use shmem for that */	idev->rx_buff.truesize = 4000; 	idev->tx_buff.truesize = 4000;			/* Initialize callbacks */	idev->change_speed           = &ircard_change_speed;	idev->wait_until_sent        = &ircard_wait_until_sent;	idev->is_receiving           = &ircard_is_receiving;     	/* Override the network functions we need to use */	idev->netdev.init            = &ircard_net_init;	idev->netdev.hard_start_xmit = &irport_hard_xmit; /* used for SIR */	idev->netdev.open            = &ircard_net_open;	idev->netdev.stop            = &ircard_net_close;	/* Open the IrDA device */	irda_device_open(idev, (char *) dev_info, self);	/*irda_device_open(idev, dev_name, self);*/	link->state &= ~DEV_CONFIG_PENDING;	return;	 cs_failed:	cs_error(link->handle, last_fn, last_ret);	ircard_release((u_long)link);}/* * Function ircard_release (arg) * *    After a card is removed, ircard_release() will unregister the net *    device, and release the PCMCIA configuration. If the device is still *    open, this will be postponed until it is closed. *  */static void ircard_release(u_long arg){	dev_link_t *link = (dev_link_t *) arg;	struct ircard_cb *self;	struct irda_device *idev;		DEBUG(0, __FUNCTION__ "(0x%p)\n", link);	self = (struct ircard_cb *) link->priv;	idev = &self->idev;	if (link->open) {		DEBUG(1, "ircard_cs: release postponed, '%s' still open\n",		      self->node.dev_name);		link->state |= DEV_STALE_CONFIG;		return;	}		CardServices(ReleaseWindow, link->win);	iounmap((void *)idev->io.membase);	CardServices(ReleaseConfiguration, link->handle);	CardServices(ReleaseIO, link->handle, &link->io);	CardServices(ReleaseIRQ, link->handle, &link->irq);		link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);}/* * Function ircard_event (event, priority, args) * *    The card status event handler. Mostly, this schedules other stuff to *    run after an event is received. A CARD_REMOVAL event also sets some *    flags to discourage the net drivers from trying to talk to the card *    any more. *  */static int ircard_event(event_t event, int priority,			event_callback_args_t *args){	dev_link_t *link = args->client_data;	struct irda_device *idev;	struct ircard_cb *self;		DEBUG(1, __FUNCTION__ "(0x%06x)\n", event);		self = (struct ircard_cb *) link->priv;	idev = &self->idev;	ASSERT(self != NULL, return -1;);	ASSERT(self->magic == IRCARD_MAGIC, return -1;);	switch (event) {	case CS_EVENT_CARD_REMOVAL:		link->state &= ~DEV_PRESENT;		if (link->state & DEV_CONFIG) {			self->stop = 1;			link->release.expires = RUN_AT(HZ/20);			add_timer(&link->release);		}		break;	case CS_EVENT_CARD_INSERTION:		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;		ircard_config(link);		break;	case CS_EVENT_PM_SUSPEND:		link->state |= DEV_SUSPEND;		/* Fall through... */	case CS_EVENT_RESET_PHYSICAL:		/* Mark the device as stopped, to block IO until later */		self->stop = 1;		if (link->state & DEV_CONFIG)			CardServices(ReleaseConfiguration, link->handle);		break;	case CS_EVENT_PM_RESUME:		link->state &= ~DEV_SUSPEND;		/* Fall through... */	case CS_EVENT_CARD_RESET:		if (link->state & DEV_CONFIG)			CardServices(RequestConfiguration, link->handle, &link->conf);		self->stop = 0;		/*		 * In a normal driver, additional code may go here to restore		 * the device state and restart IO. 		 */		irport_start(idev, idev->io.iobase2);		ircard_change_speed(idev, 9600);		break;	}	return 0;}/* * Function ircard_probe (iobase, iobase2, irq, dma) * *     * */static int ircard_probe(struct irda_device *idev){	int revision;	int iobase;	iobase = idev->io.iobase;		/* Read revision ID */	switch_bank(iobase, 3);	revision = inb(iobase+REVISION_ID); /* (bank 3) */	printk("IBM 31T1502 controller revision %d\n", revision);	/* Initialize shared memory page register */	outb(0, iobase+SH_MEM_PAGE); /* (bank 3) */	/* Enable Tx CRC just in case */	switch_bank(iobase, 0);	outb(TX_CTRL2_EN_TX_CRC, iobase+TX_CTRL2);	switch_bank(iobase, 3);	outb(0, iobase+SH_MEM_PAGE);         /* (bank 3) */	outb(0, iobase+TX_DMA_ADDR_LOW);     /* (bank 3) */	outb(PAGE_DISABLE, iobase+TX_DMA_ADDR_HIGH);    /* (bank 3) */	return 0;}/* * Function ircard_sir_to_fir (iobase) * *    Some code to make the tranceiver switch from SIR to FIR mode * */static void ircard_sir_to_fir(struct irda_device *idev, int iobase){	DEBUG(4, __FUNCTION__ "()\n");	__switch_bank(iobase, 2);		/* Set XCRVOFF */	outb(IR_TRANS_CTRL_XCVROFF, iobase+IR_TRANS_CTRL);		/* Wait a minimum of 2 us */	udelay(2);		/* Set TXD_FORCE */	outb(IR_TRANS_CTRL_XCVROFF|IR_TRANS_CTRL_TXD_FRC, 	     iobase+IR_TRANS_CTRL);		/* Wait a minimum of 2 us */	udelay(2);		/* Clear XCRVOFF */	outb(IR_TRANS_CTRL_TXD_FRC, iobase+IR_TRANS_CTRL);		/* Wait a minimum of 2 us */	udelay(2);		/* Clear TXD_FORCE */	outb(0, iobase+IR_TRANS_CTRL);}/* * Function ircard_fir_to_sir (iobase) * *    Some code to make the tranceiver switch from FIR to SIR mode * */static void ircard_fir_to_sir(struct irda_device *idev, int iobase){	DEBUG(4, __FUNCTION__ "()\n");	__switch_bank(iobase, 2);		/* Set XCRVOFF */	outb(IR_TRANS_CTRL_XCVROFF, iobase+IR_TRANS_CTRL);		/* Wait a minimum of 2 us */	udelay(2);		/* Clear XCRVOFF */	outb(0, iobase+IR_TRANS_CTRL);}/* * Function ircard_change_speed (idev, baud) * *    Change the speed of the device * */static void ircard_change_speed(struct irda_device *idev, __u32 speed){	struct ircard_cb *self;	unsigned long flags;	int iobase; 		DEBUG(4, __FUNCTION__ "()\n");	ASSERT(idev != NULL, return;);	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);	self = (struct ircard_cb *) idev->priv;	iobase = idev->io.iobase;	/* Serialize access to the driver */	spin_lock_irqsave(&idev->lock, flags);	/* Check if we are switching from FIR to SIR, or SIR to FIR */	if ((idev->io.baudrate > 1152000) && (speed < 4000000))		ircard_fir_to_sir(idev, iobase);	else if ((idev->io.baudrate < 4000000) && (speed > 1152000))		ircard_sir_to_fir(idev, iobase);	     	/* Update accounting for new speed */	idev->io.baudrate = speed;		if (speed > 115200)		/* Install the FIR transmit handler */		idev->netdev.hard_start_xmit = &ircard_hard_xmit;	else				/* Install the SIR transmit handler */		idev->netdev.hard_start_xmit = &irport_hard_xmit;		/* Reset hardware */	__switch_bank(iobase, 0);	outb_p(0x07, iobase+7);		/* Switch to bank2 and disable interrupts */	__switch_bank(iobase, 2);		switch (speed) {	default:		DEBUG(4, __FUNCTION__ "(), unknown baud rate of %d\n", speed);		/* FALLTHROUGH */	case 9600:   /* FALLTHROUGH */	case 19200:  /* FALLTHROUGH */	case 37600:  /* FALLTHROUGH */	case 57600:  /* FALLTHROUGH */	case 115200: 		irport_start(idev, idev->io.iobase2);		irport_change_speed(idev, speed);		outb_p(IR_CONF_SIR, iobase+IR_CONF);		break;	case 576000:				DEBUG(0, __FUNCTION__ "(), handling baud of 576000\n");		irport_stop(idev, idev->io.iobase2);		outb_p(IR_CONF_MIR_576|IR_CONF_MIR, iobase+IR_CONF);		break;	case 1152000:		DEBUG(0, __FUNCTION__ "(), handling baud of 1152000\n");		irport_stop(idev, idev->io.iobase2);		outb_p(IR_CONF_MIR_1152|IR_CONF_MIR, iobase+IR_CONF);		break;	case 4000000:		DEBUG(4, __FUNCTION__ "(), handling baud of 4000000\n");		irport_stop(idev, idev->io.iobase2);		outb_p(IR_CONF_FIR, iobase+IR_CONF);		break;	}	idev->netdev.tbusy = 0;		/* Enable some interrupts so we can receive frames */	if (speed > 115200) { 		/* Enable receiving, interrupts and go to bank 0*/ 		ircard_receive(idev, iobase); 	}	spin_unlock_irqrestore(&idev->lock, flags);}/* * Function ircard_hard_xmit (skb, dev) * *    Transmit the frame! * */static int ircard_hard_xmit(struct sk_buff *skb, struct net_device *dev){	struct irda_device *idev;	struct ircard_cb *self;	unsigned long flags;	int mtt, diff;	idev = (struct irda_device *) dev->priv;		self = idev->priv;	/* Lock transmit buffer */	if (irda_lock((void *) &dev->tbusy) == FALSE)		return -EBUSY;	/* Serialize access to the driver */	spin_lock_irqsave(&idev->lock, flags);	switch_bank(idev->io.iobase, 0);	/* Register this frame */	self->tx_q.queue[self->tx_q.free].start = self->tx_q.tail;	self->tx_q.queue[self->tx_q.free].len = skb->len;	self->tx_q.tail += skb->len;	/* Copy data to transmit part of shared memory */	copy_to_pc((void *) self->tx_q.queue[self->tx_q.free].start, skb->data, 		   skb->len);	self->tx_q.len++;	self->tx_q.free++;	/* Start transmit only if there is currently no transmit going on */	if (self->tx_q.len == 1) {		/* Extract minimum turnaround time from skb */		mtt = irda_get_mtt(skb);				/* Currently we just use udelay for the MTT delay */		if (mtt) {			/* Check how much time we have used already */			do_gettimeofday(&self->now);			diff = self->now.tv_usec - self->stamp.tv_usec;			if (diff < 0) 				diff += 1000000;						/* Check if the mtt is larger than the time we have			 * already used by all the protocol processing			 */			if (mtt > diff) {				DEBUG(4, __FUNCTION__ "(), delay=%d us\n", 				      mtt-diff);				/* Currently we just use udelay for the MTT                                 * delay 				 */				udelay(mtt-diff);			}		}			ircard_dma_write(idev, idev->io.iobase);	}	spin_unlock_irqrestore(&idev->lock, flags);	dev_kfree_skb(skb);	/* Not busy transmitting anymore */	dev->tbusy = 0;	/* Return success */	return 0;}/* * Function ircard_dma_write (idev, iobase) * *    Transmit data using shared memory (local DMA) * */static void ircard_dma_write(struct irda_device *idev, int iobase){	struct ircard_cb *self;	int addr;	int ptr;	self = idev->priv;	ptr = self->tx_q.ptr;	/* Disable Rx DMA */	switch_bank(iobase, 0); 	outb(0, iobase+MISC);   /* (bank 0) */	idev->io.direction = IO_XMIT;	ircard_set_mcr(iobase, MCR_INTR_EN|MCR_TX_EN, 0);	/* Set early EOM level */	outb(TX_CTRL2_EN_TX_CRC|TX_CTRL2_EARLY_EOM_16, iobase+TX_CTRL2);	/* Load the byte count to the transmit byte count register */	switch_bank(iobase, 1);	outb(self->tx_q.queue[ptr].len & 0xff, iobase+TX_BYTE_COUNT_LOW);	outb((self->tx_q.queue[ptr].len >> 8) & 0x1f, 	     iobase+TX_BYTE_COUNT_HIGH);	/* Set the Tx DMA start address (0 for the first transfer) */	switch_bank(iobase, 3);	outb(0, iobase+SH_MEM_PAGE);         /* (bank 3) */	/* Compute chip address */	addr = self->tx_q.queue[ptr].start - idev->io.membase;	outb(addr & 0xff, iobase+TX_DMA_ADDR_LOW);        /* (bank 3) */	outb(PAGE_DISABLE|((addr >> 8) & 0x3f), iobase+TX_DMA_ADDR_HIGH);	/* Set host DMA controller for transmission */	switch_bank(iobase, 0); 	outb(0xc0, iobase+MISC);   /* (bank 0) */	/*udelay(6);*/	/* Set RTS */	switch_bank(iobase, 0);	outb(TX_CTRL1_RTS|TX_CTRL1_UNDR_EOM_I/*|TX_CTRL1_AUTO_RST_EOM*/, 	     iobase+TX_CTRL1); /* (bank 0) */}/* * Function ircard_xmit_complete (idev, underrun) * *    Finished transmitting a frame

⌨️ 快捷键说明

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