📄 ncr885e.c
字号:
status = inw( &cp->xfer_status ); if (ncr885e_debug > 3) printk( KERN_INFO "%s: (rx %d) bytes=%d, xfer_status=%04x\n", dev->name, i, nb, status ); if ( status ) { skb = sp->rx_skbufs[i]; data = skb->data; stats = data + nb - 3; rxbits = (stats[0]|stats[1]<<8|stats[2]<<16); if (ncr885e_debug > 3) printk( KERN_INFO " rx_bits=%06lx\n", rxbits ); skb->dev = dev; skb_put( skb, nb-3 ); skb->protocol = eth_type_trans( skb, dev ); netif_rx( skb ); sp->rx_skbufs[i] = 0; if ( rxbits & RX_STATUS_RXOK ) { sp->stats.rx_packets++; sp->stats.rx_bytes += nb; } if ( rxbits & RX_STATUS_MCAST ) sp->stats.multicast++; } sp->rx_dirty = sp->rx_current; if ( ++sp->rx_current >= NR_RX_RING ) sp->rx_current = 0; /* fix up the one we just trashed */ cp = sp->rx_cmds + (sp->rx_dirty * 2); skb = dev_alloc_skb( RX_BUFLEN + 2 ); if ( skb != 0 ) { skb_reserve( skb, 2 ); sp->rx_skbufs[sp->rx_dirty] = skb; } if (ncr885e_debug > 2) printk( KERN_INFO "%s: ncr885e_rx: using ring index %d, filling cp @ %p\n", dev->name, sp->rx_current, cp ); outw( RX_BUFLEN, &cp->req_count ); outw( 0, &cp->res_count ); data = skb->data; outl( virt_to_bus( data ), &cp->phy_addr ); outw( 0, &cp->xfer_status ); cp = sp->rx_cmds + (sp->rx_current * 2); /* restart rx DMA */ outl( virt_to_bus( cp ), ioaddr + RX_CMD_PTR_LO ); outl( (RX_DBDMA_ENABLE << 16)|RX_CHANNEL_RUN, ioaddr + RX_CHANNEL_CONTROL ); return;}static voidncr885e_misc_ints( struct net_device *dev, unsigned short status ){ struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; struct dbdma_cmd *cp; unsigned long ioaddr = dev->base_addr; if (ncr885e_debug > 1) printk( KERN_INFO "miscellaneous interrupt handled; status=%02x\n", status ); /* various transmit errors */ if ( status & (INTERRUPT_PPET | INTERRUPT_PBFT | INTERRUPT_IIDT) ) { /* illegal instruction in tx dma */ if ( status & INTERRUPT_IIDT ) { cp = (struct dbdma_cmd *) bus_to_virt( inl( ioaddr + TX_CMD_PTR_LO )); printk( KERN_INFO "%s: tx illegal insn:\n", dev->name ); printk( KERN_INFO " tx DBDMA - cmd = %p, status = %04x\n", cp, inw( ioaddr + TX_CHANNEL_STATUS )); printk( KERN_INFO " command = %04x, phy_addr=%08x, req_count=%04x\n", inw( &cp->command ), inw( &cp->phy_addr ), inw( &cp->req_count )); } if ( status & INTERRUPT_PPET ) printk( KERN_INFO "%s: tx PCI parity error\n", dev->name ); if ( status & INTERRUPT_PBFT ) printk( KERN_INFO "%s: tx PCI bus fault\n", dev->name ); } /* look for rx errors */ if ( status & (INTERRUPT_PPER | INTERRUPT_PBFR | INTERRUPT_IIDR)) { /* illegal instruction in rx dma */ if ( status & INTERRUPT_IIDR ) {#if 0 cmd = inl( ioaddr + RX_CMD_PTR_LO ); #endif printk( KERN_ERR "%s: rx illegal DMA instruction:\n", dev->name ); printk( KERN_ERR " channel status=%04x,\n", inl( ioaddr + RX_CHANNEL_STATUS ));#if 0 show_dbdma_cmd( bus_to_virt( inl( ioaddr + RX_CMD_PTR_LO ))); printk( KERN_ERR " instr (%08x) %08x %08x %08x\n", (int) cmd, cmd[0], cmd[1], cmd[2] );#endif } /* PCI parity error */ if ( status & INTERRUPT_PPER ) printk( KERN_INFO "%s: rx PCI parity error\n", dev->name ); if ( status & INTERRUPT_PBFR ) printk( KERN_INFO "%s: rx PCI bus fault\n", dev->name ); sp->stats.rx_errors++; } if ( status & INTERRUPT_WI ) { printk( KERN_INFO "%s: link pulse\n", dev->name ); } /* bump any counters */ return;}static voidncr885e_interrupt( int irq, void *dev_id, struct pt_regs *regs ){ struct net_device *dev = (struct net_device *) dev_id; struct ncr885e_private *sp; unsigned short status; int ioaddr; if ( dev == NULL ) { printk( KERN_ERR "symba: Interrupt IRQ %d for unknown device\n", irq ); return; } ioaddr = dev->base_addr; sp = (struct ncr885e_private *) dev->priv; spin_lock( &sp->lock ); status = inw( ioaddr + INTERRUPT_CLEAR ); if (ncr885e_debug > 2) printk( KERN_INFO "%s: 53C885 interrupt 0x%02x\n", dev->name, status ); /* handle non-tx and rx interrupts first */ if ( status & ~(INTERRUPT_DIT|INTERRUPT_DIR)) ncr885e_misc_ints( dev, status ); /* look for tx interrupt: more to transmit, DBDMA stopped, or tx done */ if ( ( status & INTERRUPT_DIT ) ) { if (ncr885e_debug > 2) printk( KERN_INFO "%s: tx int; int=%02x, chan stat=%02x\n", dev->name, status, inw( ioaddr + TX_CHANNEL_STATUS )); /* turn off timer */ del_timer( &sp->tx_timeout ); sp->timeout_active = 0; /* stop DMA */ outl( TX_DBDMA_ENABLE << 16, ioaddr + TX_CHANNEL_CONTROL ); ncr885e_tx( dev ); } if ( status & INTERRUPT_DIR ) { if ( ncr885e_debug > 2 ) printk( KERN_INFO "%s: rx interrupt; int=%02x, rx channel stat=%02x\n", dev->name, status, inw( ioaddr + RX_CHANNEL_STATUS )); /* stop DMA */ outl( RX_DBDMA_ENABLE << 16, ioaddr + RX_CHANNEL_CONTROL ); /* and handle the interrupt */ ncr885e_rx( dev ); } spin_unlock( &sp->lock ); return;}/* doesn't set the address permanently, however... */static int ncr885e_set_address( struct net_device *dev, void *addr ){ struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; struct sockaddr *saddr = addr; unsigned long flags; unsigned short reg[3]; unsigned char *ioaddr, *p; int i; memcpy( dev->dev_addr, saddr->sa_data, dev->addr_len ); p = (unsigned char *) dev->dev_addr; printk( KERN_INFO "%s: setting new MAC address - ", dev->name );#if 0 for( p = (unsigned char *) dev->dev_addr, i=0; i < 6; i++, p++ ) printk("%c%2.2x", i ? ':' : ' ', *p );#endif p = (unsigned char *) ® for( i=0; i < 6; i++ ) p[i] = dev->dev_addr[i];#if 0 printk("%s: Setting new mac address - ", dev->name ); for( i=0; i < 6; i++ ) { printk("%02x", i ? ':' : ' ', p[i] ); } printk("\n");#endif /* stop rx for the change */ outl( RX_DBDMA_ENABLE << 16, ioaddr + RX_CHANNEL_CONTROL ); spin_lock_irqsave( &sp->lock, flags ); ioaddr = (unsigned char *) dev->base_addr; for( i = 0; i < 3; i++ ) { reg[i] = ((reg[i] & 0xff) << 8) | ((reg[i] >> 8) & 0xff); printk("%04x ", reg[i] ); outw( reg[i], ioaddr + STATION_ADDRESS_0 + (i*2)); } printk("\n"); spin_unlock_irqrestore( &sp->lock, flags ); /* restart rx */ outl((RX_DBDMA_ENABLE << 16)|RX_CHANNEL_RUN, ioaddr + RX_CHANNEL_CONTROL ); return 0;}static void ncr885e_tx_timeout( unsigned long data ){ struct net_device *dev = (struct net_device *) data; struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; unsigned long flags, ioaddr; int i; save_flags( flags ); cli(); ioaddr = dev->base_addr; sp->timeout_active = 0; i = sp->tx_dirty; /* if we weren't active, bail... */ if ( sp->tx_active == 0 ) { printk( KERN_INFO "%s: ncr885e_timeout...tx not active!\n", dev->name ); goto out; } printk( KERN_ERR "%s: 53C885 timed out. Resetting...\n", dev->name ); /* disable rx and tx DMA */ outl( (TX_DBDMA_ENABLE << 16), ioaddr + TX_CHANNEL_CONTROL ); outl( (RX_DBDMA_ENABLE << 16), ioaddr + RX_CHANNEL_CONTROL ); /* reset the chip */ ncr885e_config( dev ); ncr885e_enable( dev ); /* clear the wedged skb in the tx ring */ sp->tx_active = 0; ++sp->stats.tx_errors; if ( sp->tx_skbufs[i] ) { dev_kfree_skb( sp->tx_skbufs[i] ); sp->tx_skbufs[i] = 0; } /* start anew from the beginning of the ring buffer (why not?) */ sp->tx_current = 0; netif_wake_queue(dev); /* restart rx dma */ outl( (RX_DBDMA_ENABLE << 16) | RX_CHANNEL_RUN, ioaddr + RX_CHANNEL_CONTROL ); out: restore_flags( flags );}static inline voidncr885e_set_timeout( struct net_device *dev ){ struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; unsigned long flags; save_flags(flags); cli(); if ( sp->timeout_active ) del_timer( &sp->tx_timeout ); sp->tx_timeout.expires = jiffies + TX_TIMEOUT; sp->tx_timeout.function = ncr885e_tx_timeout; sp->tx_timeout.data = (unsigned long) dev; add_timer( &sp->tx_timeout ); sp->timeout_active = 1; restore_flags( flags );}/* * The goal is to set up DBDMA such that the rx ring contains only * one DMA descriptor per ring element and the tx ring has two (using * the cool features of branch- and wait-select. However, I'm not sure * if it's possible. For now, we plod through it with 3 descriptors * for tx, and two for rx. */static intncr885e_open( struct net_device *dev ){ struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; unsigned long ioaddr = dev->base_addr; struct sk_buff *skb; int i, size; char *data; struct dbdma_cmd *cp; unsigned long flags; /* allocate enough space for the tx and rx rings and a STOP descriptor */ size = (sizeof( struct dbdma_cmd ) * ((NR_TX_RING * 3) + (NR_RX_RING * 2) + 1)); cp = kmalloc( size, GFP_KERNEL ); if ( cp == 0 ) { printk( KERN_ERR "Insufficient memory (%d bytes) for DBDMA\n", size ); return -ENOMEM; } spin_lock_init( &sp->lock ); spin_lock_irqsave( &sp->lock, flags ); memset((char *) cp, 0, size ); sp->head = cp; sp->stop_cmd = cp; outl( DBDMA_STOP, &cp->command ); sp->rx_cmds = ++cp; for( i = 0; i < NR_RX_RING; i++ ) { cp = sp->rx_cmds + (i*2); skb = dev_alloc_skb( RX_BUFLEN + 2 ); /* if there is insufficient memory, make this last ring use a static buffer and leave the loop with that skb as final one */ if ( skb == 0 ) { printk( KERN_ERR "%s: insufficient memory for rx ring buffer\n", dev->name ); break; } skb_reserve( skb, 2 ); sp->rx_skbufs[i] = skb; data = skb->data; /* The DMA commands here are done such that an EOP is the only way that we should get an interrupt. This means that we could fill more than one skbuff before getting the interrupt at EOP. */ /* Handle rx DMA such that it always interrupts.... */ outw( (INPUT_MORE|INTR_ALWAYS), &cp->command ); outw( RX_BUFLEN, &cp->req_count ); outw( 0, &cp->res_count ); outl( virt_to_bus( data ), &cp->phy_addr ); outl( virt_to_bus( sp->stop_cmd ), &cp->cmd_dep ); outw( 0, &cp->xfer_status );#if 0 printk( KERN_INFO "rx at %p\n", cp ); show_dbdma_cmd( cp );#endif ++cp; outw( DBDMA_STOP, &cp->command ); } /* initialize to all rx buffers are available, fill limit is the end */ sp->rx_dirty = 0; sp->rx_current = 0; /* fill the tx ring */ sp->tx_cmds = cp+1; for( i = 0; i < NR_TX_RING; i++ ) { /* minimal setup for tx command */ cp = sp->tx_cmds + (i*3); outw( OUTPUT_LAST, &cp->command ); if (ncr885e_debug > 3) { printk( KERN_INFO "tx OUTPUT_LAST at %p\n", cp ); show_dbdma_cmd( cp ); } /* full setup for the status cmd */ cp++; outw( INPUT_LAST|INTR_ALWAYS|WAIT_IFCLR, &cp->command ); outl( virt_to_bus( &sp->tx_status[i] ), &cp->phy_addr ); outw( 2, &cp->req_count ); if ( ncr885e_debug > 3) { printk( KERN_INFO "tx INPUT_LAST cmd at %p\n", cp ); show_dbdma_cmd( cp ); } ++cp; outw( DBDMA_STOP, &cp->command ); }#if 0 /* chain the last tx DMA command to the STOP cmd */ outw((INPUT_LAST|INTR_ALWAYS|BR_ALWAYS), &cp->command ); outl( virt_to_bus( sp->stop_cmd ), &cp->cmd_dep );#endif sp->tx_active = 0; sp->tx_current = 0; sp->tx_dirty = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -