📄 if_8139.c
字号:
rltk8139_info = (Rltk8139_t *)(sc->driver_private);#ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_send(%s, %08x, %d, %d, %08lx)\n", sc->dev_name, sg_list, sg_len, total_len, key);#endif CYG_ASSERT(total_len <= TX_BUF_SIZE, "packet too long"); /* * Get the next free descriptor to send. We lock out all interrupts * and scheduling because we really, really do not want to be interrupted * at this point. * * IMPORTANT NOTE: the RealTek data sheet does not really make this clear, * but when they talk about a 'ring' of transmit descriptors, they * _really_ mean it, i.e. you _must_ use descriptor #1 after descriptor * #0 even if transmission of descriptor #0 has already completed. */ cyg_drv_isr_lock(); /* * Sanity check to see if '_send' was called even though there is no free * descriptor. This is probably unnecessary. */ if (rltk8139_info->tx_num_free_desc == 0) { cyg_drv_isr_unlock();#ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_send(%s): no free descriptor available\n", sc->dev_name);#endif return; } /* * Get the free descriptor and advance the descriptor counter modulo * TX_NUM_DESC. We assume that TX_NUM_DESC will always be a power of 2. */ desc = rltk8139_info->tx_free_desc; rltk8139_info->tx_free_desc = (rltk8139_info->tx_free_desc + 1) & (NUM_TX_DESC - 1); /* Decrement the number of free descriptors */ rltk8139_info->tx_num_free_desc -= 1; /* Reenable interrupts at this point */ cyg_drv_isr_unlock(); /* * Determine the buffer memory to use and tell the hardware about it. * Since we use fixed buffer addresses, we do not need to set up TSADx. * Memorize the key so we can call the tx_done callback correctly. * * While it would be possible to set TSADx to the packet directly if * it is stored in a linear memory area with 32 bit alignment, it seems * this happens so seldomly that it's simply not worth the extra * runtime check. */ tx_buffer = CYGARC_UNCACHED_ADDRESS(rltk8139_info->tx_buffer + TX_BUF_SIZE * desc); rltk8139_info->tx_desc_key[desc] = key; /* * Copy the data to the designated position. Note that unlike the eCos * Intel 82559 driver, we simply assume that all the scatter/gather list * elements' lengths will add up to total_len exactly, and don't check * to make sure. */ for (last_sg = &sg_list[sg_len]; sg_list < last_sg; ++sg_list) { memcpy(tx_buffer, (void *)(sg_list->buf), sg_list->len); tx_buffer += sg_list->len; } /* * Make sure the packet has the minimum ethernet packet size, padding * with zeros if necessary. */ if (total_len < MIN_ETH_FRAME_SIZE) { memset(tx_buffer, 0, MIN_ETH_FRAME_SIZE - total_len); total_len = MIN_ETH_FRAME_SIZE; } /* * Flush the data cache here if necessary. This ensures the 8139 can * read the correct data from the transmit buffer. */#ifdef CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY HAL_DCACHE_FLUSH(rltk8139_info->tx_buffer + TX_BUF_SIZE * desc, total_len);#endif /* * Now setup the correct transmit descriptor to actually send the data. * The early TX threshold is incremented by the driver until we reach a * size that prevents transmit underruns. (An earlier attempt to calculate * this parameter from the packet size didn't work). */ OUTL((rltk8139_info->tx_threshold << ERTXTH_SHIFT) | (total_len & SIZE), rltk8139_info->base_address + TSD0 + (desc<<2));}/* * This routine actually retrieves data from the receive ring by * copying it into the specified scatter/gather buffers. Again, * we assume the scatter/gather list is OK and don't check against * total length. */static voidrltk8139_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len){ Rltk8139_t *rltk8139_info; struct eth_drv_sg *last_sg; cyg_uint8 *rx_buffer; rltk8139_info = (Rltk8139_t *)(sc->driver_private); rx_buffer = rltk8139_info->rx_current; /* * Invalidate the cache line(s) mapped to the receive buffer * if necessary. */#ifdef CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY HAL_DCACHE_INVALIDATE(rx_buffer, rltk8139_info->rx_size);#endif for (last_sg = &sg_list[sg_len]; sg_list < last_sg; ++sg_list) { memcpy((void *)(sg_list->buf), rx_buffer, sg_list->len); rx_buffer += sg_list->len; }}/* * This function does all the heavy lifting associated with interrupts. */static voidrltk8139_deliver(struct eth_drv_sc *sc){ Rltk8139_t *rltk8139_info; cyg_uint16 status, pci_status; cyg_uint32 tsd; int desc; rltk8139_info = (Rltk8139_t *)(sc->driver_private); /* * The RealTek data sheet claims that reading the ISR will clear * it. This is incorrect; to clear a bit in the ISR, a '1' must be * written to the ISR instead. We immediately clear the interrupt * bits at this point. */ status = INW(rltk8139_info->base_address + ISR); OUTW(status, rltk8139_info->base_address + ISR);#ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_deliver(%s): %04x\n", sc->dev_name, status);#endif /* * Check for a PCI error. This seems like a very serious error to * me, so we will reset the chip and hope for the best. */ if (status & IR_SERR) { cyg_pci_read_config_uint16(rltk8139_info->pci_device_id, CYG_PCI_CFG_STATUS, &pci_status); cyg_pci_write_config_uint16(rltk8139_info->pci_device_id, CYG_PCI_CFG_STATUS, pci_status);#ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_deliver(%s): PCI error %04x\n", sc->dev_name, pci_status);#endif rltk8139_reset(sc); return; } /* Check for transmission complete (with errors or not) */ if ((status & IR_TER) || (status & IR_TOK)) { /* * Figure out which descriptors' status must be checked. We lock out * interrupts while manipulating the descriptor list because we do not * want to be interrupted at this point. */ while (1) { cyg_drv_isr_lock(); /* Check if all descriptors are ready, in which case we are done. */ if (rltk8139_info->tx_num_free_desc >= NUM_TX_DESC) { cyg_drv_isr_unlock(); break; } desc = (rltk8139_info->tx_free_desc - (NUM_TX_DESC - rltk8139_info->tx_num_free_desc)) & (NUM_TX_DESC - 1); cyg_drv_isr_unlock(); /* Get the current status of the descriptor */ tsd = INL(rltk8139_info->base_address + TSD0 + (desc<<2)); /* * If a transmit FIFO underrun occurred, increment the threshold * value. */ if ((tsd & TUN) && (rltk8139_info->tx_threshold < 64)) rltk8139_info->tx_threshold += 1; /* * Check if a transmission completed OK. RealTek's data sheet implies * that a successful transmission that experiences underrun will only * set TUN. This is not true; TOK is set for all successful * transmissions. */ if (tsd & TOK) { (sc->funs->eth_drv->tx_done)(sc, rltk8139_info->tx_desc_key[desc], 0); } else if (tsd & TABT) { /* * Set the CLRABT bit in TCR. Since I haven't encountered any * transmission aborts so far, I don't really know if this code * will work or not. */ OUTL(INL(rltk8139_info->base_address + TCR) & CLRABT, rltk8139_info->base_address + TCR); (sc->funs->eth_drv->tx_done)(sc, rltk8139_info->tx_desc_key[desc], -1); } else { /* * This descriptor is not ready. Since the descriptors are used * in a ring, this means that no more descriptors are ready. */ break; } /* * Clear the saved key value. This is not really necessary, since * the key value is never used to determine if a descriptor is * valid or not. However, clearing it is a tidier IMO. */ rltk8139_info->tx_desc_key[desc] = 0; /* * Increment the free descriptor count and go through the loop again * to see if more descriptors are ready. */ cyg_drv_isr_lock(); rltk8139_info->tx_num_free_desc += 1; cyg_drv_isr_unlock(); } } if (status & IR_ROK) { /* * Received a frame. Note that '_deliver' does not actually copy any * data; it just determines how many bytes are available and tells * the generic ethernet driver about it, which then calls into * the '_recv' function to copy the data. */ cyg_uint16 rx_pos; cyg_uint32 header, length; /* * CAPR contains the index into the receive buffer. It is controlled * completely in software. For some reason, CAPR points 16 bytes * before the actual start of the packet. */ rx_pos = (INW(rltk8139_info->base_address + CAPR) + 16) % RX_BUF_LEN; /* * Loop until the rx buffer is empty. I have no idea how the 8139 * determines if the buffer still contains a packet; it may check * that CAPR points 16 bytes before CBR. */ while (!(INB(rltk8139_info->base_address + CR) & BUFE)) { /* * Invalidate the data cache for the cache line containing the header * if necessary. */#ifdef CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY HAL_DCACHE_INVALIDATE(&rltk8139_info->rx_ring[rx_pos], sizeof(cyg_uint32));#endif /* * The 8139 prepends each packet with a 32 bit packet header that * contains a 16 bit length and 16 bit status field, in little-endian * byte order. */ header = HAL_LE32TOC(*((volatile cyg_uint32 *)CYGARC_UNCACHED_ADDRESS(&rltk8139_info->rx_ring[rx_pos]))); /* * If the 8139 is still copying data for this packet into the * receive ring, it will set packet length to 0xfff0. This shouldn't * ever happen because we do not use early receive. */ if ((header >> 16) == 0xFFF0) break; /* * Since the 8139 appends the ethernet CRC to every packet, we * must subtract 4 from the length to get the true packet length. */ length = (header >> 16) - 4; /* * Check if the packet was received correctly. The OpenBSD driver * resets the chip if this is not the case; we attempt to salvage * following packets by doing nothing. */ if (!(header & HDR_ROK)) { } else { /* * Packet was received OK. Determine from where to start copying * bytes. This is saved in the driver private area so rlt8139_recv * doesn't have to redetermine this information. Then, inform * the generic ethernet driver about the packet. */ rltk8139_info->rx_current = CYGARC_UNCACHED_ADDRESS(rltk8139_info->rx_ring + rx_pos + 4); rltk8139_info->rx_size = length; /* Tell eCos about the packet */ (sc->funs->eth_drv->recv)(sc, length); } /* * Update CAPR. CAPR must be aligned to a 32 bit boundary, and should * point 16 bytes before it's actual position. */ rx_pos = ((rx_pos + length + 8 + 3) & ~3) % RX_BUF_LEN; OUTW((rx_pos - 16) % RX_BUF_LEN, rltk8139_info->base_address + CAPR); } } if (status & IR_RXOVW) { /* * In case of a receive buffer overflow, the RealTek data sheet claims we * should update CAPR and then write a '1'爐o ROK in ISR. However, none of * the other 8139 drivers I have looked at do this, so we will just reset * the chip instead. */#ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_deliver(%s): receive buffer overflow\n", sc->dev_name);#endif rltk8139_reset(sc); return; } if (status & IR_FOVW) { /* * Rx FIFO overflow. According to RealTek's data sheet, this is cleared * by writing a '1' to RXOVW. Again, none of the 8139 drivers I have * seen actually do this, so we reset the chip instead. */#ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_deliver(%s): receive FIFO overflow\n", sc->dev_name);#endif rltk8139_reset(sc); return; }#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE /* Finally, reenable interrupts */#ifdef CYGPKG_DEVS_ETH_RLTK_8139_MASK_INTERRUPTS_IN_8139 OUTW(RLTK8139_IRQ, rltk8139_info->base_address + IMR);#else cyg_interrupt_unmask(rltk8139_info->vector);#endif#endif}/* * '_poll' does the same thing as '_deliver'. It is called periodically when * the ethernet driver is operated in non-interrupt mode, for instance by * RedBoot. */static voidrltk8139_poll(struct eth_drv_sc *sc){ Rltk8139_t *rltk8139_info; cyg_uint16 isr;#ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_poll(%s)\n", sc->dev_name);#endif rltk8139_info = (Rltk8139_t *)(sc->driver_private); /* * Get the current interrupt status. If anything changed, call * _deliver. */ if ((isr = INW(rltk8139_info->base_address + ISR))) rltk8139_deliver(sc);}/* * Return the interrupt vector used by this device. */static intrltk8139_int_vector(struct eth_drv_sc *sc){ return ((Rltk8139_t *)(sc->driver_private))->vector;}/* * Quick and dirty register dump. This is somewhat dangerous, since * we read the register space 32 bits at a time, regardless of actual * register sizes. */#ifdef DEBUG_RLTK8139_DRIVERvoidrltk8139_print_state(struct eth_drv_sc *sc) { int i; Rltk8139_t *rltk8139_info; rltk8139_info = (Rltk8139_t *)(sc->driver_private); for (i = IDR0; i < FFER; i += 16) { diag_printf("8139 reg address 0x%02x = 0x%08x", i, INL(rltk8139_info->base_address + i)); diag_printf(" 0x%08x", INL(rltk8139_info->base_address + i + 4)); diag_printf(" 0x%08x", INL(rltk8139_info->base_address + i + 8)); diag_printf(" 0x%08x\n", INL(rltk8139_info->base_address + i + 12)); }}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -