/*
* 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 <bsp.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <rtems.h>
#include <rtems/monitor.h>
#include <rtems/rtems_bsdnet.h>
#include <irq.h>
#include "cs8900.h"
#include <net/route.h>
/*
* 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]);
}