diff options
Diffstat (limited to 'bsps/shared/net/cs8900.c')
-rw-r--r-- | bsps/shared/net/cs8900.c | 1216 |
1 files changed, 1216 insertions, 0 deletions
diff --git a/bsps/shared/net/cs8900.c b/bsps/shared/net/cs8900.c new file mode 100644 index 0000000..452a33a --- /dev/null +++ b/bsps/shared/net/cs8900.c @@ -0,0 +1,1216 @@ +/* + ------------------------------------------------------------------------ + + Copyright Cybertec Pty Ltd, 2000 + All rights reserved Cybertec Pty Ltd, 2000 + + Port to the DIMM PC copyright (c) 2004 Angelo Fraietta + This project has been assisted by the Commonwealth Government + through the Australia Council, its arts funding and advisory body. + + COPYRIGHT (c) 1989-1998. + On-Line Applications Research Corporation (OAR). + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution or at + http://www.rtems.org/license/LICENSE. + + ------------------------------------------------------------------------ + + CS8900 RTEMS driver. + + See the header file for details. + +*/ + +#include <machine/rtems-bsd-kernel-space.h> + +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include <libchip/cs8900.h> + +/* + * We expect to be able to read a complete packet into an mbuf. + */ + +#if (MCLBYTES < 1520) +#error "CS8900 Driver must have MCLBYTES >= 1520" +#endif + +/* + * Task event usage. + */ + +#define CS8900_RX_OK_EVENT RTEMS_EVENT_1 +#define CS8900_TX_START_EVENT RTEMS_EVENT_1 +#define CS8900_TX_OK_EVENT RTEMS_EVENT_2 +#define CS8900_TX_WAIT_EVENT RTEMS_EVENT_3 + +/* + * IO Packet Page inteface. + */ + +static inline unsigned short +io_pp_get_reg_16 (cs8900_device *cs, unsigned short reg) +{ + rtems_interrupt_level level; + unsigned short data; + rtems_interrupt_disable (level); + cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR, + 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg); + data = cs8900_io_get_reg (cs, CS8900_IO_PP_DATA_PORT0); + rtems_interrupt_enable (level); + return data; +} + +static inline uint32_t +io_pp_get_reg_32 (cs8900_device *cs, uint16_t reg) +{ + rtems_interrupt_level level; + uint32_t data; + rtems_interrupt_disable (level); + cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR, + 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg); + data = cs8900_io_get_reg (cs, CS8900_IO_PP_DATA_PORT0); + data <<= 16; + data |= cs8900_io_get_reg (cs, CS8900_IO_PP_DATA_PORT1); + rtems_interrupt_enable (level); + return data; +} + +static inline void +io_pp_set_reg_16 (cs8900_device *cs, unsigned short reg, unsigned short data) +{ + rtems_interrupt_level level; + rtems_interrupt_disable (level); + cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR, + 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg); + cs8900_io_set_reg (cs, CS8900_IO_PP_DATA_PORT0, data); + rtems_interrupt_enable (level); +} + +static inline void +io_pp_set_reg_32 (cs8900_device *cs, unsigned short reg, unsigned long data) +{ + cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR, + 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg); + cs8900_io_set_reg (cs, CS8900_IO_PP_DATA_PORT0, data >> 16); + cs8900_io_set_reg (cs, CS8900_IO_PP_DATA_PORT1, data); +} + +static inline void +io_pp_bit_set_reg_16 (cs8900_device *cs, unsigned short reg, unsigned short mask) +{ + rtems_interrupt_level level; + rtems_interrupt_disable (level); + io_pp_set_reg_16 (cs, reg, io_pp_get_reg_16 (cs, reg) | mask); + rtems_interrupt_enable (level); +} + +static inline void +io_pp_bit_clear_reg_16 (cs8900_device *cs, unsigned short reg, unsigned short mask) +{ + rtems_interrupt_level level; + rtems_interrupt_disable (level); + io_pp_set_reg_16 (cs, reg, io_pp_get_reg_16 (cs, reg) & ~mask); + rtems_interrupt_enable (level); +} + +/* + * Memory Mapped Packet Page interface. + * + * If the BSP does not configure mem_base use the I/O register accesses. + */ + +static inline unsigned short +mem_pp_get_reg (cs8900_device *cs, unsigned short reg) +{ + if (!cs->mem_base) + return io_pp_get_reg_16 (cs, reg); + return cs8900_mem_get_reg (cs, reg); +} + +static inline void +mem_pp_set_reg (cs8900_device *cs, unsigned short reg, unsigned short data) +{ + if (!cs->mem_base) + io_pp_set_reg_16 (cs, reg, data); + else + cs8900_mem_set_reg (cs, reg, data); +} + +static inline void +mem_pp_bit_set_reg (cs8900_device *cs, unsigned short reg, unsigned short mask) +{ + if (!cs->mem_base) + io_pp_bit_set_reg_16 (cs, reg, mask); + else + { + rtems_interrupt_level level; + rtems_interrupt_disable (level); + mem_pp_set_reg (cs, reg, mem_pp_get_reg (cs, reg) | mask); + rtems_interrupt_enable (level); + } +} + +static inline void +mem_pp_bit_clear_reg (cs8900_device *cs, unsigned short reg, unsigned short mask) +{ + if (!cs->mem_base) + io_pp_bit_clear_reg_16 (cs, reg, mask); + else + { + rtems_interrupt_level level; + rtems_interrupt_disable (level); + mem_pp_set_reg (cs, reg, mem_pp_get_reg (cs, reg) & ~mask); + rtems_interrupt_enable (level); + } +} + +/* + * Trace defines and control structures. + */ + +#define CS8900_T_INT (0) +#define CS8900_T_RX_OK (1) +#define CS8900_T_RX_DROPPED (2) +#define CS8900_T_NO_MBUF (3) +#define CS8900_T_NO_CLUSTERS (4) +#define CS8900_T_RX_BEGIN (5) +#define CS8900_T_RX_END (6) + +#if CS8900_TRACE + +static const char *cs8900_trace_labels[] = +{ + "int", + "rx ok", + "rx dropped", + "no mbuf", + "no clusters", + "rx begin", + "rx end" +}; + +/* + * Assumes a micro-second timer such as the Coldfire. + */ + +uint32_t rtems_read_timer (); + +static inline void +cs8900_trace (cs8900_device *cs, unsigned short key, unsigned long var) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + if (cs->trace_in < CS8900_TRACE_SIZE) + { + cs->trace_key[cs->trace_in] = key; + cs->trace_var[cs->trace_in] = var; + cs->trace_time[cs->trace_in] = rtems_read_timer (); + cs->trace_in++; + } + + rtems_interrupt_enable (level); +} +#else +#define cs8900_trace(c, k, v) +#endif + +void cs8900_get_mac_addr (cs8900_device *cs, unsigned char *mac_address) +{ + unsigned short ma; + + /* + * Only ever use IO calls for this function as it can be + * called before memory mode has been enabled. + */ + + ma = io_pp_get_reg_16 (cs, CS8900_PP_IA); + mac_address[0] = ma >> 8; + mac_address[1] = ma; + + ma = io_pp_get_reg_16 (cs, CS8900_PP_IA + 2); + mac_address[2] = ma >> 8; + mac_address[3] = ma; + + ma = io_pp_get_reg_16 (cs, CS8900_PP_IA + 4); + mac_address[4] = ma >> 8; + mac_address[5] = ma; +} + +/* + * Bring the chip online. + */ + +static void +cs8900_hardware_init (cs8900_device *cs) +{ + unsigned long prod_id; + unsigned short status; + + /* + * Do nothing while the device is calibrating and checking the EEPROM. + * We must wait 20msecs. + */ + + io_pp_bit_set_reg_16 (cs, CS8900_PP_SelfCTL, CS8900_SELF_CTRL_RESET); + + rtems_task_wake_after (RTEMS_MILLISECONDS_TO_TICKS (20)); + + status = io_pp_get_reg_16 (cs, CS8900_PP_SelfST); + if (status == 0) { + printf("Reading status register again\n"); + status = io_pp_get_reg_16 (cs, CS8900_PP_SelfST); + } + + if (((status & CS8900_SELF_STATUS_INITD) == 0) || + ((status & CS8900_SELF_STATUS_INITD) && + (status & CS8900_SELF_STATUS_EEPROM_PRESENT) && + (status & CS8900_SELF_STATUS_SIBUST))) + { + printf ("CS8900: %s. Initialisation aborted.\n", + (status & CS8900_SELF_STATUS_INITD) ? + "EEPROM read/write failed to complete" : + "Failed to complete to reset"); + return; + } + + /* Set the RX queue size if not set by the BSP. */ + + if (cs->rx_queue_size == 0) + cs->rx_queue_size = 10; + + /* Probe the device for its ID */ + + prod_id = io_pp_get_reg_32 (cs, CS8900_PP_PROD_ID); + + if ((prod_id >> 16) != CS8900_ESIA_ID) + { + printf ("CS8900: Invalid EISA ID, read product code 0x%08lx\n", prod_id); + return; + } + + if ((prod_id & 0x000000ff) != 0) + { + printf ("CS8900: Unsupported product id, read product code 0x%08lx\n", + prod_id); + return; + } + + printf ("CS8900 Rev %ld, %s, %s.\n", + (prod_id >> 8) & 0x1f, + status & CS8900_SELF_STATUS_3_3_V ? "3.3V" : "5.0V", + status & CS8900_SELF_STATUS_EEPROM_PRESENT ? + "EEPROM present" : "no EEPROM"); + + /* + * Switch to memory base accesses as they are faster. No indirect access. + */ + + if (cs->mem_base) + { + io_pp_set_reg_16 (cs, CS8900_PP_MEM_BASE, cs->mem_base); + io_pp_set_reg_16 (cs, CS8900_PP_MEM_BASE + 2, (cs->mem_base >> 16) & 0xf); + + io_pp_set_reg_16 (cs, + CS8900_PP_BusCTL, + CS8900_BUS_CTRL_RESET_RX_DMA | + CS8900_BUS_CTRL_USE_SA | + CS8900_BUS_CTRL_MEMORY_ENABLE); + io_pp_set_reg_16 (cs, + CS8900_PP_BusCTL, + CS8900_BUS_CTRL_USE_SA | + CS8900_BUS_CTRL_MEMORY_ENABLE); + } + + /* + * We are now in memory mapped mode. + */ + + /* + * Program the Line Control register with the mode we want. + * + * No auto detect support at the moment. Only 10BaseT. + */ + + mem_pp_set_reg (cs, CS8900_PP_LineCFG, CS8900_LINE_CTRL_10BASET); + + /* + * Ask the user for the MAC address, the program into the device. + */ + +#define MACO(o) cs->arpcom.ac_enaddr[o] + + mem_pp_set_reg (cs, CS8900_PP_IA, + (((unsigned int) MACO (1)) << 8) | + ((unsigned int) MACO (0))); + mem_pp_set_reg (cs, CS8900_PP_IA + 2, + (((unsigned int) MACO (3)) << 8) | + ((unsigned int) MACO (2))); + mem_pp_set_reg (cs, CS8900_PP_IA + 4, + (((unsigned int) MACO (5)) << 8) | + ((unsigned int) MACO (4))); + + /* + * Set the Buffer configuration. + */ + + mem_pp_set_reg (cs, CS8900_PP_BufCFG, + CS8900_BUFFER_CONFIG_RDY_FOR_TX | + CS8900_BUFFER_CONFIG_TX_UNDERRUN | + CS8900_BUFFER_CONFIG_TX_COL_OVF | + CS8900_BUFFER_CONFIG_RX_MISSED_OVF); + + /* + * Set the Receiver configuration. + */ + + mem_pp_set_reg (cs, CS8900_PP_RxCFG, + CS8900_RX_CONFIG_RX_OK | + CS8900_RX_CONFIG_CRC_ERROR | + CS8900_RX_CONFIG_RUNT| + CS8900_RX_CONFIG_EXTRA_DATA); + + /* + * Set the Receiver control. + */ + + mem_pp_set_reg (cs, CS8900_PP_RxCTL, + CS8900_RX_CTRL_RX_OK | + CS8900_RX_CTRL_MULTICAST | + CS8900_RX_CTRL_INDIVIDUAL | + CS8900_RX_CTRL_BROADCAST); + + /* + * Set the Transmitter configuration. + */ + + mem_pp_set_reg (cs, CS8900_PP_TxCFG, + CS8900_TX_CONFIG_TX_OK | + CS8900_TX_CONFIG_OUT_OF_WINDOW | + CS8900_TX_CONFIG_JABBER | + CS8900_TX_CONFIG_16_COLLISION); + + /* + * Attach the interrupt handler. + */ + + cs8900_attach_interrupt (cs); + + /* + * Program the interrupt level we require then enable interrupts. + * + * Note, this will need to change to support other levels. + */ + + mem_pp_set_reg (cs, CS8900_PP_INT, cs->irq_level & 3); + + mem_pp_bit_set_reg (cs, CS8900_PP_BusCTL, + CS8900_BUS_CTRL_ENABLE_INT); +} + +rtems_isr +cs8900_interrupt (rtems_vector_number v, void *csp) +{ + cs8900_device *cs = csp; + unsigned short isq = 0; + struct mbuf *m; + unsigned char *p; + + ++cs->eth_stats.interrupts; + + while (1) + { + isq = mem_pp_get_reg (cs, CS8900_PP_ISQ); + + cs8900_trace (cs, CS8900_T_INT, isq); + + /* + * No more interrupts to service. + */ + + if (isq == 0) + return; + + switch (isq & 0x1f) + { + case 0x04: + + /* + * RxEvent. + */ + + ++cs->eth_stats.rx_interrupts; + + if (isq & CS8900_RX_EVENT_RX_OK) + { + m = cs->rx_ready_head; + if (m) + { + cs->rx_ready_head = m->m_nextpkt; + if (cs->rx_ready_head == 0) + cs->rx_ready_tail = 0; + m->m_nextpkt = 0; + cs->rx_ready_len--; + + p = mtod (m, unsigned char *); + + m->m_pkthdr.len = cs8900_get_data_block (cs, p); + + if (cs->rx_loaded_tail == 0) + cs->rx_loaded_head = m; + else + cs->rx_loaded_tail->m_nextpkt = m; + cs->rx_loaded_tail = m; + cs->rx_loaded_len++; + + if (cs->rx_loaded_len == 1) + { + cs8900_trace (cs, CS8900_T_RX_OK, cs->rx_loaded_len); + rtems_bsdnet_event_send (cs->rx_task, CS8900_RX_OK_EVENT); + } + } + else + { + ++cs->eth_stats.rx_dropped; + + cs8900_trace (cs, CS8900_T_RX_DROPPED, cs->rx_loaded_len); + + if (cs->rx_loaded_len == 0) + rtems_bsdnet_event_send (cs->rx_task, CS8900_RX_OK_EVENT); + } + } + else + { + if (isq & CS8900_RX_EVENT_CRC_ERROR) + ++cs->eth_stats.rx_crc_errors; + + if (isq & CS8900_RX_EVENT_RUNT) + ++cs->eth_stats.rx_runt_errors; + + if (isq & CS8900_RX_EVENT_EXTRA_DATA) + ++cs->eth_stats.rx_oversize_errors; + } + break; + + case 0x08: + + /* + * TxEvent. + */ + + ++cs->eth_stats.tx_interrupts; + + if (cs->tx_active) + { + if (isq & CS8900_TX_EVENT_TX_OK) + ++cs->eth_stats.tx_ok; + + cs->tx_active = 0; + + rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_OK_EVENT); + } + break; + + case 0x0c: + + /* + * BufEvent. + */ + + if (isq & CS8900_BUFFER_EVENT_RDY_FOR_TX) + { + if (cs->tx_active) + { + ++cs->eth_stats.tx_rdy4tx; + rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_WAIT_EVENT); + } + } + else if (isq & CS8900_BUFFER_EVENT_TX_UNDERRUN) + { + ++cs->eth_stats.tx_underrun_errors; + if (cs->tx_active) + rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_OK_EVENT); + } + else if (isq & CS8900_BUFFER_EVENT_SW_INT) + { + ++cs->eth_stats.int_swint_res; + } + break; + + case 0x10: + + /* + * RxMiss. + */ + + cs->eth_stats.rx_missed_errors += + mem_pp_get_reg (cs, CS8900_PP_RxMISS) >> 6; + break; + + case 0x12: + + /* + * TxCol. + */ + + cs->eth_stats.tx_collisions += + mem_pp_get_reg (cs, CS8900_PP_TxCol) >> 6; + break; + + default: + break; + } + } + +} + +int +cs8900_link_active (cs8900_device *cs) +{ + return ((mem_pp_get_reg (cs, CS8900_PP_LineST) & CS8900_LINE_STATUS_LINK_OK) ? + 1 : 0); +} + +static inline void +cs8900_rx_refill_queue (cs8900_device *cs) +{ + struct ifnet *ifp = &cs->arpcom.ac_if; + struct mbuf *m; + rtems_interrupt_level level; + + /* + * Hold a single queue of mbuf's at the interface. This + * will lower the latency of the driver. + */ + + while (cs->rx_ready_len < cs->rx_queue_size) + { + MGETHDR (m, M_DONTWAIT, MT_DATA); + + if (!m) + { + ++cs->eth_stats.rx_no_mbufs; + cs8900_trace (cs, CS8900_T_NO_MBUF, cs->eth_stats.rx_no_mbufs); + return; + } + + MCLGET (m, M_DONTWAIT); + + if (!m->m_ext.ext_buf) + { + ++cs->eth_stats.rx_no_clusters; + cs8900_trace (cs, CS8900_T_NO_CLUSTERS, cs->eth_stats.rx_no_clusters); + m_free (m); + return; + } + m->m_pkthdr.rcvif = ifp; + m->m_nextpkt = 0; + + rtems_interrupt_disable (level); + + if (cs->rx_ready_tail == 0) + cs->rx_ready_head = m; + else + cs->rx_ready_tail->m_nextpkt = m; + cs->rx_ready_tail = m; + cs->rx_ready_len++; + + rtems_interrupt_enable (level); + } +} + +static void +cs8900_rx_task (void *arg) +{ + cs8900_device *cs = arg; + struct ifnet *ifp = &cs->arpcom.ac_if; + rtems_event_set events; + struct mbuf *m; + struct ether_header *eh; + rtems_status_code sc; + rtems_interrupt_level level; + + /* + * Turn the receiver and transmitter on. + */ + + mem_pp_bit_set_reg (cs, CS8900_PP_LineCFG, + CS8900_LINE_CTRL_RX_ON | + CS8900_LINE_CTRL_TX_ON); + + /* + * Start the software interrupt watchdog. + */ + + mem_pp_bit_set_reg (cs, CS8900_PP_BufCFG, + CS8900_BUFFER_CONFIG_SW_INT); + ++cs->eth_stats.int_swint_req; + + /* + * Loop reading packets. + */ + + while (1) + { + cs8900_rx_refill_queue (cs); + + sc = rtems_bsdnet_event_receive (CS8900_RX_OK_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_MILLISECONDS_TO_TICKS (250), + &events); + + cs8900_rx_refill_queue (cs); + + if (sc == RTEMS_TIMEOUT) + { + /* + * We need to check the interrupt hardware in the cs8900a + * has not locked up. It seems this occurs if the ISQ + * queue fills up. + * To test we generate a software interrupt and watch + * a counter go up. If the counter does not go for 2 + * software interrupts requests we flush the ISQ queue. + */ + + if ((cs->eth_stats.int_swint_req - cs->eth_stats.int_swint_res) > 1) + { + printf ("cs8900: int lockup, isq flush\n"); + + mem_pp_bit_clear_reg (cs, CS8900_PP_BusCTL, + CS8900_BUS_CTRL_ENABLE_INT); + + while (mem_pp_get_reg (cs, CS8900_PP_ISQ) != 0); + + cs->eth_stats.int_swint_req = cs->eth_stats.int_swint_res = 0; + ++cs->eth_stats.int_lockup; + + mem_pp_bit_set_reg (cs, CS8900_PP_BusCTL, + CS8900_BUS_CTRL_ENABLE_INT); + } + + mem_pp_bit_set_reg (cs, CS8900_PP_BufCFG, + CS8900_BUFFER_CONFIG_SW_INT); + ++cs->eth_stats.int_swint_req; + } + + cs8900_trace (cs, CS8900_T_RX_BEGIN, cs->rx_loaded_len); + + while (cs->rx_loaded_len) + { + rtems_interrupt_disable (level); + + m = cs->rx_loaded_head; + if (m) + { + cs->rx_loaded_head = m->m_nextpkt; + if (cs->rx_loaded_head == 0) + cs->rx_loaded_tail = 0; + m->m_nextpkt = 0; + cs->rx_loaded_len--; + + rtems_interrupt_enable (level); + + m->m_pkthdr.rcvif = ifp; + + cs->eth_stats.rx_bytes += m->m_pkthdr.len; + + m->m_len = m->m_pkthdr.len = m->m_pkthdr.len - sizeof (struct ether_header); + + eh = mtod (m, struct ether_header *); + m->m_data += sizeof (struct ether_header); + + ++cs->eth_stats.rx_packets; + + ether_input (ifp, eh, m); + } + else + { + rtems_interrupt_enable (level); + } + } + cs8900_trace (cs, CS8900_T_RX_END, cs->rx_loaded_len); + } +} + +static void +cs8900_tx_task (void *arg) +{ + cs8900_device *cs = arg; + struct ifnet *ifp = &cs->arpcom.ac_if; + rtems_event_set events; + struct mbuf *m; + rtems_status_code sc; + + /* + * Wait for the link to come up. + */ + + rtems_task_wake_after (RTEMS_MILLISECONDS_TO_TICKS (750)); + + /* + * Loop processing the tx queue. + */ + + while (1) + { + /* + * Fetch the mbuf list from the interface's queue. + */ + + IF_DEQUEUE (&ifp->if_snd, m); + + /* + * If something actually is present send it. + */ + + if (!m) + { + ifp->if_flags &= ~IFF_OACTIVE; + + rtems_bsdnet_event_receive (CS8900_TX_START_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + } + else + { + if (cs8900_link_active (cs)) + { + int resending; + + do + { + unsigned short buf_status; + + resending = 0; + + cs->tx_active = 1; + + mem_pp_set_reg (cs, CS8900_PP_TxCMD, + CS8900_TX_CMD_STATUS_TX_START_ENTIRE | + CS8900_TX_CMD_STATUS_FORCE); + mem_pp_set_reg (cs, CS8900_PP_TxLength, m->m_pkthdr.len); + + buf_status = mem_pp_get_reg (cs, CS8900_PP_BusST); + + /* + * If the bid for memory in the device fails trash the + * transmit and try again next time. + */ + + if (buf_status & CS8900_BUS_STATUS_TX_BID_ERROR) + ++cs->eth_stats.tx_bid_errors; + else + { + /* + * If the buffer is not read enable the interrupt and then wait. + */ + + if ((buf_status & CS8900_BUS_STATUS_RDY_FOR_TX_NOW) == 0) + { + cs->eth_stats.tx_wait_for_rdy4tx++; + sc = rtems_bsdnet_event_receive (CS8900_TX_WAIT_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_MILLISECONDS_TO_TICKS (750), + &events); + if (sc == RTEMS_TIMEOUT) + { + /* + * For some reason the wait request has been dropped, + * so lets resend from the start. + */ + + printf ("tx resend\n"); + ++cs->eth_stats.tx_resends; + resending = 1; + } + } + + if (!resending) + { + cs8900_tx_load (cs, m); + cs->eth_stats.tx_packets++; + cs->eth_stats.tx_bytes += m->m_pkthdr.len; + } + } + } + while (resending); + + m_freem (m); + + do + { + rtems_bsdnet_event_receive (CS8900_TX_OK_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + } + while (cs->tx_active); + } + else + { + ++cs->eth_stats.tx_dropped; + m_freem (m); + } + } + } +} + +static void +cs8900_start (struct ifnet *ifp) +{ + cs8900_device *cs = ifp->if_softc; + + /* + * Tell the transmit daemon to wake up and send a packet. + */ + + ifp->if_flags |= IFF_OACTIVE; + + rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_START_EVENT); +} + +static void +cs8900_stop (cs8900_device *cs) +{ + mem_pp_bit_clear_reg (cs, CS8900_PP_LineCFG, + CS8900_LINE_CTRL_RX_ON | + CS8900_LINE_CTRL_TX_ON); + + mem_pp_bit_clear_reg (cs, CS8900_PP_BusCTL, + CS8900_BUS_CTRL_ENABLE_INT); +} + +static const char *eth_statistics_labels[] = +{ + "rx packets", + "tx packets", + "rx bytes", + "tx bytes", + "rx interrupts", + "tx interrupts", + "rx dropped", + "rx no mbuf", + "rx no custers", + "rx oversize errors", + "rx crc errors", + "rx runt errors", + "rx missed errors", + "tx ok", + "tx collisions", + "tx bid errors", + "tx wait for rdy4tx", + "tx rdy4tx", + "tx underrun errors", + "tx dropped", + "tx resends", + "int swint req", + "int swint res", + "int lockup", + "interrupts" +}; + +static void +cs8900_stats (cs8900_device *cs) +{ + int i; + int max_label = 0; + int len; + unsigned long *value = (unsigned long*) &cs->eth_stats.rx_packets; + + cs->eth_stats.rx_missed_errors += + mem_pp_get_reg (cs, CS8900_PP_RxMISS) >> 6; + + cs->eth_stats.tx_collisions += + mem_pp_get_reg (cs, CS8900_PP_TxCol) >> 6; + + printf ("Network Driver Stats for CS8900 :\n"); + + for (i = 0; i < (sizeof (eth_statistics_labels) / sizeof (const char *)); i++) + { + len = strlen (eth_statistics_labels[i]); + if (len > max_label) + max_label = len; + } + + max_label += 2; + + printf ("%*s - %10u %*s - %10u\n", + max_label, "rx ready len", cs->rx_ready_len, + max_label, "rx loaded len", cs->rx_loaded_len); + + for (i = 0; + i < (sizeof (eth_statistics_labels) / sizeof (const char *)); + i++) + { + printf ("%*s - %10lu", + max_label, eth_statistics_labels[i], value[i]); + + i++; + + if (i < (sizeof (eth_statistics_labels) / sizeof (const char *))) + printf (" %*s - %10lu", + max_label, eth_statistics_labels[i], value[i]); + printf ("\n"); + } + +#if CS8900_TRACE + + for (i = 0; i < cs->trace_in; i++) + { + printf ("%8ld.%03ld ", cs->trace_time[i] / 1000, cs->trace_time[i] % 1000); + + if (cs->trace_key[i] < sizeof (cs8900_trace_labels) / sizeof (char*)) + printf ("%s : ", cs8900_trace_labels[cs->trace_key[i]]); + else + printf ("unknown trace key, %d : ", cs->trace_key[i]); + + if (cs->trace_key[i] == CS8900_T_INT) + { + printf ("0x%04lx ", cs->trace_var[i]); + if (cs->trace_var[i] == 0) + printf ("end"); + else + { + switch (cs->trace_var[i] & 0x1f) + { + case 0x04: + printf ("rx event"); + break; + + case 0x08: + printf ("tx event"); + break; + + case 0x0c: + printf ("buffer event"); + break; + + case 0x10: + printf ("rx missed"); + break; + + case 0x12: + printf ("tx collisions"); + break; + + case 0x1f: + printf ("tx request"); + break; + + case 0x1e: + printf ("tx wait 4 tx"); + break; + + case 0x1d: + printf ("tx already active"); + break; + + default: + printf ("unknown event"); + break; + } + } + } + else + printf ("0x%08lx", cs->trace_var[i]); + + printf ("\n"); + } + + cs->trace_in = 0; + +#endif +} + +static void +cs8900_init (void *arg) +{ + cs8900_device *cs = arg; + struct ifnet *ifp = &cs->arpcom.ac_if; + + if (cs->rx_task == 0) + { + + /* + * Set up the hardware. + */ + + cs8900_hardware_init (cs); + + /* + * Start driver task. We have only one task. + */ + + cs->rx_task = rtems_bsdnet_newproc ("CSr0", 4096, cs8900_rx_task, cs); + cs->tx_task = rtems_bsdnet_newproc ("CSt0", 4096, cs8900_tx_task, cs); + } + +#ifdef todo + /* + * Set flags appropriately + */ + if (ifp->if_flags & IFF_PROMISC) + else +#endif + + /* + * Tell the world that we're running. + */ + + ifp->if_flags |= IFF_RUNNING; + + /* + * Set the Line Control to bring the receive and transmitter online. + */ + + mem_pp_bit_set_reg (cs, CS8900_PP_LineCFG, + CS8900_LINE_CTRL_RX_ON | + CS8900_LINE_CTRL_TX_ON); + + mem_pp_bit_set_reg (cs, CS8900_PP_BusCTL, + CS8900_BUS_CTRL_ENABLE_INT); +} + +static int +cs8900_ioctl (struct ifnet *ifp, ioctl_command_t cmd, caddr_t data) +{ + cs8900_device *cs = ifp->if_softc; + int error = 0; + + switch (cmd) + { + case SIOCGIFADDR: + case SIOCSIFADDR: + + error = ether_ioctl (ifp, cmd, data); + break; + + case SIOCSIFFLAGS: + + switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) + { + case IFF_RUNNING: + + cs8900_stop (cs); + break; + + case IFF_UP: + + cs8900_init (cs); + break; + + case IFF_UP | IFF_RUNNING: + + cs8900_stop (cs); + cs8900_init (cs); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + + cs8900_stats (cs); + break; + + /* FIXME: Multicast commands must be added here. */ + + default: + error = EINVAL; + break; + } + + return error; +} + +int +cs8900_driver_attach (struct rtems_bsdnet_ifconfig *config, int attaching) +{ + cs8900_device *cs; + struct ifnet *ifp; + int mtu; + int unit; + char *name; + + /* + * Parse driver name + */ + + if ((unit = rtems_bsdnet_parse_driver_name (config, &name)) < 0) + return 0; + + cs = config->drv_ctrl; + cs->dev = unit; + ifp = &cs->arpcom.ac_if; + + if (attaching) + { + if (ifp->if_softc) + { + printf ("Driver `%s' already in use.\n", config->name); + return 0; + } + + /* + * Process options + */ + + if (config->hardware_address) + memcpy (cs->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + else + cs8900_get_mac_addr (cs, cs->arpcom.ac_enaddr); + + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + + cs->accept_bcast = !config->ignore_broadcast; + + /* + * Set up network interface values. + */ + + ifp->if_softc = cs; + ifp->if_unit = unit; + ifp->if_name = name; + ifp->if_mtu = mtu; + ifp->if_init = cs8900_init; + ifp->if_ioctl = cs8900_ioctl; + ifp->if_start = cs8900_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + + if (ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + /* + * Attach the interface to the stack. + */ + + if_attach (ifp); + ether_ifattach (ifp); + } + else + { + if (!ifp->if_softc) + { + printf ("Driver `%s' not found.\n", config->name); + return 0; + } + + cs8900_stop (cs); + cs8900_detach_interrupt (cs); + } + + return 1; +} |