nsc-ircc.c

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

C
2,406
字号
	case 0x0C: /* same as */	case 0x0D: /* HP HSDL-1100/HSDL-2100 */		break;	case 0x0E: /* Supports SIR Mode only */		break;	case 0x0F: /* No dongle connected */		IRDA_DEBUG(0, "%s(), %s is not for IrDA mode\n",			   __FUNCTION__, dongle_types[dongle_id]);		switch_bank(iobase, BANK0); 		outb(0x62, iobase+MCR);		break;	default: 		IRDA_DEBUG(0, "%s(), invalid data_rate\n", __FUNCTION__);	}	/* Restore bank register */	outb(bank, iobase+BSR);}/* * Function nsc_ircc_change_speed (self, baud) * *    Change the speed of the device * * This function *must* be called with irq off and spin-lock. */static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed){	struct net_device *dev = self->netdev;	__u8 mcr = MCR_SIR;	int iobase; 	__u8 bank;	__u8 ier;                  /* Interrupt enable register */	IRDA_DEBUG(2, "%s(), speed=%d\n", __FUNCTION__, speed);	IRDA_ASSERT(self != NULL, return 0;);	iobase = self->io.fir_base;	/* Update accounting for new speed */	self->io.speed = speed;	/* Save current bank */	bank = inb(iobase+BSR);	/* Disable interrupts */	switch_bank(iobase, BANK0);	outb(0, iobase+IER);	/* Select Bank 2 */	switch_bank(iobase, BANK2);	outb(0x00, iobase+BGDH);	switch (speed) {	case 9600:   outb(0x0c, iobase+BGDL); break;	case 19200:  outb(0x06, iobase+BGDL); break;	case 38400:  outb(0x03, iobase+BGDL); break;	case 57600:  outb(0x02, iobase+BGDL); break;	case 115200: outb(0x01, iobase+BGDL); break;	case 576000:		switch_bank(iobase, BANK5);				/* IRCR2: MDRS is set */		outb(inb(iobase+4) | 0x04, iobase+4);	       		mcr = MCR_MIR;		IRDA_DEBUG(0, "%s(), handling baud of 576000\n", __FUNCTION__);		break;	case 1152000:		mcr = MCR_MIR;		IRDA_DEBUG(0, "%s(), handling baud of 1152000\n", __FUNCTION__);		break;	case 4000000:		mcr = MCR_FIR;		IRDA_DEBUG(0, "%s(), handling baud of 4000000\n", __FUNCTION__);		break;	default:		mcr = MCR_FIR;		IRDA_DEBUG(0, "%s(), unknown baud rate of %d\n", 			   __FUNCTION__, speed);		break;	}	/* Set appropriate speed mode */	switch_bank(iobase, BANK0);	outb(mcr | MCR_TX_DFR, iobase+MCR);	/* Give some hits to the transceiver */	nsc_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id);	/* Set FIFO threshold to TX17, RX16 */	switch_bank(iobase, BANK0);	outb(0x00, iobase+FCR);	outb(FCR_FIFO_EN, iobase+FCR);	outb(FCR_RXTH|     /* Set Rx FIFO threshold */	     FCR_TXTH|     /* Set Tx FIFO threshold */	     FCR_TXSR|     /* Reset Tx FIFO */	     FCR_RXSR|     /* Reset Rx FIFO */	     FCR_FIFO_EN,  /* Enable FIFOs */	     iobase+FCR);		/* Set FIFO size to 32 */	switch_bank(iobase, BANK2);	outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2);		/* Enable some interrupts so we can receive frames */	switch_bank(iobase, BANK0); 	if (speed > 115200) {		/* Install FIR xmit handler */		dev->hard_start_xmit = nsc_ircc_hard_xmit_fir;		ier = IER_SFIF_IE;		nsc_ircc_dma_receive(self);	} else {		/* Install SIR xmit handler */		dev->hard_start_xmit = nsc_ircc_hard_xmit_sir;		ier = IER_RXHDL_IE;	}	/* Set our current interrupt mask */	outb(ier, iobase+IER);    		/* Restore BSR */	outb(bank, iobase+BSR);	/* Make sure interrupt handlers keep the proper interrupt mask */	return(ier);}/* * Function nsc_ircc_hard_xmit (skb, dev) * *    Transmit the frame! * */static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev){	struct nsc_ircc_cb *self;	unsigned long flags;	int iobase;	__s32 speed;	__u8 bank;		self = (struct nsc_ircc_cb *) dev->priv;	IRDA_ASSERT(self != NULL, return 0;);	iobase = self->io.fir_base;	netif_stop_queue(dev);			/* Make sure tests *& speed change are atomic */	spin_lock_irqsave(&self->lock, flags);		/* Check if we need to change the speed */	speed = irda_get_next_speed(skb);	if ((speed != self->io.speed) && (speed != -1)) {		/* Check for empty frame. */		if (!skb->len) {			/* If we just sent a frame, we get called before			 * the last bytes get out (because of the SIR FIFO).			 * If this is the case, let interrupt handler change			 * the speed itself... Jean II */			if (self->io.direction == IO_RECV) {				nsc_ircc_change_speed(self, speed); 				/* TODO : For SIR->SIR, the next packet				 * may get corrupted - Jean II */				netif_wake_queue(dev);			} else {				self->new_speed = speed;				/* Queue will be restarted after speed change				 * to make sure packets gets through the				 * proper xmit handler - Jean II */			}			dev->trans_start = jiffies;			spin_unlock_irqrestore(&self->lock, flags);			dev_kfree_skb(skb);			return 0;		} else			self->new_speed = speed;	}	/* Save current bank */	bank = inb(iobase+BSR);		self->tx_buff.data = self->tx_buff.head;		self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, 					   self->tx_buff.truesize);	self->stats.tx_bytes += self->tx_buff.len;		/* Add interrupt on tx low level (will fire immediately) */	switch_bank(iobase, BANK0);	outb(IER_TXLDL_IE, iobase+IER);		/* Restore bank register */	outb(bank, iobase+BSR);	dev->trans_start = jiffies;	spin_unlock_irqrestore(&self->lock, flags);	dev_kfree_skb(skb);	return 0;}static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev){	struct nsc_ircc_cb *self;	unsigned long flags;	int iobase;	__s32 speed;	__u8 bank;	int mtt, diff;		self = (struct nsc_ircc_cb *) dev->priv;	iobase = self->io.fir_base;	netif_stop_queue(dev);		/* Make sure tests *& speed change are atomic */	spin_lock_irqsave(&self->lock, flags);	/* Check if we need to change the speed */	speed = irda_get_next_speed(skb);	if ((speed != self->io.speed) && (speed != -1)) {		/* Check for empty frame. */		if (!skb->len) {			/* If we are currently transmitting, defer to			 * interrupt handler. - Jean II */			if(self->tx_fifo.len == 0) {				nsc_ircc_change_speed(self, speed); 				netif_wake_queue(dev);			} else {				self->new_speed = speed;				/* Keep queue stopped :				 * the speed change operation may change the				 * xmit handler, and we want to make sure				 * the next packet get through the proper				 * Tx path, so block the Tx queue until				 * the speed change has been done.				 * Jean II */			}			dev->trans_start = jiffies;			spin_unlock_irqrestore(&self->lock, flags);			dev_kfree_skb(skb);			return 0;		} else {			/* Change speed after current frame */			self->new_speed = speed;		}	}	/* Save current bank */	bank = inb(iobase+BSR);	/* Register and copy this frame to DMA memory */	self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail;	self->tx_fifo.queue[self->tx_fifo.free].len = skb->len;	self->tx_fifo.tail += skb->len;	self->stats.tx_bytes += skb->len;	skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start,		      skb->len);	self->tx_fifo.len++;	self->tx_fifo.free++;	/* Start transmit only if there is currently no transmit going on */	if (self->tx_fifo.len == 1) {		/* Check if we must wait the min turn time or not */		mtt = irda_get_mtt(skb);		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) {				mtt -= diff;				/* 				 * Use timer if delay larger than 125 us, and				 * use udelay for smaller values which should				 * be acceptable				 */				if (mtt > 125) {					/* Adjust for timer resolution */					mtt = mtt / 125;										/* Setup timer */					switch_bank(iobase, BANK4);					outb(mtt & 0xff, iobase+TMRL);					outb((mtt >> 8) & 0x0f, iobase+TMRH);										/* Start timer */					outb(IRCR1_TMR_EN, iobase+IRCR1);					self->io.direction = IO_XMIT;										/* Enable timer interrupt */					switch_bank(iobase, BANK0);					outb(IER_TMR_IE, iobase+IER);										/* Timer will take care of the rest */					goto out; 				} else					udelay(mtt);			}		}				/* Enable DMA interrupt */		switch_bank(iobase, BANK0);		outb(IER_DMA_IE, iobase+IER);		/* Transmit frame */		nsc_ircc_dma_xmit(self, iobase);	} out:	/* Not busy transmitting anymore if window is not full,	 * and if we don't need to change speed */	if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0))		netif_wake_queue(self->netdev);	/* Restore bank register */	outb(bank, iobase+BSR);	dev->trans_start = jiffies;	spin_unlock_irqrestore(&self->lock, flags);	dev_kfree_skb(skb);	return 0;}/* * Function nsc_ircc_dma_xmit (self, iobase) * *    Transmit data using DMA * */static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase){	int bsr;	/* Save current bank */	bsr = inb(iobase+BSR);	/* Disable DMA */	switch_bank(iobase, BANK0);	outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);		self->io.direction = IO_XMIT;		/* Choose transmit DMA channel  */ 	switch_bank(iobase, BANK2);	outb(ECR1_DMASWP|ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1);		irda_setup_dma(self->io.dma, 		       ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start -			self->tx_buff.head) + self->tx_buff_dma,		       self->tx_fifo.queue[self->tx_fifo.ptr].len, 		       DMA_TX_MODE);	/* Enable DMA and SIR interaction pulse */ 	switch_bank(iobase, BANK0);		outb(inb(iobase+MCR)|MCR_TX_DFR|MCR_DMA_EN|MCR_IR_PLS, iobase+MCR);	/* Restore bank register */	outb(bsr, iobase+BSR);}/* * Function nsc_ircc_pio_xmit (self, iobase) * *    Transmit data using PIO. Returns the number of bytes that actually *    got transferred * */static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size){	int actual = 0;	__u8 bank;		IRDA_DEBUG(4, "%s()\n", __FUNCTION__);	/* Save current bank */	bank = inb(iobase+BSR);	switch_bank(iobase, BANK0);	if (!(inb_p(iobase+LSR) & LSR_TXEMP)) {		IRDA_DEBUG(4, "%s(), warning, FIFO not empty yet!\n",			   __FUNCTION__);		/* FIFO may still be filled to the Tx interrupt threshold */		fifo_size -= 17;	}	/* Fill FIFO with current frame */	while ((fifo_size-- > 0) && (actual < len)) {		/* Transmit next byte */		outb(buf[actual++], iobase+TXD);	}        	IRDA_DEBUG(4, "%s(), fifo_size %d ; %d sent of %d\n", 		   __FUNCTION__, fifo_size, actual, len);		/* Restore bank */	outb(bank, iobase+BSR);	return actual;}/* * Function nsc_ircc_dma_xmit_complete (self) * *    The transfer of a frame in finished. This function will only be called  *    by the interrupt handler * */static int nsc_ircc_dma_xmit_complete(struct nsc_ircc_cb *self){	int iobase;	__u8 bank;	int ret = TRUE;	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);	iobase = self->io.fir_base;	/* Save current bank */	bank = inb(iobase+BSR);	/* Disable DMA */	switch_bank(iobase, BANK0);        outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);		/* Check for underrrun! */	if (inb(iobase+ASCR) & ASCR_TXUR) {		self->stats.tx_errors++;		self->stats.tx_fifo_errors++;				/* Clear bit, by writing 1 into it */		outb(ASCR_TXUR, iobase+ASCR);	} else {		self->stats.tx_packets++;	}	/* Finished with this frame, so prepare for next */	self->tx_fifo.ptr++;	self->tx_fifo.len--;	/* Any frames to be sent back-to-back? */	if (self->tx_fifo.len) {		nsc_ircc_dma_xmit(self, iobase);				/* Not finished yet! */		ret = FALSE;	} else {		/* Reset Tx FIFO info */		self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;		self->tx_fifo.tail = self->tx_buff.head;	}	/* Make sure we have room for more frames and	 * that we don't need to change speed */	if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0)) {		/* Not busy transmitting anymore */		/* Tell the network layer, that we can accept more frames */		netif_wake_queue(self->netdev);	}	/* Restore bank */	outb(bank, iobase+BSR);		return ret;}/* * Function nsc_ircc_dma_receive (self) * *    Get ready for receiving a frame. The device will initiate a DMA *    if it starts to receive a frame. * */static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self) {	int iobase;	__u8 bsr;	iobase = self->io.fir_base;	/* Reset Tx FIFO info */	self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;	self->tx_fifo.tail = self->tx_buff.head;	/* Save current bank */	bsr = inb(iobase+BSR);	/* Disable DMA */	switch_bank(iobase, BANK0);	outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);	/* Choose DMA Rx, DMA Fairness, and Advanced mode */	switch_bank(iobase, BANK2);	outb(ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1);	self->io.direction = IO_RECV;	self->rx_buff.data = self->rx_buff.head;		/* Reset Rx FIFO. This will also flush the ST_FIFO */	switch_bank(iobase, BANK0);	outb(FCR_RXSR|FCR_FIFO_EN, iobase+FCR);	self->st_fifo.len = self->st_fifo.pending_bytes = 0;	self->st_fifo.tail = self->st_fifo.head = 0;		irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize,		       DMA_RX_MODE);	/* Enable DMA */	switch_bank(iobase, BANK0);	outb(inb(iobase+MCR)|MCR_DMA_EN, iobase+MCR);	/* Restore bank register */	outb(bsr, iobase+BSR);		return 0;}/* * Function nsc_ircc_dma_receive_complete (self) * *    Finished with receiving frames * *     */static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase){	struct st_fifo *st_fifo;	struct sk_buff *skb;	__u8 status;	__u8 bank;	int len;	st_fifo = &self->st_fifo;	/* Save current bank */	bank = inb(iobase+BSR);		/* Read all entries in status FIFO */	switch_bank(iobase, BANK5);	while ((status = inb(iobase+FRM_ST)) & FRM_ST_VLD) {		/* We must empty the status FIFO no matter what */		len = inb(iobase+RFLFL) | ((inb(iobase+RFLFH) & 0x1f) << 8);		if (st_fifo->tail >= MAX_RX_WINDOW) {			IRDA_DEBUG(0, "%s(), window is full!\n", __FUNCTION__);			continue;		}					st_fifo->entries[st_fifo->tail].status = status;		st_fifo->entries[st_fifo->tail].len = len;		st_fifo->pending_bytes += len;		st_fifo->tail++;		st_fifo->len++;	}	/* Try to process all entries in status FIFO */	while (st_fifo->len > 0) {		/* Get first entry */		status = st_fifo->entries[st_fifo->head].status;		len    = st_fifo->entries[st_fifo->head].len;		st_fifo->pending_bytes -= len;		st_fifo->head++;		st_fifo->len--;		/* Check for errors */		if (status & FRM_ST_ERR_MSK) {			if (status & FRM_ST_LOST_FR) {				/* Add number of lost frames to stats */				self->stats.rx_errors += len;				} else {				/* Skip frame */				self->stats.rx_errors++;								self->rx_buff.data += len;							if (status & FRM_ST_MAX_LEN)					self->stats.rx_length_errors++;								if (status & FRM_ST_PHY_ERR) 					self->stats.rx_frame_errors++;								if (status & FRM_ST_BAD_CRC) 					self->stats.rx_crc_errors++;			}			/* The errors below can be reported in both cases */			if (status & FRM_ST_OVR1)				self->stats.rx_fifo_errors++;		       						if (status & FRM_ST_OVR2)

⌨️ 快捷键说明

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