/* ------------------------------------------------------------------------ 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. */ #define __INSIDE_RTEMS_BSD_TCPIP_STACK__ #include #include #include #include /* * 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; }