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