void snstart(struct ifnet *ifp, struct ifaltq_subque *ifsq) { struct sn_softc *sc = ifp->if_softc; u_int len; struct mbuf *m; struct mbuf *top; int pad; int mask; u_short length; u_short numPages; u_char packet_no; int time_out; ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq); if ((ifp->if_flags & IFF_RUNNING) == 0 || ifq_is_oactive(&ifp->if_snd)) return; if (sc->pages_wanted != -1) { /* XXX should never happen */ kprintf("%s: snstart() while memory allocation pending\n", ifp->if_xname); ifq_set_oactive(&ifp->if_snd); return; } startagain: /* * Sneak a peek at the next packet */ m = ifq_dequeue(&ifp->if_snd); if (m == NULL) return; /* * Compute the frame length and set pad to give an overall even * number of bytes. Below we assume that the packet length is even. */ for (len = 0, top = m; m; m = m->m_next) len += m->m_len; pad = (len & 1); /* * We drop packets that are too large. Perhaps we should truncate * them instead? */ if (len + pad > ETHER_MAX_LEN - ETHER_CRC_LEN) { kprintf("%s: large packet discarded (A)\n", ifp->if_xname); IFNET_STAT_INC(&sc->arpcom.ac_if, oerrors, 1); m_freem(top); goto readcheck; } #ifdef SW_PAD /* * If HW padding is not turned on, then pad to ETHER_MIN_LEN. */ if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) pad = ETHER_MIN_LEN - ETHER_CRC_LEN - len; #endif /* SW_PAD */ length = pad + len; /* * The MMU wants the number of pages to be the number of 256 byte * 'pages', minus 1 (A packet can't ever have 0 pages. We also * include space for the status word, byte count and control bytes in * the allocation request. */ numPages = (length + 6) >> 8; /* * Now, try to allocate the memory */ SMC_SELECT_BANK(2); outw(BASE + MMU_CMD_REG_W, MMUCR_ALLOC | numPages); /* * Wait a short amount of time to see if the allocation request * completes. Otherwise, I enable the interrupt and wait for * completion asyncronously. */ time_out = MEMORY_WAIT_TIME; do { if (inb(BASE + INTR_STAT_REG_B) & IM_ALLOC_INT) break; } while (--time_out); if (!time_out) { /* * No memory now. Oh well, wait until the chip finds memory * later. Remember how many pages we were asking for and * enable the allocation completion interrupt. Also set a * watchdog in case we miss the interrupt. We mark the * interface active since there is no point in attempting an * snstart() until after the memory is available. */ mask = inb(BASE + INTR_MASK_REG_B) | IM_ALLOC_INT; outb(BASE + INTR_MASK_REG_B, mask); sc->intr_mask = mask; ifp->if_timer = 1; ifq_set_oactive(&ifp->if_snd); sc->pages_wanted = numPages; ifq_prepend(&ifp->if_snd, top); return; } /* * The memory allocation completed. Check the results. */ packet_no = inb(BASE + ALLOC_RESULT_REG_B); if (packet_no & ARR_FAILED) { kprintf("%s: Memory allocation failed\n", ifp->if_xname); ifq_prepend(&ifp->if_snd, top); goto startagain; } /* * We have a packet number, so tell the card to use it. */ outb(BASE + PACKET_NUM_REG_B, packet_no); /* * Point to the beginning of the packet */ outw(BASE + POINTER_REG_W, PTR_AUTOINC | 0x0000); /* * Send the packet length (+6 for status, length and control byte) * and the status word (set to zeros) */ outw(BASE + DATA_REG_W, 0); outb(BASE + DATA_REG_B, (length + 6) & 0xFF); outb(BASE + DATA_REG_B, (length + 6) >> 8); /* * Push out the data to the card. */ for (m = top; m != NULL; m = m->m_next) { /* * Push out words. */ outsw(BASE + DATA_REG_W, mtod(m, caddr_t), m->m_len / 2); /* * Push out remaining byte. */ if (m->m_len & 1) outb(BASE + DATA_REG_B, *(mtod(m, caddr_t) + m->m_len - 1)); } /* * Push out padding. */ while (pad > 1) { outw(BASE + DATA_REG_W, 0); pad -= 2; } if (pad) outb(BASE + DATA_REG_B, 0); /* * Push out control byte and unused packet byte The control byte is 0 * meaning the packet is even lengthed and no special CRC handling is * desired. */ outw(BASE + DATA_REG_W, 0); /* * Enable the interrupts and let the chipset deal with it Also set a * watchdog in case we miss the interrupt. */ mask = inb(BASE + INTR_MASK_REG_B) | (IM_TX_INT | IM_TX_EMPTY_INT); outb(BASE + INTR_MASK_REG_B, mask); sc->intr_mask = mask; outw(BASE + MMU_CMD_REG_W, MMUCR_ENQUEUE); ifq_set_oactive(&ifp->if_snd); ifp->if_timer = 1; BPF_MTAP(ifp, top); IFNET_STAT_INC(ifp, opackets, 1); m_freem(top); readcheck: /* * Is another packet coming in? We don't want to overflow the tiny * RX FIFO. If nothing has arrived then attempt to queue another * transmit packet. */ if (inw(BASE + FIFO_PORTS_REG_W) & FIFO_REMPTY) goto startagain; }
/* Resume a packet transmit operation after a memory allocation * has completed. * * This is basically a hacked up copy of snstart() which handles * a completed memory allocation the same way snstart() does. * It then passes control to snstart to handle any other queued * packets. */ static void snresume(struct ifnet *ifp) { struct sn_softc *sc = ifp->if_softc; u_int len; struct mbuf *m; struct mbuf *top; int pad; int mask; u_short length; u_short numPages; u_short pages_wanted; u_char packet_no; if (sc->pages_wanted < 0) return; pages_wanted = sc->pages_wanted; sc->pages_wanted = -1; /* * Sneak a peek at the next packet */ m = ifq_dequeue(&ifp->if_snd); if (m == NULL) { kprintf("%s: snresume() with nothing to send\n", ifp->if_xname); return; } /* * Compute the frame length and set pad to give an overall even * number of bytes. Below we assume that the packet length is even. */ for (len = 0, top = m; m; m = m->m_next) len += m->m_len; pad = (len & 1); /* * We drop packets that are too large. Perhaps we should truncate * them instead? */ if (len + pad > ETHER_MAX_LEN - ETHER_CRC_LEN) { kprintf("%s: large packet discarded (B)\n", ifp->if_xname); IFNET_STAT_INC(ifp, oerrors, 1); m_freem(top); return; } #ifdef SW_PAD /* * If HW padding is not turned on, then pad to ETHER_MIN_LEN. */ if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) pad = ETHER_MIN_LEN - ETHER_CRC_LEN - len; #endif /* SW_PAD */ length = pad + len; /* * The MMU wants the number of pages to be the number of 256 byte * 'pages', minus 1 (A packet can't ever have 0 pages. We also * include space for the status word, byte count and control bytes in * the allocation request. */ numPages = (length + 6) >> 8; SMC_SELECT_BANK(2); /* * The memory allocation completed. Check the results. If it failed, * we simply set a watchdog timer and hope for the best. */ packet_no = inb(BASE + ALLOC_RESULT_REG_B); if (packet_no & ARR_FAILED) { kprintf("%s: Memory allocation failed. Weird.\n", ifp->if_xname); ifp->if_timer = 1; ifq_prepend(&ifp->if_snd, top); goto try_start; } /* * We have a packet number, so tell the card to use it. */ outb(BASE + PACKET_NUM_REG_B, packet_no); /* * Now, numPages should match the pages_wanted recorded when the * memory allocation was initiated. */ if (pages_wanted != numPages) { kprintf("%s: memory allocation wrong size. Weird.\n", ifp->if_xname); /* * If the allocation was the wrong size we simply release the * memory once it is granted. Wait for the MMU to be un-busy. */ while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY) /* NOTHING */ ; outw(BASE + MMU_CMD_REG_W, MMUCR_FREEPKT); ifq_prepend(&ifp->if_snd, top); return; } /* * Point to the beginning of the packet */ outw(BASE + POINTER_REG_W, PTR_AUTOINC | 0x0000); /* * Send the packet length (+6 for status, length and control byte) * and the status word (set to zeros) */ outw(BASE + DATA_REG_W, 0); outb(BASE + DATA_REG_B, (length + 6) & 0xFF); outb(BASE + DATA_REG_B, (length + 6) >> 8); /* * Push out the data to the card. */ for (m = top; m != NULL; m = m->m_next) { /* * Push out words. */ outsw(BASE + DATA_REG_W, mtod(m, caddr_t), m->m_len / 2); /* * Push out remaining byte. */ if (m->m_len & 1) outb(BASE + DATA_REG_B, *(mtod(m, caddr_t) + m->m_len - 1)); } /* * Push out padding. */ while (pad > 1) { outw(BASE + DATA_REG_W, 0); pad -= 2; } if (pad) outb(BASE + DATA_REG_B, 0); /* * Push out control byte and unused packet byte The control byte is 0 * meaning the packet is even lengthed and no special CRC handling is * desired. */ outw(BASE + DATA_REG_W, 0); /* * Enable the interrupts and let the chipset deal with it Also set a * watchdog in case we miss the interrupt. */ mask = inb(BASE + INTR_MASK_REG_B) | (IM_TX_INT | IM_TX_EMPTY_INT); outb(BASE + INTR_MASK_REG_B, mask); sc->intr_mask = mask; outw(BASE + MMU_CMD_REG_W, MMUCR_ENQUEUE); BPF_MTAP(ifp, top); IFNET_STAT_INC(ifp, opackets, 1); m_freem(top); try_start: /* * Now pass control to snstart() to queue any additional packets */ ifq_clr_oactive(&ifp->if_snd); if_devstart(ifp); /* * We've sent something, so we're active. Set a watchdog in case the * TX_EMPTY interrupt is lost. */ ifq_set_oactive(&ifp->if_snd); ifp->if_timer = 1; }
static void ep_if_start(struct ifnet *ifp, struct ifaltq_subque *ifsq) { struct ep_softc *sc = ifp->if_softc; u_int len; struct mbuf *m; struct mbuf *top; int pad; ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq); if (sc->gone) { ifq_purge(&ifp->if_snd); return; } while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); if (ifq_is_oactive(&ifp->if_snd)) { return; } crit_enter(); startagain: /* Sneak a peek at the next packet */ m = ifq_dequeue(&ifp->if_snd, NULL); if (m == NULL) { crit_exit(); return; } for (len = 0, top = m; m; m = m->m_next) len += m->m_len; m = top; pad = padmap[len & 3]; /* * The 3c509 automatically pads short packets to minimum ethernet length, * but we drop packets that are too large. Perhaps we should truncate * them instead? */ if (len + pad > ETHER_MAX_LEN) { /* packet is obviously too large: toss it */ IFNET_STAT_INC(ifp, oerrors, 1); m_freem(m); goto readcheck; } if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { /* no room in FIFO */ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); /* make sure */ if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { ifq_set_oactive(&ifp->if_snd); ifq_prepend(&ifp->if_snd, top); crit_exit(); return; } } else { outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | EP_THRESH_DISABLE); } outw(BASE + EP_W1_TX_PIO_WR_1, len); outw(BASE + EP_W1_TX_PIO_WR_1, 0x0); /* Second dword meaningless */ if (EP_FTST(sc, F_ACCESS_32_BITS)) { for (top = m; m != NULL; m = m->m_next) { outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 4); if (m->m_len & 3) outsb(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t) + (m->m_len & (~3)), m->m_len & 3); } } else { for (top = m; m != NULL; m = m->m_next) { outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 2); if (m->m_len & 1) outb(BASE + EP_W1_TX_PIO_WR_1, *(mtod(m, caddr_t) + m->m_len - 1)); } } while (pad--) outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ BPF_MTAP(ifp, top); ifp->if_timer = 2; IFNET_STAT_INC(ifp, opackets, 1); m_freem(top); /* * Is another packet coming in? We don't want to overflow the tiny RX * fifo. */ readcheck: if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) { /* * we check if we have packets left, in that case we prepare to come * back later */ if (!ifq_is_empty(&ifp->if_snd)) outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8); crit_exit(); return; } goto startagain; }