nsc-ircc.c

来自「linux 内核源代码」· C语言 代码 · 共 2,406 行 · 第 1/4 页

C
2,406
字号
				self->stats.rx_fifo_errors++;		} else {			/*  			 * First we must make sure that the frame we			 * want to deliver is all in main memory. If we			 * cannot tell, then we check if the Rx FIFO is			 * empty. If not then we will have to take a nap			 * and try again later.  			 */			if (st_fifo->pending_bytes < self->io.fifo_size) {				switch_bank(iobase, BANK0);				if (inb(iobase+LSR) & LSR_RXDA) {					/* Put this entry back in fifo */					st_fifo->head--;					st_fifo->len++;					st_fifo->pending_bytes += len;					st_fifo->entries[st_fifo->head].status = status;					st_fifo->entries[st_fifo->head].len = len;					/*  					 * DMA not finished yet, so try again 					 * later, set timer value, resolution 					 * 125 us 					 */					switch_bank(iobase, BANK4);					outb(0x02, iobase+TMRL); /* x 125 us */					outb(0x00, iobase+TMRH);					/* Start timer */					outb(IRCR1_TMR_EN, iobase+IRCR1);					/* Restore bank register */					outb(bank, iobase+BSR);										return FALSE; /* I'll be back! */				}			}			/* 			 * Remember the time we received this frame, so we can			 * reduce the min turn time a bit since we will know			 * how much time we have used for protocol processing			 */			do_gettimeofday(&self->stamp);			skb = dev_alloc_skb(len+1);			if (skb == NULL)  {				IRDA_WARNING("%s(), memory squeeze, "					     "dropping frame.\n",					     __FUNCTION__);				self->stats.rx_dropped++;				/* Restore bank register */				outb(bank, iobase+BSR);				return FALSE;			}						/* Make sure IP header gets aligned */			skb_reserve(skb, 1); 			/* Copy frame without CRC */			if (self->io.speed < 4000000) {				skb_put(skb, len-2);				skb_copy_to_linear_data(skb,							self->rx_buff.data,							len - 2);			} else {				skb_put(skb, len-4);				skb_copy_to_linear_data(skb,							self->rx_buff.data,							len - 4);			}			/* Move to next frame */			self->rx_buff.data += len;			self->stats.rx_bytes += len;			self->stats.rx_packets++;			skb->dev = self->netdev;			skb_reset_mac_header(skb);			skb->protocol = htons(ETH_P_IRDA);			netif_rx(skb);			self->netdev->last_rx = jiffies;		}	}	/* Restore bank register */	outb(bank, iobase+BSR);	return TRUE;}/* * Function nsc_ircc_pio_receive (self) * *    Receive all data in receiver FIFO * */static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self) {	__u8 byte;	int iobase;	iobase = self->io.fir_base;		/*  Receive all characters in Rx FIFO */	do {		byte = inb(iobase+RXD);		async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, 				  byte);	} while (inb(iobase+LSR) & LSR_RXDA); /* Data available */	}/* * Function nsc_ircc_sir_interrupt (self, eir) * *    Handle SIR interrupt * */static void nsc_ircc_sir_interrupt(struct nsc_ircc_cb *self, int eir){	int actual;	/* Check if transmit FIFO is low on data */	if (eir & EIR_TXLDL_EV) {		/* Write data left in transmit buffer */		actual = nsc_ircc_pio_write(self->io.fir_base, 					   self->tx_buff.data, 					   self->tx_buff.len, 					   self->io.fifo_size);		self->tx_buff.data += actual;		self->tx_buff.len  -= actual;				self->io.direction = IO_XMIT;		/* Check if finished */		if (self->tx_buff.len > 0)			self->ier = IER_TXLDL_IE;		else { 			self->stats.tx_packets++;			netif_wake_queue(self->netdev);			self->ier = IER_TXEMP_IE;		}				}	/* Check if transmission has completed */	if (eir & EIR_TXEMP_EV) {		/* Turn around and get ready to receive some data */		self->io.direction = IO_RECV;		self->ier = IER_RXHDL_IE;		/* Check if we need to change the speed?		 * Need to be after self->io.direction to avoid race with		 * nsc_ircc_hard_xmit_sir() - Jean II */		if (self->new_speed) {			IRDA_DEBUG(2, "%s(), Changing speed!\n", __FUNCTION__);			self->ier = nsc_ircc_change_speed(self,							  self->new_speed);			self->new_speed = 0;			netif_wake_queue(self->netdev);			/* Check if we are going to FIR */			if (self->io.speed > 115200) {				/* No need to do anymore SIR stuff */				return;			}		}	}	/* Rx FIFO threshold or timeout */	if (eir & EIR_RXHDL_EV) {		nsc_ircc_pio_receive(self);		/* Keep receiving */		self->ier = IER_RXHDL_IE;	}}/* * Function nsc_ircc_fir_interrupt (self, eir) * *    Handle MIR/FIR interrupt * */static void nsc_ircc_fir_interrupt(struct nsc_ircc_cb *self, int iobase, 				   int eir){	__u8 bank;	bank = inb(iobase+BSR);		/* Status FIFO event*/	if (eir & EIR_SFIF_EV) {		/* Check if DMA has finished */		if (nsc_ircc_dma_receive_complete(self, iobase)) {			/* Wait for next status FIFO interrupt */			self->ier = IER_SFIF_IE;		} else {			self->ier = IER_SFIF_IE | IER_TMR_IE;		}	} else if (eir & EIR_TMR_EV) { /* Timer finished */		/* Disable timer */		switch_bank(iobase, BANK4);		outb(0, iobase+IRCR1);		/* Clear timer event */		switch_bank(iobase, BANK0);		outb(ASCR_CTE, iobase+ASCR);		/* Check if this is a Tx timer interrupt */		if (self->io.direction == IO_XMIT) {			nsc_ircc_dma_xmit(self, iobase);			/* Interrupt on DMA */			self->ier = IER_DMA_IE;		} else {			/* Check (again) if DMA has finished */			if (nsc_ircc_dma_receive_complete(self, iobase)) {				self->ier = IER_SFIF_IE;			} else {				self->ier = IER_SFIF_IE | IER_TMR_IE;			}		}	} else if (eir & EIR_DMA_EV) {		/* Finished with all transmissions? */		if (nsc_ircc_dma_xmit_complete(self)) {			if(self->new_speed != 0) {				/* As we stop the Tx queue, the speed change				 * need to be done when the Tx fifo is				 * empty. Ask for a Tx done interrupt */				self->ier = IER_TXEMP_IE;			} else {				/* Check if there are more frames to be				 * transmitted */				if (irda_device_txqueue_empty(self->netdev)) {					/* Prepare for receive */					nsc_ircc_dma_receive(self);					self->ier = IER_SFIF_IE;				} else					IRDA_WARNING("%s(), potential "						     "Tx queue lockup !\n",						     __FUNCTION__);			}		} else {			/*  Not finished yet, so interrupt on DMA again */			self->ier = IER_DMA_IE;		}	} else if (eir & EIR_TXEMP_EV) {		/* The Tx FIFO has totally drained out, so now we can change		 * the speed... - Jean II */		self->ier = nsc_ircc_change_speed(self, self->new_speed);		self->new_speed = 0;		netif_wake_queue(self->netdev);		/* Note : nsc_ircc_change_speed() restarted Rx fifo */	}	outb(bank, iobase+BSR);}/* * Function nsc_ircc_interrupt (irq, dev_id, regs) * *    An interrupt from the chip has arrived. Time to do some work * */static irqreturn_t nsc_ircc_interrupt(int irq, void *dev_id){	struct net_device *dev = dev_id;	struct nsc_ircc_cb *self;	__u8 bsr, eir;	int iobase;	self = dev->priv;	spin_lock(&self->lock);		iobase = self->io.fir_base;	bsr = inb(iobase+BSR); 	/* Save current bank */	switch_bank(iobase, BANK0);		self->ier = inb(iobase+IER); 	eir = inb(iobase+EIR) & self->ier; /* Mask out the interesting ones */ 	outb(0, iobase+IER); /* Disable interrupts */		if (eir) {		/* Dispatch interrupt handler for the current speed */		if (self->io.speed > 115200)			nsc_ircc_fir_interrupt(self, iobase, eir);		else			nsc_ircc_sir_interrupt(self, eir);	}		outb(self->ier, iobase+IER); /* Restore interrupts */	outb(bsr, iobase+BSR);       /* Restore bank register */	spin_unlock(&self->lock);	return IRQ_RETVAL(eir);}/* * Function nsc_ircc_is_receiving (self) * *    Return TRUE is we are currently receiving a frame * */static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self){	unsigned long flags;	int status = FALSE;	int iobase;	__u8 bank;	IRDA_ASSERT(self != NULL, return FALSE;);	spin_lock_irqsave(&self->lock, flags);	if (self->io.speed > 115200) {		iobase = self->io.fir_base;		/* Check if rx FIFO is not empty */		bank = inb(iobase+BSR);		switch_bank(iobase, BANK2);		if ((inb(iobase+RXFLV) & 0x3f) != 0) {			/* We are receiving something */			status =  TRUE;		}		outb(bank, iobase+BSR);	} else 		status = (self->rx_buff.state != OUTSIDE_FRAME);		spin_unlock_irqrestore(&self->lock, flags);	return status;}/* * Function nsc_ircc_net_open (dev) * *    Start the device * */static int nsc_ircc_net_open(struct net_device *dev){	struct nsc_ircc_cb *self;	int iobase;	char hwname[32];	__u8 bank;		IRDA_DEBUG(4, "%s()\n", __FUNCTION__);		IRDA_ASSERT(dev != NULL, return -1;);	self = (struct nsc_ircc_cb *) dev->priv;		IRDA_ASSERT(self != NULL, return 0;);		iobase = self->io.fir_base;		if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, dev->name, dev)) {		IRDA_WARNING("%s, unable to allocate irq=%d\n",			     driver_name, self->io.irq);		return -EAGAIN;	}	/*	 * Always allocate the DMA channel after the IRQ, and clean up on 	 * failure.	 */	if (request_dma(self->io.dma, dev->name)) {		IRDA_WARNING("%s, unable to allocate dma=%d\n",			     driver_name, self->io.dma);		free_irq(self->io.irq, dev);		return -EAGAIN;	}		/* Save current bank */	bank = inb(iobase+BSR);		/* turn on interrupts */	switch_bank(iobase, BANK0);	outb(IER_LS_IE | IER_RXHDL_IE, iobase+IER);	/* Restore bank register */	outb(bank, iobase+BSR);	/* Ready to play! */	netif_start_queue(dev);		/* Give self a hardware name */	sprintf(hwname, "NSC-FIR @ 0x%03x", self->io.fir_base);	/* 	 * Open new IrLAP layer instance, now that everything should be	 * initialized properly 	 */	self->irlap = irlap_open(dev, &self->qos, hwname);	return 0;}/* * Function nsc_ircc_net_close (dev) * *    Stop the device * */static int nsc_ircc_net_close(struct net_device *dev){	struct nsc_ircc_cb *self;	int iobase;	__u8 bank;	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);		IRDA_ASSERT(dev != NULL, return -1;);	self = (struct nsc_ircc_cb *) dev->priv;	IRDA_ASSERT(self != NULL, return 0;);	/* Stop device */	netif_stop_queue(dev);		/* Stop and remove instance of IrLAP */	if (self->irlap)		irlap_close(self->irlap);	self->irlap = NULL;		iobase = self->io.fir_base;	disable_dma(self->io.dma);	/* Save current bank */	bank = inb(iobase+BSR);	/* Disable interrupts */	switch_bank(iobase, BANK0);	outb(0, iobase+IER);        	free_irq(self->io.irq, dev);	free_dma(self->io.dma);	/* Restore bank register */	outb(bank, iobase+BSR);	return 0;}/* * Function nsc_ircc_net_ioctl (dev, rq, cmd) * *    Process IOCTL commands for this device * */static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){	struct if_irda_req *irq = (struct if_irda_req *) rq;	struct nsc_ircc_cb *self;	unsigned long flags;	int ret = 0;	IRDA_ASSERT(dev != NULL, return -1;);	self = dev->priv;	IRDA_ASSERT(self != NULL, return -1;);	IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd);		switch (cmd) {	case SIOCSBANDWIDTH: /* Set bandwidth */		if (!capable(CAP_NET_ADMIN)) {			ret = -EPERM;			break;		}		spin_lock_irqsave(&self->lock, flags);		nsc_ircc_change_speed(self, irq->ifr_baudrate);		spin_unlock_irqrestore(&self->lock, flags);		break;	case SIOCSMEDIABUSY: /* Set media busy */		if (!capable(CAP_NET_ADMIN)) {			ret = -EPERM;			break;		}		irda_device_set_media_busy(self->netdev, TRUE);		break;	case SIOCGRECEIVING: /* Check if we are receiving right now */		/* This is already protected */		irq->ifr_receiving = nsc_ircc_is_receiving(self);		break;	default:		ret = -EOPNOTSUPP;	}	return ret;}static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev){	struct nsc_ircc_cb *self = (struct nsc_ircc_cb *) dev->priv;		return &self->stats;}static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state){     	struct nsc_ircc_cb *self = platform_get_drvdata(dev); 	int bank;	unsigned long flags; 	int iobase = self->io.fir_base;	if (self->io.suspended)		return 0;	IRDA_DEBUG(1, "%s, Suspending\n", driver_name);	rtnl_lock();	if (netif_running(self->netdev)) {		netif_device_detach(self->netdev);		spin_lock_irqsave(&self->lock, flags);		/* Save current bank */		bank = inb(iobase+BSR);		/* Disable interrupts */		switch_bank(iobase, BANK0);		outb(0, iobase+IER);		/* Restore bank register */		outb(bank, iobase+BSR);		spin_unlock_irqrestore(&self->lock, flags);		free_irq(self->io.irq, self->netdev);		disable_dma(self->io.dma);	}	self->io.suspended = 1;	rtnl_unlock();	return 0;}static int nsc_ircc_resume(struct platform_device *dev){ 	struct nsc_ircc_cb *self = platform_get_drvdata(dev); 	unsigned long flags;	if (!self->io.suspended)		return 0;	IRDA_DEBUG(1, "%s, Waking up\n", driver_name);	rtnl_lock();	nsc_ircc_setup(&self->io);	nsc_ircc_init_dongle_interface(self->io.fir_base, self->io.dongle_id);	if (netif_running(self->netdev)) {		if (request_irq(self->io.irq, nsc_ircc_interrupt, 0,				self->netdev->name, self->netdev)) { 		    	IRDA_WARNING("%s, unable to allocate irq=%d\n",				     driver_name, self->io.irq);			/*			 * Don't fail resume process, just kill this			 * network interface			 */			unregister_netdevice(self->netdev);		} else {			spin_lock_irqsave(&self->lock, flags);			nsc_ircc_change_speed(self, self->io.speed);			spin_unlock_irqrestore(&self->lock, flags);			netif_device_attach(self->netdev);		}	} else {		spin_lock_irqsave(&self->lock, flags);		nsc_ircc_change_speed(self, 9600);		spin_unlock_irqrestore(&self->lock, flags);	}	self->io.suspended = 0;	rtnl_unlock(); 	return 0;}MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");MODULE_DESCRIPTION("NSC IrDA Device Driver");MODULE_LICENSE("GPL");module_param(qos_mtt_bits, int, 0);MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");module_param_array(io, int, NULL, 0);MODULE_PARM_DESC(io, "Base I/O addresses");module_param_array(irq, int, NULL, 0);MODULE_PARM_DESC(irq, "IRQ lines");module_param_array(dma, int, NULL, 0);MODULE_PARM_DESC(dma, "DMA channels");module_param(dongle_id, int, 0);MODULE_PARM_DESC(dongle_id, "Type-id of used dongle");module_init(nsc_ircc_init);module_exit(nsc_ircc_cleanup);

⌨️ 快捷键说明

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