From d6434e1042f3b0a6dfe1b1f615af369486f9b1fa Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sat, 9 Oct 1999 00:00:47 +0000 Subject: Merge with 2.3.19. --- drivers/net/pcmcia/.cvsignore | 2 + drivers/net/pcmcia/3c589_cs.c | 1178 +++++++++++++++++++++ drivers/net/pcmcia/Config.in | 16 + drivers/net/pcmcia/Makefile | 40 + drivers/net/pcmcia/pcnet_cs.c | 1403 ++++++++++++++++++++++++ drivers/net/pcmcia/ray_cs.c | 2352 +++++++++++++++++++++++++++++++++++++++++ drivers/net/pcmcia/ray_cs.h | 60 ++ drivers/net/pcmcia/rayctl.h | 725 +++++++++++++ 8 files changed, 5776 insertions(+) create mode 100644 drivers/net/pcmcia/.cvsignore create mode 100644 drivers/net/pcmcia/3c589_cs.c create mode 100644 drivers/net/pcmcia/Config.in create mode 100644 drivers/net/pcmcia/Makefile create mode 100644 drivers/net/pcmcia/pcnet_cs.c create mode 100644 drivers/net/pcmcia/ray_cs.c create mode 100644 drivers/net/pcmcia/ray_cs.h create mode 100644 drivers/net/pcmcia/rayctl.h (limited to 'drivers/net/pcmcia') diff --git a/drivers/net/pcmcia/.cvsignore b/drivers/net/pcmcia/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/net/pcmcia/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c new file mode 100644 index 000000000..6e511f4b7 --- /dev/null +++ b/drivers/net/pcmcia/3c589_cs.c @@ -0,0 +1,1178 @@ +/*====================================================================== + + A PCMCIA ethernet driver for the 3com 3c589 card. + + Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu + + 3c589_cs.c 1.134 1999/09/15 15:33:09 + + The network driver code is based on Donald Becker's 3c589 code: + + Written 1994 by Donald Becker. + Copyright 1993 United States Government as represented by the + Director, National Security Agency. This software may be used and + distributed according to the terms of the GNU Public License, + incorporated herein by reference. + Donald Becker may be reached at becker@cesdis1.gsfc.nasa.gov + +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* To minimize the size of the driver source I only define operating + constants if they are used several times. You'll need the manual + if you want to understand driver details. */ +/* Offsets from base I/O address. */ +#define EL3_DATA 0x00 +#define EL3_TIMER 0x0a +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e + +#define EEPROM_READ 0x0080 +#define EEPROM_BUSY 0x8000 + +#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) + +/* The top five bits written to EL3_CMD are a command, the lower + 11 bits are the parameter, if applicable. */ +enum c509cmd { + TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, + RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, + TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, + FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, + SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, + SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, + StatsDisable = 22<<11, StopCoax = 23<<11, +}; + +enum c509status { + IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, + TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, + IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 +}; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { + RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 +}; + +/* Register window 1 offsets, the window used in normal operation. */ +#define TX_FIFO 0x00 +#define RX_FIFO 0x00 +#define RX_STATUS 0x08 +#define TX_STATUS 0x0B +#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */ + +#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */ +#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */ +#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ +#define MEDIA_LED 0x0001 /* Enable link light on 3C589E cards. */ + +/* Time in jiffies before concluding Tx hung */ +#define TX_TIMEOUT ((400*HZ)/1000) + +struct el3_private { + dev_node_t node; + struct net_device_stats stats; + /* For transceiver monitoring */ + struct timer_list media; + u_short media_status; + u_short fast_poll; + u_long last_irq; +}; + +static char *if_names[] = { "auto", "10baseT", "10base2", "AUI" }; + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"3c589_cs.c 1.134 1999/09/15 15:33:09 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Special hook for setting if_port when module is loaded */ +static int if_port = 0; + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +MODULE_PARM(if_port, "i"); +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); + +/*====================================================================*/ + +static void tc589_config(dev_link_t *link); +static void tc589_release(u_long arg); +static int tc589_event(event_t event, int priority, + event_callback_args_t *args); + +static ushort read_eeprom(short ioaddr, int index); +static void tc589_reset(struct net_device *dev); +static void media_check(u_long arg); +static int el3_config(struct net_device *dev, struct ifmap *map); +static int el3_open(struct net_device *dev); +static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void update_stats(int addr, struct net_device *dev); +static struct net_device_stats *el3_get_stats(struct net_device *dev); +static int el3_rx(struct net_device *dev); +static int el3_close(struct net_device *dev); + +static void set_multicast_list(struct net_device *dev); + +static dev_info_t dev_info = "3c589_cs"; + +static dev_link_t *tc589_attach(void); +static void tc589_detach(dev_link_t *); + +static dev_link_t *dev_list = NULL; + +/*====================================================================== + + This bit of code is used to avoid unregistering network devices + at inappropriate times. 2.2 and later kernels are fairly picky + about when this can happen. + +======================================================================*/ + +static void flush_stale_links(void) +{ + dev_link_t *link, *next; + for (link = dev_list; link; link = next) { + next = link->next; + if (link->state & DEV_STALE_LINK) + tc589_detach(link); + } +} + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================== + + We never need to do anything when a tc589 device is "initialized" + by the net software, because we only register already-found cards. + +======================================================================*/ + +static int tc589_init(struct net_device *dev) +{ + return 0; +} + +/*====================================================================== + + tc589_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + +======================================================================*/ + +static dev_link_t *tc589_attach(void) +{ + client_reg_t client_reg; + dev_link_t *link; + struct net_device *dev; + int i, ret; + + DEBUG(0, "3c589_attach()\n"); + flush_stale_links(); + + /* Create new ethernet device */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + link->release.function = &tc589_release; + link->release.data = (u_long)link; + link->io.NumPorts1 = 16; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + link->io.IOAddrLines = 4; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = &el3_interrupt; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); + memset(dev, 0, sizeof(struct net_device)); + + /* Make up a EL3-specific-data structure. */ + dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL); + memset(dev->priv, 0, sizeof(struct el3_private)); + + /* The EL3-specific entries in the device structure. */ + dev->hard_start_xmit = &el3_start_xmit; + dev->set_config = &el3_config; + dev->get_stats = &el3_get_stats; + dev->set_multicast_list = &set_multicast_list; + ether_setup(dev); + dev->name = ((struct el3_private *)dev->priv)->node.dev_name; + dev->init = &tc589_init; + dev->open = &el3_open; + dev->stop = &el3_close; + dev->tbusy = 1; + link->priv = link->irq.Instance = dev; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &tc589_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + tc589_detach(link); + return NULL; + } + + return link; +} /* tc589_attach */ + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void tc589_detach(dev_link_t *link) +{ + dev_link_t **linkp; + long flags; + + DEBUG(0, "3c589_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + save_flags(flags); + cli(); + if (link->state & DEV_RELEASE_PENDING) { + del_timer(&link->release); + link->state &= ~DEV_RELEASE_PENDING; + } + restore_flags(flags); + + if (link->state & DEV_CONFIG) { + tc589_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free bits */ + *linkp = link->next; + if (link->priv) { + struct net_device *dev = link->priv; + if (link->dev != NULL) + unregister_netdev(dev); + if (dev->priv) + kfree(dev->priv); + kfree(link->priv); + } + kfree(link); + +} /* tc589_detach */ + +/*====================================================================== + + tc589_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + ethernet device available to the system. + +======================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +static void tc589_config(dev_link_t *link) +{ + client_handle_t handle; + struct net_device *dev; + tuple_t tuple; + cisparse_t parse; + u_short buf[32]; + int last_fn, last_ret, i, j, multi = 0; + short ioaddr, *phys_addr; + char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; + + handle = link->handle; + dev = link->priv; + phys_addr = (short *)dev->dev_addr; + + DEBUG(0, "3c589_config(0x%p)\n", link); + + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, handle, &tuple); + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Is this a 3c562? */ + tuple.DesiredTuple = CISTPL_MANFID; + tuple.Attributes = TUPLE_RETURN_COMMON; + if ((CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) && + (CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS)) { + if (le16_to_cpu(buf[0]) != MANFID_3COM) + printk(KERN_INFO "3c589_cs: hmmm, is this really a " + "3Com card??\n"); + multi = (le16_to_cpu(buf[1]) == PRODID_3COM_3C562); + } + + /* Configure card */ + link->state |= DEV_CONFIG; + + /* For the 3c562, the base address must be xx00-xx7f */ + for (i = j = 0; j < 0x400; j += 0x10) { + if (multi && (j & 0x80)) continue; + link->io.BasePort1 = j ^ 0x300; + i = CardServices(RequestIO, link->handle, &link->io); + if (i == CS_SUCCESS) break; + } + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIO, i); + goto failed; + } + CS_CHECK(RequestIRQ, link->handle, &link->irq); + CS_CHECK(RequestConfiguration, link->handle, &link->conf); + + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + dev->tbusy = 0; + if (register_netdev(dev) != 0) { + printk(KERN_NOTICE "3c589_cs: register_netdev() failed\n"); + goto failed; + } + + link->state &= ~DEV_CONFIG_PENDING; + ioaddr = dev->base_addr; + EL3WINDOW(0); + + /* The 3c589 has an extra EEPROM for configuration info, including + the hardware address. The 3c562 puts the address in the CIS. */ + tuple.DesiredTuple = 0x88; + if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) { + CardServices(GetTupleData, handle, &tuple); + for (i = 0; i < 3; i++) + phys_addr[i] = htons(buf[i]); + } else { + for (i = 0; i < 3; i++) + phys_addr[i] = htons(read_eeprom(ioaddr, i)); + if (phys_addr[0] == 0x6060) { + printk(KERN_NOTICE "3c589_cs: IO port conflict at 0x%03lx" + "-0x%03lx\n", dev->base_addr, dev->base_addr+15); + goto failed; + } + } + + link->dev = &((struct el3_private *)dev->priv)->node; + + /* The address and resource configuration register aren't loaded from + the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version. */ + outw(0x3f00, ioaddr + 8); + + /* The if_port symbol can be set when the module is loaded */ + if ((if_port >= 0) && (if_port <= 3)) + dev->if_port = if_port; + else + printk(KERN_NOTICE "3c589_cs: invalid if_port requested\n"); + + printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, hw_addr ", + dev->name, (multi ? "562" : "589"), dev->base_addr, + dev->irq); + for (i = 0; i < 6; i++) + printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); + i = inl(ioaddr); + printk(KERN_INFO " %dK FIFO split %s Rx:Tx, %s xcvr\n", + (i & 7) ? 32 : 8, ram_split[(i >> 16) & 3], + if_names[dev->if_port]); + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + tc589_release((u_long)link); + return; + +} /* tc589_config */ + +/*====================================================================== + + After a card is removed, tc589_release() will unregister the net + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. + +======================================================================*/ + +static void tc589_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + + DEBUG(0, "3c589_release(0x%p)\n", link); + + if (link->open) { + DEBUG(1, "3c589_cs: release postponed, '%s' still open\n", + link->dev->dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING); + +} /* tc589_release */ + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the net drivers from trying + to talk to the card any more. + +======================================================================*/ + +static int tc589_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + struct net_device *dev = link->priv; + + DEBUG(1, "3c589_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + dev->tbusy = 1; dev->start = 0; + link->release.expires = jiffies + HZ/20; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + tc589_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) { + if (link->open) { + dev->tbusy = 1; dev->start = 0; + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + CardServices(RequestConfiguration, link->handle, &link->conf); + if (link->open) { + tc589_reset(dev); + dev->tbusy = 0; dev->start = 1; + } + } + break; + } + return 0; +} /* tc589_event */ + +/*====================================================================*/ + +/* + Use this for commands that may take time to finish +*/ +static void wait_for_completion(struct net_device *dev, int cmd) +{ + int i = 100; + outw(cmd, dev->base_addr + EL3_CMD); + while (--i > 0) + if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break; + if (i == 0) + printk(KERN_NOTICE "%s: command 0x%04x did not complete!\n", + dev->name, cmd); +} + +/* + Read a word from the EEPROM using the regular EEPROM access register. + Assume that we are in register window zero. +*/ +static ushort read_eeprom(short ioaddr, int index) +{ + int i; + outw(EEPROM_READ + index, ioaddr + 10); + /* Reading the eeprom takes 162 us */ + for (i = 1620; i >= 0; i--) + if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0) + break; + return inw(ioaddr + 12); +} + +/* + Set transceiver type, perhaps to something other than what the user + specified in dev->if_port. +*/ +static void tc589_set_xcvr(struct net_device *dev, int if_port) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + ushort ioaddr = dev->base_addr; + + EL3WINDOW(0); + switch (if_port) { + case 0: case 1: outw(0, ioaddr + 6); break; + case 2: outw(3<<14, ioaddr + 6); break; + case 3: outw(1<<14, ioaddr + 6); break; + } + /* On PCMCIA, this just turns on the LED */ + outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD); + /* 10baseT interface, enable link beat and jabber check. */ + EL3WINDOW(4); + outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA); + EL3WINDOW(1); + if (if_port == 2) + lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000); + else + lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800); +} + +static void dump_status(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + EL3WINDOW(1); + printk(KERN_INFO " irq status %04x, rx status %04x, tx status " + "%02x tx free %04x\n", inw(ioaddr+EL3_STATUS), + inw(ioaddr+RX_STATUS), inb(ioaddr+TX_STATUS), + inw(ioaddr+TX_FREE)); + EL3WINDOW(4); + printk(KERN_INFO " diagnostics: fifo %04x net %04x ethernet %04x" + " media %04x\n", inw(ioaddr+0x04), inw(ioaddr+0x06), + inw(ioaddr+0x08), inw(ioaddr+0x0a)); + EL3WINDOW(1); +} + +/* Reset and restore all of the 3c589 registers. */ +static void tc589_reset(struct net_device *dev) +{ + ushort ioaddr = dev->base_addr; + int i; + + EL3WINDOW(0); + outw(0x0001, ioaddr + 4); /* Activate board. */ + outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */ + + /* Set the station address in window 2. */ + EL3WINDOW(2); + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + i); + + tc589_set_xcvr(dev, dev->if_port); + + /* Switch to the stats window, and clear all stats by reading. */ + outw(StatsDisable, ioaddr + EL3_CMD); + EL3WINDOW(6); + for (i = 0; i < 9; i++) + inb(ioaddr+i); + inw(ioaddr + 10); + inw(ioaddr + 12); + + /* Switch to register set 1 for normal use. */ + EL3WINDOW(1); + + /* Accept b-cast and phys addr only. */ + outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); + outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ + outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ + outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ + /* Allow status bits to be seen. */ + outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); + /* Ack all pending events, and set active indicator mask. */ + outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, + ioaddr + EL3_CMD); + outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull + | AdapterFailure, ioaddr + EL3_CMD); +} + +static int el3_config(struct net_device *dev, struct ifmap *map) +{ + if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { + if (map->port <= 3) { + dev->if_port = map->port; + printk(KERN_INFO "%s: switched to %s port\n", + dev->name, if_names[dev->if_port]); + tc589_set_xcvr(dev, dev->if_port); + } else + return -EINVAL; + } + return 0; +} + +static int el3_open(struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + dev_link_t *link; + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (!DEV_OK(link)) + return -ENODEV; + + link->open++; + MOD_INC_USE_COUNT; + dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; + + tc589_reset(dev); + lp->media.function = &media_check; + lp->media.data = (u_long)dev; + lp->media.expires = jiffies + HZ; + add_timer(&lp->media); + + DEBUG(1, "%s: opened, status %4.4x.\n", + dev->name, inw(dev->base_addr + EL3_STATUS)); + + return 0; /* Always succeed */ +} + +static void el3_tx_timeout(struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + int ioaddr = dev->base_addr; + + printk(KERN_NOTICE "%s: Transmit timed out!\n", dev->name); + dump_status(dev); + lp->stats.tx_errors++; + dev->trans_start = jiffies; + /* Issue TX_RESET and TX_START commands. */ + wait_for_completion(dev, TxReset); + outw(TxEnable, ioaddr + EL3_CMD); + dev->tbusy = 0; +} + +static void pop_tx_status(struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + int ioaddr = dev->base_addr; + int i; + + /* Clear the Tx status stack. */ + for (i = 32; i > 0; i--) { + u_char tx_status = inb(ioaddr + TX_STATUS); + if (!(tx_status & 0x84)) break; + /* reset transmitter on jabber error or underrun */ + if (tx_status & 0x30) + wait_for_completion(dev, TxReset); + if (tx_status & 0x38) { + DEBUG(1, "%s: transmit error: status 0x%02x\n", + dev->name, tx_status); + outw(TxEnable, ioaddr + EL3_CMD); + lp->stats.tx_aborted_errors++; + } + outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ + } +} + +static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + int ioaddr = dev->base_addr; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy) { + if (jiffies - dev->trans_start < TX_TIMEOUT) + return 1; + el3_tx_timeout(dev); + } + + DEBUG(3, "%s: el3_start_xmit(length = %ld) called, " + "status %4.4x.\n", dev->name, (long)skb->len, + inw(ioaddr + EL3_STATUS)); + + /* Avoid timer-based retransmission conflicts. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) + printk(KERN_NOTICE "%s: transmitter access conflict.\n", + dev->name); + else { + lp->stats.tx_bytes += skb->len; + /* Put out the doubleword header... */ + outw(skb->len, ioaddr + TX_FIFO); + outw(0x00, ioaddr + TX_FIFO); + /* ... and the packet rounded to a doubleword. */ + outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); + + dev->trans_start = jiffies; + if (inw(ioaddr + TX_FREE) > 1536) { + dev->tbusy = 0; + } else + /* Interrupt us when the FIFO has room for max-sized packet. */ + outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); + } + + dev_kfree_skb(skb); + pop_tx_status(dev); + + return 0; +} + +/* The EL3 interrupt handler. */ +static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct el3_private *lp; + int ioaddr, status; + int i = 0; + + if ((dev == NULL) || !dev->start) + return; + lp = (struct el3_private *)dev->priv; + ioaddr = dev->base_addr; + +#ifdef PCMCIA_DEBUG + if (dev->interrupt) { + printk(KERN_NOTICE "%s: re-entering the interrupt handler.\n", + dev->name); + return; + } + dev->interrupt = 1; + DEBUG(3, "%s: interrupt, status %4.4x.\n", + dev->name, inw(ioaddr + EL3_STATUS)); +#endif + + while ((status = inw(ioaddr + EL3_STATUS)) & + (IntLatch | RxComplete | StatsFull)) { + if ((dev->start == 0) || ((status & 0xe000) != 0x2000)) { + DEBUG(1, "%s: interrupt from dead card\n", dev->name); + break; + } + + if (status & RxComplete) + el3_rx(dev); + + if (status & TxAvailable) { + DEBUG(3, " TX room bit was handled.\n"); + /* There's room in the FIFO for a full-sized packet. */ + outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); + dev->tbusy = 0; + mark_bh(NET_BH); + } + + if (status & TxComplete) + pop_tx_status(dev); + + if (status & (AdapterFailure | RxEarly | StatsFull)) { + /* Handle all uncommon interrupts. */ + if (status & StatsFull) /* Empty statistics. */ + update_stats(ioaddr, dev); + if (status & RxEarly) { /* Rx early is unused. */ + el3_rx(dev); + outw(AckIntr | RxEarly, ioaddr + EL3_CMD); + } + if (status & AdapterFailure) { + u16 fifo_diag; + EL3WINDOW(4); + fifo_diag = inw(ioaddr + 4); + EL3WINDOW(1); + printk(KERN_NOTICE "%s: adapter failure, FIFO diagnostic" + " register %04x.\n", dev->name, fifo_diag); + if (fifo_diag & 0x0400) { + /* Tx overrun */ + wait_for_completion(dev, TxReset); + outw(TxEnable, ioaddr + EL3_CMD); + } + if (fifo_diag & 0x2000) { + /* Rx underrun */ + wait_for_completion(dev, RxReset); + set_multicast_list(dev); + outw(RxEnable, ioaddr + EL3_CMD); + } + outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); + } + } + + if (++i > 10) { + printk(KERN_NOTICE "%s: infinite loop in interrupt, " + "status %4.4x.\n", dev->name, status); + /* Clear all interrupts */ + outw(AckIntr | 0xFF, ioaddr + EL3_CMD); + break; + } + /* Acknowledge the IRQ. */ + outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); + } + + lp->last_irq = jiffies; +#ifdef PCMCIA_DEBUG + DEBUG(3, "%s: exiting interrupt, status %4.4x.\n", + dev->name, inw(ioaddr + EL3_STATUS)); + dev->interrupt = 0; +#endif + return; +} + +static void media_check(u_long arg) +{ + struct net_device *dev = (struct net_device *)(arg); + struct el3_private *lp = (struct el3_private *)dev->priv; + int ioaddr = dev->base_addr; + u_short media, errs; + u_long flags; + + if (dev->start == 0) goto reschedule; + + EL3WINDOW(1); + /* Check for pending interrupt with expired latency timer: with + this, we can limp along even if the interrupt is blocked */ + if ((inw(ioaddr + EL3_STATUS) & IntLatch) && + (inb(ioaddr + EL3_TIMER) == 0xff)) { + if (!lp->fast_poll) + printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); + el3_interrupt(dev->irq, dev, NULL); + lp->fast_poll = HZ; + } + if (lp->fast_poll) { + lp->fast_poll--; + lp->media.expires = jiffies + 1; + add_timer(&lp->media); + return; + } + + save_flags(flags); + cli(); + EL3WINDOW(4); + media = inw(ioaddr+WN4_MEDIA) & 0xc810; + + /* Ignore collisions unless we've had no irq's recently */ + if (jiffies - lp->last_irq < HZ) { + media &= ~0x0010; + } else { + /* Try harder to detect carrier errors */ + EL3WINDOW(6); + outw(StatsDisable, ioaddr + EL3_CMD); + errs = inb(ioaddr + 0); + outw(StatsEnable, ioaddr + EL3_CMD); + lp->stats.tx_carrier_errors += errs; + if (errs || (lp->media_status & 0x0010)) media |= 0x0010; + } + + if (media != lp->media_status) { + if ((media & lp->media_status & 0x8000) && + ((lp->media_status ^ media) & 0x0800)) + printk(KERN_INFO "%s: %s link beat\n", dev->name, + (lp->media_status & 0x0800 ? "lost" : "found")); + else if ((media & lp->media_status & 0x4000) && + ((lp->media_status ^ media) & 0x0010)) + printk(KERN_INFO "%s: coax cable %s\n", dev->name, + (lp->media_status & 0x0010 ? "ok" : "problem")); + if (dev->if_port == 0) { + if (media & 0x8000) { + if (media & 0x0800) + printk(KERN_INFO "%s: flipped to 10baseT\n", + dev->name); + else + tc589_set_xcvr(dev, 2); + } else if (media & 0x4000) { + if (media & 0x0010) + tc589_set_xcvr(dev, 1); + else + printk(KERN_INFO "%s: flipped to 10base2\n", + dev->name); + } + } + lp->media_status = media; + } + + EL3WINDOW(1); + restore_flags(flags); + +reschedule: + lp->media.expires = jiffies + HZ; + add_timer(&lp->media); +} + +static struct net_device_stats *el3_get_stats(struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + unsigned long flags; + dev_link_t *link; + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (DEV_OK(link)) { + save_flags(flags); + cli(); + update_stats(dev->base_addr, dev); + restore_flags(flags); + } + return &lp->stats; +} + +/* + Update statistics. We change to register window 6, so this should be run + single-threaded if the device is active. This is expected to be a rare + operation, and it's simpler for the rest of the driver to assume that + window 1 is always valid rather than use a special window-state variable. +*/ +static void update_stats(int ioaddr, struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + + DEBUG(2, "%s: updating the statistics.\n", dev->name); + /* Turn off statistics updates while reading. */ + outw(StatsDisable, ioaddr + EL3_CMD); + /* Switch to the stats window, and read everything. */ + EL3WINDOW(6); + lp->stats.tx_carrier_errors += inb(ioaddr + 0); + lp->stats.tx_heartbeat_errors += inb(ioaddr + 1); + /* Multiple collisions. */ inb(ioaddr + 2); + lp->stats.collisions += inb(ioaddr + 3); + lp->stats.tx_window_errors += inb(ioaddr + 4); + lp->stats.rx_fifo_errors += inb(ioaddr + 5); + lp->stats.tx_packets += inb(ioaddr + 6); + /* Rx packets */ inb(ioaddr + 7); + /* Tx deferrals */ inb(ioaddr + 8); + inw(ioaddr + 10); /* Total Rx and Tx octets. */ + inw(ioaddr + 12); + + /* Back to window 1, and turn statistics back on. */ + EL3WINDOW(1); + outw(StatsEnable, ioaddr + EL3_CMD); +} + +static int el3_rx(struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + int ioaddr = dev->base_addr; + int worklimit = 32; + short rx_status; + + DEBUG(3, "%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n", + dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); + while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) && + (--worklimit >= 0)) { + if (rx_status & 0x4000) { /* Error, update stats. */ + short error = rx_status & 0x3800; + lp->stats.rx_errors++; + switch (error) { + case 0x0000: lp->stats.rx_over_errors++; break; + case 0x0800: lp->stats.rx_length_errors++; break; + case 0x1000: lp->stats.rx_frame_errors++; break; + case 0x1800: lp->stats.rx_length_errors++; break; + case 0x2000: lp->stats.rx_frame_errors++; break; + case 0x2800: lp->stats.rx_crc_errors++; break; + } + } else { + short pkt_len = rx_status & 0x7ff; + struct sk_buff *skb; + + skb = dev_alloc_skb(pkt_len+5); + + DEBUG(3, " Receiving packet size %d status %4.4x.\n", + pkt_len, rx_status); + if (skb != NULL) { + skb->dev = dev; + + skb_reserve(skb, 2); + insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), + (pkt_len+3)>>2); + skb->protocol = eth_type_trans(skb, dev); + + netif_rx(skb); + lp->stats.rx_packets++; + lp->stats.rx_bytes += skb->len; + } else { + DEBUG(1, "%s: couldn't allocate a sk_buff of" + " size %d.\n", dev->name, pkt_len); + lp->stats.rx_dropped++; + } + } + /* Pop the top of the Rx FIFO */ + wait_for_completion(dev, RxDiscard); + } + if (worklimit == 0) + printk(KERN_NOTICE "%s: too much work in el3_rx!\n", dev->name); + return 0; +} + +/* Set or clear the multicast filter for this adapter. + num_addrs == -1 Promiscuous mode, receive all packets + num_addrs == 0 Normal mode, clear multicast list + num_addrs > 0 Multicast mode, receive normal and MC packets, and do + best-effort filtering. + */ +static void set_multicast_list(struct net_device *dev) +{ + short ioaddr = dev->base_addr; + dev_link_t *link; + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (!(DEV_OK(link))) return; +#ifdef PCMCIA_DEBUG + if (pc_debug > 2) { + static int old = 0; + if (old != dev->mc_count) { + old = dev->mc_count; + DEBUG(0, "%s: setting Rx mode to %d addresses.\n", + dev->name, old); + } + } +#endif + if (dev->flags & IFF_PROMISC) + outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, + ioaddr + EL3_CMD); + else if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) + outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD); + else + outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); +} + +static int el3_close(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + dev_link_t *link; + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (link == NULL) + return -ENODEV; + + DEBUG(1, "%s: shutting down ethercard.\n", dev->name); + + if (DEV_OK(link)) { + /* Turn off statistics ASAP. We update lp->stats below. */ + outw(StatsDisable, ioaddr + EL3_CMD); + + /* Disable the receiver and transmitter. */ + outw(RxDisable, ioaddr + EL3_CMD); + outw(TxDisable, ioaddr + EL3_CMD); + + if (dev->if_port == 2) + /* Turn off thinnet power. Green! */ + outw(StopCoax, ioaddr + EL3_CMD); + else if (dev->if_port == 1) { + /* Disable link beat and jabber */ + EL3WINDOW(4); + outw(0, ioaddr + WN4_MEDIA); + } + + /* Switching back to window 0 disables the IRQ. */ + EL3WINDOW(0); + /* But we explicitly zero the IRQ line select anyway. */ + outw(0x0f00, ioaddr + WN0_IRQ); + + /* Check if the card still exists */ + if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000) + update_stats(ioaddr, dev); + } + + link->open--; + dev->start = 0; + del_timer(&((struct el3_private *)dev->priv)->media); + if (link->state & DEV_STALE_CONFIG) { + link->release.expires = jiffies + HZ/20; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + + MOD_DEC_USE_COUNT; + + return 0; +} + +/*====================================================================*/ + +static int __init init_3c589_cs(void) +{ + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "3c589_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &tc589_attach, &tc589_detach); + return 0; +} + +static void __exit exit_3c589_cs(void) +{ + DEBUG(0, "3c589_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + tc589_detach(dev_list); +} + +module_init(init_3c589_cs); +module_exit(exit_3c589_cs); diff --git a/drivers/net/pcmcia/Config.in b/drivers/net/pcmcia/Config.in new file mode 100644 index 000000000..cc4efbf88 --- /dev/null +++ b/drivers/net/pcmcia/Config.in @@ -0,0 +1,16 @@ +# +# PCMCIA Network device configuration +# + +mainmenu_option next_comment +comment 'PCMCIA network devices' + +dep_tristate 'PCMCIA ethernet cards (NE2000 compatibles: DE-650, ...)' CONFIG_PCMCIA_PCNET $CONFIG_PCMCIA +dep_tristate '3Com 3c589 PCMCIA card' CONFIG_PCMCIA_3C589 $CONFIG_PCMCIA +dep_tristate 'Aviator/Raytheon 2.4MHz wireless' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA + +if [ "$CONFIG_PCMCIA_3C589" = "y" -o "$CONFIG_PCMCIA_RAYCS" = "y" -o "$CONFIG_PCMCIA_PCNET" = "y" ]; then + define_bool CONFIG_PCMCIA_NETCARD y +fi + +endmenu diff --git a/drivers/net/pcmcia/Makefile b/drivers/net/pcmcia/Makefile new file mode 100644 index 000000000..3b59b1b93 --- /dev/null +++ b/drivers/net/pcmcia/Makefile @@ -0,0 +1,40 @@ +# +# drivers/net/pcmcia/Makefile +# +# Makefile for the Linux PCMCIA network device drivers. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +O_TARGET := pcmcia_net.o +O_OBJS := +M_OBJS := +MOD_LIST_NAME := PCMCIA_MODULES + +ifeq ($(CONFIG_PCMCIA_PCNET),y) + O_OBJS += pcnet_cs.o +else + ifeq ($(CONFIG_PCMCIA_PCNET),m) + MX_OBJS += pcnet_cs.o + endif +endif + +ifeq ($(CONFIG_PCMCIA_3C589),y) + O_OBJS += 3c589_cs.o +else + ifeq ($(CONFIG_PCMCIA_3C589),m) + MX_OBJS += 3c589_cs.o + endif +endif + +ifeq ($(CONFIG_PCMCIA_RAYCS),y) + OX_OBJS += ray_cs.o +else + ifeq ($(CONFIG_PCMCIA_RAYCS),m) + MX_OBJS += ray_cs.o + endif +endif + +include $(TOPDIR)/Rules.make diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c new file mode 100644 index 000000000..f5d4b3c49 --- /dev/null +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -0,0 +1,1403 @@ +/*====================================================================== + + A PCMCIA ethernet driver for NS8390-based cards + + This driver supports the D-Link DE-650 and Linksys EthernetCard + cards, the newer D-Link and Linksys combo cards, Accton EN2212 + cards, the RPTI EP400, and the PreMax PE-200 in non-shared-memory + mode, and the IBM Credit Card Adapter, the NE4100, the Thomas + Conrad ethernet card, and the Kingston KNE-PCM/x in shared-memory + mode. It will also handle the Socket EA card in either mode. + + Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu + + pcnet_cs.c 1.99 1999/09/15 15:33:09 + + The network driver code is based on Donald Becker's NE2000 code: + + Written 1992,1993 by Donald Becker. + Copyright 1993 United States Government as represented by the + Director, National Security Agency. This software may be used and + distributed according to the terms of the GNU Public License, + incorporated herein by reference. + Donald Becker may be reached at becker@cesdis1.gsfc.nasa.gov + + Based also on Keith Moore's changes to Don Becker's code, for IBM + CCAE support. Drivers merged back together, and shared-memory + Socket EA support added, by Ken Raeburn, September 1995. + +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include <../drivers/net/8390.h> + +#include +#include +#include +#include +#include +#include +#include + +#define PCNET_CMD 0x00 +#define PCNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */ +#define PCNET_RESET 0x1f /* Issue a read to reset, a write to clear. */ +#define PCNET_MISC 0x18 /* For IBM CCAE and Socket EA cards */ + +#define PCNET_START_PG 0x40 /* First page of TX buffer */ +#define PCNET_STOP_PG 0x80 /* Last page +1 of RX ring */ + +/* Socket EA cards have a larger packet buffer */ +#define SOCKET_START_PG 0x01 +#define SOCKET_STOP_PG 0xff + +#define PCNET_RDC_TIMEOUT 0x02 /* Max wait in jiffies for Tx RDC */ + +static char *if_names[] = { "auto", "10baseT", "10base2"}; + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"pcnet_cs.c 1.99 1999/09/15 15:33:09 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +/* Transceiver type, for Socket EA and IBM CC cards. */ +static int if_port = 1; + +/* Use 64K packet buffer, for Socket EA cards. */ +static int use_big_buf = 1; + +/* Shared memory speed, in ns */ +static int mem_speed = 0; + +/* Insert a pause in block_output after sending a packet */ +static int delay_output = 0; + +/* Length of delay, in microseconds */ +static int delay_time = 4; + +/* Use shared memory, if available? */ +static int use_shmem = -1; + +/* Ugh! Let the user hardwire the hardware address for queer cards */ +static int hw_addr[6] = { 0, /* ... */ }; + +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM(if_port, "i"); +MODULE_PARM(use_big_buf, "i"); +MODULE_PARM(mem_speed, "i"); +MODULE_PARM(delay_output, "i"); +MODULE_PARM(delay_time, "i"); +MODULE_PARM(use_shmem, "i"); +MODULE_PARM(hw_addr, "6i"); + +/*====================================================================*/ + +static void pcnet_config(dev_link_t *link); +static void pcnet_release(u_long arg); +static int pcnet_event(event_t event, int priority, + event_callback_args_t *args); + +static int pcnet_open(struct net_device *dev); +static int pcnet_close(struct net_device *dev); +static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs); +static void ei_watchdog(u_long arg); + +static void pcnet_reset_8390(struct net_device *dev); + +static int set_config(struct net_device *dev, struct ifmap *map); + +static int setup_shmem_window(dev_link_t *link, int start_pg, + int stop_pg, int cm_offset); +static int setup_dma_config(dev_link_t *link, int start_pg, + int stop_pg); + +static dev_info_t dev_info = "pcnet_cs"; + +static dev_link_t *pcnet_attach(void); +static void pcnet_detach(dev_link_t *); + +static dev_link_t *dev_list; + +/*====================================================================*/ + +typedef struct hw_info_t { + u_long offset; + u_char a0, a1, a2; + u_long flags; +} hw_info_t; + +#define DELAY_OUTPUT 0x01 +#define HAS_MISC_REG 0x02 +#define USE_BIG_BUF 0x04 +#define HAS_IBM_MISC 0x08 +#define IS_DL10019A 0x10 +#define USE_SHMEM 0x80 /* autodetected */ + +static hw_info_t hw_info[] = { + { /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT }, + { /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 }, + { /* APEX MultiCard */ 0x03f4, 0x00, 0x20, 0xe5, 0 }, + { /* ASANTE FriendlyNet */ 0x4910, 0x00, 0x00, 0x94, + DELAY_OUTPUT | HAS_IBM_MISC }, + { /* Danpex EN-6200P2 */ 0x0110, 0x00, 0x40, 0xc7, 0 }, + { /* DataTrek NetCard */ 0x0ff0, 0x00, 0x20, 0xe8, 0 }, + { /* Dayna CommuniCard E */ 0x0110, 0x00, 0x80, 0x19, 0 }, + { /* D-Link DE-650 */ 0x0040, 0x00, 0x80, 0xc8, 0 }, + { /* EP-210 Ethernet */ 0x0110, 0x00, 0x40, 0x33, 0 }, + { /* EP4000 Ethernet */ 0x01c0, 0x00, 0x00, 0xb4, 0 }, + { /* Epson EEN10B */ 0x0ff0, 0x00, 0x00, 0x48, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* ELECOM Laneed LD-CDWA */ 0xb8, 0x08, 0x00, 0x42, 0 }, + { /* Hypertec Ethernet */ 0x01c0, 0x00, 0x40, 0x4c, 0 }, + { /* IBM CCAE */ 0x0ff0, 0x08, 0x00, 0x5a, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM CCAE */ 0x0ff0, 0x00, 0x04, 0xac, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM CCAE */ 0x0ff0, 0x00, 0x06, 0x29, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM FME */ 0x0374, 0x08, 0x00, 0x5a, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM FME */ 0x0374, 0x00, 0x04, 0xac, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Kansai KLA-PCM/T */ 0x0ff0, 0x00, 0x60, 0x87, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* NSC DP83903 */ 0x0374, 0x00, 0xc0, 0xa8, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* NSC DP83903 */ 0x0374, 0x00, 0xa0, 0xb0, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* NSC DP83903 */ 0x0198, 0x00, 0x20, 0xe0, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* I-O DATA PCLA/T */ 0x0ff0, 0x00, 0xa0, 0xb0, 0 }, + { /* Katron PE-520 */ 0x0110, 0x00, 0x40, 0xf6, 0 }, + { /* Kingston KNE-PCM/x */ 0x0ff0, 0x00, 0xc0, 0xf0, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Kingston KNE-PCM/x */ 0x0ff0, 0xe2, 0x0c, 0x0f, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Kingston KNE-PC2 */ 0x0180, 0x00, 0xc0, 0xf0, 0 }, + { /* Maxtech PCN2000 */ 0x5000, 0x00, 0x00, 0xe8, 0 }, + { /* NDC Instant-Link */ 0x003a, 0x00, 0x80, 0xc6, 0 }, + { /* NE2000 Compatible */ 0x0ff0, 0x00, 0xa0, 0x0c, 0 }, + { /* Network General Sniffer */ 0x0ff0, 0x00, 0x00, 0x65, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Panasonic VEL211 */ 0x0ff0, 0x00, 0x80, 0x45, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* PreMax PE-200 */ 0x07f0, 0x00, 0x20, 0xe0, 0 }, + { /* RPTI EP400 */ 0x0110, 0x00, 0x40, 0x95, 0 }, + { /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 }, + { /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b, + DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF }, + { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 }, + { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 } +}; + +#define NR_INFO (sizeof(hw_info)/sizeof(hw_info_t)) + +static hw_info_t default_info = +{ /* Unknown NE2000 Clone */ 0x00, 0x00, 0x00, 0x00, 0 }; +static hw_info_t dl_fast_info = +{ /* D-Link EtherFast */ 0x00, 0x00, 0x00, 0x00, IS_DL10019A }; + +typedef struct pcnet_dev_t { + struct net_device dev; + dev_node_t node; + u_long flags; + caddr_t base; + struct timer_list watchdog; + int stale; + u_short fast_poll; +} pcnet_dev_t; + +/*====================================================================== + + This bit of code is used to avoid unregistering network devices + at inappropriate times. 2.2 and later kernels are fairly picky + about when this can happen. + +======================================================================*/ + +static void flush_stale_links(void) +{ + dev_link_t *link, *next; + for (link = dev_list; link; link = next) { + next = link->next; + if (link->state & DEV_STALE_LINK) + pcnet_detach(link); + } +} + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================== + + We never need to do anything when a pcnet device is "initialized" + by the net software, because we only register already-found cards. + +======================================================================*/ + +static int pcnet_init(struct net_device *dev) +{ + return 0; +} + +/*====================================================================== + + pcnet_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + +======================================================================*/ + +static dev_link_t *pcnet_attach(void) +{ + client_reg_t client_reg; + dev_link_t *link; + pcnet_dev_t *info; + struct net_device *dev; + int i, ret; + + DEBUG(0, "pcnet_attach()\n"); + flush_stale_links(); + + /* Create new ethernet device */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + link->release.function = &pcnet_release; + link->release.data = (u_long)link; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + + info = kmalloc(sizeof(struct pcnet_dev_t), GFP_KERNEL); + memset(info, 0, sizeof(struct pcnet_dev_t)); + dev = &info->dev; + ethdev_init(dev); + dev->name = info->node.dev_name; + dev->init = &pcnet_init; + dev->open = &pcnet_open; + dev->stop = &pcnet_close; + dev->set_config = &set_config; + dev->tbusy = 1; + link->priv = info; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &pcnet_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + pcnet_detach(link); + return NULL; + } + + return link; +} /* pcnet_attach */ + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void pcnet_detach(dev_link_t *link) +{ + dev_link_t **linkp; + long flags; + + DEBUG(0, "pcnet_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + save_flags(flags); + cli(); + if (link->state & DEV_RELEASE_PENDING) { + del_timer(&link->release); + link->state &= ~DEV_RELEASE_PENDING; + } + restore_flags(flags); + + if (link->state & DEV_CONFIG) { + pcnet_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free bits */ + *linkp = link->next; + if (link->priv) { + struct net_device *dev = link->priv; + if (link->dev != NULL) + unregister_netdev(dev); + if (dev->priv) + kfree_s(dev->priv, sizeof(struct ei_device)); + kfree_s(dev, sizeof(struct pcnet_dev_t)); + } + kfree_s(link, sizeof(struct dev_link_t)); + +} /* pcnet_detach */ + +/*====================================================================== + + For the Linksys EtherFast 10/100 card + +======================================================================*/ + +static hw_info_t *get_dl_fast(dev_link_t *link) +{ + struct net_device *dev = link->priv; + int i; + u_char sum; + + for (sum = 0, i = 0x14; i < 0x1c; i++) + sum += inb_p(dev->base_addr + i); + if (sum != 0xff) + return NULL; + for (i = 0; i < 6; i++) + dev->dev_addr[i] = inb_p(dev->base_addr + 0x14 + i); + return &dl_fast_info; +} + +/*====================================================================== + + This probes for a card's hardware address, for card types that + encode this information in their CIS. + +======================================================================*/ + +static hw_info_t *get_hwinfo(dev_link_t *link) +{ + struct net_device *dev = link->priv; + win_req_t req; + memreq_t mem; + u_char *base, *virt; + int i, j; + + /* Allocate a small memory window */ + req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + req.Base = 0; req.Size = 0; + req.AccessSpeed = 0; + link->win = (window_handle_t)link->handle; + i = CardServices(RequestWindow, &link->win, &req); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestWindow, i); + return NULL; + } + + virt = ioremap(req.Base, req.Size); + mem.Page = 0; + for (i = 0; i < NR_INFO; i++) { + mem.CardOffset = hw_info[i].offset & ~(req.Size-1); + CardServices(MapMemPage, link->win, &mem); + base = &virt[hw_info[i].offset & (req.Size-1)]; + if ((readb(base+0) == hw_info[i].a0) && + (readb(base+2) == hw_info[i].a1) && + (readb(base+4) == hw_info[i].a2)) + break; + } + if (i < NR_INFO) { + for (j = 0; j < 6; j++) + dev->dev_addr[j] = readb(base + (j<<1)); + } + + iounmap(virt); + j = CardServices(ReleaseWindow, link->win); + if (j != CS_SUCCESS) + cs_error(link->handle, ReleaseWindow, j); + return (i < NR_INFO) ? hw_info+i : NULL; +} /* get_hwinfo */ + +/*====================================================================== + + This probes for a card's hardware address by reading the PROM. + It checks the address against a list of known types, then falls + back to a simple NE2000 clone signature check. + +======================================================================*/ + +static hw_info_t *get_prom(dev_link_t *link) +{ + struct net_device *dev = link->priv; + unsigned char prom[32]; + int i, j, ioaddr; + + /* This is lifted straight from drivers/net/ne.c */ + struct { + unsigned char value, offset; + } program_seq[] = { + {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ + {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */ + {0x00, EN0_RCNTLO}, /* Clear the count regs. */ + {0x00, EN0_RCNTHI}, + {0x00, EN0_IMR}, /* Mask completion irq. */ + {0xFF, EN0_ISR}, + {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */ + {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ + {32, EN0_RCNTLO}, + {0x00, EN0_RCNTHI}, + {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ + {0x00, EN0_RSARHI}, + {E8390_RREAD+E8390_START, E8390_CMD}, + }; + + ioaddr = dev->base_addr; + + pcnet_reset_8390(dev); + udelay(10000); + + for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) + outb_p(program_seq[i].value, ioaddr + program_seq[i].offset); + + for (i = 0; i < 32; i++) + prom[i] = inb(ioaddr + PCNET_DATAPORT); + for (i = 0; i < NR_INFO; i++) { + if ((prom[0] == hw_info[i].a0) && + (prom[2] == hw_info[i].a1) && + (prom[4] == hw_info[i].a2)) + break; + } + if ((i < NR_INFO) || ((prom[28] == 0x57) && (prom[30] == 0x57))) { + for (j = 0; j < 6; j++) + dev->dev_addr[j] = prom[j<<1]; + return (i < NR_INFO) ? hw_info+i : &default_info; + } + return NULL; +} /* get_prom */ + +/*====================================================================== + + This should be totally unnecessary... but when we can't figure + out the hardware address any other way, we'll let the user hard + wire it when the module is initialized. + +======================================================================*/ + +static hw_info_t *get_hwired(dev_link_t *link) +{ + struct net_device *dev = link->priv; + int i; + + for (i = 0; i < 6; i++) + if (hw_addr[i] != 0) break; + if (i == 6) + return NULL; + + for (i = 0; i < 6; i++) + dev->dev_addr[i] = hw_addr[i]; + + return &default_info; +} /* get_hwired */ + +/*====================================================================== + + pcnet_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + ethernet device available to the system. + +======================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +#define CFG_CHECK(fn, args...) \ +if (CardServices(fn, args) != 0) goto next_entry + +static int try_io_port(dev_link_t *link) +{ + int j, ret; + if (link->io.NumPorts1 == 32) { + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + if (link->io.NumPorts2 > 0) { + /* for master/slave multifunction cards */ + link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; + link->irq.Attributes = + IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; + } + } else { + /* This should be two 16-port windows */ + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; + } + if (link->io.BasePort1 == 0) { + for (j = 0; j < 0x400; j += 0x20) { + link->io.BasePort1 = j ^ 0x300; + link->io.BasePort2 = (j ^ 0x300) + 0x10; + ret = CardServices(RequestIO, link->handle, &link->io); + if (ret == CS_SUCCESS) return ret; + } + return ret; + } else { + return CardServices(RequestIO, link->handle, &link->io); + } +} + +static void pcnet_config(dev_link_t *link) +{ + client_handle_t handle; + tuple_t tuple; + cisparse_t parse; + pcnet_dev_t *info; + struct net_device *dev; + int i, last_ret, last_fn, start_pg, stop_pg, cm_offset; + int manfid = 0, prodid = 0, has_shmem = 0; + u_short buf[64]; + hw_info_t *hw_info; + + handle = link->handle; + info = link->priv; + dev = &info->dev; + + DEBUG(0, "pcnet_config(0x%p)\n", link); + + tuple.Attributes = 0; + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + tuple.DesiredTuple = CISTPL_MANFID; + tuple.Attributes = TUPLE_RETURN_COMMON; + if ((CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) && + (CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS)) { + manfid = le16_to_cpu(buf[0]); + prodid = le16_to_cpu(buf[1]); + } + + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + tuple.Attributes = 0; + CS_CHECK(GetFirstTuple, handle, &tuple); + while (last_ret == CS_SUCCESS) { + cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); + cistpl_io_t *io = &(parse.cftable_entry.io); + + CFG_CHECK(GetTupleData, handle, &tuple); + CFG_CHECK(ParseTuple, handle, &tuple, &parse); + if ((cfg->index == 0) || (cfg->io.nwin == 0)) + goto next_entry; + + link->conf.ConfigIndex = cfg->index; + /* For multifunction cards, by convention, we configure the + network function with window 0, and serial with window 1 */ + if (io->nwin > 1) { + i = (io->win[1].len > io->win[0].len); + link->io.BasePort2 = io->win[1-i].base; + link->io.NumPorts2 = io->win[1-i].len; + } else { + i = link->io.NumPorts2 = 0; + } + has_shmem = ((cfg->mem.nwin == 1) && + (cfg->mem.win[0].len >= 0x4000)); + link->io.BasePort1 = io->win[i].base; + link->io.NumPorts1 = io->win[i].len; + if (link->io.NumPorts1 + link->io.NumPorts2 >= 32) { + last_ret = try_io_port(link); + if (last_ret == CS_SUCCESS) break; + } + next_entry: + last_ret = CardServices(GetNextTuple, handle, &tuple); + } + if (last_ret != CS_SUCCESS) { + cs_error(handle, RequestIO, last_ret); + goto failed; + } + + CS_CHECK(RequestIRQ, handle, &link->irq); + + if (link->io.NumPorts2 == 8) { + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status = CCSR_AUDIO_ENA; + } + if ((manfid == MANFID_IBM) && + (prodid == PRODID_IBM_HOME_AND_AWAY)) + link->conf.ConfigIndex |= 0x10; + + CS_CHECK(RequestConfiguration, handle, &link->conf); + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + if ((if_port == 1) || (if_port == 2)) + dev->if_port = if_port; + else + printk(KERN_NOTICE "pcnet_cs: invalid if_port requested\n"); + dev->tbusy = 0; + if (register_netdev(dev) != 0) { + printk(KERN_NOTICE "pcnet_cs: register_netdev() failed\n"); + goto failed; + } + + hw_info = get_hwinfo(link); + if (hw_info == NULL) + hw_info = get_prom(link); + if (hw_info == NULL) + hw_info = get_dl_fast(link); + if (hw_info == NULL) + hw_info = get_hwired(link); + + if (hw_info == NULL) { + printk(KERN_NOTICE "pcnet_cs: unable to read hardware net address\n"); + goto config_undo; + } + + info->flags = hw_info->flags; + /* Check for user overrides */ + info->flags |= (delay_output) ? DELAY_OUTPUT : 0; + if ((manfid == MANFID_SOCKET) && (prodid == PRODID_SOCKET_LPE)) + info->flags &= ~USE_BIG_BUF; + if (!use_big_buf) + info->flags &= ~USE_BIG_BUF; + + if (info->flags & USE_BIG_BUF) { + start_pg = SOCKET_START_PG; + stop_pg = SOCKET_STOP_PG; + cm_offset = 0x10000; + } else { + start_pg = PCNET_START_PG; + stop_pg = PCNET_STOP_PG; + cm_offset = 0; + } + + /* has_shmem is ignored if use_shmem != -1 */ + if ((use_shmem == 0) || (!has_shmem && (use_shmem == -1)) || + (setup_shmem_window(link, start_pg, stop_pg, cm_offset) != 0)) + setup_dma_config(link, start_pg, stop_pg); + + ei_status.name = "NE2000"; + ei_status.word16 = 1; + ei_status.reset_8390 = &pcnet_reset_8390; + + link->dev = &info->node; + link->state &= ~DEV_CONFIG_PENDING; + + printk(KERN_INFO "%s: NE2000 Compatible: io %#3lx, irq %d,", + dev->name, dev->base_addr, dev->irq); + if (info->flags & USE_SHMEM) + printk (" mem %#5lx,", dev->mem_start); + if (info->flags & HAS_MISC_REG) + printk(" %s xcvr,", if_names[dev->if_port]); + printk(" hw_addr "); + for (i = 0; i < 6; i++) + printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); + return; + +config_undo: + unregister_netdev(dev); + goto failed; +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + pcnet_release((u_long)link); + return; +} /* pcnet_config */ + +/*====================================================================== + + After a card is removed, pcnet_release() will unregister the net + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. + +======================================================================*/ + +static void pcnet_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + pcnet_dev_t *info = link->priv; + + DEBUG(0, "pcnet_release(0x%p)\n", link); + + if (link->open) { + DEBUG(1, "pcnet_cs: release postponed, '%s' still open\n", + info->node.dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + + if (info->flags & USE_SHMEM) { + iounmap(info->base); + CardServices(ReleaseWindow, link->win); + } + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING); + +} /* pcnet_release */ + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the net drivers from trying + to talk to the card any more. + +======================================================================*/ + +static int pcnet_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + pcnet_dev_t *info = link->priv; + + DEBUG(2, "pcnet_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + info->dev.tbusy = 1; info->dev.start = 0; + link->release.expires = jiffies + HZ/20; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT; + pcnet_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) { + if (link->open) { + info->dev.tbusy = 1; info->dev.start = 0; + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + CardServices(RequestConfiguration, link->handle, &link->conf); + if (link->open) { + pcnet_reset_8390(&info->dev); + NS8390_init(&info->dev, 1); + info->dev.tbusy = 0; info->dev.start = 1; + } + } + break; + } + return 0; +} /* pcnet_event */ + +/*====================================================================*/ + +static void set_misc_reg(struct net_device *dev) +{ + int nic_base = dev->base_addr; + pcnet_dev_t *info = (pcnet_dev_t *)dev; + u_char tmp; + + if (info->flags & HAS_MISC_REG) { + tmp = inb_p(nic_base + PCNET_MISC) & ~3; + if (dev->if_port == 2) + tmp |= 1; + if (info->flags & USE_BIG_BUF) + tmp |= 2; + if (info->flags & HAS_IBM_MISC) + tmp |= 8; + outb_p(tmp, nic_base + PCNET_MISC); + } +} + +/*====================================================================*/ + +static int pcnet_open(struct net_device *dev) +{ + pcnet_dev_t *info = (pcnet_dev_t *)dev; + dev_link_t *link; + + DEBUG(2, "pcnet_open('%s')\n", dev->name); + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (!DEV_OK(link)) + return -ENODEV; + + link->open++; + MOD_INC_USE_COUNT; + + /* For D-Link EtherFast, wait for something(?) to happen */ + if (info->flags & IS_DL10019A) { + int i; + for (i = 0; i < 20; i++) { + if ((inb(dev->base_addr+0x1c) & 0x01) == 0) break; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + } + } + + set_misc_reg(dev); + request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev); + + info->watchdog.function = &ei_watchdog; + info->watchdog.data = (u_long)info; + info->watchdog.expires = jiffies + HZ; + add_timer(&info->watchdog); + + return ei_open(dev); +} /* pcnet_open */ + +/*====================================================================*/ + +static int pcnet_close(struct net_device *dev) +{ + dev_link_t *link; + + DEBUG(2, "pcnet_close('%s')\n", dev->name); + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (link == NULL) + return -ENODEV; + free_irq(dev->irq, dev); + + link->open--; dev->start = 0; + del_timer(&((pcnet_dev_t *)dev)->watchdog); + if (link->state & DEV_STALE_CONFIG) { + link->release.expires = jiffies + HZ/20; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + + MOD_DEC_USE_COUNT; + + return 0; +} /* pcnet_close */ + +/*====================================================================== + + Hard reset the card. This used to pause for the same period that + a 8390 reset command required, but that shouldn't be necessary. + +======================================================================*/ + +static void pcnet_reset_8390(struct net_device *dev) +{ + int nic_base = dev->base_addr; + int i; + + ei_status.txing = ei_status.dmaing = 0; + + outb(inb(nic_base + PCNET_RESET), nic_base + PCNET_RESET); + + for (i = 0; i < 100; i++) { + if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0) + break; + udelay(100L); + } + outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */ + + if (i == 100) + printk(KERN_ERR "%s: pcnet_reset_8390() did not complete.\n", + dev->name); + set_misc_reg(dev); + +} /* pcnet_reset_8390 */ + +/* ======================================================================= */ + +static int set_config(struct net_device *dev, struct ifmap *map) +{ + pcnet_dev_t *info = (pcnet_dev_t *)dev; + if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { + if ((map->port != 0) && !(info->flags & HAS_MISC_REG)) { + printk(KERN_NOTICE "%s: transceiver selection not " + "implemented\n", dev->name); + return -EINVAL; + } + if ((map->port == 1) || (map->port == 2)) { + dev->if_port = map->port; + printk(KERN_INFO "%s: switched to %s port\n", + dev->name, if_names[dev->if_port]); + } else + return -EINVAL; + } + return 0; +} + +/* ======================================================================= */ + +static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs) +{ + pcnet_dev_t *info = dev_id; + info->stale = 0; + ei_interrupt(irq, dev_id, regs); +} + +static void ei_watchdog(u_long arg) +{ + pcnet_dev_t *info = (pcnet_dev_t *)(arg); + struct net_device *dev = &info->dev; + int nic_base = dev->base_addr; + + if (dev->start == 0) goto reschedule; + + /* Check for pending interrupt with expired latency timer: with + this, we can limp along even if the interrupt is blocked */ + outb_p(E8390_NODMA+E8390_PAGE0, nic_base + E8390_CMD); + if (info->stale++ && inb_p(nic_base + EN0_ISR)) { + if (!info->fast_poll) + printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); + ei_irq_wrapper(dev->irq, dev, NULL); + info->fast_poll = HZ; + } + if (info->fast_poll) { + info->fast_poll--; + info->watchdog.expires = jiffies + 1; + add_timer(&info->watchdog); + return; + } + +reschedule: + info->watchdog.expires = jiffies + HZ; + add_timer(&info->watchdog); +} + +/* ======================================================================= */ + +static void dma_get_8390_hdr(struct net_device *dev, + struct e8390_pkt_hdr *hdr, + int ring_page) +{ + int nic_base = dev->base_addr; + + if (ei_status.dmaing) { + printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input." + "[DMAstat:%1x][irqlock:%1x][intr:%ld]\n", + dev->name, ei_status.dmaing, ei_status.irqlock, + (long)dev->interrupt); + return; + } + + ei_status.dmaing |= 0x01; + outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD); + outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); + outb_p(0, nic_base + EN0_RCNTHI); + outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */ + outb_p(ring_page, nic_base + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD); + + insw(nic_base + PCNET_DATAPORT, hdr, + sizeof(struct e8390_pkt_hdr)>>1); + /* Fix for big endian systems */ + hdr->count = le16_to_cpu(hdr->count); + + outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +/* ======================================================================= */ + +static void dma_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset) +{ + int nic_base = dev->base_addr; + int xfer_count = count; + char *buf = skb->data; + +#ifdef PCMCIA_DEBUG + if ((ei_debug > 4) && (count != 4)) + printk(KERN_DEBUG "%s: [bi=%d]\n", dev->name, count+4); +#endif + if (ei_status.dmaing) { + printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input." + "[DMAstat:%1x][irqlock:%1x][intr:%ld]\n", + dev->name, ei_status.dmaing, ei_status.irqlock, + (long)dev->interrupt); + return; + } + ei_status.dmaing |= 0x01; + outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD); + outb_p(count & 0xff, nic_base + EN0_RCNTLO); + outb_p(count >> 8, nic_base + EN0_RCNTHI); + outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); + outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD); + + insw(nic_base + PCNET_DATAPORT,buf,count>>1); + if (count & 0x01) + buf[count-1] = inb(nic_base + PCNET_DATAPORT), xfer_count++; + + /* This was for the ALPHA version only, but enough people have + encountering problems that it is still here. */ +#ifdef PCMCIA_DEBUG + if (ei_debug > 4) { /* DMA termination address check... */ + int addr, tries = 20; + do { + /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here + -- it's broken for Rx on some cards! */ + int high = inb_p(nic_base + EN0_RSARHI); + int low = inb_p(nic_base + EN0_RSARLO); + addr = (high << 8) + low; + if (((ring_offset + xfer_count) & 0xff) == (addr & 0xff)) + break; + } while (--tries > 0); + if (tries <= 0) + printk(KERN_NOTICE "%s: RX transfer address mismatch," + "%#4.4x (expected) vs. %#4.4x (actual).\n", + dev->name, ring_offset + xfer_count, addr); + } +#endif + outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} /* dma_block_input */ + +/*====================================================================*/ + +static void dma_block_output(struct net_device *dev, int count, + const unsigned char *buf, + const int start_page) +{ + int nic_base = dev->base_addr; + pcnet_dev_t *info = (pcnet_dev_t *)dev; +#ifdef PCMCIA_DEBUG + int retries = 0; +#endif + u_long dma_start; + +#ifdef PCMCIA_DEBUG + if (ei_debug > 4) + printk(KERN_DEBUG "%s: [bo=%d]\n", dev->name, count); +#endif + + /* Round the count up for word writes. Do we need to do this? + What effect will an odd byte count have on the 8390? + I should check someday. */ + if (count & 0x01) + count++; + if (ei_status.dmaing) { + printk(KERN_NOTICE "%s: DMAing conflict in dma_block_output." + "[DMAstat:%1x][irqlock:%1x][intr:%ld]\n", + dev->name, ei_status.dmaing, ei_status.irqlock, + (long)dev->interrupt); + return; + } + ei_status.dmaing |= 0x01; + /* We should already be in page 0, but to be safe... */ + outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base+PCNET_CMD); + +#ifdef PCMCIA_DEBUG + retry: +#endif + + outb_p(ENISR_RDC, nic_base + EN0_ISR); + + /* Now the normal output. */ + outb_p(count & 0xff, nic_base + EN0_RCNTLO); + outb_p(count >> 8, nic_base + EN0_RCNTHI); + outb_p(0x00, nic_base + EN0_RSARLO); + outb_p(start_page, nic_base + EN0_RSARHI); + + outb_p(E8390_RWRITE+E8390_START, nic_base + PCNET_CMD); + outsw(nic_base + PCNET_DATAPORT, buf, count>>1); + + dma_start = jiffies; + +#ifdef PCMCIA_DEBUG + /* This was for the ALPHA version only, but enough people have + encountering problems that it is still here. */ + if (ei_debug > 4) { /* DMA termination address check... */ + int addr, tries = 20; + do { + int high = inb_p(nic_base + EN0_RSARHI); + int low = inb_p(nic_base + EN0_RSARLO); + addr = (high << 8) + low; + if ((start_page << 8) + count == addr) + break; + } while (--tries > 0); + if (tries <= 0) { + printk(KERN_NOTICE "%s: Tx packet transfer address mismatch," + "%#4.4x (expected) vs. %#4.4x (actual).\n", + dev->name, (start_page << 8) + count, addr); + if (retries++ == 0) + goto retry; + } + } +#endif + + while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0) + if (jiffies - dma_start > PCNET_RDC_TIMEOUT) { + printk(KERN_NOTICE "%s: timeout waiting for Tx RDC.\n", + dev->name); + pcnet_reset_8390(dev); + NS8390_init(dev, 1); + break; + } + + outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + if (info->flags & DELAY_OUTPUT) + udelay((long)delay_time); + ei_status.dmaing &= ~0x01; +} + +/*====================================================================*/ + +static int setup_dma_config(dev_link_t *link, int start_pg, + int stop_pg) +{ + struct net_device *dev = link->priv; + + ei_status.tx_start_page = start_pg; + ei_status.rx_start_page = start_pg + TX_PAGES; + ei_status.stop_page = stop_pg; + + /* set up block i/o functions */ + ei_status.get_8390_hdr = &dma_get_8390_hdr; + ei_status.block_input = &dma_block_input; + ei_status.block_output = &dma_block_output; + + return 0; +} + +/*====================================================================*/ + +static void copyin(unsigned char *dest, unsigned char *src, int c) +{ + unsigned short *d = (unsigned short *) dest; + unsigned short *s = (unsigned short *) src; + int odd; + + if (c <= 0) + return; + odd = (c & 01); c >>= 1; + + if (c) { + do { *d++ = __raw_readw(s++); } while (--c); + } + /* get last byte by fetching a word and masking */ + if (odd) + *((unsigned char *)d) = readw(s) & 0xff; +} + +static void copyout(unsigned char *dest, const unsigned char *src, int c) +{ + volatile unsigned short *d = (unsigned short *) dest; + unsigned short *s = (unsigned short *) src; + int odd; + + if (c <= 0) + return; + odd = (c & 01); c >>= 1; + + if (c) { + do { __raw_writew(*s++, d++); } while (--c); + } + /* copy last byte doing a read-modify-write */ + if (odd) + writew((readw(d) & 0xff00) | *(u_char *)s, d); +} + +/*====================================================================*/ + +static void shmem_get_8390_hdr(struct net_device *dev, + struct e8390_pkt_hdr *hdr, + int ring_page) +{ + void *xfer_start = (void *)(dev->rmem_start + (ring_page << 8) + - (ei_status.rx_start_page << 8)); + + copyin((void *)hdr, xfer_start, sizeof(struct e8390_pkt_hdr)); + /* Fix for big endian systems */ + hdr->count = le16_to_cpu(hdr->count); +} + +/*====================================================================*/ + +static void shmem_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset) +{ + void *xfer_start = (void *)(dev->rmem_start + ring_offset + - (ei_status.rx_start_page << 8)); + char *buf = skb->data; + + if (xfer_start + count > (void *)dev->rmem_end) { + /* We must wrap the input move. */ + int semi_count = (void*)dev->rmem_end - xfer_start; + copyin(buf, xfer_start, semi_count); + buf += semi_count; + ring_offset = ei_status.rx_start_page << 8; + xfer_start = (void *)dev->rmem_start; + count -= semi_count; + } + copyin(buf, xfer_start, count); +} + +/*====================================================================*/ + +static void shmem_block_output(struct net_device *dev, int count, + const unsigned char *buf, + const int start_page) +{ + void *shmem = (void *)dev->mem_start + (start_page << 8); + shmem -= ei_status.tx_start_page << 8; + + if (ei_debug > 4) + printk(KERN_DEBUG "[bo=%d @ %x]\n", count, start_page); + + copyout(shmem, buf, count); +} + +/*====================================================================*/ + +static int setup_shmem_window(dev_link_t *link, int start_pg, + int stop_pg, int cm_offset) +{ + struct net_device *dev = link->priv; + pcnet_dev_t *info = link->priv; + win_req_t req; + memreq_t mem; + int i, window_size, offset, last_ret, last_fn; + + window_size = (stop_pg - start_pg) << 8; + if (window_size > 32 * 1024) + window_size = 32 * 1024; + + /* Make sure it's a power of two. */ + while ((window_size & (window_size - 1)) != 0) + window_size += window_size & ~(window_size - 1); + + /* Allocate a memory window */ + req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE; + req.Attributes |= WIN_USE_WAIT; + req.Base = 0; req.Size = window_size; + req.AccessSpeed = mem_speed; + link->win = (window_handle_t)link->handle; + CS_CHECK(RequestWindow, &link->win, &req); + + mem.CardOffset = (start_pg << 8) + cm_offset; + offset = mem.CardOffset % window_size; + mem.CardOffset -= offset; + mem.Page = 0; + CS_CHECK(MapMemPage, link->win, &mem); + + /* Try scribbling on the buffer */ + info->base = ioremap(req.Base, window_size); + for (i = 0; i < (TX_PAGES<<8); i += 2) + __raw_writew((i>>1), info->base+offset+i); + udelay(100); + for (i = 0; i < (TX_PAGES<<8); i += 2) + if (__raw_readw(info->base+offset+i) != (i>>1)) break; + pcnet_reset_8390(dev); + if (i != (TX_PAGES<<8)) { + iounmap(info->base); + CardServices(ReleaseWindow, link->win); + info->base = NULL; link->win = NULL; + goto failed; + } + + dev->mem_start = (u_long)info->base + offset; + dev->rmem_start = dev->mem_start + (TX_PAGES<<8); + dev->mem_end = dev->rmem_end = (u_long)info->base + req.Size; + + ei_status.tx_start_page = start_pg; + ei_status.rx_start_page = start_pg + TX_PAGES; + ei_status.stop_page = start_pg + ((req.Size - offset) >> 8); + + /* set up block i/o functions */ + ei_status.get_8390_hdr = &shmem_get_8390_hdr; + ei_status.block_input = &shmem_block_input; + ei_status.block_output = &shmem_block_output; + + info->flags |= USE_SHMEM; + return 0; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + return 1; +} + +/*====================================================================*/ + +static int __init init_pcnet_cs(void) +{ + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "pcnet_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &pcnet_attach, &pcnet_detach); + return 0; +} + +static void __exit exit_pcnet_cs(void) +{ + DEBUG(0, "pcnet_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + pcnet_detach(dev_list); +} + +module_init(init_pcnet_cs); +module_exit(exit_pcnet_cs); diff --git a/drivers/net/pcmcia/ray_cs.c b/drivers/net/pcmcia/ray_cs.c new file mode 100644 index 000000000..8af63739c --- /dev/null +++ b/drivers/net/pcmcia/ray_cs.c @@ -0,0 +1,2352 @@ +/*============================================================================= + * + * A PCMCIA client driver for the Raylink wireless LAN card. + * The starting point for this module was the skeleton.c in the + * PCMCIA 2.9.12 package written by David Hinds, dhinds@allegro.stanford.edu + * + * + * Copyright (c) 1998 Corey Thomas (corey@world.std.com) + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of version 2 only of the GNU General Public License as + * published by the Free Software Foundation. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * +=============================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "rayctl.h" +#include "ray_cs.h" + +/* All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If + you do not define PCMCIA_DEBUG at all, all the debug code will be + left out. If you compile with PCMCIA_DEBUG=0, the debug code will + be present but disabled -- but it can then be enabled for specific + modules at load time with a 'pc_debug=#' option to insmod. + + I found that adding -DPCMCIA_DEBUG to the compile options during + the 'make config' resulted in cardmgr not finding any sockets. + Therefore, this module uses RAYLINK_DEBUG instead. + The module option to use is ray_debug=# + where # is 1 for modest output + 2 for more output + ... +*/ + +#ifdef RAYLINK_DEBUG +static int ray_debug = RAYLINK_DEBUG; +MODULE_PARM(ray_debug, "i"); +/* #define DEBUG(n, args...) if (ray_debug>(n)) printk(KERN_DEBUG args); */ +#define DEBUG(n, args...) if (ray_debug>(n)) printk(args); +#else +#define DEBUG(n, args...) +#endif +/** Prototypes based on PCMCIA skeleton driver *******************************/ +void ray_config(dev_link_t *link); +void ray_release(u_long arg); +int ray_event(event_t event, int priority, event_callback_args_t *args); +dev_link_t *ray_attach(void); +void ray_detach(dev_link_t *); + +/***** Prototypes indicated by device structure ******************************/ +int ray_dev_close(struct net_device *dev); +int ray_dev_config(struct net_device *dev, struct ifmap *map); +struct enet_statistics *ray_get_stats(struct net_device *dev); +int ray_dev_init(struct net_device *dev); +int ray_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +int ray_open(struct net_device *dev); +int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static void ray_update_multi_list(struct net_device *dev, int all); +int encapsulate_frame(ray_dev_t *local, struct tx_msg *ptx, UCHAR msg_type, + unsigned char *data, int len); +int translate_frame(ray_dev_t *local, struct tx_msg *ptx, + unsigned char *data, int len); +void ray_build_header(ray_dev_t *local, struct tx_msg *ptx, UCHAR msg_type, + unsigned char *data); +void untranslate(ray_dev_t *local, struct sk_buff *skb, int len); + +/***** Prototypes for raylink functions **************************************/ +int asc_to_int(char a); +void authenticate(ray_dev_t *local); +int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type); +void authenticate_timeout(u_long); +int get_free_ccs(ray_dev_t *local); +int get_free_tx_ccs(ray_dev_t *local); +void init_startup_params(ray_dev_t *local); +int parse_addr(char *in_str, UCHAR *out); +int ray_hw_xmit(unsigned char* data, int len, struct net_device* dev, UCHAR type); +int ray_init(struct net_device *dev); +int interrupt_ecf(ray_dev_t *local, int ccs); +void ray_reset(struct net_device *dev); +void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value, int len); +void verify_dl_startup(u_long); + +/* Prototypes for interrpt time functions **********************************/ +void ray_interrupt(int reg, void *dev_id, struct pt_regs *regs); +void clear_interrupt(ray_dev_t *local); +void rx_deauthenticate(ray_dev_t *local, struct rcs *prcs, + unsigned int pkt_addr, int rx_len); +int copy_from_rx_buff(ray_dev_t *local, UCHAR *dest, int pkt_addr, int len); +void ray_rx(struct net_device *dev, ray_dev_t *local, struct rcs *prcs); +void release_frag_chain(ray_dev_t *local, struct rcs *prcs); +void rx_authenticate(ray_dev_t *local, struct rcs *prcs, + unsigned int pkt_addr, int rx_len); +void rx_data(struct net_device *dev, struct rcs *prcs, unsigned int pkt_addr, + int rx_len); +void associate(ray_dev_t *local); + +/* Card command functions */ +int dl_startup_params(struct net_device *dev); +void join_net(u_long local); +void start_net(u_long local); +/* void start_net(ray_dev_t *local); */ + +int ray_cs_proc_read(char *buf, char **start, off_t off, int len, int spare); + +/* Create symbol table for registering with kernel in init_module */ +EXPORT_SYMBOL(ray_dev_ioctl); +EXPORT_SYMBOL(ray_rx); + +/*===========================================================================*/ +/* Parameters that can be set with 'insmod' */ +/* Bit map of interrupts to choose from */ +/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */ +static u_long irq_mask = 0xdeb8; +MODULE_PARM(irq_mask,"i"); + +/* ADHOC=0, Infrastructure=1 */ +static int net_type = ADHOC; +MODULE_PARM(net_type,"i"); + +/* Hop dwell time in Kus (1024 us units defined by 802.11) */ +static int hop_dwell = 128; +MODULE_PARM(hop_dwell,"i"); + +/* Beacon period in Kus */ +static int beacon_period = 256; +MODULE_PARM(beacon_period,"i"); + +/* power save mode (0 = off, 1 = save power) */ +static int psm = 0; +MODULE_PARM(psm,"i"); + +/* String for network's Extended Service Set ID. 32 Characters max */ +static char *essid = NULL; +MODULE_PARM(essid,"s"); + +/* Default to encapsulation unless translation requested */ +static int translate = 1; +MODULE_PARM(translate,"i"); + +static int country = USA; +MODULE_PARM(country,"i"); + +static int sniffer = 0; +MODULE_PARM(sniffer,"i"); + +static int bc = 0; +MODULE_PARM(bc,"i"); + +/* 48 bit physical card address if overriding card's real physical + * address is required. Since IEEE 802.11 addresses are 48 bits + * like ethernet, an int can't be used, so a string is used. To + * allow use of addresses starting with a decimal digit, the first + * character must be a letter and will be ignored. This letter is + * followed by up to 12 hex digits which are the address. If less + * than 12 digits are used, the address will be left filled with 0's. + * Note that bit 0 of the first byte is the broadcast bit, and evil + * things will happen if it is not 0 in a card address. + */ +static char *phy_addr = NULL; +MODULE_PARM(phy_addr,"s"); + + +/* The dev_info variable is the "key" that is used to match up this + device driver with appropriate cards, through the card configuration + database. +*/ +static dev_info_t dev_info = "ray_cs"; + +/* A linked list of "instances" of the ray device. Each actual + PCMCIA card corresponds to one device instance, and is described + by one dev_link_t structure (defined in ds.h). +*/ +static dev_link_t *dev_list = NULL; + +/* A dev_link_t structure has fields for most things that are needed + to keep track of a socket, but there will usually be some device + specific information that also needs to be kept track of. The + 'priv' pointer in a dev_link_t structure can be used to point to + a device-specific private data structure, like this. +*/ +static const unsigned int ray_mem_speed = 0x2A; + +static UCHAR b5_default_startup_parms[] = { + 0, 0, /* Adhoc station */ + 'L','I','N','U','X', 0, 0, 0, /* 32 char ESSID */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, /* Active scan, CA Mode */ + 0, 0, 0, 0, 0, 0, /* No default MAC addr */ + 0x7f, 0xff, /* Frag threshold */ + 0x00, 0x80, /* Hop time 128 Kus*/ + 0x01, 0x00, /* Beacon period 256 Kus */ + 0x01, 0x07, 0xa3, /* DTIM, retries, ack timeout*/ + 0x1d, 0x82, 0x4e, /* SIFS, DIFS, PIFS */ + 0x7f, 0xff, /* RTS threshold */ + 0x04, 0xe2, 0x38, 0xA4, /* scan_dwell, max_scan_dwell */ + 0x05, /* assoc resp timeout thresh */ + 0x08, 0x02, 0x08, /* adhoc, infra, super cycle max*/ + 0, /* Promiscuous mode */ + 0x0c, 0x0bd, /* Unique word */ + 0x32, /* Slot time */ + 0xff, 0xff, /* roam-low snr, low snr count */ + 0x05, 0xff, /* Infra, adhoc missed bcn thresh */ + 0x01, 0x0b, 0x4f, /* USA, hop pattern, hop pat length */ +/* b4 - b5 differences start here */ + 0x00, 0x3f, /* CW max */ + 0x00, 0x0f, /* CW min */ + 0x04, 0x08, /* Noise gain, limit offset */ + 0x28, 0x28, /* det rssi, med busy offsets */ + 7, /* det sync thresh */ + 0, 2, 2, /* test mode, min, max */ + 0, /* allow broadcast SSID probe resp */ + 0, 0, /* privacy must start, can join */ + 2, 0, 0, 0, 0, 0, 0, 0 /* basic rate set */ +}; + +static UCHAR b4_default_startup_parms[] = { + 0, 0, /* Adhoc station */ + 'L','I','N','U','X', 0, 0, 0, /* 32 char ESSID */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, /* Active scan, CA Mode */ + 0, 0, 0, 0, 0, 0, /* No default MAC addr */ + 0x7f, 0xff, /* Frag threshold */ + 0x02, 0x00, /* Hop time */ + 0x00, 0x01, /* Beacon period */ + 0x01, 0x07, 0xa3, /* DTIM, retries, ack timeout*/ + 0x1d, 0x82, 0xce, /* SIFS, DIFS, PIFS */ + 0x7f, 0xff, /* RTS threshold */ + 0xfb, 0x1e, 0xc7, 0x5c, /* scan_dwell, max_scan_dwell */ + 0x05, /* assoc resp timeout thresh */ + 0x04, 0x02, 0x4, /* adhoc, infra, super cycle max*/ + 0, /* Promiscuous mode */ + 0x0c, 0x0bd, /* Unique word */ + 0x4e, /* Slot time (TBD seems wrong)*/ + 0xff, 0xff, /* roam-low snr, low snr count */ + 0x05, 0xff, /* Infra, adhoc missed bcn thresh */ + 0x01, 0x0b, 0x4e, /* USA, hop pattern, hop pat length */ +/* b4 - b5 differences start here */ + 0x3f, 0x0f, /* CW max, min */ + 0x04, 0x08, /* Noise gain, limit offset */ + 0x28, 0x28, /* det rssi, med busy offsets */ + 7, /* det sync thresh */ + 0, 2, 2 /* test mode, min, max*/ +}; +/*===========================================================================*/ +static unsigned char eth2_llc[] = {0xaa, 0xaa, 3, 0, 0, 0}; + +static char rcsid[] = " $Id: ray_cs.c,v 1.60 1999/09/01 20:58:45 corey Exp $ - Corey Thomas corey@world.std.com"; + +#ifdef CONFIG_PROC_FS +struct proc_dir_entry ray_cs_proc_entry = { + 0, /* Dynamic inode # */ + 6,"ray_cs", /* name length and name */ + S_IFREG | S_IRUGO, /* mode */ + 1, 0, 0, /* nlinks, owner, group */ + 0, /* size (unused) */ + NULL, /* operations (default) */ + &ray_cs_proc_read, /* function to read data */ + /* The end ?? */ +}; +#endif +/*===========================================================================*/ +void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} +/*============================================================================= + ray_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + The dev_link structure is initialized, but we don't actually + configure the card at this point -- we wait until we receive a + card insertion event. +=============================================================================*/ +dev_link_t *ray_attach(void) +{ + client_reg_t client_reg; + dev_link_t *link; + ray_dev_t *local; + int ret; + struct net_device *dev; + + DEBUG(1, "ray_attach()\n"); + + /* Initialize the dev_link_t structure */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + link->release.function = &ray_release; + link->release.data = (u_long)link; + + /* The io structure describes IO port mapping. None used here */ + link->io.NumPorts1 = 0; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.IOAddrLines = 5; + + /* Interrupt setup. For PCMCIA, driver takes what's given */ + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + link->irq.IRQInfo2 = irq_mask; + link->irq.Handler = &ray_interrupt; + + /* General socket configuration */ + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + /* Allocate space for private device-specific data */ + dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); + memset(dev, 0, sizeof(struct net_device)); + link->priv = dev; + link->irq.Instance = dev; + + local = kmalloc(sizeof(ray_dev_t), GFP_KERNEL); + memset(local, 0, sizeof(ray_dev_t)); + dev->priv = local; + local->finder = link; + link->dev = &local->node; + local->card_status = CARD_INSERTED; + local->authentication_state = UNAUTHENTICATED; + local->num_multi = 0; + DEBUG(2,"ray_attach link = %p, dev = %p, local = %p, intr = %p\n", + link,dev,local,&ray_interrupt); + + /* Raylink entries in the device structure */ + dev->hard_start_xmit = &ray_dev_start_xmit; + dev->set_config = &ray_dev_config; + dev->get_stats = &ray_get_stats; + dev->do_ioctl = &ray_dev_ioctl; + + dev->set_multicast_list = &set_multicast_list; + + DEBUG(2,"ray_cs ray_attach calling ether_setup.)\n"); + ether_setup(dev); + dev->name = local->node.dev_name; + dev->init = &ray_dev_init; + dev->open = &ray_open; + dev->stop = &ray_dev_close; + dev->tbusy = 1; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &ray_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + + DEBUG(2,"ray_cs ray_attach calling CardServices(RegisterClient...)\n"); + + init_timer(&local->timer); + + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + printk("ray_cs ray_attach RegisterClient unhappy - detaching\n"); + cs_error(link->handle, RegisterClient, ret); + ray_detach(link); + return NULL; + } + DEBUG(2,"ray_cs ray_attach ending\n"); + return link; +} /* ray_attach */ +/*============================================================================= + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. +=============================================================================*/ +void ray_detach(dev_link_t *link) +{ + dev_link_t **linkp; + struct net_device *dev; + long flags; + + DEBUG(1, "ray_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + save_flags(flags); + cli(); + if (link->state & DEV_RELEASE_PENDING) { + del_timer(&link->release); + link->state &= ~DEV_RELEASE_PENDING; + } + restore_flags(flags); + + /* If the device is currently configured and active, we won't + actually delete it yet. Instead, it is marked so that when + the release() function is called, that will trigger a proper + detach(). + */ + if (link->state & DEV_CONFIG) { + ray_release((u_long)link); + if(link->state & DEV_STALE_CONFIG) { + DEBUG(0,"ray_cs: detach postponed, '%s' " + "still locked\n", link->dev->dev_name); + link->state |= DEV_STALE_LINK; + return; + } + } + + /* Break the link with Card Services */ + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free pieces */ + *linkp = link->next; + if (link->priv) { + dev = link->priv; + if (dev->priv) + kfree_s(dev->priv, sizeof(ray_dev_t)); + + kfree_s(link->priv, sizeof(struct net_device)); + } + kfree_s(link, sizeof(struct dev_link_t)); + DEBUG(2,"ray_cs ray_detach ending\n"); +} /* ray_detach */ +/*============================================================================= + ray_config() is run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + ethernet device available to the system. +=============================================================================*/ +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed +#define MAX_TUPLE_SIZE 80 +void ray_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + tuple_t tuple; + cisparse_t parse; + int last_fn, last_ret; + int i; + u_char buf[80]; + win_req_t req; + memreq_t mem; + struct net_device *dev = (struct net_device *)link->priv; + ray_dev_t *local = (ray_dev_t *)dev->priv; + + DEBUG(1, "ray_config(0x%p)\n", link); + + /* This reads the card's CONFIG tuple to find its configuration regs */ + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, handle, &tuple); + tuple.TupleData = buf; + tuple.TupleDataMax = MAX_TUPLE_SIZE; + tuple.TupleOffset = 0; + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + /* Now allocate an interrupt line. Note that this does not + actually assign a handler to the interrupt. + */ + CS_CHECK(RequestIRQ, link->handle, &link->irq); + dev->irq = link->irq.AssignedIRQ; + + /* This actually configures the PCMCIA socket -- setting up + the I/O windows and the interrupt mapping. + */ + CS_CHECK(RequestConfiguration, link->handle, &link->conf); + +/*** Set up 32k window for shared memory (transmit and control) ************/ + req.Attributes = WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT; + req.Base = 0; + req.Size = 0x8000; + req.AccessSpeed = ray_mem_speed; + link->win = (window_handle_t)link->handle; + CS_CHECK(RequestWindow, &link->win, &req); + mem.CardOffset = 0x0000; mem.Page = 0; + CS_CHECK(MapMemPage, link->win, &mem); + local->sram = (UCHAR *)(ioremap(req.Base,req.Size)); + +/*** Set up 16k window for shared memory (receive buffer) ***************/ + req.Attributes = WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT; + req.Base = 0; + req.Size = 0x4000; + req.AccessSpeed = ray_mem_speed; + local->rmem_handle = (window_handle_t)link->handle; + CS_CHECK(RequestWindow, &local->rmem_handle, &req); + mem.CardOffset = 0x8000; mem.Page = 0; + CS_CHECK(MapMemPage, local->rmem_handle, &mem); + local->rmem = (UCHAR *)(ioremap(req.Base,req.Size)); + +/*** Set up window for attribute memory ***********************************/ + req.Attributes = WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_AM | WIN_ENABLE | WIN_USE_WAIT; + req.Base = 0; + req.Size = 0x1000; + req.AccessSpeed = ray_mem_speed; + local->amem_handle = (window_handle_t)link->handle; + CS_CHECK(RequestWindow, &local->amem_handle, &req); + mem.CardOffset = 0x0000; mem.Page = 0; + CS_CHECK(MapMemPage, local->amem_handle, &mem); + local->amem = (UCHAR *)(ioremap(req.Base,req.Size)); + + DEBUG(3,"ray_config sram=%p\n",local->sram); + DEBUG(3,"ray_config rmem=%p\n",local->rmem); + DEBUG(3,"ray_config amem=%p\n",local->amem); + if (ray_init(dev) < 0) { + ray_release((u_long)link); + return; + } + + i = register_netdev(dev); + if (i != 0) { + printk("ray_config register_netdev() failed\n"); + ray_release((u_long)link); + return; + } + + link->state &= ~DEV_CONFIG_PENDING; + DEBUG(0, "ray_cs device loaded\n"); + + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); + + ray_release((u_long)link); +} /* ray_config */ +/*===========================================================================*/ +int ray_init(struct net_device *dev) +{ + int i; + UCHAR *p; + struct ccs *pccs; + ray_dev_t *local = (ray_dev_t *)dev->priv; + dev_link_t *link = local->finder; + DEBUG(1, "ray_init(0x%p)\n", dev); + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_init - device not present\n"); + return -1; + } + + local->net_type = net_type; + local->sta_type = TYPE_STA; + + /* Copy the startup results to local memory */ + memcpy_fromio(&local->startup_res, local->sram + ECF_TO_HOST_BASE,\ + sizeof(struct startup_res_6)); + + /* Check Power up test status and get mac address from card */ + if (local->startup_res.startup_word != 0x80) { +DEBUG(0,"ray_init ERROR card status = %2x\n", local->startup_res.startup_word); + local->card_status = CARD_INIT_ERROR; + return -1; + } + + local->fw_ver = local->startup_res.firmware_version[0]; + local->fw_bld = local->startup_res.firmware_version[1]; + local->fw_var = local->startup_res.firmware_version[2]; + DEBUG(1,"ray_init firmware version %d.%d \n",local->fw_ver, local->fw_bld); + + local->tib_length = 0x20; + if ((local->fw_ver == 5) && (local->fw_bld >= 30)) + local->tib_length = local->startup_res.tib_length; + DEBUG(2,"ray_init tib_length = 0x%02x\n", local->tib_length); + /* Initialize CCS's to buffer free state */ + pccs = (struct ccs *)(local->sram + CCS_BASE); + for (i=0; ibuffer_status); + } + init_startup_params(local); + + /* copy mac address to startup parameters */ + if (parse_addr(phy_addr, local->sparm.b4.a_mac_addr)) + { + p = local->sparm.b4.a_mac_addr; + DEBUG(1,"ray_cs phy address overridden = %2x %2x %2x %2x %2x %2x\n",\ + p[0],p[1],p[2],p[3],p[4],p[5]); + } + else + { + memcpy(&local->sparm.b4.a_mac_addr, + &local->startup_res.station_addr, ADDRLEN); + p = local->sparm.b4.a_mac_addr; + DEBUG(1,"ray_cs phy addr= %2x %2x %2x %2x %2x %2x\n",\ + p[0],p[1],p[2],p[3],p[4],p[5]); + } + + clear_interrupt(local); /* Clear any interrupt from the card */ + local->card_status = CARD_AWAITING_PARAM; + DEBUG(2,"ray_init ending\n"); + return 0; +} /* ray_init */ +/*===========================================================================*/ +/* Download startup parameters to the card and command it to read them */ +int dl_startup_params(struct net_device *dev) +{ + int ccsindex; + ray_dev_t *local = (ray_dev_t *)dev->priv; + struct ccs *pccs; + dev_link_t *link = local->finder; + + DEBUG(1,"dl_startup_params entered\n"); + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_cs dl_startup_params - device not present\n"); + return -1; + } + + /* Copy parameters to host to ECF area */ + if (local->fw_ver == 0x55) + memcpy_toio(local->sram + HOST_TO_ECF_BASE, &local->sparm.b4, + sizeof(struct b4_startup_params)); + else + memcpy_toio(local->sram + HOST_TO_ECF_BASE, &local->sparm.b5, + sizeof(struct b5_startup_params)); + + + /* Fill in the CCS fields for the ECF */ + if ((ccsindex = get_free_ccs(local)) == -1) return -1; + local->dl_param_ccs = ccsindex; + pccs = ((struct ccs *)(local->sram + CCS_BASE)) + ccsindex; + writeb(CCS_DOWNLOAD_STARTUP_PARAMS, &pccs->cmd); + DEBUG(2,"dl_startup_params start ccsindex = %d\n", local->dl_param_ccs); + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + DEBUG(0,"ray dl_startup_params failed - ECF not ready for intr\n"); + local->card_status = CARD_DL_PARAM_ERROR; + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + return -2; + } + local->card_status = CARD_DL_PARAM; + /* Start kernel timer to wait for dl startup to complete. */ + local->timer.expires = jiffies + HZ/2; + local->timer.data = (long)local; + local->timer.function = &verify_dl_startup; + add_timer(&local->timer); + DEBUG(2,"ray_cs dl_startup_params started timer for verify_dl_startup\n"); + return 0; +} /* dl_startup_params */ +/*===========================================================================*/ +void init_startup_params(ray_dev_t *local) +{ + int i; + static char hop_pattern_length[] = { 1, + USA_HOP_MOD, EUROPE_HOP_MOD, + JAPAN_HOP_MOD, KOREA_HOP_MOD, + SPAIN_HOP_MOD, FRANCE_HOP_MOD, + ISRAEL_HOP_MOD, AUSTRALIA_HOP_MOD, + JAPAN_TEST_HOP_MOD + }; + + if (country > JAPAN_TEST) country = USA; + else + if (country < USA) country = USA; + /* structure for hop time and beacon period is defined here using + * New 802.11D6.1 format. Card firmware is still using old format + * until version 6. + * Before After + * a_hop_time ms byte a_hop_time ms byte + * a_hop_time 2s byte a_hop_time ls byte + * a_hop_time ls byte a_beacon_period ms byte + * a_beacon_period a_beacon_period ls byte + * + * a_hop_time = uS a_hop_time = KuS + * a_beacon_period = hops a_beacon_period = KuS + */ /* 64ms = 010000 */ + if (local->fw_ver == 0x55) { + memcpy((UCHAR *)&local->sparm.b4, b4_default_startup_parms, + sizeof(struct b4_startup_params)); + /* Translate sane kus input values to old build 4/5 format */ + /* i = hop time in uS truncated to 3 bytes */ + i = (hop_dwell * 1024) & 0xffffff; + local->sparm.b4.a_hop_time[0] = (i >> 16) & 0xff; + local->sparm.b4.a_hop_time[1] = (i >> 8) & 0xff; + local->sparm.b4.a_beacon_period[0] = 0; + local->sparm.b4.a_beacon_period[1] = + ((beacon_period/hop_dwell) - 1) & 0xff; + local->sparm.b4.a_curr_country_code = country; + local->sparm.b4.a_hop_pattern_length = + hop_pattern_length[(int)country] - 1; + if (bc) + { + local->sparm.b4.a_ack_timeout = 0x50; + local->sparm.b4.a_sifs = 0x3f; + } + } + else { /* Version 5 uses real kus values */ + memcpy((UCHAR *)&local->sparm.b5, b5_default_startup_parms, + sizeof(struct b5_startup_params)); + + local->sparm.b5.a_hop_time[0] = (hop_dwell >> 8) & 0xff; + local->sparm.b5.a_hop_time[1] = hop_dwell & 0xff; + local->sparm.b5.a_beacon_period[0] = (beacon_period >> 8) & 0xff; + local->sparm.b5.a_beacon_period[1] = beacon_period & 0xff; + if (psm) + local->sparm.b5.a_power_mgt_state = 1; + local->sparm.b5.a_curr_country_code = country; + local->sparm.b5.a_hop_pattern_length = + hop_pattern_length[(int)country]; + } + + local->sparm.b4.a_network_type = net_type & 0x01; + local->sparm.b4.a_acting_as_ap_status = TYPE_STA; + + if (essid != NULL) + strncpy(local->sparm.b4.a_current_ess_id, essid, ESSID_SIZE); +} /* init_startup_params */ +/*===========================================================================*/ +void verify_dl_startup(u_long data) +{ + ray_dev_t *local = (ray_dev_t *)data; + struct ccs *pccs = ((struct ccs *)(local->sram + CCS_BASE)) + local->dl_param_ccs; + UCHAR status; +/* UCHAR *p = local->sram + HOST_TO_ECF_BASE; */ + dev_link_t *link = local->finder; + + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_cs verify_dl_startup - device not present\n"); + return; + } +#ifdef RAYLINK_DEBUG + { + int i; + DEBUG(2,"verify_dl_startup parameters sent via ccs %d:\n",\ + local->dl_param_ccs); + for (i=0; isram + HOST_TO_ECF_BASE + i)); + } + DEBUG(1,"\n"); + } +#endif + + status = readb(&pccs->buffer_status); + if (status!= CCS_BUFFER_FREE) + { + DEBUG(0,"Download startup params failed. Status = %d\n",status); + local->card_status = CARD_DL_PARAM_ERROR; + return; + } + if (local->sparm.b4.a_network_type == ADHOC) + start_net((u_long)local); + else + join_net((u_long)local); + + return; +} /* end verify_dl_startup */ +/*===========================================================================*/ +/* Command card to start a network */ +void start_net(u_long data) +{ + ray_dev_t *local = (ray_dev_t *)data; + struct ccs *pccs; + int ccsindex; + dev_link_t *link = local->finder; + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_cs start_net - device not present\n"); + return; + } + /* Fill in the CCS fields for the ECF */ + if ((ccsindex = get_free_ccs(local)) == -1) return; + pccs = ((struct ccs *)(local->sram + CCS_BASE)) + ccsindex; + writeb(CCS_START_NETWORK, &pccs->cmd); + writeb(0, &pccs->var.start_network.update_param); + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + DEBUG(1,"ray start net failed - card not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + return; + } + local->card_status = CARD_DOING_ACQ; + return; +} /* end start_net */ +/*===========================================================================*/ +/* Command card to join a network */ +void join_net(u_long data) +{ + ray_dev_t *local = (ray_dev_t *)data; + + struct ccs *pccs; + int ccsindex; + dev_link_t *link = local->finder; + + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_cs join_net - device not present\n"); + return; + } + /* Fill in the CCS fields for the ECF */ + if ((ccsindex = get_free_ccs(local)) == -1) return; + pccs = ((struct ccs *)(local->sram + CCS_BASE)) + ccsindex; + writeb(CCS_JOIN_NETWORK, &pccs->cmd); + writeb(0, &pccs->var.join_network.update_param); + writeb(0, &pccs->var.join_network.net_initiated); + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + DEBUG(1,"ray join net failed - card not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + return; + } + local->card_status = CARD_DOING_ACQ; + return; +} +/*============================================================================ + After a card is removed, ray_release() will unregister the net + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. +=============================================================================*/ +void ray_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + struct net_device *dev = link->priv; + ray_dev_t *local = dev->priv; + int i; + + DEBUG(1, "ray_release(0x%p)\n", link); + /* If the device is currently in use, we won't release until it + is actually closed. + */ + if (link->open) { + DEBUG(1, "ray_cs: release postponed, '%s' still open\n", + link->dev->dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + del_timer(&local->timer); + if (link->dev != '\0') unregister_netdev(dev); + /* Unlink the device chain */ + link->dev = NULL; + + iounmap(local->sram); + iounmap(local->rmem); + iounmap(local->amem); + /* Do bother checking to see if these succeed or not */ + i = CardServices(ReleaseWindow, link->win); + if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseWindow(link->win) ret = %x\n",i); + i = CardServices(ReleaseWindow, local->amem_handle); + if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseWindow(local->amem) ret = %x\n",i); + i = CardServices(ReleaseWindow, local->rmem_handle); + if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseWindow(local->rmem) ret = %x\n",i); + i = CardServices(ReleaseConfiguration, link->handle); + if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseConfiguration ret = %x\n",i); + i = CardServices(ReleaseIRQ, link->handle, &link->irq); + if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseIRQ ret = %x\n",i); + + link->state &= ~DEV_CONFIG; + if (link->state & DEV_STALE_LINK) ray_detach(link); + DEBUG(2,"ray_release ending\n"); +} /* ray_release */ +/*============================================================================= + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the net drivers from trying + to talk to the card any more. + + When a CARD_REMOVAL event is received, we immediately set a flag + to block future accesses to this device. All the functions that + actually access the device should check this flag to make sure + the card is still present. +=============================================================================*/ +int ray_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + struct net_device *dev = link->priv; + ray_dev_t *local = (ray_dev_t *)dev->priv; + DEBUG(1, "ray_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + dev->tbusy = 1; dev->start = 0; + if (link->state & DEV_CONFIG) { + link->release.expires = jiffies + HZ/20; + add_timer(&link->release); + del_timer(&local->timer); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + ray_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) { + if (link->open) { + dev->tbusy = 1; + dev->start = 0; + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + CardServices(RequestConfiguration, link->handle, &link->conf); + if (link->open) { + ray_reset(dev); + dev->tbusy = 0; + dev->start = 1; + } + } + break; + } + return 0; + DEBUG(2,"ray_event ending\n"); +} /* ray_event */ +/*===========================================================================*/ +int ray_dev_init(struct net_device *dev) +{ + int i; + ray_dev_t *local = dev->priv; + dev_link_t *link = local->finder; + + DEBUG(1,"ray_dev_init(dev=%p)\n",dev); + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_dev_init - device not present\n"); + return -1; + } + /* Download startup parameters */ + if ( (i = dl_startup_params(dev)) < 0) + { + DEBUG(0,"ray_dev_init dl_startup_params failed - returns 0x%x/n",i); + return -1; + } + + /* copy mac and broadcast addresses to linux device */ + memcpy(&dev->dev_addr, &local->sparm.b4.a_mac_addr, ADDRLEN); + memset(dev->broadcast, 0xff, ETH_ALEN); + +#ifdef RAYLINK_DEBUG + { + UCHAR *p; + p = (UCHAR *)(local->startup_res.station_addr); + DEBUG(1,"ray_dev_init card hardware mac addr = %2x %2x %2x %2x %2x %2x\n",\ + p[0],p[1],p[2],p[3],p[4],p[5]); + } +#endif + + DEBUG(2,"ray_dev_init ending\n"); + return 0; +} +/*===========================================================================*/ +int ray_dev_config(struct net_device *dev, struct ifmap *map) +{ + ray_dev_t *local = dev->priv; + dev_link_t *link = local->finder; + /* Dummy routine to satisfy device structure */ + DEBUG(1,"ray_dev_config(dev=%p,ifmap=%p)\n",dev,map); + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_dev_config - device not present\n"); + return -1; + } + + return 0; +} +/*===========================================================================*/ +int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + ray_dev_t *local = dev->priv; + dev_link_t *link = local->finder; + short length; + + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_dev_start_xmit - device not present\n"); + return -1; + } + DEBUG(3,"ray_dev_start_xmit(skb=%p, dev=%p)\n",skb,dev); + if (dev->tbusy) + { + DEBUG(2,"ray_dev_start_xmit busy\n"); + return 1; + } + if (local->authentication_state == NEED_TO_AUTH) { + DEBUG(0,"ray_cs Sending authentication request.\n"); + if (!build_auth_frame (local, local->auth_id, OPEN_AUTH_REQUEST)) { + local->authentication_state = AUTHENTICATED; + dev->tbusy = 1; + return 1; + } + } + + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + switch (ray_hw_xmit( skb->data, length, dev, DATA_TYPE)) { + case XMIT_NO_CCS: + case XMIT_NEED_AUTH: + dev->tbusy = 1; + return 1; + case XMIT_NO_INTR: + case XMIT_MSG_BAD: + case XMIT_OK: + default: + dev->trans_start = jiffies; + dev_kfree_skb(skb); + return 0; + } + return 0; +} /* ray_dev_start_xmit */ +/*===========================================================================*/ +int ray_hw_xmit(unsigned char* data, int len, struct net_device* dev, + UCHAR msg_type) +{ + ray_dev_t *local = (ray_dev_t *)dev->priv; + struct ccs *pccs; + int ccsindex; + int offset; + struct tx_msg *ptx; /* Address of xmit buffer in PC space */ + short int addr; /* Address of xmit buffer in card space */ + + DEBUG(3,"ray_hw_xmit(data=%p, len=%d, dev=%p)\n",data,len,dev); + if (len + TX_HEADER_LENGTH > TX_BUF_SIZE) + { + DEBUG(0,"ray_hw_xmit packet to large %d bytes\n",len); + return XMIT_MSG_BAD; + } + if ((ccsindex = get_free_tx_ccs(local)) == -1) + { + DEBUG(2,"ray_hw_xmit - No free tx ccs\n"); + dev->tbusy = 1; + return XMIT_NO_CCS; + } + addr = TX_BUF_BASE + (ccsindex << 11); + + if (msg_type == DATA_TYPE) { + local->stats.tx_bytes += len; + local->stats.tx_packets++; + } + + ptx = (struct tx_msg *)(local->sram + addr); + + ray_build_header(local, ptx, msg_type, data); + if (translate) { + offset = translate_frame(local, ptx, data, len); + } + else { /* Encapsulate frame */ + /* TBD TIB length will move address of ptx->var */ + memcpy( (UCHAR *)&ptx->var, data, len); + offset = 0; + } + + /* fill in the CCS */ + pccs = ((struct ccs *)(local->sram + CCS_BASE)) + ccsindex; + len += TX_HEADER_LENGTH + offset; + writeb(CCS_TX_REQUEST, &pccs->cmd); + writeb(addr >> 8, &pccs->var.tx_request.tx_data_ptr[0]); + writeb(local->tib_length, &pccs->var.tx_request.tx_data_ptr[1]); + writeb(len >> 8, &pccs->var.tx_request.tx_data_length[0]); + writeb(len & 0xff, &pccs->var.tx_request.tx_data_length[1]); +/* TBD still need psm_cam? */ + writeb(PSM_CAM, &pccs->var.tx_request.pow_sav_mode); + writeb(local->net_default_tx_rate, &pccs->var.tx_request.tx_rate); + writeb(0, &pccs->var.tx_request.antenna); + DEBUG(3,"ray_hw_xmit default_tx_rate = 0x%x\n",\ + local->net_default_tx_rate); + + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + DEBUG(2,"ray_hw_xmit failed - ECF not ready for intr\n"); +/* TBD very inefficient to copy packet to buffer, and then not + send it, but the alternative is to queue the messages and that + won't be done for a while. Maybe set tbusy until a CCS is free? +*/ + writeb(CCS_BUFFER_FREE, &pccs->buffer_status); + return XMIT_NO_INTR; + } + return XMIT_OK; +} /* end ray_hw_xmit */ +/*===========================================================================*/ +int translate_frame(ray_dev_t *local, struct tx_msg *ptx, unsigned char *data, + int len) +{ + unsigned short int proto = ((struct ethhdr *)data)->h_proto; + if (ntohs(proto) >= 1536) { /* DIX II ethernet frame */ + DEBUG(3,"ray_cs translate_frame DIX II\n"); + /* Copy LLC header to card buffer */ + memcpy_toio((UCHAR *)&ptx->var, eth2_llc, sizeof(eth2_llc)); + memcpy_toio( ((UCHAR *)&ptx->var) + sizeof(eth2_llc), (UCHAR *)&proto, 2); + if ((proto == 0xf380) || (proto == 0x3781)) { + /* This is the selective translation table, only 2 entries */ + writeb(0xf8, (UCHAR *) &((struct snaphdr_t *)ptx->var)->org[3]); + } + /* Copy body of ethernet packet without ethernet header */ + memcpy_toio((UCHAR *)&ptx->var + sizeof(struct snaphdr_t), \ + data + ETH_HLEN, len - ETH_HLEN); + return sizeof(struct snaphdr_t) - ETH_HLEN; + } + else { /* already 802 type, and proto is length */ + DEBUG(3,"ray_cs translate_frame 802\n"); + if (proto == 0xffff) { /* evil netware IPX 802.3 without LLC */ + DEBUG(3,"ray_cs translate_frame evil IPX\n"); + memcpy_toio((UCHAR *)&ptx->var, data + ETH_HLEN, len - ETH_HLEN); + return 0 - ETH_HLEN; + } + memcpy_toio((UCHAR *)&ptx->var, data + ETH_HLEN, len - ETH_HLEN); + return 0 - ETH_HLEN; + } + /* TBD do other frame types */ +} /* end translate_frame */ +/*===========================================================================*/ +void ray_build_header(ray_dev_t *local, struct tx_msg *ptx, UCHAR msg_type, + unsigned char *data) +{ + writeb(PROTOCOL_VER | msg_type, &ptx->mac.frame_ctl_1); +/*** IEEE 802.11 Address field assignments ************* + addr_1 addr_2 addr_3 + AP destination AP(BSSID) source + Infra Terminal AP terminal destination + Adhoc destination terminal BSSID +*******************************************************/ + if (local->net_type == ADHOC) { + writeb(0, &ptx->mac.frame_ctl_2); + memcpy_toio(ptx->mac.addr_1, ((struct ethhdr *)data)->h_dest, 2 * ADDRLEN); + memcpy_toio(ptx->mac.addr_3, local->bss_id, ADDRLEN); + } + else /* infrastructure */ + { + if (local->sparm.b4.a_acting_as_ap_status) + { + writeb(FC2_FROM_DS, &ptx->mac.frame_ctl_2);; + memcpy_toio(ptx->mac.addr_1, ((struct ethhdr *)data)->h_dest, ADDRLEN); + memcpy_toio(ptx->mac.addr_2, local->bss_id, 6); + memcpy_toio(ptx->mac.addr_3, ((struct ethhdr *)data)->h_source, ADDRLEN); + } + else /* Terminal */ + { + writeb(FC2_TO_DS, &ptx->mac.frame_ctl_2); + memcpy_toio(ptx->mac.addr_1, local->bss_id, ADDRLEN); + memcpy_toio(ptx->mac.addr_2, ((struct ethhdr *)data)->h_source, ADDRLEN); + memcpy_toio(ptx->mac.addr_3, ((struct ethhdr *)data)->h_dest, ADDRLEN); + } + } +} /* end encapsulate_frame */ +/*===========================================================================*/ +int ray_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + ray_dev_t *local = (ray_dev_t *)dev->priv; + dev_link_t *link = local->finder; + int err = 0; + + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_dev_ioctl - device not present\n"); + return -1; + } + DEBUG(2,"ray_cs IOCTL dev=%p, ifr=%p, cmd = 0x%x\n",dev,ifr,cmd); + /* Validate the command */ + switch (cmd) + { + default: + DEBUG(0,"ray_dev_ioctl cmd = 0x%x\n", cmd); + err = -EOPNOTSUPP; + } + return err; +} /* end ray_dev_ioctl */ +/*===========================================================================*/ +int ray_open(struct net_device *dev) +{ + dev_link_t *link; + ray_dev_t *local = (ray_dev_t *)dev->priv; + + DEBUG(1, "ray_open('%s')\n", dev->name); + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (!DEV_OK(link)) + return -ENODEV; + + if (link->open == 0) local->num_multi = 0; + link->open++; + MOD_INC_USE_COUNT; + + dev->interrupt = 0; + if (sniffer) dev->tbusy = 1; + else dev->tbusy = 0; + dev->start = 1; + + DEBUG(2,"ray_open ending\n"); + return 0; +} /* end ray_open */ +/*===========================================================================*/ +int ray_dev_close(struct net_device *dev) +{ + dev_link_t *link; + + DEBUG(1, "ray_dev_close('%s')\n", dev->name); + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (link == NULL) + return -ENODEV; + + link->open--; dev->start = 0; + if (link->state & DEV_STALE_CONFIG) { + link->release.expires = jiffies + HZ/20; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + + MOD_DEC_USE_COUNT; + + return 0; +} /* end ray_dev_close */ +/*===========================================================================*/ +void ray_reset(struct net_device *dev) { + DEBUG(1,"ray_reset entered\n"); + return; +} +/*===========================================================================*/ +/* Cause a firmware interrupt if it is ready for one */ +/* Return nonzero if not ready */ +int interrupt_ecf(ray_dev_t *local, int ccs) +{ + int i = 50; +/* UCHAR *p = (local->amem + CIS_OFFSET + ECF_INTR_OFFSET); */ + dev_link_t *link = local->finder; + + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_cs interrupt_ecf - device not present\n"); + return -1; + } + DEBUG(2,"interrupt_ecf(local=%p, ccs = 0x%x\n",local,ccs); + +/* while ( i && (*p & ECF_INTR_SET)) i--; */ + while ( i && + (readb(local->amem + CIS_OFFSET + ECF_INTR_OFFSET) & ECF_INTR_SET)) + i--; + if (i == 0) { + DEBUG(2,"ray_cs interrupt_ecf card not ready for interrupt\n"); + return -1; + } + + *(local->sram + SCB_BASE) = ccs; + writeb(ECF_INTR_SET, local->amem + CIS_OFFSET + ECF_INTR_OFFSET); + return 0; +} /* interrupt_ecf */ +/*===========================================================================*/ +/* Get next free transmit CCS */ +/* Return - index of current tx ccs */ +int get_free_tx_ccs(ray_dev_t *local) +{ + int i; + struct ccs *pccs = (struct ccs *)(local->sram + CCS_BASE); + dev_link_t *link = local->finder; + + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_cs get_free_tx_ccs - device not present\n"); + return -1; + } + + for (i=0; i < NUMBER_OF_TX_CCS; i++) { + if (readb(&(pccs+i)->buffer_status) == CCS_BUFFER_FREE) { + writeb(CCS_BUFFER_BUSY, &(pccs+i)->buffer_status); + writeb(CCS_END_LIST, &(pccs+i)->link); + return i; + } + } + DEBUG(1,"ray_cs ERROR no free tx CCS for raylink card\n"); + return -1; +} /* get_free_tx_ccs */ +/*===========================================================================*/ +/* Get next free CCS */ +/* Return - index of current ccs */ +int get_free_ccs(ray_dev_t *local) +{ + int i; + struct ccs *pccs = (struct ccs *)(local->sram + CCS_BASE); + dev_link_t *link = local->finder; + + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_cs get_free_ccs - device not present\n"); + return -1; + } + for (i = NUMBER_OF_TX_CCS; i < NUMBER_OF_CCS; i++) { + if (readb(&(pccs+i)->buffer_status) == CCS_BUFFER_FREE) { + writeb(CCS_BUFFER_BUSY, &(pccs+i)->buffer_status); + writeb(CCS_END_LIST, &(pccs+i)->link); + return i; + } + } + DEBUG(1,"ray_cs ERROR no free CCS for raylink card\n"); + return -1; +} /* get_free_ccs */ +/*===========================================================================*/ +void authenticate_timeout(u_long data) +{ + ray_dev_t *local = (ray_dev_t *)data; + del_timer(&local->timer); + DEBUG(0,"ray_cs Authentication with access point failed - timeout\n"); + join_net((u_long)local); +} +/*===========================================================================*/ +int asc_to_int(char a) +{ + if (a < '0') return -1; + if (a <= '9') return (a - '0'); + if (a < 'A') return -1; + if (a <= 'F') return (10 + a - 'A'); + if (a < 'a') return -1; + if (a <= 'f') return (10 + a - 'a'); + return -1; +} +/*===========================================================================*/ +int parse_addr(char *in_str, UCHAR *out) +{ + int len; + int i,j,k; + int status; + + if (in_str == NULL) return 0; + if ((len = strlen(in_str)) < 2) return 0; + memset(out, 0, ADDRLEN); + + status = 1; + j = len - 1; + if (j > 12) j = 12; + i = 5; + + while (j > 0) + { + if ((k = asc_to_int(in_str[j--])) != -1) out[i] = k; + else return 0; + + if (j == 0) break; + if ((k = asc_to_int(in_str[j--])) != -1) out[i] += k << 4; + else return 0; + if (!i--) break; + } + return status; +} +/*===========================================================================*/ +struct enet_statistics *ray_get_stats(struct net_device *dev) +{ + ray_dev_t *local = (ray_dev_t *)dev->priv; + dev_link_t *link = local->finder; + struct status *p = (struct status *)(local->sram + STATUS_BASE); + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_cs enet_statistics - device not present\n"); + return &local->stats; + } + if (p->mrx_overflow_for_host) + { + local->stats.rx_over_errors += ntohs(p->mrx_overflow); + p->mrx_overflow = 0; + p->mrx_overflow_for_host = 0; + } + if (p->mrx_checksum_error_for_host) + { + local->stats.rx_crc_errors += ntohs(p->mrx_checksum_error); + p->mrx_checksum_error = 0; + p->mrx_checksum_error_for_host = 0; + } + if (p->rx_hec_error_for_host) + { + local->stats.rx_frame_errors += ntohs(p->rx_hec_error); + p->rx_hec_error = 0; + p->rx_hec_error_for_host = 0; + } + return &local->stats; +} +/*===========================================================================*/ +void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value, int len) +{ + ray_dev_t *local = (ray_dev_t *)dev->priv; + dev_link_t *link = local->finder; + int ccsindex; + int i; + struct ccs *pccs; + + if (!(link->state & DEV_PRESENT)) { + DEBUG(0,"ray_update_parm - device not present\n"); + return; + } + + if ((ccsindex = get_free_ccs(local)) == -1) + { + DEBUG(0,"ray_update_parm - No free ccs\n"); + return; + } + pccs = ((struct ccs *)(local->sram + CCS_BASE)) + ccsindex; + writeb(CCS_UPDATE_PARAMS, &pccs->cmd); + writeb(objid, &pccs->var.update_param.object_id); + writeb(1, &pccs->var.update_param.number_objects); + writeb(0, &pccs->var.update_param.failure_cause); + for (i=0; isram + HOST_TO_ECF_BASE); + } + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + DEBUG(0,"ray_cs associate failed - ECF not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + } +} +/*===========================================================================*/ +static void ray_update_multi_list(struct net_device *dev, int all) +{ + struct dev_mc_list *dmi, **dmip; + int ccsindex; + struct ccs *pccs; + int i = 0; + ray_dev_t *local = (ray_dev_t *)dev->priv; + dev_link_t *link = local->finder; + UCHAR *p = local->sram + HOST_TO_ECF_BASE; + + if (!(link->state & DEV_PRESENT)) { + DEBUG(1,"ray_update_multi_list - device not present\n"); + return; + } + else + DEBUG(1,"ray_update_multi_list(%p)\n",dev); + if ((ccsindex = get_free_ccs(local)) == -1) + { + DEBUG(1,"ray_update_multi - No free ccs\n"); + return; + } + pccs = ((struct ccs *)(local->sram + CCS_BASE)) + ccsindex; + writeb(CCS_UPDATE_MULTICAST_LIST, &pccs->cmd); + + if (all) { + writeb(0xff, &pccs->var); + local->num_multi = 0xff; + } + else { + /* Copy the kernel's list of MC addresses to card */ + for (dmip=&dev->mc_list; (dmi=*dmip)!=NULL; dmip=&dmi->next) { + memcpy_toio(p, dmi->dmi_addr, ETH_ALEN); + DEBUG(1,"ray_update_multi add addr %02x%02x%02x%02x%02x%02x\n",dmi->dmi_addr[0],dmi->dmi_addr[1],dmi->dmi_addr[2],dmi->dmi_addr[3],dmi->dmi_addr[4],dmi->dmi_addr[5]); + p += ETH_ALEN; + i++; + } + if (i > 256/ADDRLEN) i = 256/ADDRLEN; + writeb((UCHAR)i, &pccs->var); + DEBUG(1,"ray_cs update_multi %d addresses in list\n", i); + /* Interrupt the firmware to process the command */ + local->num_multi = i; + } + if (interrupt_ecf(local, ccsindex)) { + DEBUG(1,"ray_cs update_multi failed - ECF not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + } +} /* end ray_update_multi_list */ +/*===========================================================================*/ +static void set_multicast_list(struct net_device *dev) +{ + ray_dev_t *local = (ray_dev_t *)dev->priv; + UCHAR promisc; + + DEBUG(1,"ray_cs set_multicast_list(%p)\n",dev); + + if (dev->flags & IFF_PROMISC) + { + if (local->sparm.b5.a_promiscuous_mode == 0) { + DEBUG(1,"ray_cs set_multicast_list promisc on\n"); + local->sparm.b5.a_promiscuous_mode = 1; + promisc = 1; + ray_update_parm(dev, OBJID_promiscuous_mode, \ + &promisc, sizeof(promisc)); + } + } + else { + if (local->sparm.b5.a_promiscuous_mode == 1) { + DEBUG(1,"ray_cs set_multicast_list promisc off\n"); + local->sparm.b5.a_promiscuous_mode = 0; + promisc = 0; + ray_update_parm(dev, OBJID_promiscuous_mode, \ + &promisc, sizeof(promisc)); + } + } + + if (dev->flags & IFF_ALLMULTI) ray_update_multi_list(dev, 1); + else + { + if (local->num_multi != dev->mc_count) ray_update_multi_list(dev, 0); + } +} /* end set_multicast_list */ +/*============================================================================= + * All routines below here are run at interrupt time. +=============================================================================*/ +void ray_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + dev_link_t *link; + ray_dev_t *local; + struct ccs *pccs; + struct rcs *prcs; + UCHAR rcsindex; + UCHAR tmp; + UCHAR cmd; + UCHAR status; + + if (dev == NULL) { + link = dev_list; + dev = (struct net_device *)link->priv; + DEBUG(4,"ray_cs interrupt dev = %p, link = %p\n",dev,link); + if (dev->irq != irq) + { + DEBUG(0,"ray_cs interrupt irq %d for unknown device.\n", irq); + return; + } + } + DEBUG(4,"ray_cs: interrupt for *dev=%p\n",dev); + + if (dev->interrupt) { + printk("ray_cs Reentering interrupt handler not allowed\n"); + return; + } + dev->interrupt = 1; + local = (ray_dev_t *)dev->priv; + link = (dev_link_t *)local->finder; + if ( ! (link->state & DEV_PRESENT) || link->state & DEV_SUSPEND ) { + DEBUG(1,"ray_cs interrupt from device not present or suspended.\n"); + return; + } + rcsindex = ((struct scb *)(local->sram))->rcs_index; + + if (rcsindex >= (NUMBER_OF_CCS + NUMBER_OF_RCS)) + { + DEBUG(1,"ray_cs interrupt bad rcsindex = 0x%x\n",rcsindex); + clear_interrupt(local); + dev->interrupt = 0; + return; + } + if (rcsindex < NUMBER_OF_CCS) /* If it's a returned CCS */ + { + pccs = ((struct ccs *) (local->sram + CCS_BASE)) + rcsindex; + cmd = readb(&pccs->cmd); + status = readb(&pccs->buffer_status); + switch (cmd) + { + case CCS_DOWNLOAD_STARTUP_PARAMS: /* Happens in firmware someday */ + del_timer(&local->timer); + if (status == CCS_COMMAND_COMPLETE) { + DEBUG(1,"ray_cs interrupt download_startup_parameters OK\n"); + } + else { + DEBUG(1,"ray_cs interrupt download_startup_parameters fail\n"); + } + break; + case CCS_UPDATE_PARAMS: + DEBUG(1,"ray_cs interrupt update params done\n"); + if (status != CCS_COMMAND_COMPLETE) { + tmp = readb(&pccs->var.update_param.failure_cause); + DEBUG(0,"ray_cs interrupt update params failed - reason %d\n",tmp); + } + break; + case CCS_REPORT_PARAMS: + DEBUG(1,"ray_cs interrupt report params done\n"); + break; + case CCS_UPDATE_MULTICAST_LIST: /* Note that this CCS isn't returned */ + DEBUG(1,"ray_cs interrupt CCS Update Multicast List done\n"); + break; + case CCS_UPDATE_POWER_SAVINGS_MODE: + DEBUG(1,"ray_cs interrupt update power save mode done\n"); + break; + case CCS_START_NETWORK: + case CCS_JOIN_NETWORK: + if (status == CCS_COMMAND_COMPLETE) { + if (readb(&pccs->var.start_network.net_initiated) == 1) { + DEBUG(0,"ray_cs interrupt network \"%s\"started\n",\ + local->sparm.b4.a_current_ess_id); + } + else { + DEBUG(0,"ray_cs interrupt network \"%s\" joined\n",\ + local->sparm.b4.a_current_ess_id); + } + memcpy_fromio(&local->bss_id,pccs->var.start_network.bssid,ADDRLEN); + + if (local->fw_ver == 0x55) local->net_default_tx_rate = 3; + else local->net_default_tx_rate = + readb(&pccs->var.start_network.net_default_tx_rate); + local->encryption = readb(&pccs->var.start_network.encryption); + if (!sniffer && (local->net_type == INFRA) + && !(local->sparm.b4.a_acting_as_ap_status)) { + authenticate(local); + } + local->card_status = CARD_ACQ_COMPLETE; + } + else { + local->card_status = CARD_ACQ_FAILED; + + del_timer(&local->timer); + local->timer.expires = jiffies + HZ*5; + local->timer.data = (long)local; + if (status == CCS_START_NETWORK) { + DEBUG(0,"ray_cs interrupt network \"%s\" start failed\n",\ + local->sparm.b4.a_current_ess_id); + local->timer.function = &start_net; + } + else { + DEBUG(0,"ray_cs interrupt network \"%s\" join failed\n",\ + local->sparm.b4.a_current_ess_id); + local->timer.function = &join_net; + } + add_timer(&local->timer); + } + break; + case CCS_START_ASSOCIATION: + if (status == CCS_COMMAND_COMPLETE) { + local->card_status = CARD_ASSOC_COMPLETE; + DEBUG(0,"ray_cs association successful\n"); + } + else + { + DEBUG(0,"ray_cs association failed,\n"); + local->card_status = CARD_ASSOC_FAILED; + join_net((u_long)local); + } + break; + case CCS_TX_REQUEST: + if (status == CCS_COMMAND_COMPLETE) { + DEBUG(3,"ray_cs interrupt tx request complete\n"); + } + else { + DEBUG(1,"ray_cs interrupt tx request failed\n"); + } + if (!sniffer) dev->tbusy = 0; + mark_bh(NET_BH); + break; + case CCS_TEST_MEMORY: + DEBUG(1,"ray_cs interrupt mem test done\n"); + break; + case CCS_SHUTDOWN: + DEBUG(1,"ray_cs interrupt Unexpected CCS returned - Shutdown\n"); + break; + case CCS_DUMP_MEMORY: + DEBUG(1,"ray_cs interrupt dump memory done\n"); + break; + case CCS_START_TIMER: + DEBUG(2,"ray_cs interrupt DING - raylink timer expired\n"); + break; + default: + DEBUG(1,"ray_cs interrupt Unexpected CCS 0x%x returned 0x%x\n",\ + rcsindex, cmd); + } + writeb(CCS_BUFFER_FREE, &pccs->buffer_status); + } + else /* It's an RCS */ + { + prcs = ((struct rcs *)(local->sram + CCS_BASE)) + rcsindex; + + switch (readb(&prcs->interrupt_id)) + { + case PROCESS_RX_PACKET: + ray_rx(dev, local, prcs); + break; + case REJOIN_NET_COMPLETE: + DEBUG(1,"ray_cs interrupt rejoin net complete\n"); + local->card_status = CARD_ACQ_COMPLETE; + /* do we need to clear tx buffers CCS's? */ + if (local->sparm.b4.a_network_type == ADHOC) { + if (!sniffer) dev->tbusy = 0; + } + else { + memcpy_fromio(&local->bss_id, prcs->var.rejoin_net_complete.bssid, ADDRLEN); + DEBUG(1,"ray_cs new BSSID = %02x%02x%02x%02x%02x%02x\n",\ + local->bss_id[0], local->bss_id[1], local->bss_id[2],\ + local->bss_id[3], local->bss_id[4], local->bss_id[5]); + if (!sniffer) authenticate(local); + } + break; + case ROAMING_INITIATED: + DEBUG(1,"ray_cs interrupt roaming initiated\n"); + dev->tbusy = 1; + local->card_status = CARD_DOING_ACQ; + break; + case JAPAN_CALL_SIGN_RXD: + DEBUG(1,"ray_cs interrupt japan call sign rx\n"); + break; + default: + DEBUG(1,"ray_cs Unexpected interrupt for RCS 0x%x cmd = 0x%x\n",\ + rcsindex, readb(&prcs->interrupt_id)); + break; + } + writeb(CCS_BUFFER_FREE, &prcs->buffer_status); + } + clear_interrupt(local); + dev->interrupt = 0; +} /* ray_interrupt */ +/*===========================================================================*/ +void ray_rx(struct net_device *dev, ray_dev_t *local, struct rcs *prcs) +{ + int rx_len; + unsigned int pkt_addr; + UCHAR *pmsg; + DEBUG(4,"ray_rx process rx packet\n"); + + /* Calculate address of packet within Rx buffer */ + pkt_addr = ((readb(&prcs->var.rx_packet.rx_data_ptr[0]) << 8) + + readb(&prcs->var.rx_packet.rx_data_ptr[1])) & RX_BUFF_END; + /* Length of first packet fragment */ + rx_len = (readb(&prcs->var.rx_packet.rx_data_length[0]) << 8) + + readb(&prcs->var.rx_packet.rx_data_length[1]); + + pmsg = local->rmem + pkt_addr; + switch(readb(pmsg)) + { + case DATA_TYPE: + DEBUG(4,"ray_rx data type\n"); + rx_data(dev, prcs, pkt_addr, rx_len); + break; + case AUTHENTIC_TYPE: + DEBUG(4,"ray_rx authentic type\n"); + if (sniffer) rx_data(dev, prcs, pkt_addr, rx_len); + else rx_authenticate(local, prcs, pkt_addr, rx_len); + break; + case DEAUTHENTIC_TYPE: + DEBUG(4,"ray_rx deauth type\n"); + if (sniffer) rx_data(dev, prcs, pkt_addr, rx_len); + else rx_deauthenticate(local, prcs, pkt_addr, rx_len); + break; + case NULL_MSG_TYPE: + DEBUG(3,"ray_cs rx NULL msg\n"); + break; + case BEACON_TYPE: + DEBUG(4,"ray_rx beacon type\n"); + if (sniffer) rx_data(dev, prcs, pkt_addr, rx_len); + + copy_from_rx_buff(local, (UCHAR *)&local->last_bcn, pkt_addr, + rx_len < sizeof(struct beacon_rx) ? + rx_len : sizeof(struct beacon_rx)); + + /* Get the statistics so the card counters never overflow */ + ray_get_stats(dev); + break; + default: + DEBUG(0,"ray_cs unknown pkt type %2x\n", readb(pmsg)); + break; + } + +} /* end ray_rx */ +/*===========================================================================*/ +void rx_data(struct net_device *dev, struct rcs *prcs, unsigned int pkt_addr, + int rx_len) +{ + struct sk_buff *skb = NULL; + struct rcs *prcslink = prcs; + ray_dev_t *local = dev->priv; + UCHAR *rx_ptr; + int total_len; + int tmp; + + if (!sniffer) { + if (translate) { +/* TBD length needs fixing for translated header */ + if (rx_len < (ETH_HLEN + RX_MAC_HEADER_LENGTH) || + rx_len > (dev->mtu + RX_MAC_HEADER_LENGTH + ETH_HLEN + FCS_LEN)) + { + DEBUG(0,"ray_cs invalid packet length %d received \n",rx_len); + return; + } + } + else /* encapsulated ethernet */ { + if (rx_len < (ETH_HLEN + RX_MAC_HEADER_LENGTH) || + rx_len > (dev->mtu + RX_MAC_HEADER_LENGTH + ETH_HLEN + FCS_LEN)) + { + DEBUG(0,"ray_cs invalid packet length %d received \n",rx_len); + return; + } + } + } + DEBUG(4,"ray_cs rx_data packet\n"); + /* If fragmented packet, verify sizes of fragments add up */ + if (prcs->var.rx_packet.next_frag_rcs_index != 0xFF) { + DEBUG(1,"ray_cs rx'ed fragment\n"); + tmp = (readb(&prcs->var.rx_packet.totalpacketlength[0]) << 8) + + readb(&prcs->var.rx_packet.totalpacketlength[1]); + total_len = tmp; + prcslink = prcs; + do { + tmp -= (readb(&prcslink->var.rx_packet.rx_data_length[0]) << 8) + + readb(&prcslink->var.rx_packet.rx_data_length[1]); + if (readb(&prcslink->var.rx_packet.next_frag_rcs_index) == 0xFF + || tmp < 0) break; + prcslink = ((struct rcs *)(local->sram + CCS_BASE)) + + readb(&prcslink->link_field); + } while (1); + + if (tmp < 0) + { + DEBUG(0,"ray_cs rx_data fragment lengths don't add up\n"); + local->stats.rx_dropped++; + release_frag_chain(local, prcs); + return; + } + } + else { /* Single unfragmented packet */ + total_len = rx_len; + } + + skb = dev_alloc_skb( total_len+5 ); + if (skb == NULL) + { + DEBUG(0,"ray_cs rx_data could not allocate skb\n"); + local->stats.rx_dropped++; + if (readb(&prcs->var.rx_packet.next_frag_rcs_index) != 0xFF) + release_frag_chain(local, prcs); + return; + } + skb_reserve( skb, 2); /* Align IP on 16 byte (TBD check this)*/ + skb->dev = dev; + + DEBUG(4,"ray_cs rx_data total_len = %x, rx_len = %x\n",total_len,rx_len); + +/************************/ + /* Reserve enough room for the whole damn packet. */ + rx_ptr = skb_put( skb, total_len); + /* Copy the whole packet to sk_buff */ + rx_ptr += copy_from_rx_buff(local, rx_ptr, pkt_addr & RX_BUFF_END, rx_len); + + /* Now, deal with encapsulation/translation/sniffer */ + if (!sniffer) { + if (!translate) { + /* Encapsulated ethernet, so just lop off 802.11 MAC header */ +/* TBD reserve skb_reserve( skb, RX_MAC_HEADER_LENGTH); */ + skb_pull( skb, RX_MAC_HEADER_LENGTH); + } + else { + /* Do translation */ + untranslate(local, skb, total_len); + } + } + else + { /* sniffer mode, so just pass whole packet */ }; + +/************************/ + /* Now pick up the rest of the fragments if any */ + tmp = 17; + if (readb(&prcs->var.rx_packet.next_frag_rcs_index) != 0xFF) { + prcslink = prcs; + DEBUG(1,"ray_cs rx_data in fragment loop\n"); + do { + prcslink = ((struct rcs *)(local->sram + CCS_BASE)) + + readb(&prcslink->var.rx_packet.next_frag_rcs_index); + rx_len = (( readb(&prcslink->var.rx_packet.rx_data_length[0]) << 8) + + readb(&prcslink->var.rx_packet.rx_data_length[1])) + & RX_BUFF_END; + pkt_addr = (( readb(&prcslink->var.rx_packet.rx_data_ptr[0]) << 8) + + readb(&prcslink->var.rx_packet.rx_data_ptr[1])) + & RX_BUFF_END; + + rx_ptr += copy_from_rx_buff(local, rx_ptr, pkt_addr, rx_len); + + } while (tmp-- && + readb(&prcslink->var.rx_packet.next_frag_rcs_index) != 0xFF); + release_frag_chain(local, prcs); + } + + skb->protocol = eth_type_trans(skb,dev); + netif_rx(skb); + + local->stats.rx_packets++; + local->stats.rx_bytes += skb->len; +} /* end rx_data */ +/*===========================================================================*/ +void untranslate(ray_dev_t *local, struct sk_buff *skb, int len) +{ + snaphdr_t *psnap = (snaphdr_t *)(skb->data + RX_MAC_HEADER_LENGTH); + struct mac_header *pmac = (struct mac_header *)skb->data; + unsigned short type = *(unsigned short *)psnap->ethertype; + unsigned int xsap = *(unsigned int *)psnap & 0x00ffffff; + unsigned int org = (*(unsigned int *)psnap->org) & 0x00ffffff; + int delta; + struct ethhdr *peth; + UCHAR srcaddr[ADDRLEN]; + UCHAR destaddr[ADDRLEN]; + int i; + + if (local->sparm.b5.a_acting_as_ap_status != TYPE_STA) + memcpy(destaddr, pmac->addr_3, ADDRLEN); + else + memcpy(destaddr, pmac->addr_1, ADDRLEN); + memcpy(srcaddr, pmac->addr_2, ADDRLEN); + + DEBUG(3,"skb->data before untranslate"); + for (i=0;i<64;i++) + DEBUG(3,"%02x ",skb->data[i]); + DEBUG(3,"\ntype = %08x, xsap = %08x, org = %08x\n",type,xsap,org); + DEBUG(3,"untranslate skb->data = %p\n",skb->data); + + if ( xsap != SNAP_ID) { + /* not a snap type so leave it alone */ + DEBUG(3,"ray_cs untranslate NOT SNAP %x\n", *(unsigned int *)psnap & 0x00ffffff); + + delta = RX_MAC_HEADER_LENGTH - ETH_HLEN; + peth = (struct ethhdr *)(skb->data + delta); + peth->h_proto = htons(len - RX_MAC_HEADER_LENGTH); + } + else { /* Its a SNAP */ + if (org == BRIDGE_ENCAP) { /* EtherII and nuke the LLC */ + DEBUG(3,"ray_cs untranslate Bridge encap\n"); + delta = RX_MAC_HEADER_LENGTH + + sizeof(struct snaphdr_t) - ETH_HLEN; + peth = (struct ethhdr *)(skb->data + delta); + peth->h_proto = type; + } + else { + if (org == RFC1042_ENCAP) { + switch (type) { + case RAY_IPX_TYPE: + case APPLEARP_TYPE: + DEBUG(3,"ray_cs untranslate RFC IPX/AARP\n"); + delta = RX_MAC_HEADER_LENGTH - ETH_HLEN; + peth = (struct ethhdr *)(skb->data + delta); + peth->h_proto = htons(len - RX_MAC_HEADER_LENGTH); + break; + default: + DEBUG(3,"ray_cs untranslate RFC default\n"); + delta = RX_MAC_HEADER_LENGTH + + sizeof(struct snaphdr_t) - ETH_HLEN; + peth = (struct ethhdr *)(skb->data + delta); + peth->h_proto = type; + break; + } + } + else { + printk("ray_cs untranslate very confused by packet\n"); + delta = RX_MAC_HEADER_LENGTH - ETH_HLEN; + peth = (struct ethhdr *)(skb->data + delta); + peth->h_proto = type; + } + } + } +/* TBD reserve skb_reserve(skb, delta); */ + skb_pull(skb, delta); + DEBUG(3,"untranslate after skb_pull(%d), skb->data = %p\n",delta,skb->data); + memcpy(peth->h_dest, destaddr, ADDRLEN); + memcpy(peth->h_source, srcaddr, ADDRLEN); + DEBUG(3,"skb->data after untranslate:"); + for (i=0;i<64;i++) + DEBUG(3,"%02x ",skb->data[i]); + DEBUG(3,"\n"); +} /* end untranslate */ +/*===========================================================================*/ +/* Copy data from circular receive buffer to PC memory. + * dest = destination address in PC memory + * pkt_addr = source address in receive buffer + * len = length of packet to copy + */ +int copy_from_rx_buff(ray_dev_t *local, UCHAR *dest, int pkt_addr, int length) +{ + int wrap_bytes = (pkt_addr + length) - (RX_BUFF_END + 1); + if (wrap_bytes <= 0) + { + memcpy_fromio(dest,local->rmem + pkt_addr,length); + } + else /* Packet wrapped in circular buffer */ + { + memcpy_fromio(dest,local->rmem+pkt_addr,length - wrap_bytes); + memcpy_fromio(dest + length - wrap_bytes, local->rmem, wrap_bytes); + } + return length; +} +/*===========================================================================*/ +void release_frag_chain(ray_dev_t *local, struct rcs* prcs) +{ + struct rcs *prcslink = prcs; + int tmp = 17; + unsigned rcsindex = readb(&prcs->var.rx_packet.next_frag_rcs_index); + + while (tmp--) { + writeb(CCS_BUFFER_FREE, &prcslink->buffer_status); + if (rcsindex >= (NUMBER_OF_CCS + NUMBER_OF_RCS)) { + DEBUG(1,"ray_cs interrupt bad rcsindex = 0x%x\n",rcsindex); + break; + } + prcslink = ((struct rcs *)(local->sram + CCS_BASE)) + rcsindex; + rcsindex = readb(&prcslink->var.rx_packet.next_frag_rcs_index); + } + writeb(CCS_BUFFER_FREE, &prcslink->buffer_status); +} +/*===========================================================================*/ +void authenticate(ray_dev_t *local) +{ + dev_link_t *link = local->finder; + DEBUG(0,"ray_cs Starting authentication.\n"); + if (!(link->state & DEV_PRESENT)) { + DEBUG(1,"ray_cs authenticate - device not present\n"); + return; + } + + del_timer(&local->timer); + if (build_auth_frame(local, local->bss_id, OPEN_AUTH_REQUEST)) { + local->timer.function = &join_net; + } + else { + local->timer.function = &authenticate_timeout; + } + local->timer.expires = jiffies + HZ*2; + local->timer.data = (long)local; + add_timer(&local->timer); + local->authentication_state = AWAITING_RESPONSE; +} /* end authenticate */ +/*===========================================================================*/ +void rx_authenticate(ray_dev_t *local, struct rcs *prcs, + unsigned int pkt_addr, int rx_len) +{ + UCHAR buff[256]; + struct rx_msg *msg = (struct rx_msg *)buff; + + del_timer(&local->timer); + + copy_from_rx_buff(local, buff, pkt_addr, rx_len & 0xff); + /* if we are trying to get authenticated */ + if (local->sparm.b4.a_network_type == ADHOC) { + DEBUG(1,"ray_cs rx_auth var= %02x %02x %02x %02x %02x %02x\n", msg->var[0],msg->var[1],msg->var[2],msg->var[3],msg->var[4],msg->var[5]); + if (msg->var[2] == 1) { + DEBUG(0,"ray_cs Sending authentication response.\n"); + if (!build_auth_frame (local, msg->mac.addr_2, OPEN_AUTH_RESPONSE)) { + local->authentication_state = NEED_TO_AUTH; + memcpy(local->auth_id, msg->mac.addr_2, ADDRLEN); + } + } + } + else /* Infrastructure network */ + { + if (local->authentication_state == AWAITING_RESPONSE) { + /* Verify authentication sequence #2 and success */ + if (msg->var[2] == 2) { + if ((msg->var[3] | msg->var[4]) == 0) { + DEBUG(1,"Authentication successful\n"); + local->card_status = CARD_AUTH_COMPLETE; + associate(local); + local->authentication_state = AUTHENTICATED; + } + else { + DEBUG(0,"Authentication refused\n"); + local->card_status = CARD_AUTH_REFUSED; + join_net((u_long)local); + local->authentication_state = UNAUTHENTICATED; + } + } + } + } + +} /* end rx_authenticate */ +/*===========================================================================*/ +void associate(ray_dev_t *local) +{ + struct ccs *pccs; + dev_link_t *link = local->finder; + struct net_device *dev = link->priv; + int ccsindex; + if (!(link->state & DEV_PRESENT)) { + DEBUG(1,"ray_cs associate - device not present\n"); + return; + } + /* If no tx buffers available, return*/ + if ((ccsindex = get_free_ccs(local)) == -1) + { +/* TBD should never be here but... what if we are? */ + DEBUG(1,"ray_cs associate - No free ccs\n"); + return; + } + DEBUG(1,"ray_cs Starting association with access point\n"); + pccs = ((struct ccs *)(local->sram + CCS_BASE)) + ccsindex; + /* fill in the CCS */ + writeb(CCS_START_ASSOCIATION, &pccs->cmd); + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + DEBUG(1,"ray_cs associate failed - ECF not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + + del_timer(&local->timer); + local->timer.expires = jiffies + HZ*2; + local->timer.data = (long)local; + local->timer.function = &join_net; + add_timer(&local->timer); + local->card_status = CARD_ASSOC_FAILED; + return; + } + if (!sniffer) dev->tbusy = 0; + +} /* end associate */ +/*===========================================================================*/ +void rx_deauthenticate(ray_dev_t *local, struct rcs *prcs, + unsigned int pkt_addr, int rx_len) +{ +/* UCHAR buff[256]; + struct rx_msg *msg = (struct rx_msg *)buff; +*/ + DEBUG(0,"Deauthentication frame received\n"); + local->authentication_state = UNAUTHENTICATED; + /* Need to reauthenticate or rejoin depending on reason code */ +/* copy_from_rx_buff(local, buff, pkt_addr, rx_len & 0xff); + */ +} +/*===========================================================================*/ +void clear_interrupt(ray_dev_t *local) +{ + writeb(0, local->amem + CIS_OFFSET + HCS_INTR_OFFSET); +} +/*===========================================================================*/ +#ifdef CONFIG_PROC_FS +#define MAXDATA (PAGE_SIZE - 80) + +static char *card_status[] = { + "Card inserted - uninitialized", /* 0 */ + "Card not downloaded", /* 1 */ + "Waiting for download parameters", /* 2 */ + "Card doing acquisition", /* 3 */ + "Acquisition complete", /* 4 */ + "Authentication complete", /* 5 */ + "Association complete", /* 6 */ + "???", "???", "???", "???", /* 7 8 9 10 undefined */ + "Card init error", /* 11 */ + "Download parameters error", /* 12 */ + "???", /* 13 */ + "Acquisition failed", /* 14 */ + "Authentication refused", /* 15 */ + "Association failed" /* 16 */ +}; + +static char *nettype[] = {"Adhoc", "Infra "}; +static char *framing[] = {"Encapsulation", "Translation"} +; +/*===========================================================================*/ +int ray_cs_proc_read(char *buf, char **start, off_t offset, + int len, int unused) +{ +/* Print current values which are not available via other means + * eg ifconfig + */ + int i; + dev_link_t *link = dev_list; + struct net_device *dev = (struct net_device *)link->priv; + ray_dev_t *local = (ray_dev_t *)dev->priv; + UCHAR *p; + struct freq_hop_element *pfh; + UCHAR c[33]; + + len = 0; + + len += sprintf(buf + len, "Raylink Wireless LAN driver status\n"); + len += sprintf(buf + len, "%s\n", rcsid); + /* build 4 does not report version, and field is 0x55 after memtest */ + len += sprintf(buf + len, "Firmware version = "); + if (local->fw_ver == 0x55) + len += sprintf(buf + len, "4 - Use dump_cis for more details\n"); + else + len += sprintf(buf + len, "%2d.%02d.%02d\n", + local->fw_ver, local->fw_bld, local->fw_var); + + for (i=0; i<32; i++) c[i] = local->sparm.b5.a_current_ess_id[i]; + c[32] = 0; + len += sprintf(buf + len, "%s network ESSID = \"%s\"\n", + nettype[local->sparm.b5.a_network_type], c); + + p = local->bss_id; + len += sprintf(buf + len, + "BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + p[0],p[1],p[2],p[3],p[4],p[5]); + + len += sprintf(buf + len, "Country code = %d\n", + local->sparm.b5.a_curr_country_code); + + i = local->card_status; + if (i < 0) i = 10; + if (i > 16) i = 10; + len += sprintf(buf + len, "Card status = %s\n", card_status[i]); + + len += sprintf(buf + len, "Framing mode = %s\n",framing[translate]); + + /* Pull some fields out of last beacon received */ + len += sprintf(buf + len, "Beacon Interval = %d Kus\n", + local->last_bcn.beacon_intvl[0] + + 256 * local->last_bcn.beacon_intvl[1]); + + p = local->last_bcn.elements; + if (p[0] == C_ESSID_ELEMENT_ID) p += p[1] + 2; + else { + len += sprintf(buf + len, "Parse beacon failed at essid element id = %d\n",p[0]); + return len; + } + + if (p[0] == C_SUPPORTED_RATES_ELEMENT_ID) { + len += sprintf(buf + len, "Supported rate codes = "); + for (i=2; idwell_time[0] + 256 * pfh->dwell_time[1]); + len += sprintf(buf + len, "Hop set = %d \n", pfh->hop_set); + len += sprintf(buf + len, "Hop pattern = %d \n", pfh->hop_pattern); + len += sprintf(buf + len, "Hop index = %d \n", pfh->hop_index); + p += p[1] + 2; + } + else { + len += sprintf(buf + len, "Parse beacon failed at FH param element\n"); + return len; + } + return len; +} + +#endif +/*===========================================================================*/ +int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type) +{ + int addr; + struct ccs *pccs; + struct tx_msg *ptx; + int ccsindex; + + /* If no tx buffers available, return */ + if ((ccsindex = get_free_tx_ccs(local)) == -1) + { +/* TBD should never be here but... what if we are? */ + DEBUG(1,"ray_cs send authenticate - No free tx ccs\n"); + return -1; + } + + pccs = ((struct ccs *)(local->sram + CCS_BASE)) + ccsindex; + + /* Address in card space */ + addr = TX_BUF_BASE + (ccsindex << 11); + /* fill in the CCS */ + writeb(CCS_TX_REQUEST, &pccs->cmd); + writeb(addr >> 8, pccs->var.tx_request.tx_data_ptr); + writeb(0x20, pccs->var.tx_request.tx_data_ptr + 1); + writeb(TX_AUTHENTICATE_LENGTH_MSB, pccs->var.tx_request.tx_data_length); + writeb(TX_AUTHENTICATE_LENGTH_LSB,pccs->var.tx_request.tx_data_length + 1); + writeb(0, &pccs->var.tx_request.pow_sav_mode); + + ptx = (struct tx_msg *)(local->sram + addr); + /* fill in the mac header */ + writeb(PROTOCOL_VER | AUTHENTIC_TYPE, &ptx->mac.frame_ctl_1); + writeb(0, &ptx->mac.frame_ctl_2); + + memcpy_toio(ptx->mac.addr_1, dest, ADDRLEN); + memcpy_toio(ptx->mac.addr_2, local->sparm.b4.a_mac_addr, ADDRLEN); + memcpy_toio(ptx->mac.addr_3, local->bss_id, ADDRLEN); + + /* Fill in msg body with protocol 00 00, sequence 01 00 ,status 00 00 */ + memset_io(ptx->var, 0, 6); + writeb(auth_type & 0xff, ptx->var + 2); + + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + DEBUG(1,"ray_cs send authentication request failed - ECF not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + return -1; + } + return 0; +} /* End build_auth_frame */ +/*===========================================================================*/ +static int __init init_ray_cs(void) +{ + int rc; + servinfo_t serv; + + DEBUG(1, "%s\n", rcsid); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "ray: Card Services release does not match!\n"); + return -1; + } + rc = register_pcmcia_driver(&dev_info, &ray_attach, &ray_detach); + DEBUG(1, "raylink init_module register_pcmcia_driver returns 0x%x\n",rc); +#ifdef CONFIG_PROC_FS + proc_register(&proc_root, &ray_cs_proc_entry); +#endif + if (translate != 0) translate = 1; + return 0; +} /* init_ray_cs */ + +#ifndef MODULE + +static char init_ess_id[ESSID_SIZE]; +static int __init essid_setup(char *str) +{ + strncpy(init_ess_id, str, ESSID_SIZE); + essid = init_ess_id; + return 1; +} +__setup("essid=", essid_setup); + +#endif + +/*===========================================================================*/ + +static void __exit exit_ray_cs(void) +{ + DEBUG(0, "ray_cs: cleanup_module\n"); + + unregister_pcmcia_driver(&dev_info); + while (dev_list != NULL) { + if (dev_list->state & DEV_CONFIG) ray_release((u_long)dev_list); + ray_detach(dev_list); + } +#ifdef CONFIG_PROC_FS + proc_unregister(&proc_root, ray_cs_proc_entry.low_ino); +#endif +} /* exit_ray_cs */ + +module_init(init_ray_cs); +module_exit(exit_ray_cs); + +/*===========================================================================*/ diff --git a/drivers/net/pcmcia/ray_cs.h b/drivers/net/pcmcia/ray_cs.h new file mode 100644 index 000000000..28502954e --- /dev/null +++ b/drivers/net/pcmcia/ray_cs.h @@ -0,0 +1,60 @@ +/* Raytheon wireless LAN PCMCIA card driver for Linux + A PCMCIA client driver for the Raylink wireless network card + Written by Corey Thomas +*/ + +#ifndef RAYLINK_H + +struct beacon_rx { + struct mac_header mac; + UCHAR timestamp[8]; + UCHAR beacon_intvl[2]; + UCHAR capability[2]; + UCHAR elements[sizeof(struct essid_element) + + sizeof(struct rates_element) + + sizeof(struct freq_hop_element) + + sizeof(struct japan_call_sign_element) + + sizeof(struct tim_element)]; +}; + +typedef struct ray_dev_t { + int card_status; + int authentication_state; + dev_node_t node; + window_handle_t amem_handle; /* handle to window for attribute memory */ + window_handle_t rmem_handle; /* handle to window for rx buffer on card */ + UCHAR *sram; /* pointer to beginning of shared RAM */ + UCHAR *amem; /* pointer to attribute mem window */ + UCHAR *rmem; /* pointer to receive buffer window */ + dev_link_t *finder; /* pointer back to dev_link_t for card */ + struct timer_list timer; + int dl_param_ccs; + union { + struct b4_startup_params b4; + struct b5_startup_params b5; + } sparm; + int timeout_flag; + UCHAR supported_rates[8]; + UCHAR japan_call_sign[12]; + struct startup_res_6 startup_res; + int num_multi; + /* Network parameters from start/join */ + UCHAR bss_id[6]; + UCHAR auth_id[6]; + UCHAR net_default_tx_rate; + UCHAR encryption; + struct enet_statistics stats; + + UCHAR net_type; + UCHAR sta_type; + UCHAR fw_ver; + UCHAR fw_bld; + UCHAR fw_var; + UCHAR ASIC_version; + UCHAR assoc_id[2]; + UCHAR tib_length; + struct beacon_rx last_bcn; +} ray_dev_t; +/*****************************************************************************/ + +#endif /* RAYLINK_H */ diff --git a/drivers/net/pcmcia/rayctl.h b/drivers/net/pcmcia/rayctl.h new file mode 100644 index 000000000..a301b0bd2 --- /dev/null +++ b/drivers/net/pcmcia/rayctl.h @@ -0,0 +1,725 @@ +#ifndef RAYLINK_H + +typedef unsigned char UCHAR; + +/****** IEEE 802.11 constants ************************************************/ +#define ADDRLEN 6 +/* Frame control 1 bit fields */ +#define PROTOCOL_VER 0x00 +#define DATA_TYPE 0x08 +#define ASSOC_REQ_TYPE 0x00 +#define ASSOC_RESP_TYPE 0x10 +#define REASSOC_REQ_TYPE 0x20 +#define REASSOC_RESP_TYPE 0x30 +#define NULL_MSG_TYPE 0x48 +#define BEACON_TYPE 0x80 +#define DISASSOC_TYPE 0xA0 +#define PSPOLL_TYPE 0xA4 +#define AUTHENTIC_TYPE 0xB0 +#define DEAUTHENTIC_TYPE 0xC0 +/* Frame control 2 bit fields */ +#define FC2_TO_DS 0x01 +#define FC2_FROM_DS 0x02 +#define FC2_MORE_FRAG 0x04 +#define FC2_RETRY 0x08 +#define FC2_PSM 0x10 +#define FC2_MORE_DATA 0x20 +#define FC2_WEP 0x40 +#define FC2_ORDER 0x80 +/*****************************************************************************/ +/* 802.11 element ID's and lengths */ +#define C_BP_CAPABILITY_ESS 0x01 +#define C_BP_CAPABILITY_IBSS 0x02 +#define C_BP_CAPABILITY_CF_POLLABLE 0x04 +#define C_BP_CAPABILITY_CF_POLL_REQUEST 0x08 +#define C_BP_CAPABILITY_PRIVACY 0x10 + +#define C_ESSID_ELEMENT_ID 0 +#define C_ESSID_ELEMENT_MAX_LENGTH 32 + +#define C_SUPPORTED_RATES_ELEMENT_ID 1 +#define C_SUPPORTED_RATES_ELEMENT_LENGTH 2 + +#define C_FH_PARAM_SET_ELEMENT_ID 2 +#define C_FH_PARAM_SET_ELEMENT_LNGTH 5 + +#define C_CF_PARAM_SET_ELEMENT_ID 4 +#define C_CF_PARAM_SET_ELEMENT_LNGTH 6 + +#define C_TIM_ELEMENT_ID 5 +#define C_TIM_BITMAP_LENGTH 251 +#define C_TIM_BMCAST_BIT 0x01 + +#define C_IBSS_ELEMENT_ID 6 +#define C_IBSS_ELEMENT_LENGTH 2 + +#define C_JAPAN_CALL_SIGN_ELEMENT_ID 51 +#define C_JAPAN_CALL_SIGN_ELEMENT_LNGTH 12 + +#define C_DISASSOC_REASON_CODE_LEN 2 +#define C_DISASSOC_REASON_CODE_DEFAULT 8 + +#define C_CRC_LEN 4 +#define C_NUM_SUPPORTED_RATES 8 +/****** IEEE 802.11 mac header for type data packets *************************/ +struct mac_header { + UCHAR frame_ctl_1; + UCHAR frame_ctl_2; + UCHAR duration_lsb; + UCHAR duration_msb; + UCHAR addr_1[ADDRLEN]; + UCHAR addr_2[ADDRLEN]; + UCHAR addr_3[ADDRLEN]; + UCHAR seq_frag_num[2]; +/* UCHAR addr_4[ADDRLEN]; *//* only present for AP to AP (TO DS and FROM DS */ +}; +/****** IEEE 802.11 frame element structures *********************************/ +struct essid_element +{ + UCHAR id; + UCHAR length; + UCHAR text[C_ESSID_ELEMENT_MAX_LENGTH]; +}; +struct rates_element +{ + UCHAR id; + UCHAR length; + UCHAR value[8]; +}; +struct freq_hop_element +{ + UCHAR id; + UCHAR length; + UCHAR dwell_time[2]; + UCHAR hop_set; + UCHAR hop_pattern; + UCHAR hop_index; +}; +struct tim_element +{ + UCHAR id; + UCHAR length; + UCHAR dtim_count; + UCHAR dtim_period; + UCHAR bitmap_control; + UCHAR tim[C_TIM_BITMAP_LENGTH]; +}; +struct ibss_element +{ + UCHAR id; + UCHAR length; + UCHAR atim_window[2]; +}; +struct japan_call_sign_element +{ + UCHAR id; + UCHAR length; + UCHAR call_sign[12]; +}; +/****** Beacon message structures ********************************************/ +/* .elements is a large lump of max size because elements are variable size */ +struct infra_beacon +{ + UCHAR timestamp[8]; + UCHAR beacon_intvl[2]; + UCHAR capability[2]; + UCHAR elements[sizeof(struct essid_element) + + sizeof(struct rates_element) + + sizeof(struct freq_hop_element) + + sizeof(struct japan_call_sign_element) + + sizeof(struct tim_element)]; +}; +struct adhoc_beacon +{ + UCHAR timestamp[8]; + UCHAR beacon_intvl[2]; + UCHAR capability[2]; + UCHAR elements[sizeof(struct essid_element) + + sizeof(struct rates_element) + + sizeof(struct freq_hop_element) + + sizeof(struct japan_call_sign_element) + + sizeof(struct ibss_element)]; +}; +/*****************************************************************************/ +/*****************************************************************************/ + + +/* #define C_MAC_HDR_2_WEP 0x40 */ +/* TX/RX CCS constants */ +#define TX_HEADER_LENGTH 0x1C +#define RX_MAC_HEADER_LENGTH 0x18 +#define TX_AUTHENTICATE_LENGTH (TX_HEADER_LENGTH + 6) +#define TX_AUTHENTICATE_LENGTH_MSB (TX_AUTHENTICATE_LENGTH >> 8) +#define TX_AUTHENTICATE_LENGTH_LSB (TX_AUTHENTICATE_LENGTH & 0xff) +#define FCS_LEN 4 + +#define ADHOC 0 +#define INFRA 1 + +#define TYPE_STA 0 +#define TYPE_AP 1 + +#define PASSIVE_SCAN 1 +#define ACTIVE_SCAN 1 + +#define PSM_CAM 0 + +/* Country codes */ +#define USA 1 +#define EUROPE 2 +#define JAPAN 3 +#define KOREA 4 +#define SPAIN 5 +#define FRANCE 6 +#define ISRAEL 7 +#define AUSTRALIA 8 +#define JAPAN_TEST 9 + +/* Hop pattern lengths */ +#define USA_HOP_MOD 79 +#define EUROPE_HOP_MOD 79 +#define JAPAN_HOP_MOD 23 +#define KOREA_HOP_MOD 23 +#define SPAIN_HOP_MOD 27 +#define FRANCE_HOP_MOD 35 +#define ISRAEL_HOP_MOD 35 +#define AUSTRALIA_HOP_MOD 47 +#define JAPAN_TEST_HOP_MOD 23 + +#define ESSID_SIZE 32 +/**********************************************************************/ +/* CIS Register Constants */ +#define CIS_OFFSET 0x0f00 +/* Configuration Option Register (0x0F00) */ +#define COR_OFFSET 0x00 +#define COR_SOFT_RESET 0x80 +#define COR_LEVEL_IRQ 0x40 +#define COR_CONFIG_NUM 0x01 +#define COR_DEFAULT (COR_LEVEL_IRQ | COR_CONFIG_NUM) + +/* Card Configuration and Status Register (0x0F01) */ +#define CCSR_OFFSET 0x01 +#define CCSR_HOST_INTR_PENDING 0x01 +#define CCSR_POWER_DOWN 0x04 + +/* HCS Interrupt Register (0x0F05) */ +#define HCS_INTR_OFFSET 0x05 +/* #define HCS_INTR_OFFSET 0x0A */ +#define HCS_INTR_CLEAR 0x00 + +/* ECF Interrupt Register (0x0F06) */ +#define ECF_INTR_OFFSET 0x06 +/* #define ECF_INTR_OFFSET 0x0C */ +#define ECF_INTR_SET 0x01 + +/* Authorization Register 0 (0x0F08) */ +#define AUTH_0_ON 0x57 + +/* Authorization Register 1 (0x0F09) */ +#define AUTH_1_ON 0x82 + +/* Program Mode Register (0x0F0A) */ +#define PC2PM 0x02 +#define PC2CAL 0x10 +#define PC2MLSE 0x20 + +/* PC Test Mode Register (0x0F0B) */ +#define PC_TEST_MODE 0x08 + +/* Frequency Control Word (0x0F10) */ +/* Range 0x02 - 0xA6 */ + +/* Test Mode Control 1-4 (0x0F14 - 0x0F17) */ + +/**********************************************************************/ + +/* Shared RAM Area */ +#define SCB_BASE 0x0000 +#define STATUS_BASE 0x0100 +#define HOST_TO_ECF_BASE 0x0200 +#define ECF_TO_HOST_BASE 0x0300 +#define CCS_BASE 0x0400 +#define RCS_BASE 0x0800 +#define INFRA_TIM_BASE 0x0C00 +#define SSID_LIST_BASE 0x0D00 +#define TX_BUF_BASE 0x1000 +#define RX_BUF_BASE 0x8000 + +#define NUMBER_OF_CCS 64 +#define NUMBER_OF_RCS 64 +/*#define NUMBER_OF_TX_CCS 14 */ +#define NUMBER_OF_TX_CCS 14 + +#define TX_BUF_SIZE (2048 - sizeof(struct tx_msg)) +#define RX_BUFF_END 0x3FFF +/* Values for buffer_status */ +#define CCS_BUFFER_FREE 0 +#define CCS_BUFFER_BUSY 1 +#define CCS_COMMAND_COMPLETE 2 +#define CCS_COMMAND_FAILED 3 + +/* Values for cmd */ +#define CCS_DOWNLOAD_STARTUP_PARAMS 1 +#define CCS_UPDATE_PARAMS 2 +#define CCS_REPORT_PARAMS 3 +#define CCS_UPDATE_MULTICAST_LIST 4 +#define CCS_UPDATE_POWER_SAVINGS_MODE 5 +#define CCS_START_NETWORK 6 +#define CCS_JOIN_NETWORK 7 +#define CCS_START_ASSOCIATION 8 +#define CCS_TX_REQUEST 9 +#define CCS_TEST_MEMORY 0xa +#define CCS_SHUTDOWN 0xb +#define CCS_DUMP_MEMORY 0xc +#define CCS_START_TIMER 0xe +#define CCS_LAST_CMD CCS_START_TIMER + +/* Values for link field */ +#define CCS_END_LIST 0xff + +/* values for buffer_status field */ +#define RCS_BUFFER_FREE 0 +#define RCS_BUFFER_BUSY 1 +#define RCS_COMPLETE 2 +#define RCS_FAILED 3 +#define RCS_BUFFER_RELEASE 0xFF + +/* values for interrupt_id field */ +#define PROCESS_RX_PACKET 0x80 /* */ +#define REJOIN_NET_COMPLETE 0x81 /* RCS ID: Rejoin Net Complete */ +#define ROAMING_INITIATED 0x82 /* RCS ID: Roaming Initiated */ +#define JAPAN_CALL_SIGN_RXD 0x83 /* RCS ID: New Japan Call Sign */ + +/*****************************************************************************/ +/* Memory types for dump memory command */ +#define C_MEM_PROG 0 +#define C_MEM_XDATA 1 +#define C_MEM_SFR 2 +#define C_MEM_IDATA 3 + +/*** Return values for hw_xmit **********/ +#define XMIT_OK (0) +#define XMIT_MSG_BAD (-1) +#define XMIT_NO_CCS (-2) +#define XMIT_NO_INTR (-3) +#define XMIT_NEED_AUTH (-4) + +/*** Values for card status */ +#define CARD_INSERTED (0) + +#define CARD_AWAITING_PARAM (1) +#define CARD_INIT_ERROR (11) + +#define CARD_DL_PARAM (2) +#define CARD_DL_PARAM_ERROR (12) + +#define CARD_DOING_ACQ (3) + +#define CARD_ACQ_COMPLETE (4) +#define CARD_ACQ_FAILED (14) + +#define CARD_AUTH_COMPLETE (5) +#define CARD_AUTH_REFUSED (15) + +#define CARD_ASSOC_COMPLETE (6) +#define CARD_ASSOC_FAILED (16) + +/*** Values for authentication_state */ +#define UNAUTHENTICATED (0) +#define AWAITING_RESPONSE (1) +#define AUTHENTICATED (2) +#define NEED_TO_AUTH (3) + +/*** Values for authentication type */ +#define OPEN_AUTH_REQUEST (1) +#define OPEN_AUTH_RESPONSE (2) + + +/***********************************************************************/ +/* Parameter passing structure for update/report parameter CCS's */ +struct object_id { + void *object_addr; + unsigned char object_length; +}; + +#define OBJID_network_type 0 +#define OBJID_acting_as_ap_status 1 +#define OBJID_current_ess_id 2 +#define OBJID_scanning_mode 3 +#define OBJID_power_mgt_state 4 +#define OBJID_mac_address 5 +#define OBJID_frag_threshold 6 +#define OBJID_hop_time 7 +#define OBJID_beacon_period 8 +#define OBJID_dtim_period 9 +#define OBJID_retry_max 10 +#define OBJID_ack_timeout 11 +#define OBJID_sifs 12 +#define OBJID_difs 13 +#define OBJID_pifs 14 +#define OBJID_rts_threshold 15 +#define OBJID_scan_dwell_time 16 +#define OBJID_max_scan_dwell_time 17 +#define OBJID_assoc_resp_timeout 18 +#define OBJID_adhoc_scan_cycle_max 19 +#define OBJID_infra_scan_cycle_max 20 +#define OBJID_infra_super_cycle_max 21 +#define OBJID_promiscuous_mode 22 +#define OBJID_unique_word 23 +#define OBJID_slot_time 24 +#define OBJID_roaming_low_snr 25 +#define OBJID_low_snr_count_thresh 26 +#define OBJID_infra_missed_bcn 27 +#define OBJID_adhoc_missed_bcn 28 +#define OBJID_curr_country_code 29 +#define OBJID_hop_pattern 30 +#define OBJID_reserved 31 +#define OBJID_cw_max_msb 32 +#define OBJID_cw_min_msb 33 +#define OBJID_noise_filter_gain 34 +#define OBJID_noise_limit_offset 35 +#define OBJID_det_rssi_thresh_offset 36 +#define OBJID_med_busy_thresh_offset 37 +#define OBJID_det_sync_thresh 38 +#define OBJID_test_mode 39 +#define OBJID_test_min_chan_num 40 +#define OBJID_test_max_chan_num 41 +#define OBJID_allow_bcast_ID_prbrsp 42 +#define OBJID_privacy_must_start 43 +#define OBJID_privacy_can_join 44 +#define OBJID_basic_rate_set 45 + +/**** Configuration/Status/Control Area ***************************/ +/* System Control Block (SCB) Area + * Located at Shared RAM offset 0 + */ +struct scb { + UCHAR ccs_index; + UCHAR rcs_index; +}; + +/****** Status area at Shared RAM offset 0x0100 ******************************/ +struct status { + UCHAR mrx_overflow_for_host; /* 0=ECF may write, 1=host may write*/ + UCHAR mrx_checksum_error_for_host; /* 0=ECF may write, 1=host may write*/ + UCHAR rx_hec_error_for_host; /* 0=ECF may write, 1=host may write*/ + UCHAR reserved1; + short mrx_overflow; /* ECF increments on rx overflow */ + short mrx_checksum_error; /* ECF increments on rx CRC error */ + short rx_hec_error; /* ECF incs on mac header CRC error */ + UCHAR rxnoise; /* Average RSL measurement */ +}; + +/****** Host-to-ECF Data Area at Shared RAM offset 0x200 *********************/ +struct host_to_ecf_area { + +}; + +/****** ECF-to-Host Data Area at Shared RAM offset 0x0300 ********************/ +struct startup_res_518 { + UCHAR startup_word; + UCHAR station_addr[ADDRLEN]; + UCHAR calc_prog_chksum; + UCHAR calc_cis_chksum; + UCHAR ecf_spare[7]; + UCHAR japan_call_sign[12]; +}; + +struct startup_res_6 { + UCHAR startup_word; + UCHAR station_addr[ADDRLEN]; + UCHAR reserved; + UCHAR supp_rates[8]; + UCHAR japan_call_sign[12]; + UCHAR calc_prog_chksum; + UCHAR calc_cis_chksum; + UCHAR firmware_version[3]; + UCHAR asic_version; + UCHAR tib_length; +}; + +struct start_join_net_params { + UCHAR net_type; + UCHAR ssid[ESSID_SIZE]; + UCHAR reserved; + UCHAR privacy_can_join; +}; + +/****** Command Control Structure area at Shared ram offset 0x0400 ***********/ +/* Structures for command specific parameters (ccs.var) */ +struct update_param_cmd { + UCHAR object_id; + UCHAR number_objects; + UCHAR failure_cause; +}; +struct report_param_cmd { + UCHAR object_id; + UCHAR number_objects; + UCHAR failure_cause; + UCHAR length; +}; +struct start_network_cmd { + UCHAR update_param; + UCHAR bssid[ADDRLEN]; + UCHAR net_initiated; + UCHAR net_default_tx_rate; + UCHAR encryption; +}; +struct join_network_cmd { + UCHAR update_param; + UCHAR bssid[ADDRLEN]; + UCHAR net_initiated; + UCHAR net_default_tx_rate; + UCHAR encryption; +}; +struct tx_requested_cmd { + + UCHAR tx_data_ptr[2]; + UCHAR tx_data_length[2]; + UCHAR host_reserved[2]; + UCHAR reserved[3]; + UCHAR tx_rate; + UCHAR pow_sav_mode; + UCHAR retries; + UCHAR antenna; +}; +struct tx_requested_cmd_4 { + + UCHAR tx_data_ptr[2]; + UCHAR tx_data_length[2]; + UCHAR dest_addr[ADDRLEN]; + UCHAR pow_sav_mode; + UCHAR retries; + UCHAR station_id; +}; +struct memory_dump_cmd { + UCHAR memory_type; + UCHAR memory_ptr[2]; + UCHAR length; +}; +struct update_association_cmd { + UCHAR status; + UCHAR aid[2]; +}; +struct start_timer_cmd { + UCHAR duration[2]; +}; + +struct ccs { + UCHAR buffer_status; /* 0 = buffer free, 1 = buffer busy */ + /* 2 = command complete, 3 = failed */ + UCHAR cmd; /* command to ECF */ + UCHAR link; /* link to next CCS, FF=end of list */ + /* command specific parameters */ + union { + char reserved[13]; + struct update_param_cmd update_param; + struct report_param_cmd report_param; + UCHAR nummulticast; + UCHAR mode; + struct start_network_cmd start_network; + struct join_network_cmd join_network; + struct tx_requested_cmd tx_request; + struct memory_dump_cmd memory_dump; + struct update_association_cmd update_assoc; + struct start_timer_cmd start_timer; + } var; +}; + +/*****************************************************************************/ +/* Transmit buffer structures */ +struct tib_structure { + UCHAR ccs_index; + UCHAR psm; + UCHAR pass_fail; + UCHAR retry_count; + UCHAR max_retries; + UCHAR frags_remaining; + UCHAR no_rb; + UCHAR rts_reqd; + UCHAR csma_tx_cntrl_2; + UCHAR sifs_tx_cntrl_2; + UCHAR tx_dma_addr_1[2]; + UCHAR tx_dma_addr_2[2]; + UCHAR var_dur_2mhz[2]; + UCHAR var_dur_1mhz[2]; + UCHAR max_dur_2mhz[2]; + UCHAR max_dur_1mhz[2]; + UCHAR hdr_len; + UCHAR max_frag_len[2]; + UCHAR var_len[2]; + UCHAR phy_hdr_4; + UCHAR mac_hdr_1; + UCHAR mac_hdr_2; + UCHAR sid[2]; +}; + +struct phy_header { + UCHAR sfd[2]; + UCHAR hdr_3; + UCHAR hdr_4; +}; +struct rx_msg { + struct mac_header mac; + UCHAR var[1]; +}; + +struct tx_msg { + struct tib_structure tib; + struct phy_header phy; + struct mac_header mac; + UCHAR var[1]; +}; + +/****** ECF Receive Control Stucture (RCS) Area at Shared RAM offset 0x0800 */ +/* Structures for command specific parameters (rcs.var) */ +struct rx_packet_cmd { + UCHAR rx_data_ptr[2]; + UCHAR rx_data_length[2]; + UCHAR rx_sig_lev; + UCHAR next_frag_rcs_index; + UCHAR totalpacketlength[2]; +}; +struct rejoin_net_cmplt_cmd { + UCHAR reserved; + UCHAR bssid[ADDRLEN]; +}; +struct japan_call_sign_rxd { + UCHAR rxd_call_sign[8]; + UCHAR reserved[5]; +}; + +struct rcs { + UCHAR buffer_status; + UCHAR interrupt_id; + UCHAR link_field; + /* command specific parameters */ + union { + UCHAR reserved[13]; + struct rx_packet_cmd rx_packet; + struct rejoin_net_cmplt_cmd rejoin_net_complete; + struct japan_call_sign_rxd japan_call_sign; + } var; +}; + +/****** Startup parameter structures for both versions of firmware ***********/ +struct b4_startup_params { + UCHAR a_network_type; /* C_ADHOC, C_INFRA */ + UCHAR a_acting_as_ap_status; /* C_TYPE_STA, C_TYPE_AP */ + UCHAR a_current_ess_id[ESSID_SIZE]; /* Null terminated unless 32 long */ + UCHAR a_scanning_mode; /* passive 0, active 1 */ + UCHAR a_power_mgt_state; /* CAM 0, */ + UCHAR a_mac_addr[ADDRLEN]; /* */ + UCHAR a_frag_threshold[2]; /* 512 */ + UCHAR a_hop_time[2]; /* 16k * 2**n, n=0-4 in Kus */ + UCHAR a_beacon_period[2]; /* n * a_hop_time in Kus */ + UCHAR a_dtim_period; /* in beacons */ + UCHAR a_retry_max; /* */ + UCHAR a_ack_timeout; /* */ + UCHAR a_sifs; /* */ + UCHAR a_difs; /* */ + UCHAR a_pifs; /* */ + UCHAR a_rts_threshold[2]; /* */ + UCHAR a_scan_dwell_time[2]; /* */ + UCHAR a_max_scan_dwell_time[2]; /* */ + UCHAR a_assoc_resp_timeout_thresh; /* */ + UCHAR a_adhoc_scan_cycle_max; /* */ + UCHAR a_infra_scan_cycle_max; /* */ + UCHAR a_infra_super_scan_cycle_max; /* */ + UCHAR a_promiscuous_mode; /* */ + UCHAR a_unique_word[2]; /* */ + UCHAR a_slot_time; /* */ + UCHAR a_roaming_low_snr_thresh; /* */ + UCHAR a_low_snr_count_thresh; /* */ + UCHAR a_infra_missed_bcn_thresh; /* */ + UCHAR a_adhoc_missed_bcn_thresh; /* */ + UCHAR a_curr_country_code; /* C_USA */ + UCHAR a_hop_pattern; /* */ + UCHAR a_hop_pattern_length; /* */ +/* b4 - b5 differences start here */ + UCHAR a_cw_max; /* */ + UCHAR a_cw_min; /* */ + UCHAR a_noise_filter_gain; /* */ + UCHAR a_noise_limit_offset; /* */ + UCHAR a_det_rssi_thresh_offset; /* */ + UCHAR a_med_busy_thresh_offset; /* */ + UCHAR a_det_sync_thresh; /* */ + UCHAR a_test_mode; /* */ + UCHAR a_test_min_chan_num; /* */ + UCHAR a_test_max_chan_num; /* */ + UCHAR a_rx_tx_delay; /* */ + UCHAR a_current_bss_id[ADDRLEN]; /* */ + UCHAR a_hop_set; /* */ +}; +struct b5_startup_params { + UCHAR a_network_type; /* C_ADHOC, C_INFRA */ + UCHAR a_acting_as_ap_status; /* C_TYPE_STA, C_TYPE_AP */ + UCHAR a_current_ess_id[ESSID_SIZE]; /* Null terminated unless 32 long */ + UCHAR a_scanning_mode; /* passive 0, active 1 */ + UCHAR a_power_mgt_state; /* CAM 0, */ + UCHAR a_mac_addr[ADDRLEN]; /* */ + UCHAR a_frag_threshold[2]; /* 512 */ + UCHAR a_hop_time[2]; /* 16k * 2**n, n=0-4 in Kus */ + UCHAR a_beacon_period[2]; /* n * a_hop_time in Kus */ + UCHAR a_dtim_period; /* in beacons */ + UCHAR a_retry_max; /* 4 */ + UCHAR a_ack_timeout; /* */ + UCHAR a_sifs; /* */ + UCHAR a_difs; /* */ + UCHAR a_pifs; /* */ + UCHAR a_rts_threshold[2]; /* */ + UCHAR a_scan_dwell_time[2]; /* */ + UCHAR a_max_scan_dwell_time[2]; /* */ + UCHAR a_assoc_resp_timeout_thresh; /* */ + UCHAR a_adhoc_scan_cycle_max; /* */ + UCHAR a_infra_scan_cycle_max; /* */ + UCHAR a_infra_super_scan_cycle_max; /* */ + UCHAR a_promiscuous_mode; /* */ + UCHAR a_unique_word[2]; /* */ + UCHAR a_slot_time; /* */ + UCHAR a_roaming_low_snr_thresh; /* */ + UCHAR a_low_snr_count_thresh; /* */ + UCHAR a_infra_missed_bcn_thresh; /* */ + UCHAR a_adhoc_missed_bcn_thresh; /* */ + UCHAR a_curr_country_code; /* C_USA */ + UCHAR a_hop_pattern; /* */ + UCHAR a_hop_pattern_length; /* */ +/* b4 - b5 differences start here */ + UCHAR a_cw_max[2]; /* */ + UCHAR a_cw_min[2]; /* */ + UCHAR a_noise_filter_gain; /* */ + UCHAR a_noise_limit_offset; /* */ + UCHAR a_det_rssi_thresh_offset; /* */ + UCHAR a_med_busy_thresh_offset; /* */ + UCHAR a_det_sync_thresh; /* */ + UCHAR a_test_mode; /* */ + UCHAR a_test_min_chan_num; /* */ + UCHAR a_test_max_chan_num; /* */ + UCHAR a_allow_bcast_SSID_probe_rsp; + UCHAR a_privacy_must_start; + UCHAR a_privacy_can_join; + UCHAR a_basic_rate_set[8]; +}; + +/*****************************************************************************/ +#define RAY_IOCG_PARMS (SIOCDEVPRIVATE) +#define RAY_IOCS_PARMS (SIOCDEVPRIVATE + 1) +#define RAY_DO_CMD (SIOCDEVPRIVATE + 2) + +/****** ethernet <-> 802.11 translation **************************************/ +typedef struct snaphdr_t +{ + UCHAR dsap; + UCHAR ssap; + UCHAR ctrl; + UCHAR org[3]; + UCHAR ethertype[2]; +} snaphdr_t; + +#define BRIDGE_ENCAP 0xf80000 +#define RFC1042_ENCAP 0 +#define SNAP_ID 0x0003aaaa +#define RAY_IPX_TYPE 0x8137 +#define APPLEARP_TYPE 0x80f3 +/*****************************************************************************/ +#endif /* #ifndef RAYLINK_H */ -- cgit v1.2.3