forked from Imagelibrary/rtems
511 lines
11 KiB
Plaintext
511 lines
11 KiB
Plaintext
/*
|
|
* 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]);
|
|
}
|