summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/legacy/ray_cs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/legacy/ray_cs.c')
-rw-r--r--drivers/net/wireless/legacy/ray_cs.c2819
1 files changed, 2819 insertions, 0 deletions
diff --git a/drivers/net/wireless/legacy/ray_cs.c b/drivers/net/wireless/legacy/ray_cs.c
new file mode 100644
index 000000000000..1f57a0055bbd
--- /dev/null
+++ b/drivers/net/wireless/legacy/ray_cs.c
@@ -0,0 +1,2819 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*=============================================================================
+ *
+ * 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, dahinds@users.sourceforge.net
+ *
+ * Copyright (c) 1998 Corey Thomas (corey@world.std.com)
+ *
+ * Changes:
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000
+ * - reorganize kmallocs in ray_attach, checking all for failure
+ * and releasing the previous allocations if one fails
+ *
+ * Daniele Bellucci <bellucda@tiscali.it> - 07/10/2003
+ * - Audit copy_to_user in ioctl(SIOCGIWESSID)
+ *
+=============================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/ptrace.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/skbuff.h>
+#include <linux/ieee80211.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <linux/uaccess.h>
+
+/* Warning : these stuff will slow down the driver... */
+#define WIRELESS_SPY /* Enable spying addresses */
+/* Definitions we need for spy */
+typedef struct iw_statistics iw_stats;
+typedef u_char mac_addr[ETH_ALEN]; /* Hardware address */
+
+#include "rayctl.h"
+#include "ray_cs.h"
+
+
+/** Prototypes based on PCMCIA skeleton driver *******************************/
+static int ray_config(struct pcmcia_device *link);
+static void ray_release(struct pcmcia_device *link);
+static void ray_detach(struct pcmcia_device *p_dev);
+
+/***** Prototypes indicated by device structure ******************************/
+static int ray_dev_close(struct net_device *dev);
+static int ray_dev_config(struct net_device *dev, struct ifmap *map);
+static struct net_device_stats *ray_get_stats(struct net_device *dev);
+static int ray_dev_init(struct net_device *dev);
+
+static int ray_open(struct net_device *dev);
+static netdev_tx_t 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);
+static int translate_frame(ray_dev_t *local, struct tx_msg __iomem *ptx,
+ unsigned char *data, int len);
+static void ray_build_header(ray_dev_t *local, struct tx_msg __iomem *ptx,
+ UCHAR msg_type, unsigned char *data);
+static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len);
+static iw_stats *ray_get_wireless_stats(struct net_device *dev);
+static const struct iw_handler_def ray_handler_def;
+
+/***** Prototypes for raylink functions **************************************/
+static void authenticate(ray_dev_t *local);
+static int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type);
+static void authenticate_timeout(struct timer_list *t);
+static int get_free_ccs(ray_dev_t *local);
+static int get_free_tx_ccs(ray_dev_t *local);
+static void init_startup_params(ray_dev_t *local);
+static int parse_addr(char *in_str, UCHAR *out);
+static int ray_hw_xmit(unsigned char *data, int len, struct net_device *dev, UCHAR type);
+static int ray_init(struct net_device *dev);
+static int interrupt_ecf(ray_dev_t *local, int ccs);
+static void ray_reset(struct net_device *dev);
+static void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value, int len);
+static void verify_dl_startup(struct timer_list *t);
+
+/* Prototypes for interrpt time functions **********************************/
+static irqreturn_t ray_interrupt(int reg, void *dev_id);
+static void clear_interrupt(ray_dev_t *local);
+static void rx_deauthenticate(ray_dev_t *local, struct rcs __iomem *prcs,
+ unsigned int pkt_addr, int rx_len);
+static int copy_from_rx_buff(ray_dev_t *local, UCHAR *dest, int pkt_addr, int len);
+static void ray_rx(struct net_device *dev, ray_dev_t *local, struct rcs __iomem *prcs);
+static void release_frag_chain(ray_dev_t *local, struct rcs __iomem *prcs);
+static void rx_authenticate(ray_dev_t *local, struct rcs __iomem *prcs,
+ unsigned int pkt_addr, int rx_len);
+static void rx_data(struct net_device *dev, struct rcs __iomem *prcs,
+ unsigned int pkt_addr, int rx_len);
+static void associate(ray_dev_t *local);
+
+/* Card command functions */
+static int dl_startup_params(struct net_device *dev);
+static void join_net(struct timer_list *t);
+static void start_net(struct timer_list *t);
+
+/*===========================================================================*/
+/* Parameters that can be set with 'insmod' */
+
+/* ADHOC=0, Infrastructure=1 */
+static int net_type = ADHOC;
+
+/* Hop dwell time in Kus (1024 us units defined by 802.11) */
+static int hop_dwell = 128;
+
+/* Beacon period in Kus */
+static int beacon_period = 256;
+
+/* power save mode (0 = off, 1 = save power) */
+static int psm;
+
+/* String for network's Extended Service Set ID. 32 Characters max */
+static char *essid;
+
+/* Default to encapsulation unless translation requested */
+static bool translate = true;
+
+static int country = USA;
+
+static int sniffer;
+
+static int bc;
+
+/* 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;
+
+static unsigned int ray_mem_speed = 500;
+
+/* WARNING: THIS DRIVER IS NOT CAPABLE OF HANDLING MULTIPLE DEVICES! */
+static struct pcmcia_device *this_device = NULL;
+
+MODULE_AUTHOR("Corey Thomas <corey@world.std.com>");
+MODULE_DESCRIPTION("Raylink/WebGear wireless LAN driver");
+MODULE_LICENSE("GPL");
+
+module_param(net_type, int, 0);
+module_param(hop_dwell, int, 0);
+module_param(beacon_period, int, 0);
+module_param(psm, int, 0);
+module_param(essid, charp, 0);
+module_param(translate, bool, 0);
+module_param(country, int, 0);
+module_param(sniffer, int, 0);
+module_param(bc, int, 0);
+module_param(phy_addr, charp, 0);
+module_param(ray_mem_speed, int, 0);
+
+static const 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 const 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 */
+ 0, /* rx/tx delay */
+ 0, 0, 0, 0, 0, 0, /* current BSS id */
+ 0 /* hop set */
+};
+
+/*===========================================================================*/
+static const u8 eth2_llc[] = { 0xaa, 0xaa, 3, 0, 0, 0 };
+
+static const 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
+};
+
+static const char rcsid[] =
+ "Raylink/WebGear wireless LAN - Corey <Thomas corey@world.std.com>";
+
+static const struct net_device_ops ray_netdev_ops = {
+ .ndo_init = ray_dev_init,
+ .ndo_open = ray_open,
+ .ndo_stop = ray_dev_close,
+ .ndo_start_xmit = ray_dev_start_xmit,
+ .ndo_set_config = ray_dev_config,
+ .ndo_get_stats = ray_get_stats,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int ray_probe(struct pcmcia_device *p_dev)
+{
+ ray_dev_t *local;
+ struct net_device *dev;
+
+ dev_dbg(&p_dev->dev, "ray_attach()\n");
+
+ /* Allocate space for private device-specific data */
+ dev = alloc_etherdev(sizeof(ray_dev_t));
+ if (!dev)
+ goto fail_alloc_dev;
+
+ local = netdev_priv(dev);
+ local->finder = p_dev;
+
+ /* The io structure describes IO port mapping. None used here */
+ p_dev->resource[0]->end = 0;
+ p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+
+ /* General socket configuration */
+ p_dev->config_flags |= CONF_ENABLE_IRQ;
+ p_dev->config_index = 1;
+
+ p_dev->priv = dev;
+
+ local->finder = p_dev;
+ local->card_status = CARD_INSERTED;
+ local->authentication_state = UNAUTHENTICATED;
+ local->num_multi = 0;
+ dev_dbg(&p_dev->dev, "ray_attach p_dev = %p, dev = %p, local = %p, intr = %p\n",
+ p_dev, dev, local, &ray_interrupt);
+
+ /* Raylink entries in the device structure */
+ dev->netdev_ops = &ray_netdev_ops;
+ dev->wireless_handlers = &ray_handler_def;
+#ifdef WIRELESS_SPY
+ local->wireless_data.spy_data = &local->spy_data;
+ dev->wireless_data = &local->wireless_data;
+#endif /* WIRELESS_SPY */
+
+
+ dev_dbg(&p_dev->dev, "ray_cs ray_attach calling ether_setup.)\n");
+ netif_stop_queue(dev);
+
+ timer_setup(&local->timer, NULL, 0);
+
+ this_device = p_dev;
+ return ray_config(p_dev);
+
+fail_alloc_dev:
+ return -ENOMEM;
+} /* ray_attach */
+
+static void ray_detach(struct pcmcia_device *link)
+{
+ struct net_device *dev;
+ ray_dev_t *local;
+
+ dev_dbg(&link->dev, "ray_detach\n");
+
+ this_device = NULL;
+ dev = link->priv;
+
+ ray_release(link);
+
+ local = netdev_priv(dev);
+ del_timer_sync(&local->timer);
+
+ if (link->priv) {
+ unregister_netdev(dev);
+ free_netdev(dev);
+ }
+ dev_dbg(&link->dev, "ray_cs ray_detach ending\n");
+} /* ray_detach */
+
+#define MAX_TUPLE_SIZE 128
+static int ray_config(struct pcmcia_device *link)
+{
+ int ret = 0;
+ int i;
+ struct net_device *dev = (struct net_device *)link->priv;
+ ray_dev_t *local = netdev_priv(dev);
+
+ dev_dbg(&link->dev, "ray_config\n");
+
+ /* Determine card type and firmware version */
+ printk(KERN_INFO "ray_cs Detected: %s%s%s%s\n",
+ link->prod_id[0] ? link->prod_id[0] : " ",
+ link->prod_id[1] ? link->prod_id[1] : " ",
+ link->prod_id[2] ? link->prod_id[2] : " ",
+ link->prod_id[3] ? link->prod_id[3] : " ");
+
+ /* Now allocate an interrupt line. Note that this does not
+ actually assign a handler to the interrupt.
+ */
+ ret = pcmcia_request_irq(link, ray_interrupt);
+ if (ret)
+ goto failed;
+ dev->irq = link->irq;
+
+ ret = pcmcia_enable_device(link);
+ if (ret)
+ goto failed;
+
+/*** Set up 32k window for shared memory (transmit and control) ************/
+ link->resource[2]->flags |= WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT;
+ link->resource[2]->start = 0;
+ link->resource[2]->end = 0x8000;
+ ret = pcmcia_request_window(link, link->resource[2], ray_mem_speed);
+ if (ret)
+ goto failed;
+ ret = pcmcia_map_mem_page(link, link->resource[2], 0);
+ if (ret)
+ goto failed;
+ local->sram = ioremap(link->resource[2]->start,
+ resource_size(link->resource[2]));
+ if (!local->sram)
+ goto failed;
+
+/*** Set up 16k window for shared memory (receive buffer) ***************/
+ link->resource[3]->flags |=
+ WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT;
+ link->resource[3]->start = 0;
+ link->resource[3]->end = 0x4000;
+ ret = pcmcia_request_window(link, link->resource[3], ray_mem_speed);
+ if (ret)
+ goto failed;
+ ret = pcmcia_map_mem_page(link, link->resource[3], 0x8000);
+ if (ret)
+ goto failed;
+ local->rmem = ioremap(link->resource[3]->start,
+ resource_size(link->resource[3]));
+ if (!local->rmem)
+ goto failed;
+
+/*** Set up window for attribute memory ***********************************/
+ link->resource[4]->flags |=
+ WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_AM | WIN_ENABLE | WIN_USE_WAIT;
+ link->resource[4]->start = 0;
+ link->resource[4]->end = 0x1000;
+ ret = pcmcia_request_window(link, link->resource[4], ray_mem_speed);
+ if (ret)
+ goto failed;
+ ret = pcmcia_map_mem_page(link, link->resource[4], 0);
+ if (ret)
+ goto failed;
+ local->amem = ioremap(link->resource[4]->start,
+ resource_size(link->resource[4]));
+ if (!local->amem)
+ goto failed;
+
+ dev_dbg(&link->dev, "ray_config sram=%p\n", local->sram);
+ dev_dbg(&link->dev, "ray_config rmem=%p\n", local->rmem);
+ dev_dbg(&link->dev, "ray_config amem=%p\n", local->amem);
+ if (ray_init(dev) < 0) {
+ ray_release(link);
+ return -ENODEV;
+ }
+
+ SET_NETDEV_DEV(dev, &link->dev);
+ i = register_netdev(dev);
+ if (i != 0) {
+ printk("ray_config register_netdev() failed\n");
+ ray_release(link);
+ return i;
+ }
+
+ printk(KERN_INFO "%s: RayLink, irq %d, hw_addr %pM\n",
+ dev->name, dev->irq, dev->dev_addr);
+
+ return 0;
+
+failed:
+ ray_release(link);
+ return -ENODEV;
+} /* ray_config */
+
+static inline struct ccs __iomem *ccs_base(ray_dev_t *dev)
+{
+ return dev->sram + CCS_BASE;
+}
+
+static inline struct rcs __iomem *rcs_base(ray_dev_t *dev)
+{
+ /*
+ * This looks nonsensical, since there is a separate
+ * RCS_BASE. But the difference between a "struct rcs"
+ * and a "struct ccs" ends up being in the _index_ off
+ * the base, so the base pointer is the same for both
+ * ccs/rcs.
+ */
+ return dev->sram + CCS_BASE;
+}
+
+/*===========================================================================*/
+static int ray_init(struct net_device *dev)
+{
+ int i;
+ struct ccs __iomem *pccs;
+ ray_dev_t *local = netdev_priv(dev);
+ struct pcmcia_device *link = local->finder;
+ dev_dbg(&link->dev, "ray_init(0x%p)\n", dev);
+ if (!(pcmcia_dev_present(link))) {
+ dev_dbg(&link->dev, "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) {
+ printk(KERN_INFO "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];
+ dev_dbg(&link->dev, "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;
+ dev_dbg(&link->dev, "ray_init tib_length = 0x%02x\n", local->tib_length);
+ /* Initialize CCS's to buffer free state */
+ pccs = ccs_base(local);
+ for (i = 0; i < NUMBER_OF_CCS; i++) {
+ writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status);
+ }
+ init_startup_params(local);
+
+ /* copy mac address to startup parameters */
+ if (!parse_addr(phy_addr, local->sparm.b4.a_mac_addr)) {
+ memcpy(&local->sparm.b4.a_mac_addr,
+ &local->startup_res.station_addr, ADDRLEN);
+ }
+
+ clear_interrupt(local); /* Clear any interrupt from the card */
+ local->card_status = CARD_AWAITING_PARAM;
+ dev_dbg(&link->dev, "ray_init ending\n");
+ return 0;
+} /* ray_init */
+
+/*===========================================================================*/
+/* Download startup parameters to the card and command it to read them */
+static int dl_startup_params(struct net_device *dev)
+{
+ int ccsindex;
+ ray_dev_t *local = netdev_priv(dev);
+ struct ccs __iomem *pccs;
+ struct pcmcia_device *link = local->finder;
+
+ dev_dbg(&link->dev, "dl_startup_params entered\n");
+ if (!(pcmcia_dev_present(link))) {
+ dev_dbg(&link->dev, "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)) < 0)
+ return -1;
+ local->dl_param_ccs = ccsindex;
+ pccs = ccs_base(local) + ccsindex;
+ writeb(CCS_DOWNLOAD_STARTUP_PARAMS, &pccs->cmd);
+ dev_dbg(&link->dev, "dl_startup_params start ccsindex = %d\n",
+ local->dl_param_ccs);
+ /* Interrupt the firmware to process the command */
+ if (interrupt_ecf(local, ccsindex)) {
+ printk(KERN_INFO "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.function = verify_dl_startup;
+ add_timer(&local->timer);
+ dev_dbg(&link->dev,
+ "ray_cs dl_startup_params started timer for verify_dl_startup\n");
+ return 0;
+} /* dl_startup_params */
+
+/*===========================================================================*/
+static void init_startup_params(ray_dev_t *local)
+{
+ int i;
+
+ 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(&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 */
+
+/*===========================================================================*/
+static void verify_dl_startup(struct timer_list *t)
+{
+ ray_dev_t *local = from_timer(local, t, timer);
+ struct ccs __iomem *pccs = ccs_base(local) + local->dl_param_ccs;
+ UCHAR status;
+ struct pcmcia_device *link = local->finder;
+
+ if (!(pcmcia_dev_present(link))) {
+ dev_dbg(&link->dev, "ray_cs verify_dl_startup - device not present\n");
+ return;
+ }
+#if 0
+ {
+ int i;
+ printk(KERN_DEBUG
+ "verify_dl_startup parameters sent via ccs %d:\n",
+ local->dl_param_ccs);
+ for (i = 0; i < sizeof(struct b5_startup_params); i++) {
+ printk(" %2x",
+ (unsigned int)readb(local->sram +
+ HOST_TO_ECF_BASE + i));
+ }
+ printk("\n");
+ }
+#endif
+
+ status = readb(&pccs->buffer_status);
+ if (status != CCS_BUFFER_FREE) {
+ printk(KERN_INFO
+ "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(&local->timer);
+ else
+ join_net(&local->timer);
+} /* end verify_dl_startup */
+
+/*===========================================================================*/
+/* Command card to start a network */
+static void start_net(struct timer_list *t)
+{
+ ray_dev_t *local = from_timer(local, t, timer);
+ struct ccs __iomem *pccs;
+ int ccsindex;
+ struct pcmcia_device *link = local->finder;
+ if (!(pcmcia_dev_present(link))) {
+ dev_dbg(&link->dev, "ray_cs start_net - device not present\n");
+ return;
+ }
+ /* Fill in the CCS fields for the ECF */
+ if ((ccsindex = get_free_ccs(local)) < 0)
+ return;
+ pccs = ccs_base(local) + 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)) {
+ dev_dbg(&link->dev, "ray start net failed - card not ready for intr\n");
+ writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status);
+ return;
+ }
+ local->card_status = CARD_DOING_ACQ;
+} /* end start_net */
+
+/*===========================================================================*/
+/* Command card to join a network */
+static void join_net(struct timer_list *t)
+{
+ ray_dev_t *local = from_timer(local, t, timer);
+
+ struct ccs __iomem *pccs;
+ int ccsindex;
+ struct pcmcia_device *link = local->finder;
+
+ if (!(pcmcia_dev_present(link))) {
+ dev_dbg(&link->dev, "ray_cs join_net - device not present\n");
+ return;
+ }
+ /* Fill in the CCS fields for the ECF */
+ if ((ccsindex = get_free_ccs(local)) < 0)
+ return;
+ pccs = ccs_base(local) + 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)) {
+ dev_dbg(&link->dev, "ray join net failed - card not ready for intr\n");
+ writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status);
+ return;
+ }
+ local->card_status = CARD_DOING_ACQ;
+}
+
+
+static void ray_release(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ ray_dev_t *local = netdev_priv(dev);
+
+ dev_dbg(&link->dev, "ray_release\n");
+
+ del_timer(&local->timer);
+
+ iounmap(local->sram);
+ iounmap(local->rmem);
+ iounmap(local->amem);
+ pcmcia_disable_device(link);
+
+ dev_dbg(&link->dev, "ray_release ending\n");
+}
+
+static int ray_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open)
+ netif_device_detach(dev);
+
+ return 0;
+}
+
+static int ray_resume(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open) {
+ ray_reset(dev);
+ netif_device_attach(dev);
+ }
+
+ return 0;
+}
+
+/*===========================================================================*/
+static int ray_dev_init(struct net_device *dev)
+{
+#ifdef RAY_IMMEDIATE_INIT
+ int i;
+#endif /* RAY_IMMEDIATE_INIT */
+ ray_dev_t *local = netdev_priv(dev);
+ struct pcmcia_device *link = local->finder;
+
+ dev_dbg(&link->dev, "ray_dev_init(dev=%p)\n", dev);
+ if (!(pcmcia_dev_present(link))) {
+ dev_dbg(&link->dev, "ray_dev_init - device not present\n");
+ return -1;
+ }
+#ifdef RAY_IMMEDIATE_INIT
+ /* Download startup parameters */
+ if ((i = dl_startup_params(dev)) < 0) {
+ printk(KERN_INFO "ray_dev_init dl_startup_params failed - "
+ "returns 0x%x\n", i);
+ return -1;
+ }
+#else /* RAY_IMMEDIATE_INIT */
+ /* Postpone the card init so that we can still configure the card,
+ * for example using the Wireless Extensions. The init will happen
+ * in ray_open() - Jean II */
+ dev_dbg(&link->dev,
+ "ray_dev_init: postponing card init to ray_open() ; Status = %d\n",
+ local->card_status);
+#endif /* RAY_IMMEDIATE_INIT */
+
+ /* copy mac and broadcast addresses to linux device */
+ eth_hw_addr_set(dev, local->sparm.b4.a_mac_addr);
+ eth_broadcast_addr(dev->broadcast);
+
+ dev_dbg(&link->dev, "ray_dev_init ending\n");
+ return 0;
+}
+
+/*===========================================================================*/
+static int ray_dev_config(struct net_device *dev, struct ifmap *map)
+{
+ ray_dev_t *local = netdev_priv(dev);
+ struct pcmcia_device *link = local->finder;
+ /* Dummy routine to satisfy device structure */
+ dev_dbg(&link->dev, "ray_dev_config(dev=%p,ifmap=%p)\n", dev, map);
+ if (!(pcmcia_dev_present(link))) {
+ dev_dbg(&link->dev, "ray_dev_config - device not present\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*===========================================================================*/
+static netdev_tx_t ray_dev_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ ray_dev_t *local = netdev_priv(dev);
+ struct pcmcia_device *link = local->finder;
+ short length = skb->len;
+
+ if (!pcmcia_dev_present(link)) {
+ dev_dbg(&link->dev, "ray_dev_start_xmit - device not present\n");
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ dev_dbg(&link->dev, "ray_dev_start_xmit(skb=%p, dev=%p)\n", skb, dev);
+ if (local->authentication_state == NEED_TO_AUTH) {
+ dev_dbg(&link->dev, "ray_cs Sending authentication request.\n");
+ if (!build_auth_frame(local, local->auth_id, OPEN_AUTH_REQUEST)) {
+ local->authentication_state = AUTHENTICATED;
+ netif_stop_queue(dev);
+ return NETDEV_TX_BUSY;
+ }
+ }
+
+ if (length < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN))
+ return NETDEV_TX_OK;
+ length = ETH_ZLEN;
+ }
+ switch (ray_hw_xmit(skb->data, length, dev, DATA_TYPE)) {
+ case XMIT_NO_CCS:
+ case XMIT_NEED_AUTH:
+ netif_stop_queue(dev);
+ return NETDEV_TX_BUSY;
+ case XMIT_NO_INTR:
+ case XMIT_MSG_BAD:
+ case XMIT_OK:
+ default:
+ dev_kfree_skb(skb);
+ }
+
+ return NETDEV_TX_OK;
+} /* ray_dev_start_xmit */
+
+/*===========================================================================*/
+static int ray_hw_xmit(unsigned char *data, int len, struct net_device *dev,
+ UCHAR msg_type)
+{
+ ray_dev_t *local = netdev_priv(dev);
+ struct ccs __iomem *pccs;
+ int ccsindex;
+ int offset;
+ struct tx_msg __iomem *ptx; /* Address of xmit buffer in PC space */
+ short int addr; /* Address of xmit buffer in card space */
+
+ pr_debug("ray_hw_xmit(data=%p, len=%d, dev=%p)\n", data, len, dev);
+ if (len + TX_HEADER_LENGTH > TX_BUF_SIZE) {
+ printk(KERN_INFO "ray_hw_xmit packet too large: %d bytes\n",
+ len);
+ return XMIT_MSG_BAD;
+ }
+ switch (ccsindex = get_free_tx_ccs(local)) {
+ case ECCSBUSY:
+ pr_debug("ray_hw_xmit tx_ccs table busy\n");
+ fallthrough;
+ case ECCSFULL:
+ pr_debug("ray_hw_xmit No free tx ccs\n");
+ fallthrough;
+ case ECARDGONE:
+ netif_stop_queue(dev);
+ return XMIT_NO_CCS;
+ default:
+ break;
+ }
+ addr = TX_BUF_BASE + (ccsindex << 11);
+
+ if (msg_type == DATA_TYPE) {
+ local->stats.tx_bytes += len;
+ local->stats.tx_packets++;
+ }
+
+ ptx = 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_toio(&ptx->var, data, len);
+ offset = 0;
+ }
+
+ /* fill in the CCS */
+ pccs = ccs_base(local) + 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);
+ pr_debug("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)) {
+ pr_debug("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 */
+
+/*===========================================================================*/
+static int translate_frame(ray_dev_t *local, struct tx_msg __iomem *ptx,
+ unsigned char *data, int len)
+{
+ __be16 proto = ((struct ethhdr *)data)->h_proto;
+ if (ntohs(proto) >= ETH_P_802_3_MIN) { /* DIX II ethernet frame */
+ pr_debug("ray_cs translate_frame DIX II\n");
+ /* Copy LLC header to card buffer */
+ memcpy_toio(&ptx->var, eth2_llc, sizeof(eth2_llc));
+ memcpy_toio(((void __iomem *)&ptx->var) + sizeof(eth2_llc),
+ (UCHAR *) &proto, 2);
+ if (proto == htons(ETH_P_AARP) || proto == htons(ETH_P_IPX)) {
+ /* This is the selective translation table, only 2 entries */
+ writeb(0xf8,
+ &((struct snaphdr_t __iomem *)ptx->var)->org[2]);
+ }
+ /* Copy body of ethernet packet without ethernet header */
+ memcpy_toio((void __iomem *)&ptx->var +
+ sizeof(struct snaphdr_t), data + ETH_HLEN,
+ len - ETH_HLEN);
+ return (int)sizeof(struct snaphdr_t) - ETH_HLEN;
+ } else { /* already 802 type, and proto is length */
+ pr_debug("ray_cs translate_frame 802\n");
+ if (proto == htons(0xffff)) { /* evil netware IPX 802.3 without LLC */
+ pr_debug("ray_cs translate_frame evil IPX\n");
+ memcpy_toio(&ptx->var, data + ETH_HLEN, len - ETH_HLEN);
+ return 0 - ETH_HLEN;
+ }
+ memcpy_toio(&ptx->var, data + ETH_HLEN, len - ETH_HLEN);
+ return 0 - ETH_HLEN;
+ }
+ /* TBD do other frame types */
+} /* end translate_frame */
+
+/*===========================================================================*/
+static void ray_build_header(ray_dev_t *local, struct tx_msg __iomem *ptx,
+ UCHAR msg_type, unsigned char *data)
+{
+ writeb(PROTOCOL_VER | msg_type, &ptx->mac.frame_ctl_1);
+/*** IEEE 802.11 Address field assignments *************
+ TODS FROMDS addr_1 addr_2 addr_3 addr_4
+Adhoc 0 0 dest src (terminal) BSSID N/A
+AP to Terminal 0 1 dest AP(BSSID) source N/A
+Terminal to AP 1 0 AP(BSSID) src (terminal) dest N/A
+AP to AP 1 1 dest AP src AP dest source
+*******************************************************/
+ if (local->net_type == ADHOC) {
+ writeb(0, &ptx->mac.frame_ctl_2);
+ memcpy_toio(ptx->mac.addr_1, ((struct ethhdr *)data)->h_dest,
+ ADDRLEN);
+ memcpy_toio(ptx->mac.addr_2, ((struct ethhdr *)data)->h_source,
+ 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 */
+
+/*====================================================================*/
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get protocol name
+ */
+static int ray_get_name(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ strcpy(wrqu->name, "IEEE 802.11-FH");
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
<