summaryrefslogblamecommitdiffstats
path: root/c/src/libchip/network/cs8900.c.bsp
blob: a31d1b0180d6a02a933e63746ba8fde107642ca5 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
  











                                                                       
 

                                          
 
                
 





                      
 
                  




                               
 
                      

  
                      

   
                                                                         
 







                                                        

  
                        

   
                                                
 




                                            

  
 









                                                                  
 




                  

  







                                                     
 













                                                     

  





                                                       
 























































                                          

  

             
 





                                                                 

 

                                                    
 
 
 


                                                     

 

                                                    
 

                                               
 


                                                                                   
                        
                                                                   
      
                                         

 
                                                                        

                      

                                        
                        
                                                                   
      


              













                                                                                   
 
                                                                  
 

                                       

          
                                       

 
                                                                             
 













                                 
                                                  
                 
 

                                      
 

                                      




             
                                                  
 





                               












                                             

                                    
     
                         
      
                                                                               








                      








                                            
     
                        
      
     
 




                  

                                    
     
                     
      
                                                                           


   
                                                
 
                                              

 
                                                
 
                                             

 


                                                                            
 
































































































































































                                                                                     
 
/*
 * $Id$
 *
 * 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]);
}