/* (c) 1999 Symbol Technologies, all rights reserved. */
/* May be distributed under the terms of GNU's General Public License, the
   text of which can be found online at http://www.gun.org/copyleft/gpl.html */
#ifndef __SPECTRUM24_CS_H__
#define __SPECTRUM24_CS_H__

/* these includes give us a clue as to our build environment */
#include <pcmcia/config.h>
#include <pcmcia/k_compat.h>

#if defined(PCMCIA_DEBUG) && !defined(DEBUG)
#define DEBUG
#endif

/* I'm not sure whether or not the make call from the pcmcia package will take
   care of this, so better safe than sorry */

#ifdef CONFIG_SMP
#ifndef __SMP__
#define __SMP__
#endif
#include <linux/smp.h>
#endif

#ifdef CONFIG_MODVERSIONS
#ifndef MODVERSIONS
#define MODVERSIONS
#endif
#include <linux/modversions.h>
#endif

#ifndef MODULE
#error "This code is intended to be used as a module."
#endif

/* Linux Headers that are useful */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/bitops.h>

#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>

#include <linux/wireless.h>

/* pcmcia headers */
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include <pcmcia/version.h>

/* spectrum24 interface stuff */
#include "spectrum24.h"

/* Set up all the normal useful macros */

#define MIN(x,y) (x) > (y) ? y : x

#ifdef DEBUG
#define debug(str,args...) printk(KERN_DEBUG str,## args) ; printk("\n")
#else
#define debug(str,args...)
#endif

#ifndef FALSE
#  define FALSE	0
#endif

#ifndef TRUE
#  define TRUE	!FALSE
#endif

#define MAC_ADDR_LEN	ETH_ALEN

#ifndef IW_ESSID_MAX_SIZE
#define IW_ESSID_MAX_SIZE 32
#endif

/* local structures */

typedef struct local_info
{
  dev_node_t node;
  struct net_device_stats stats;
#ifdef WIRELESS_EXT
  struct iw_statistics iw_stats;
#endif
  volatile u_char *mem_area;
  char essid[IW_ESSID_MAX_SIZE];
  int essid_len;
  int adapter_mode;
  int psp;
  int io_mode;
  struct map_info
  {
    u_char hop_set,hop_pattern,dtim;
  }map;

} local_info_t;

/* defines for adapter_mode */
#define MU	0
#define MAP	1

/* declaring an empty string here should cause it to broadcast 
for an essid and then set its essid to the first one it finds */
#define DEFAULT_ESSID	""

/* this is used by wireless extensions to determine the name of the card. */
#define IF_NAME		"Spectrum24"

#define MAX_ARGS	15
#define MEM_WIN_SIZE	(32 * 1024)

typedef struct eth_header
{
  u_char dest_addr[MAC_ADDR_LEN],src_addr[MAC_ADDR_LEN];
} eth_hdr_t;

typedef struct rt_buff
{
  u_short rt_next;		// 0x00
  u_short rt_len;		// 0x02
  u_short rt_data_off;		// 0x04
  u_char unused1[2];		// 0x06
  u_short rt_timestamp_MSW;	// 0x08
  u_char rt_dest_addr[6];	// 0x0A
  u_char rt_src_addr[6];	// 0x10
  u_short rt_timestamp_LSW;	// 0x16
  u_char unused2[4];		// 0x18
  u_char rt_RSSI;		// 0x1C
  u_char rt_freq;		// 0x1D
  u_char unused3[2];		// 0x1E
} rt_buff_t;

/* function prototypes */

static dev_link_t *s24_attach(void);
static void s24_detach(dev_link_t *);
static int s24_eth_config (struct device *, struct ifmap *);
static void s24_config(dev_link_t *);
static void s24_release(u_long);
static int s24_tx(struct sk_buff *, struct device *);
static int s24_open(struct device *dev);
static void s24_interrupt IRQ(int , void *, struct pt_regs *);
static int s24_event(event_t event, int priority, event_callback_args_t *args);
static int s24_stop(struct device *dev);
static struct net_device_stats *s24_stats(struct device *dev);

static void s24_reset(struct device *dev);
static void s24_multicast(struct device *);
static int s24_init(struct device *dev);
static int s24_ioctl(struct device *dev, struct ifreq *ifr, int cmd);

static void fill_stats(struct device *dev);

#ifdef HAS_WIRELESS_EXTENSIONS
static struct iw_statistics * s24_wireless_stats(struct device *dev);
#endif


/************************************************************************
* cs_error								*
*									*
* displays CardServices errors						*
************************************************************************/

static inline void cs_error(client_handle_t handle, int func, int ret)
{
    error_info_t err = { func, ret };
    CardServices(ReportError, handle, &err);
}

/************************************************************************
* save_LMA, restore_LMA							*
*									*
* saves the state of the LMA buffers so interrupts don't destroy other  *
* card functions							*
************************************************************************/
static inline u_short save_LMA(struct device *dev)
{
  local_info_t *info = dev->priv;
  
  if (info->io_mode)
  {
    unsigned ioaddr = dev->base_addr;
    u_char save[2];
    
    save[0] = inb(ioaddr + LMA0);
    save[1] = inb(ioaddr + LMA1);
    
    return *(u_short *)save;
  }
  
  return 0;  
}

static inline void restore_LMA(struct device *dev, u_short val)
{
  local_info_t *info = dev->priv;
  
  if (info->io_mode)
  {
    unsigned ioaddr = dev->base_addr;
    u_char *save = (u_char *)&val;
    
    outb(save[0], ioaddr + LMA0);
    outb(save[1], ioaddr + LMA1);
  }
}


/************************************************************************
* get_bytes							*
*									*
* grabs n bytes from the card and puts them in the buffer pointed to by	*
* buffer.								*
************************************************************************/

static inline void get_bytes(struct device *dev, u_char *buffer, u_short start, size_t count)
{
  local_info_t *info = dev->priv;
  debug("get_bytes");
  
  if (info->io_mode)
  {
    int i;
    unsigned ioaddr = dev->base_addr;
    debug("io mode");
    
    outb(start & 0xFF, ioaddr + LMA0);
    outb((start & 0xFF00) >> 8, ioaddr + LMA1);

    for (i = 0 ; i < count ; i++)
    {
      /* since the address autoincrements, we can just read and be happy */
      buffer[i] = inb(ioaddr + MEM0);
    }
  }
  else
  {
    memcpy(buffer,(void *)(info->mem_area + start),count);
  }
}

/************************************************************************
* put_bytes								*
*									*
* grabs count bytes from buffer and writes them to the card.		*
************************************************************************/

static inline void put_bytes(struct device *dev, const u_char *buffer, u_short start, size_t count)
{
  local_info_t *info = dev->priv;
  debug("put_bytes");
  
  if (info->io_mode)
  {
    int i;
    unsigned ioaddr = dev->base_addr;
    
    debug("io mode");
    
    outb(start & 0xFF, ioaddr + LMA0);
    outb((start & 0xFF00) >> 8, ioaddr + LMA1);
    
    for (i = 0 ; i < count ; i++)
    {
      /* since the address autoincrements, we can just write and be happy */
      outb(buffer[i], ioaddr + MEM0);
    }
  }
  else
  {
#if defined(DEBUG) && 1
    if (count == 2)
    {
      debug("Short value is %d.",le16_to_cpu(*(u_short *)buffer));
    }
#endif
    debug("About to do memcpy to location %x for %x bytes",start,count);
    memcpy((void *)(info->mem_area + start),buffer,count);
  }
}

/************************************************************************
* get_byte								*
*									*
* returns one byte from the card at location loc			*
************************************************************************/

static inline u_char get_byte(struct device *dev, u_short loc)
{
  local_info_t *info = dev->priv;
  
  if (info->io_mode)
  {
    unsigned ioaddr = dev->base_addr;
    
    outb(loc & 0xFF, ioaddr + LMA0);
    outb((loc & 0xFF00) >> 8, ioaddr + LMA1);

    return inb(ioaddr + MEM0);
  }

  return info->mem_area[loc];
}

/************************************************************************
* put_byte								*
*									*
* writes byte to the card at location loc				*
************************************************************************/

static inline void put_byte(struct device *dev, u_char byte, u_short loc)
{
  local_info_t *info = dev->priv;
  
  if (info->io_mode)
  {
    unsigned ioaddr = dev->base_addr;
    
    outb(loc & 0xFF, ioaddr + LMA0);
    outb((loc & 0xFF00) >> 8, ioaddr + LMA1);
    
    outb(byte, ioaddr + MEM0);
  }
  else
  {
    info->mem_area[loc] = byte;
  }
}

/* the last of these functions (maybe) */

static inline u_short get_word(struct device *dev, u_short loc)
{
  u_short word;
  get_bytes(dev,(u_char *)&word,loc,2);
  return le16_to_cpu(word);
}

static inline void put_word(struct device *dev, u_short word, u_short loc)
{
  word = le16_to_cpu(word);
  put_bytes(dev,(u_char *)&word,loc,2);
}

/************************************************************************
* wait_on_woc								*
*									*
* waits for the woc bit to go true. due to the conditions under which	*
* it's called, it shouldn't have to be reentrant.			*
************************************************************************/

static inline int wait_on_woc(struct device *dev, u_long timeout)
{
  int i;
  int ret_val = 0;
  
  debug("Waiting on WOC for %lu milliseconds", timeout);
  
  for (i = jiffies + (((HZ * timeout)/1000) ? ((HZ * timeout)/1000) : 1); i >= jiffies ; )
  {
    if (get_byte(dev,WOC))
    {
      ret_val = 1;
      break;
    }
  }  

  return ret_val;
}

/************************************************************************
* moon_cmd								*
*									*
* send a command to the card						*
* this assumes that you have already made sure that you have the woc	*
* bit.									*
************************************************************************/

static inline void moon_cmd(struct device *dev, u_char cmd, const u_char * args, int no_args)
{
  unsigned ioaddr = dev->base_addr;
  put_byte(dev,0,WOC);
  put_byte(dev,cmd,HCB);
  
  debug("Got command %.2x, with %d args",cmd,no_args);
  
  if (no_args > 0)
    put_bytes(dev,args,HCB+1,no_args);

  outb(GEN_P188_INT | inb(ioaddr+CMD),ioaddr+CMD);
}

/************************************************************************
* moon_cmd_byte								*
*									*
* send a command to the card with a one byte argument			*
* this assumes that you have already made sure that you have the woc	*
* bit.									*
************************************************************************/

static inline void moon_cmd_byte(struct device *dev, u_char cmd, const u_char arg)
{
  unsigned ioaddr = dev->base_addr;
  put_byte(dev,0,WOC);
  put_byte(dev,cmd,HCB);
  put_byte(dev,arg,HCB+1);

  outb(GEN_P188_INT | inb(ioaddr+CMD),ioaddr+CMD);
}

#ifdef DEBUG
/************************************************************************
* print_buffer								*
*									*
* a quick and dirty function to dump the contents of a memory location	*
* used only for debugging. I could optimize it for io mode, but it's	*
* for debugging, what do I care how efficient it is :p			*
************************************************************************/

static inline void print_buffer(u_long start, u_long end, struct device *dev)
{
  int q;
  for ( ; start <= end ; start += 0x10)
  {
    printk(KERN_DEBUG);
    for (q = 0 ; q < 0x10 ; q++) 
      printk("%.2x ", get_byte(dev,start + q));
    printk("\n");
  }
}
#else
#  define print_buffer(X, Y, Z)
#endif /* DEBUG */

#endif /* __SPECTRUM24_CS_H__ */ 