/* * RTEMS CS8900 Driver Setup for the DIMM-PC/i386 made by Kontron. * * 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. * * Port performed by Chris Johns, Cybertec Pty Ltd, Jan 2004. * Based on the Cybertec CS8900 driver setup for the SFP-101. * */ #define CS8900_VERBOSE 0 #define HAVE_MRB_CS8900_DATA_BUS_SWAPPED 1 #include #include #include #include #include #include #include #include #include #include #include "cs8900.h" #include /* * Loopback interface. */ extern int rtems_bsdnet_loopattach (struct rtems_bsdnet_ifconfig *, int); static struct rtems_bsdnet_ifconfig loopback_config = { "lo0", /* name */ rtems_bsdnet_loopattach, /* attach function */ NULL, /* link to next interface */ "127.0.0.1", /* IP address */ "255.0.0.0", /* IP net mask */ }; /* * Network configuration */ struct rtems_bsdnet_config rtems_bsdnet_config = { &loopback_config, NULL, 20, /* Network task priority */ 32 * 1024, /* Mbuf capacity */ 96 * 1024, /* Mbuf cluster capacity */ }; static void cs8900_isr (); static void cs8900_int_off (const rtems_irq_connect_data* unused); static void cs8900_int_on (const rtems_irq_connect_data* unused); static int cs8900_int_is_on (const rtems_irq_connect_data *irq); /** * The device's data. */ static cs8900_device cs8900; static rtems_irq_connect_data cs8900_irq = { 0, cs8900_isr, cs8900_int_on, cs8900_int_off, cs8900_int_is_on }; #if CS8900_VERBOSE static int cs8900_io_verbose = 1; #endif /** * Device structure for attaching to the BSD stack. */ static struct rtems_bsdnet_ifconfig cs8900_ifconfig = { "cs0", /* name */ cs8900_driver_attach, /* attach funtion */ NULL, /* next interface */ NULL, /* ip address */ NULL, /* ip netmask */ NULL, /* hardware address */ 0, /* ignore broadcast */ 0, /* mtu */ 0, /* rbuf count */ 0, /* xbuf count */ 0, /* port */ 0, /* irno */ 0, /* bpar */ 0 /* drv ctrl */ }; /* * Commands to register. */ rtems_monitor_command_entry_t rtems_bsdnet_commands[] = { { "ifstats", "Show the interface stats.\n", 0, (void*) rtems_bsdnet_show_if_stats, 0, 0, }, { "ipstats", "Show the IP stats.\n", 0, (void*) rtems_bsdnet_show_ip_stats, 0, 0, }, { "routes", "Show the inet routes.\n", 0, (void*) rtems_bsdnet_show_inet_routes, 0, 0, }, { "mbufs", "Show the mbuf stats.\n", 0, (void*) rtems_bsdnet_show_mbuf_stats, 0, 0, }, { "icmp", "Show the ICMP stats.\n", 0, (void*) rtems_bsdnet_show_icmp_stats, 0, 0, }, { "udp", "Show the UDP stats.\n", 0, (void*) rtems_bsdnet_show_udp_stats, 0, 0, }, { "tcp", "Show the TCP stats.\n", 0, (void*) rtems_bsdnet_show_tcp_stats, 0, 0, } }; static void cs8900_isr () { /* * Note: we could have a high priority task here to call the * drivers handler. The would lower the interrupt latancy * we aother wise have. */ cs8900_interrupt (cs8900_irq.name, &cs8900); } static void cs8900_int_on (const rtems_irq_connect_data *unused) { } static void cs8900_int_off (const rtems_irq_connect_data *unused) { } static int cs8900_int_is_on (const rtems_irq_connect_data *irq) { return BSP_irq_enabled_at_i8259s (irq->name); } void cs8900_io_set_reg (cs8900_device *cs, unsigned short reg, unsigned short data) { #if CS8900_VERBOSE if (cs8900_io_verbose) printk ("CS8900: io set reg=0x%04x, data=0x%04x\n", reg, data); #endif outport_word (cs->io_base + reg, data); } unsigned short cs8900_io_get_reg (cs8900_device *cs, unsigned short reg) { unsigned short data; inport_word (cs->io_base + reg, data); #if CS8900_VERBOSE if (cs8900_io_verbose) printk ("CS8900: io get reg=0x%04x, data=0x%04x\n", reg, data); #endif return data; } void cs8900_mem_set_reg (cs8900_device *cs, unsigned long reg, unsigned short data) { printk ("CS8900: mem_set_reg register access called. Only IO supported.\n"); while (1); } unsigned short cs8900_mem_get_reg (cs8900_device *cs, unsigned long reg) { printk ("CS8900: mem_get_reg register access called. Only IO supported.\n"); while (1); return 0; } void cs8900_put_data_block (cs8900_device *cs, int len, unsigned char *data) { unsigned short *src = (unsigned short *) ((unsigned long) data); for (; len > 1; len -= 2) outport_word (cs->io_base, *src++); if (len) outport_word (cs->io_base, *src++); } unsigned short cs8900_get_data_block (cs8900_device *cs, unsigned char *data) { unsigned short *dst; int cnt; int len; /* * Drop the Rx status first. */ inport_word (cs->io_base, len); /* * Now the length. */ inport_word (cs->io_base, len); dst = (unsigned short *) ((unsigned long) data); cnt = len >> 1; while (cnt--) inport_word (cs->io_base, *dst++); if (len & 1) inport_word (cs->io_base, *dst++); return len; } void cs8900_tx_load (cs8900_device *cs, struct mbuf *m) { unsigned int len; unsigned char *src = 0; unsigned short *wsrc = 0; unsigned char remainder = 0; unsigned char word[2]; while (m) { /* * We can get empty mbufs from the stack. */ len = m->m_len; src = mtod (m, unsigned char*); if (len) { if (remainder) { #if HAVE_MRB_CS8900_DATA_BUS_SWAPPED word[1] = *src++; #else word[0] = *src++; #endif outport_word (cs->io_base, *((unsigned short*) (unsigned long) &word)); len--; remainder = 0; } if (len & 1) { remainder = 1; len--; } wsrc = (unsigned short*) src; for (; len; len -= 2, src += 2) outport_word (cs->io_base, *wsrc++); if (remainder) #if HAVE_MRB_CS8900_DATA_BUS_SWAPPED word[0] = *src++; #else word[1] = *src++; #endif } m = m->m_next; } if (remainder) { #if HAVE_MRB_CS8900_DATA_BUS_SWAPPED word[1] = *src++; #else word[0] = *src++; #endif outport_word (cs->io_base, *((unsigned short*) (unsigned long) &word)); } } void cs8900_attach_interrupt (cs8900_device *cs) { BSP_install_rtems_irq_handler (&cs8900_irq); } void cs8900_detach_interrupt (cs8900_device *cs) { BSP_remove_rtems_irq_handler (&cs8900_irq); } void BSP_cs8900_attach (unsigned long io_base, unsigned long mem_base, int intrp, const char* ip, const char* nm, const char* gw) { cs8900_device *cs = &cs8900; int flags; struct sockaddr_in address; struct sockaddr_in netmask; struct sockaddr_in broadcast; struct sockaddr_in gateway; int cmd; printf ("cso: io=0x%0lx mem=0 irq=%d\n", io_base, intrp); memset (cs, 0, sizeof (cs8900)); cs->dev = 0; cs->rx_queue_size = 30; cs->io_base = io_base; if (mem_base) printf ("cs8900: memory mode is currently not supported.\n"); cs->mem_base = 0; switch (intrp) { case 5: cs->irq_level = 3; break; case 10: cs->irq_level = 0; break; case 11: cs->irq_level = 1; break; case 12: cs->irq_level = 2; break; default: printf ("cs8900: unsupported IRQ level\n"); return; } cs8900_irq.name = intrp; /* * Get the MAC adress from the CS8900. */ cs8900_get_mac_addr (cs, cs->mac_address); /* * Setup the BSD interface configure structure. */ cs8900_ifconfig.drv_ctrl = cs; cs8900_ifconfig.hardware_address = cs->mac_address; printf ("CS8900 initialisation\n"); rtems_bsdnet_attach (&cs8900_ifconfig); /* * Configure the interface using the boot configuration. */ flags = IFF_UP; if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name, SIOCSIFFLAGS, &flags) < 0) { printf ("error: can't bring up %s: %s\n", cs8900_ifconfig.name, strerror (errno)); return; } if (ip && strlen (ip) && nm && strlen (nm)) { printf ("%s: addr: %s netmask: %s gateway: %s\n", cs8900_ifconfig.name, ip, nm, gw ? gw : "none"); memset (&netmask, '\0', sizeof netmask); netmask.sin_len = sizeof netmask; netmask.sin_family = AF_INET; if (!inet_aton (nm, &netmask.sin_addr)) { printf ("error: cannot parse the network mask: %s\n", nm); return; } memset (&address, '\0', sizeof address); address.sin_len = sizeof address; address.sin_family = AF_INET; if (!inet_aton (ip, &address.sin_addr)) { printf ("error: cannot parse the ip address: %s\n", ip); return; } if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name, SIOCSIFNETMASK, &netmask) < 0) { printf ("error: can't set %s netmask: %s\n", cs8900_ifconfig.name, strerror (errno)); return; } if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name, SIOCSIFADDR, &address) < 0) { printf ("error: can't set %s address: %s\n", cs8900_ifconfig.name, strerror (errno)); return; } memset (&broadcast, '\0', sizeof broadcast); broadcast.sin_len = sizeof broadcast; broadcast.sin_family = AF_INET; broadcast.sin_addr.s_addr = (address.sin_addr.s_addr & netmask.sin_addr.s_addr) | ~netmask.sin_addr.s_addr; if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name, SIOCSIFBRDADDR, &broadcast) < 0) { printf ("error: can't set %s broadcast address: %s\n", cs8900_ifconfig.name, strerror (errno)); return; } if (gw && strlen (gw)) { address.sin_addr.s_addr = INADDR_ANY; netmask.sin_addr.s_addr = INADDR_ANY; memset (&gateway, '\0', sizeof gateway); gateway.sin_len = sizeof gateway; gateway.sin_family = AF_INET; if (!inet_aton (gw, &gateway.sin_addr)) printf ("warning: cannot parse the gateway address: %s\n", ip); else { if (rtems_bsdnet_rtrequest (RTM_ADD, (struct sockaddr *) &address, (struct sockaddr *) &gateway, (struct sockaddr *) &netmask, (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL) < 0) printf ("error: can't set default route: %s\n", strerror (errno)); } } } else { rtems_bsdnet_do_bootp_and_rootfs (); } for (cmd = 0; cmd < sizeof (rtems_bsdnet_commands) / sizeof (rtems_monitor_command_entry_t); cmd++) rtems_monitor_insert_cmd (&rtems_bsdnet_commands[cmd]); }