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 + -
显示快捷键?