3c527.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,676 行 · 第 1/3 页
C
1,676 行
/** * mc32_rx_ring - process the receive ring * @dev: 3c527 that needs its receive ring processing * * * We have received one or more indications from the card that a * receive has completed. The buffer ring thus contains dirty * entries. We walk the ring by iterating over the circular rx_ring * array, starting at the next dirty buffer (which happens to be the * one we finished up at last time around). * * For each completed packet, we will either copy it and pass it up * the stack or, if the packet is near MTU sized, we allocate * another buffer and flip the old one up the stack. * * We must succeed in keeping a buffer on the ring. If necessary we * will toss a received packet rather than lose a ring entry. Once * the first uncompleted descriptor is found, we move the * End-Of-List bit to include the buffers just processed. * */static void mc32_rx_ring(struct net_device *dev){ struct mc32_local *lp = netdev_priv(dev); volatile struct skb_header *p; u16 rx_ring_tail; u16 rx_old_tail; int x=0; rx_old_tail = rx_ring_tail = lp->rx_ring_tail; do { p=lp->rx_ring[rx_ring_tail].p; if(!(p->status & (1<<7))) { /* Not COMPLETED */ break; } if(p->status & (1<<6)) /* COMPLETED_OK */ { u16 length=p->length; struct sk_buff *skb; struct sk_buff *newskb; /* Try to save time by avoiding a copy on big frames */ if ((length > RX_COPYBREAK) && ((newskb=dev_alloc_skb(1532)) != NULL)) { skb=lp->rx_ring[rx_ring_tail].skb; skb_put(skb, length); skb_reserve(newskb,18); lp->rx_ring[rx_ring_tail].skb=newskb; p->data=isa_virt_to_bus(newskb->data); } else { skb=dev_alloc_skb(length+2); if(skb==NULL) { lp->net_stats.rx_dropped++; goto dropped; } skb_reserve(skb,2); memcpy(skb_put(skb, length), lp->rx_ring[rx_ring_tail].skb->data, length); } skb->protocol=eth_type_trans(skb,dev); skb->dev=dev; dev->last_rx = jiffies; lp->net_stats.rx_packets++; lp->net_stats.rx_bytes += length; netif_rx(skb); } dropped: p->length = 1532; p->status = 0; rx_ring_tail=next_rx(rx_ring_tail); } while(x++<48); /* If there was actually a frame to be processed, place the EOL bit */ /* at the descriptor prior to the one to be filled next */ if (rx_ring_tail != rx_old_tail) { lp->rx_ring[prev_rx(rx_ring_tail)].p->control |= CONTROL_EOL; lp->rx_ring[prev_rx(rx_old_tail)].p->control &= ~CONTROL_EOL; lp->rx_ring_tail=rx_ring_tail; }}/** * mc32_tx_ring - process completed transmits * @dev: 3c527 that needs its transmit ring processing * * * This operates in a similar fashion to mc32_rx_ring. We iterate * over the transmit ring. For each descriptor which has been * processed by the card, we free its associated buffer and note * any errors. This continues until the transmit ring is emptied * or we reach a descriptor that hasn't yet been processed by the * card. * */static void mc32_tx_ring(struct net_device *dev) { struct mc32_local *lp = netdev_priv(dev); volatile struct skb_header *np; /* * We rely on head==tail to mean 'queue empty'. * This is why lp->tx_count=TX_RING_LEN-1: in order to prevent * tx_ring_head wrapping to tail and confusing a 'queue empty' * condition with 'queue full' */ while (lp->tx_ring_tail != atomic_read(&lp->tx_ring_head)) { u16 t; t=next_tx(lp->tx_ring_tail); np=lp->tx_ring[t].p; if(!(np->status & (1<<7))) { /* Not COMPLETED */ break; } lp->net_stats.tx_packets++; if(!(np->status & (1<<6))) /* Not COMPLETED_OK */ { lp->net_stats.tx_errors++; switch(np->status&0x0F) { case 1: lp->net_stats.tx_aborted_errors++; break; /* Max collisions */ case 2: lp->net_stats.tx_fifo_errors++; break; case 3: lp->net_stats.tx_carrier_errors++; break; case 4: lp->net_stats.tx_window_errors++; break; /* CTS Lost */ case 5: lp->net_stats.tx_aborted_errors++; break; /* Transmit timeout */ } } /* Packets are sent in order - this is basically a FIFO queue of buffers matching the card ring */ lp->net_stats.tx_bytes+=lp->tx_ring[t].skb->len; dev_kfree_skb_irq(lp->tx_ring[t].skb); lp->tx_ring[t].skb=NULL; atomic_inc(&lp->tx_count); netif_wake_queue(dev); lp->tx_ring_tail=t; }} /** * mc32_interrupt - handle an interrupt from a 3c527 * @irq: Interrupt number * @dev_id: 3c527 that requires servicing * @regs: Registers (unused) * * * An interrupt is raised whenever the 3c527 writes to the command * register. This register contains the message it wishes to send us * packed into a single byte field. We keep reading status entries * until we have processed all the control items, but simply count * transmit and receive reports. When all reports are in we empty the * transceiver rings as appropriate. This saves the overhead of * multiple command requests. * * Because MCA is level-triggered, we shouldn't miss indications. * Therefore, we needn't ask the card to suspend interrupts within * this handler. The card receives an implicit acknowledgment of the * current interrupt when we read the command register. * */static irqreturn_t mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs){ struct net_device *dev = dev_id; struct mc32_local *lp; int ioaddr, status, boguscount = 0; int rx_event = 0; int tx_event = 0; if (dev == NULL) { printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq); return IRQ_NONE; } ioaddr = dev->base_addr; lp = netdev_priv(dev); /* See whats cooking */ while((inb(ioaddr+HOST_STATUS)&HOST_STATUS_CWR) && boguscount++<2000) { status=inb(ioaddr+HOST_CMD);#ifdef DEBUG_IRQ printk("Status TX%d RX%d EX%d OV%d BC%d\n", (status&7), (status>>3)&7, (status>>6)&1, (status>>7)&1, boguscount);#endif switch(status&7) { case 0: break; case 6: /* TX fail */ case 2: /* TX ok */ tx_event = 1; break; case 3: /* Halt */ case 4: /* Abort */ complete(&lp->xceiver_cmd); break; default: printk("%s: strange tx ack %d\n", dev->name, status&7); } status>>=3; switch(status&7) { case 0: break; case 2: /* RX */ rx_event=1; break; case 3: /* Halt */ case 4: /* Abort */ complete(&lp->xceiver_cmd); break; case 6: /* Out of RX buffers stat */ /* Must restart rx */ lp->net_stats.rx_dropped++; mc32_rx_ring(dev); mc32_start_transceiver(dev); break; default: printk("%s: strange rx ack %d\n", dev->name, status&7); } status>>=3; if(status&1) { /* * No thread is waiting: we need to tidy * up ourself. */ if (lp->cmd_nonblocking) { up(&lp->cmd_mutex); if (lp->mc_reload_wait) mc32_reset_multicast_list(dev); } else complete(&lp->execution_cmd); } if(status&2) { /* * We get interrupted once per * counter that is about to overflow. */ mc32_update_stats(dev); } } /* * Process the transmit and receive rings */ if(tx_event) mc32_tx_ring(dev); if(rx_event) mc32_rx_ring(dev); return IRQ_HANDLED;}/** * mc32_close - user configuring the 3c527 down * @dev: 3c527 card to shut down * * The 3c527 is a bus mastering device. We must be careful how we * shut it down. It may also be running shared interrupt so we have * to be sure to silence it properly * * We indicate that the card is closing to the rest of the * driver. Otherwise, it is possible that the card may run out * of receive buffers and restart the transceiver while we're * trying to close it. * * We abort any receive and transmits going on and then wait until * any pending exec commands have completed in other code threads. * In theory we can't get here while that is true, in practice I am * paranoid * * We turn off the interrupt enable for the board to be sure it can't * intefere with other devices. */static int mc32_close(struct net_device *dev){ struct mc32_local *lp = netdev_priv(dev); int ioaddr = dev->base_addr; u8 regs; u16 one=1; lp->xceiver_desired_state = HALTED; netif_stop_queue(dev); /* * Send the indications on command (handy debug check) */ mc32_command(dev, 4, &one, 2); /* Shut down the transceiver */ mc32_halt_transceiver(dev); /* Ensure we issue no more commands beyond this point */ down(&lp->cmd_mutex); /* Ok the card is now stopping */ regs=inb(ioaddr+HOST_CTRL); regs&=~HOST_CTRL_INTE; outb(regs, ioaddr+HOST_CTRL); mc32_flush_rx_ring(dev); mc32_flush_tx_ring(dev); mc32_update_stats(dev); return 0;}/** * mc32_get_stats - hand back stats to network layer * @dev: The 3c527 card to handle * * We've collected all the stats we can in software already. Now * it's time to update those kept on-card and return the lot. * */static struct net_device_stats *mc32_get_stats(struct net_device *dev){ struct mc32_local *lp = netdev_priv(dev); mc32_update_stats(dev); return &lp->net_stats;}/** * do_mc32_set_multicast_list - attempt to update multicasts * @dev: 3c527 device to load the list on * @retry: indicates this is not the first call. * * * Actually set or clear the multicast filter for this adaptor. The * locking issues are handled by this routine. We have to track * state as it may take multiple calls to get the command sequence * completed. We just keep trying to schedule the loads until we * manage to process them all. * * num_addrs == -1 Promiscuous mode, receive all packets * * num_addrs == 0 Normal mode, clear multicast list * * num_addrs > 0 Multicast mode, receive normal and MC packets, * and do best-effort filtering. * * See mc32_update_stats() regards setting the SAV BP bit. * */static void do_mc32_set_multicast_list(struct net_device *dev, int retry){ struct mc32_local *lp = netdev_priv(dev); u16 filt = (1<<2); /* Save Bad Packets, for stats purposes */ if (dev->flags&IFF_PROMISC) /* Enable promiscuous mode */ filt |= 1; else if((dev->flags&IFF_ALLMULTI) || dev->mc_count > 10) { dev->flags|=IFF_PROMISC; filt |= 1; } else if(dev->mc_count) { unsigned char block[62]; unsigned char *bp; struct dev_mc_list *dmc=dev->mc_list; int i; if(retry==0) lp->mc_list_valid = 0; if(!lp->mc_list_valid) { block[1]=0; block[0]=dev->mc_count; bp=block+2; for(i=0;i<dev->mc_count;i++) { memcpy(bp, dmc->dmi_addr, 6); bp+=6; dmc=dmc->next; } if(mc32_command_nowait(dev, 2, block, 2+6*dev->mc_count)==-1) { lp->mc_reload_wait = 1; return; } lp->mc_list_valid=1; } } if(mc32_command_nowait(dev, 0, &filt, 2)==-1) { lp->mc_reload_wait = 1; } else { lp->mc_reload_wait = 0; }}/** * mc32_set_multicast_list - queue multicast list update * @dev: The 3c527 to use * * Commence loading the multicast list. This is called when the kernel * changes the lists. It will override any pending list we are trying to * load. */static void mc32_set_multicast_list(struct net_device *dev){ do_mc32_set_multicast_list(dev,0);}/** * mc32_reset_multicast_list - reset multicast list * @dev: The 3c527 to use * * Attempt the next step in loading the multicast lists. If this attempt * fails to complete then it will be scheduled and this function called * again later from elsewhere. */static void mc32_reset_multicast_list(struct net_device *dev){ do_mc32_set_multicast_list(dev,1);}static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ strcpy(info->driver, DRV_NAME); strcpy(info->version, DRV_VERSION); sprintf(info->bus_info, "MCA 0x%lx", dev->base_addr);}static u32 netdev_get_msglevel(struct net_device *dev){ return mc32_debug;}static void netdev_set_msglevel(struct net_device *dev, u32 level){ mc32_debug = level;}static struct ethtool_ops netdev_ethtool_ops = { .get_drvinfo = netdev_get_drvinfo, .get_msglevel = netdev_get_msglevel, .set_msglevel = netdev_set_msglevel,};#ifdef MODULEstatic struct net_device *this_device;/** * init_module - entry point * * Probe and locate a 3c527 card. This really should probe and locate * all the 3c527 cards in the machine not just one of them. Yes you can * insmod multiple modules for now but it's a hack. */int init_module(void){ this_device = mc32_probe(-1); if (IS_ERR(this_device)) return PTR_ERR(this_device); return 0;}/** * cleanup_module - free resources for an unload * * Unloading time. We release the MCA bus resources and the interrupt * at which point everything is ready to unload. The card must be stopped * at this point or we would not have been called. When we unload we * leave the card stopped but not totally shut down. When the card is * initialized it must be rebooted or the rings reloaded before any * transmit operations are allowed to start scribbling into memory. */void cleanup_module(void){ unregister_netdev(this_device); cleanup_card(this_device); free_netdev(this_device);}#endif /* MODULE */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?