static int net_send_packet(struct sk_buff *skb, struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; if (dev->tbusy) { /* If we get here, some higher level has decided we are broken. There should really be a "kick me" function call instead. */ int tickssofar = jiffies - dev->trans_start; if (tickssofar < 5) return 1; printk("%s: transmit timed out, %s?\n", dev->name, tx_done(dev) ? "IRQ conflict" : "network cable problem"); /* Try to restart the adaptor. */ chipset_init(dev, 1); dev->tbusy=0; dev->trans_start = jiffies; } /* If some higher layer thinks we've missed an tx-done interrupt we are passed NULL. Caution: dev_tint() handles the cli()/sti() itself. */ if (skb == NULL) { dev_tint(dev); return 0; } /* For ethernet, fill in the header. This should really be done by a higher level, rather than duplicated for each ethernet adaptor. */ if (!skb->arp && dev->rebuild_header(skb->data, dev)) { skb->dev = dev; arp_queue (skb); return 0; } skb->arp=1; /* Block a timer-based transmit from overlapping. This could better be done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ if (set_bit(0, (void*)&dev->tbusy) != 0) printk("%s: Transmitter access conflict.\n", dev->name); else { short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; hardware_send_packet(ioaddr, buf, length); dev->trans_start = jiffies; } if (skb->free) kfree_skb (skb, FREE_WRITE); /* You might need to clean up and record Tx statistics here. */ if (inw(ioaddr) == /*RU*/81) lp->stats.tx_aborted_errors++; return 0; }
static int el_start_xmit(struct sk_buff *skb, struct device *dev) { if (dev->tbusy) { if (jiffies - dev->trans_start < 20) { if (el_debug > 2) printk(" transmitter busy, deferred.\n"); return 1; } if (el_debug) printk ("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n", dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS)); el_status.stats.tx_errors++; #ifdef oldway el_reset(dev); #else outb(TX_NORM, TX_CMD); outb(RX_NORM, RX_CMD); outb(AX_OFF, AX_CMD); /* Just trigger a false interrupt. */ #endif outb(AX_RX, AX_CMD); /* Aux control, irq and receive enabled */ dev->tbusy = 0; dev->trans_start = jiffies; } if (skb == NULL) { dev_tint(dev); return 0; } /* Fill in the ethernet header. */ if (!skb->arp && dev->rebuild_header(skb->data, dev)) { skb->dev = dev; arp_queue (skb); return 0; } skb->arp=1; if (skb->len <= 0) return 0; /* Avoid timer-based retransmission conflicts. */ if (set_bit(0, (void*)&dev->tbusy) != 0) printk("%s: Transmitter access conflict.\n", dev->name); else { int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN); unsigned char *buf = skb->data; el_status.tx_pkt_start = gp_start; el_status.collisions = 0; outb(AX_SYS, AX_CMD); inb(RX_STATUS); inb(TX_STATUS); outb(0x00, RX_BUF_CLR); /* Set rx packet area to 0. */ outw(gp_start, GP_LOW); outsb(DATAPORT,buf,skb->len); outw(gp_start, GP_LOW); outb(AX_XMIT, AX_CMD); /* Trigger xmit. */ dev->trans_start = jiffies; } if (el_debug > 2) printk(" queued xmit.\n"); if (skb->free) kfree_skb (skb, FREE_WRITE); return 0; }
static int elp_start_xmit(struct sk_buff *skb, struct device *dev) { elp_device * adapter = (elp_device *) dev->priv; CHECK_NULL(dev); /* * not sure what this does, but the 3c509 driver does it, so... */ if (skb == NULL) { dev_tint(dev); return 0; } /* * Fill in the ethernet header * (for kernels prior to 1.1.4 only) */ #if (ELP_KERNEL_TYPE < 2) IS_SKB(skb); if (!skb->arp && dev->rebuild_header(SKB_DATA, dev)) { skb->dev = dev; IS_SKB(skb); arp_queue (skb); return 0; } #endif /* * if we ended up with a munged length, don't send it */ if (skb->len <= 0) return 0; if (elp_debug >= 3) printk("%s: request to send packet of length %d\n", dev->name, (int)skb->len); /* * if the transmitter is still busy, we have a transmit timeout... */ if (dev->tbusy) { int tickssofar = jiffies - dev->trans_start; if (tickssofar < 200) /* was 500, AJT */ return 1; printk("%s: transmit timed out, resetting adapter\n", dev->name); if ((INB(adapter->io_addr+PORT_STATUS)&STATUS_ACRF) != 0) printk("%s: hmmm...seemed to have missed an interrupt!\n", dev->name); adapter_reset(adapter); dev->trans_start = jiffies; dev->tbusy = 0; } /* * send the packet at skb->data for skb->len */ if (!send_packet(adapter, (unsigned char *)SKB_DATA, skb->len)) { printk("%s: send packet PCB failed\n", dev->name); return 1; } if (elp_debug >= 3) printk("%s: packet of length %d sent\n", dev->name, (int)skb->len); /* * start the transmit timeout */ dev->trans_start = jiffies; /* * the transmitter is now busy */ dev->tbusy = 1; /* * if we have been asked to free the buffer, do so */ #if (ELP_KERNEL_TYPE < 4) if (skb->free) { IS_SKB(skb); kfree_skb(skb, FREE_WRITE); } #else dev_kfree_skb(skb, FREE_WRITE); #endif return 0; }
err_t ether_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr) { struct pbuf *q; struct eth_hdr *ethhdr; struct eth_addr *dest, mcastaddr; struct ip_addr *queryaddr; err_t err; int i; int loopback = 0; //kprintf("ether: xmit %d bytes, %d bufs\n", p->tot_len, pbuf_clen(p)); if ((netif->flags & NETIF_UP) == 0) return -ENETDOWN; if (pbuf_header(p, ETHER_HLEN)) { kprintf(KERN_ERR "ether_output: not enough room for Ethernet header in pbuf\n"); stats.link.err++; return -EBUF; } // Construct Ethernet header. Start with looking up deciding which // MAC address to use as a destination address. Broadcasts and // multicasts are special, all other addresses are looked up in the // ARP table. queryaddr = ipaddr; if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, &netif->netmask)) { dest = (struct eth_addr *) ðbroadcast; } else if (ip_addr_ismulticast(ipaddr)) { // Hash IP multicast address to MAC address. mcastaddr.addr[0] = 0x01; mcastaddr.addr[1] = 0x0; mcastaddr.addr[2] = 0x5e; mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; mcastaddr.addr[4] = ip4_addr3(ipaddr); mcastaddr.addr[5] = ip4_addr4(ipaddr); dest = &mcastaddr; } else if (ip_addr_cmp(ipaddr, &netif->ipaddr)) { dest = &netif->hwaddr; loopback = 1; } else { if (ip_addr_maskcmp(ipaddr, &netif->ipaddr, &netif->netmask)) { // Use destination IP address if the destination is on the same subnet as we are. queryaddr = ipaddr; } else { // Otherwise we use the default router as the address to send the Ethernet frame to. queryaddr = &netif->gw; } dest = arp_lookup(queryaddr); } // If the arp_lookup() didn't find an address, we send out an ARP query for the IP address. if (dest == NULL) { q = arp_query(netif, &netif->hwaddr, queryaddr); if (q != NULL) { err = dev_transmit((dev_t) netif->state, q); if (err < 0) { kprintf(KERN_ERR "ether: error %d sending arp packet\n", err); pbuf_free(q); stats.link.drop++; return err; } } // Queue packet for transmission, when the ARP reply returns err = arp_queue(netif, p, queryaddr); if (err < 0) { kprintf(KERN_ERR "ether: error %d queueing packet\n", err); stats.link.drop++; stats.link.memerr++; return err; } return 0; } ethhdr = p->payload; for (i = 0; i < 6; i++) { ethhdr->dest.addr[i] = dest->addr[i]; ethhdr->src.addr[i] = netif->hwaddr.addr[i]; } ethhdr->type = htons(ETHTYPE_IP); if (loopback) { struct pbuf *q; q = pbuf_dup(PBUF_RAW, p); if (!q) return -ENOMEM; err = ether_input(netif, q); if (err < 0) { pbuf_free(q); return err; } } else { err = dev_transmit((dev_t) netif->state, p); if (err < 0) { kprintf(KERN_ERR "ether: error %d sending packet\n", err); return err; } } return 0; }
/* * Copy a buffer to the adapter transmit page memory. * Start sending. */ static int d_link_start_xmit(struct sk_buff *skb, struct device *dev) { int transmit_from; int len; int tickssofar; unsigned char *buffer = skb->data; /* * If some higher layer thinks we've missed a * tx-done interrupt we are passed NULL. * Caution: dev_tint() handles the cli()/sti() itself. */ if (skb == NULL) { dev_tint(dev); return 0; } /* For ethernet, fill in the header (hardware addresses) with an arp. */ if (!skb->arp) if(dev->rebuild_header(skb->data, dev)) { skb->dev = dev; arp_queue (skb); return 0; } skb->arp = 1; if (free_tx_pages <= 0) { /* Do timeouts, to avoid hangs. */ tickssofar = jiffies - dev->trans_start; if (tickssofar < 5) return 1; /* else */ printk("%s: transmit timed out (%d), %s?\n", dev->name, tickssofar, "network cable problem" ); /* Restart the adapter. */ adapter_init(dev); } /* Start real output */ PRINTK(("d_link_start_xmit:len=%d, page %d/%d\n", skb->len, tx_fifo_in, free_tx_pages)); if ((len = skb->len) < RUNT) len = RUNT; cli(); select_nic(); tx_fifo[tx_fifo_in] = transmit_from = tx_page_adr(tx_fifo_in) - len; tx_fifo_in = (tx_fifo_in + 1) % TX_PAGES; /* Next free tx page */ d_link_setup_address(transmit_from, RW_ADDR); for ( ; len > 0; --len, ++buffer) d_link_put_byte(*buffer); if (free_tx_pages-- == TX_PAGES) { /* No transmission going on */ dev->trans_start = jiffies; dev->tbusy = 0; /* allow more packets into adapter */ /* Send page and generate an interrupt */ d_link_setup_address(transmit_from, TX_ADDR); d_link_put_command(TX_ENABLE); } else { dev->tbusy = !free_tx_pages; select_prn(); } sti(); /* interrupts back on */ if (skb->free) kfree_skb (skb, FREE_WRITE); return 0; }
int wd8003_start_xmit(struct sk_buff *skb, struct device *dev) { unsigned char cmd; int len; cli(); if (dev->tbusy) { /* put in a time out. */ if (jiffies - dev->trans_start < 30) { sti(); return (1); } printk ("wd8003 transmit timed out. \n"); } dev->tbusy = 1; if (skb == NULL) { sti(); wd_trs(dev); return (0); } /* this should check to see if it's been killed. */ if (skb->dev != dev) { sti(); return (0); } if (!skb->arp) { if ( dev->rebuild_header (skb+1, dev)) { cli(); if (skb->dev == dev) { arp_queue (skb); } cli (); /* arp_queue turns them back on. */ dev->tbusy = 0; sti(); return (0); } } memcpy ((unsigned char *)dev->mem_start, skb+1, skb->len); len = skb->len; /* now we need to set up the card info. */ dev->trans_start = jiffies; len=max(len, ETHER_MIN_LEN); /* actually we should zero out the extra memory. */ /* printk ("start_xmit len - %d\n", len);*/ cmd=inb_p(WD_COMM); cmd &= ~CPAGE; outb_p(cmd, WD_COMM); interrupt_mask |= TRANS_MASK; if (!(dev->interrupt)) outb (interrupt_mask, WD_IMR); outb_p(len&0xff,WD_TB0); outb_p(len>>8,WD_TB1); cmd |= CTRANS; outb_p(cmd,WD_COMM); sti(); if (skb->free) { kfree_skb (skb, FREE_WRITE); } return (0); }