X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Flibertas%2Fmain.c;h=0be89573716c14eb8e8e01013ce672fbc79ed734;hb=652e3f208619dfe75867349d6164afe735eaf159;hp=55dce8dbd0d299f7f97be136b3b88ee5b2afaa42;hpb=d9268fb9a124d067cf93710a85bb6c158d131c97;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 55dce8d..0be8957 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -6,11 +6,11 @@ #include #include -#include #include #include #include #include +#include #include #include @@ -20,8 +20,9 @@ #include "dev.h" #include "wext.h" #include "debugfs.h" +#include "scan.h" #include "assoc.h" -#include "join.h" +#include "cmd.h" #define DRIVER_RELEASE_VERSION "323.p0" const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION @@ -37,6 +38,11 @@ EXPORT_SYMBOL_GPL(lbs_debug); module_param_named(libertas_debug, lbs_debug, int, 0644); +/* This global structure is used to send the confirm_sleep command as + * fast as possible down to the firmware. */ +struct cmd_confirm_sleep confirm_sleep; + + #define LBS_TX_PWR_DEFAULT 20 /*100mW */ #define LBS_TX_PWR_US_DEFAULT 20 /*100mW */ #define LBS_TX_PWR_JP_DEFAULT 16 /*50mW */ @@ -216,13 +222,15 @@ u8 lbs_data_rate_to_fw_index(u32 rate) static ssize_t lbs_anycast_get(struct device *dev, struct device_attribute *attr, char * buf) { + struct lbs_private *priv = to_net_dev(dev)->priv; struct cmd_ds_mesh_access mesh_access; + int ret; memset(&mesh_access, 0, sizeof(mesh_access)); - lbs_prepare_and_send_command(to_net_dev(dev)->priv, - CMD_MESH_ACCESS, - CMD_ACT_MESH_GET_ANYCAST, - CMD_OPTION_WAITFORRSP, 0, (void *)&mesh_access); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access); + if (ret) + return ret; return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0])); } @@ -233,22 +241,27 @@ static ssize_t lbs_anycast_get(struct device *dev, static ssize_t lbs_anycast_set(struct device *dev, struct device_attribute *attr, const char * buf, size_t count) { + struct lbs_private *priv = to_net_dev(dev)->priv; struct cmd_ds_mesh_access mesh_access; uint32_t datum; + int ret; memset(&mesh_access, 0, sizeof(mesh_access)); sscanf(buf, "%x", &datum); mesh_access.data[0] = cpu_to_le32(datum); - lbs_prepare_and_send_command((to_net_dev(dev))->priv, - CMD_MESH_ACCESS, - CMD_ACT_MESH_SET_ANYCAST, - CMD_OPTION_WAITFORRSP, 0, (void *)&mesh_access); + ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access); + if (ret) + return ret; + return strlen(buf); } -int lbs_add_rtap(struct lbs_private *priv); -void lbs_remove_rtap(struct lbs_private *priv); +static int lbs_add_rtap(struct lbs_private *priv); +static void lbs_remove_rtap(struct lbs_private *priv); +static int lbs_add_mesh(struct lbs_private *priv); +static void lbs_remove_mesh(struct lbs_private *priv); + /** * Get function for sysfs attribute rtap @@ -270,10 +283,12 @@ static ssize_t lbs_rtap_set(struct device *dev, struct lbs_private *priv = to_net_dev(dev)->priv; sscanf(buf, "%x", &monitor_mode); - if (monitor_mode != LBS_MONITOR_OFF) { - if(priv->monitormode == monitor_mode) + if (monitor_mode) { + if (priv->monitormode == monitor_mode) return strlen(buf); - if (priv->monitormode == LBS_MONITOR_OFF) { + if (!priv->monitormode) { + if (priv->infra_open || priv->mesh_open) + return -EBUSY; if (priv->mode == IW_MODE_INFRA) lbs_send_deauthentication(priv); else if (priv->mode == IW_MODE_ADHOC) @@ -284,9 +299,9 @@ static ssize_t lbs_rtap_set(struct device *dev, } else { - if (priv->monitormode == LBS_MONITOR_OFF) + if (!priv->monitormode) return strlen(buf); - priv->monitormode = LBS_MONITOR_OFF; + priv->monitormode = 0; lbs_remove_rtap(priv); if (priv->currenttxskb) { @@ -305,60 +320,62 @@ static ssize_t lbs_rtap_set(struct device *dev, } /** - * lbs_rtap attribute to be exported per mshX interface - * through sysfs (/sys/class/net/mshX/libertas-rtap) + * lbs_rtap attribute to be exported per ethX interface + * through sysfs (/sys/class/net/ethX/lbs_rtap) */ -static DEVICE_ATTR(lbs_rtap, 0644, lbs_rtap_get, - lbs_rtap_set ); +static DEVICE_ATTR(lbs_rtap, 0644, lbs_rtap_get, lbs_rtap_set ); /** - * anycast_mask attribute to be exported per mshX interface - * through sysfs (/sys/class/net/mshX/anycast_mask) + * Get function for sysfs attribute mesh */ -static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set); - -static ssize_t lbs_autostart_enabled_get(struct device *dev, +static ssize_t lbs_mesh_get(struct device *dev, struct device_attribute *attr, char * buf) { - struct cmd_ds_mesh_access mesh_access; - - memset(&mesh_access, 0, sizeof(mesh_access)); - lbs_prepare_and_send_command(to_net_dev(dev)->priv, - CMD_MESH_ACCESS, - CMD_ACT_MESH_GET_AUTOSTART_ENABLED, - CMD_OPTION_WAITFORRSP, 0, (void *)&mesh_access); - - return sprintf(buf, "%d\n", le32_to_cpu(mesh_access.data[0])); + struct lbs_private *priv = to_net_dev(dev)->priv; + return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev); } -static ssize_t lbs_autostart_enabled_set(struct device *dev, +/** + * Set function for sysfs attribute mesh + */ +static ssize_t lbs_mesh_set(struct device *dev, struct device_attribute *attr, const char * buf, size_t count) { - struct cmd_ds_mesh_access mesh_access; - uint32_t datum; - struct lbs_private *priv = (to_net_dev(dev))->priv; + struct lbs_private *priv = to_net_dev(dev)->priv; + int enable; int ret; - memset(&mesh_access, 0, sizeof(mesh_access)); - sscanf(buf, "%d", &datum); - mesh_access.data[0] = cpu_to_le32(datum); + sscanf(buf, "%x", &enable); + enable = !!enable; + if (enable == !!priv->mesh_dev) + return count; - ret = lbs_prepare_and_send_command(priv, - CMD_MESH_ACCESS, - CMD_ACT_MESH_SET_AUTOSTART_ENABLED, - CMD_OPTION_WAITFORRSP, 0, (void *)&mesh_access); - if (ret == 0) - priv->mesh_autostart_enabled = datum ? 1 : 0; + ret = lbs_mesh_config(priv, enable, priv->curbssparams.channel); + if (ret) + return ret; - return strlen(buf); + if (enable) + lbs_add_mesh(priv); + else + lbs_remove_mesh(priv); + + return count; } -static DEVICE_ATTR(autostart_enabled, 0644, - lbs_autostart_enabled_get, lbs_autostart_enabled_set); +/** + * lbs_mesh attribute to be exported per ethX interface + * through sysfs (/sys/class/net/ethX/lbs_mesh) + */ +static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set); + +/** + * anycast_mask attribute to be exported per mshX interface + * through sysfs (/sys/class/net/mshX/anycast_mask) + */ +static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set); static struct attribute *lbs_mesh_sysfs_entries[] = { &dev_attr_anycast_mask.attr, - &dev_attr_autostart_enabled.attr, NULL, }; @@ -367,152 +384,91 @@ static struct attribute_group lbs_mesh_attr_group = { }; /** - * @brief Check if the device can be open and wait if necessary. - * - * @param dev A pointer to net_device structure - * @return 0 - * - * For USB adapter, on some systems the device open handler will be - * called before FW ready. Use the following flag check and wait - * function to work around the issue. - * - */ -static int pre_open_check(struct net_device *dev) -{ - struct lbs_private *priv = (struct lbs_private *) dev->priv; - int i = 0; - - while (!priv->fw_ready && i < 20) { - i++; - msleep_interruptible(100); - } - if (!priv->fw_ready) { - lbs_pr_err("firmware not ready\n"); - return -1; - } - - return 0; -} - -/** - * @brief This function opens the device + * @brief This function opens the ethX or mshX interface * * @param dev A pointer to net_device structure - * @return 0 + * @return 0 or -EBUSY if monitor mode active */ static int lbs_dev_open(struct net_device *dev) { - struct lbs_private *priv = (struct lbs_private *) dev->priv; + struct lbs_private *priv = (struct lbs_private *) dev->priv ; + int ret = 0; lbs_deb_enter(LBS_DEB_NET); - priv->open = 1; + spin_lock_irq(&priv->driver_lock); - if (priv->connect_status == LBS_CONNECTED) - netif_carrier_on(priv->dev); - else - netif_carrier_off(priv->dev); - - if (priv->mesh_dev) { - if (priv->mesh_connect_status == LBS_CONNECTED) - netif_carrier_on(priv->mesh_dev); - else - netif_carrier_off(priv->mesh_dev); + if (priv->monitormode) { + ret = -EBUSY; + goto out; } - lbs_deb_leave(LBS_DEB_NET); - return 0; -} -/** - * @brief This function opens the mshX interface - * - * @param dev A pointer to net_device structure - * @return 0 - */ -static int lbs_mesh_open(struct net_device *dev) -{ - struct lbs_private *priv = (struct lbs_private *) dev->priv ; + if (dev == priv->mesh_dev) { + priv->mesh_open = 1; + priv->mesh_connect_status = LBS_CONNECTED; + netif_carrier_on(dev); + } else { + priv->infra_open = 1; - if (pre_open_check(dev) == -1) - return -1; - priv->mesh_open = 1 ; - netif_wake_queue(priv->mesh_dev); + if (priv->connect_status == LBS_CONNECTED) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + } - priv->mesh_connect_status = LBS_CONNECTED; + if (!priv->tx_pending_len) + netif_wake_queue(dev); + out: - netif_carrier_on(priv->mesh_dev); - netif_wake_queue(priv->mesh_dev); - if (priv->infra_open == 0) - return lbs_dev_open(priv->dev) ; - return 0; + spin_unlock_irq(&priv->driver_lock); + lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); + return ret; } /** - * @brief This function opens the ethX interface + * @brief This function closes the mshX interface * * @param dev A pointer to net_device structure * @return 0 */ -static int lbs_open(struct net_device *dev) +static int lbs_mesh_stop(struct net_device *dev) { - struct lbs_private *priv = (struct lbs_private *) dev->priv ; + struct lbs_private *priv = (struct lbs_private *) (dev->priv); - if(pre_open_check(dev) == -1) - return -1; - priv->infra_open = 1 ; - netif_wake_queue(priv->dev); - if (priv->open == 0) - return lbs_dev_open(priv->dev) ; - return 0; -} + lbs_deb_enter(LBS_DEB_MESH); + spin_lock_irq(&priv->driver_lock); -static int lbs_dev_close(struct net_device *dev) -{ - struct lbs_private *priv = dev->priv; + priv->mesh_open = 0; + priv->mesh_connect_status = LBS_DISCONNECTED; - lbs_deb_enter(LBS_DEB_NET); + netif_stop_queue(dev); + netif_carrier_off(dev); - netif_carrier_off(priv->dev); - priv->open = 0; + spin_unlock_irq(&priv->driver_lock); - lbs_deb_leave(LBS_DEB_NET); + lbs_deb_leave(LBS_DEB_MESH); return 0; } /** - * @brief This function closes the mshX interface - * - * @param dev A pointer to net_device structure - * @return 0 - */ -static int lbs_mesh_close(struct net_device *dev) -{ - struct lbs_private *priv = (struct lbs_private *) (dev->priv); - - priv->mesh_open = 0; - netif_stop_queue(priv->mesh_dev); - if (priv->infra_open == 0) - return lbs_dev_close(dev); - else - return 0; -} - -/** * @brief This function closes the ethX interface * * @param dev A pointer to net_device structure * @return 0 */ -static int lbs_close(struct net_device *dev) +static int lbs_eth_stop(struct net_device *dev) { struct lbs_private *priv = (struct lbs_private *) dev->priv; - netif_stop_queue(dev); + lbs_deb_enter(LBS_DEB_NET); + + spin_lock_irq(&priv->driver_lock); priv->infra_open = 0; - if (priv->mesh_open == 0) - return lbs_dev_close(dev); - else - return 0; + netif_stop_queue(dev); + spin_unlock_irq(&priv->driver_lock); + + lbs_deb_leave(LBS_DEB_NET); + return 0; } static void lbs_tx_timeout(struct net_device *dev) @@ -525,14 +481,20 @@ static void lbs_tx_timeout(struct net_device *dev) dev->trans_start = jiffies; - if (priv->currenttxskb) { - priv->eventcause = 0x01000000; - lbs_send_tx_feedback(priv); - } + if (priv->currenttxskb) + lbs_send_tx_feedback(priv, 0); + /* XX: Shouldn't we also call into the hw-specific driver to kick it somehow? */ lbs_host_to_card_done(priv); + /* More often than not, this actually happens because the + firmware has crapped itself -- rather than just a very + busy medium. So send a harmless command, and if/when + _that_ times out, we'll kick it in the head. */ + lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0, + 0, 0, NULL); + lbs_deb_leave(LBS_DEB_TX); } @@ -540,25 +502,18 @@ void lbs_host_to_card_done(struct lbs_private *priv) { unsigned long flags; + lbs_deb_enter(LBS_DEB_THREAD); + spin_lock_irqsave(&priv->driver_lock, flags); priv->dnld_sent = DNLD_RES_RECEIVED; /* Wake main thread if commands are pending */ - if (!priv->cur_cmd) + if (!priv->cur_cmd || priv->tx_pending_len > 0) wake_up_interruptible(&priv->waitq); - /* Don't wake netif queues if we're in monitor mode and - a TX packet is already pending, or if there are commands - queued to be sent. */ - if (!priv->currenttxskb && list_empty(&priv->cmdpendingq)) { - if (priv->dev && priv->connect_status == LBS_CONNECTED) - netif_wake_queue(priv->dev); - - if (priv->mesh_dev && priv->mesh_connect_status == LBS_CONNECTED) - netif_wake_queue(priv->mesh_dev); - } spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_THREAD); } EXPORT_SYMBOL_GPL(lbs_host_to_card_done); @@ -572,6 +527,7 @@ static struct net_device_stats *lbs_get_stats(struct net_device *dev) { struct lbs_private *priv = (struct lbs_private *) dev->priv; + lbs_deb_enter(LBS_DEB_NET); return &priv->stats; } @@ -580,34 +536,27 @@ static int lbs_set_mac_address(struct net_device *dev, void *addr) int ret = 0; struct lbs_private *priv = (struct lbs_private *) dev->priv; struct sockaddr *phwaddr = addr; + struct cmd_ds_802_11_mac_address cmd; lbs_deb_enter(LBS_DEB_NET); /* In case it was called from the mesh device */ - dev = priv->dev ; - - memset(priv->current_addr, 0, ETH_ALEN); - - /* dev->dev_addr is 8 bytes */ - lbs_deb_hex(LBS_DEB_NET, "dev->dev_addr", dev->dev_addr, ETH_ALEN); - - lbs_deb_hex(LBS_DEB_NET, "addr", phwaddr->sa_data, ETH_ALEN); - memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN); + dev = priv->dev; - ret = lbs_prepare_and_send_command(priv, CMD_802_11_MAC_ADDRESS, - CMD_ACT_SET, - CMD_OPTION_WAITFORRSP, 0, NULL); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + memcpy(cmd.macadd, phwaddr->sa_data, ETH_ALEN); + ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); if (ret) { lbs_deb_net("set MAC address failed\n"); - ret = -1; goto done; } - lbs_deb_hex(LBS_DEB_NET, "priv->macaddr", priv->current_addr, ETH_ALEN); - memcpy(dev->dev_addr, priv->current_addr, ETH_ALEN); + memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN); + memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN); if (priv->mesh_dev) - memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN); + memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN); done: lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); @@ -624,53 +573,51 @@ static int lbs_copy_multicast_address(struct lbs_private *priv, memcpy(&priv->multicastlist[i], mcptr->dmi_addr, ETH_ALEN); mcptr = mcptr->next; } - return i; - } static void lbs_set_multicast_list(struct net_device *dev) { struct lbs_private *priv = dev->priv; - int oldpacketfilter; + int old_mac_control; DECLARE_MAC_BUF(mac); lbs_deb_enter(LBS_DEB_NET); - oldpacketfilter = priv->currentpacketfilter; + old_mac_control = priv->mac_control; if (dev->flags & IFF_PROMISC) { lbs_deb_net("enable promiscuous mode\n"); - priv->currentpacketfilter |= + priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE; - priv->currentpacketfilter &= + priv->mac_control &= ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE | CMD_ACT_MAC_MULTICAST_ENABLE); } else { /* Multicast */ - priv->currentpacketfilter &= + priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; if (dev->flags & IFF_ALLMULTI || dev->mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) { lbs_deb_net( "enabling all multicast\n"); - priv->currentpacketfilter |= + priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; - priv->currentpacketfilter &= + priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; } else { - priv->currentpacketfilter &= + priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE; if (!dev->mc_count) { lbs_deb_net("no multicast addresses, " "disabling multicast\n"); - priv->currentpacketfilter &= + priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; } else { int i; - priv->currentpacketfilter |= + priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; priv->nr_of_multicastmacaddr = @@ -680,7 +627,7 @@ static void lbs_set_multicast_list(struct net_device *dev) dev->mc_count); for (i = 0; i < dev->mc_count; i++) { - lbs_deb_net("Multicast address %d:%s\n", + lbs_deb_net("Multicast address %d: %s\n", i, print_mac(mac, priv->multicastlist[i])); } @@ -693,9 +640,8 @@ static void lbs_set_multicast_list(struct net_device *dev) } } - if (priv->currentpacketfilter != oldpacketfilter) { - lbs_set_mac_packet_filter(priv); - } + if (priv->mac_control != old_mac_control) + lbs_set_mac_control(priv); lbs_deb_leave(LBS_DEB_NET); } @@ -713,102 +659,139 @@ static int lbs_thread(void *data) struct net_device *dev = data; struct lbs_private *priv = dev->priv; wait_queue_t wait; - u8 ireg = 0; lbs_deb_enter(LBS_DEB_THREAD); init_waitqueue_entry(&wait, current); - set_freezable(); - for (;;) { - lbs_deb_thread( "main-thread 111: intcounter=%d currenttxskb=%p dnld_sent=%d\n", - priv->intcounter, priv->currenttxskb, priv->dnld_sent); + int shouldsleep; + u8 resp_idx; + + lbs_deb_thread("1: currenttxskb %p, dnld_sent %d\n", + priv->currenttxskb, priv->dnld_sent); add_wait_queue(&priv->waitq, &wait); set_current_state(TASK_INTERRUPTIBLE); spin_lock_irq(&priv->driver_lock); - if ((priv->psstate == PS_STATE_SLEEP) || - (!priv->intcounter && (priv->dnld_sent || priv->cur_cmd || list_empty(&priv->cmdpendingq)))) { - lbs_deb_thread("main-thread sleeping... Conn=%d IntC=%d PS_mode=%d PS_State=%d\n", - priv->connect_status, priv->intcounter, - priv->psmode, priv->psstate); + if (kthread_should_stop()) + shouldsleep = 0; /* Bye */ + else if (priv->surpriseremoved) + shouldsleep = 1; /* We need to wait until we're _told_ to die */ + else if (priv->psstate == PS_STATE_SLEEP) + shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ + else if (priv->cmd_timed_out) + shouldsleep = 0; /* Command timed out. Recover */ + else if (!priv->fw_ready) + shouldsleep = 1; /* Firmware not ready. We're waiting for it */ + else if (priv->dnld_sent) + shouldsleep = 1; /* Something is en route to the device already */ + else if (priv->tx_pending_len > 0) + shouldsleep = 0; /* We've a packet to send */ + else if (priv->cur_cmd) + shouldsleep = 1; /* Can't send a command; one already running */ + else if (!list_empty(&priv->cmdpendingq)) + shouldsleep = 0; /* We have a command to send */ + else if (__kfifo_len(priv->event_fifo)) + shouldsleep = 0; /* We have an event to process */ + else if (priv->resp_len[priv->resp_idx]) + shouldsleep = 0; /* We have a command response */ + else + shouldsleep = 1; /* No command */ + + if (shouldsleep) { + lbs_deb_thread("sleeping, connect_status %d, " + "psmode %d, psstate %d\n", + priv->connect_status, + priv->psmode, priv->psstate); spin_unlock_irq(&priv->driver_lock); schedule(); } else spin_unlock_irq(&priv->driver_lock); - lbs_deb_thread("main-thread 222 (waking up): intcounter=%d currenttxskb=%p dnld_sent=%d\n", - priv->intcounter, priv->currenttxskb, priv->dnld_sent); + lbs_deb_thread("2: currenttxskb %p, dnld_send %d\n", + priv->currenttxskb, priv->dnld_sent); set_current_state(TASK_RUNNING); remove_wait_queue(&priv->waitq, &wait); - try_to_freeze(); - lbs_deb_thread("main-thread 333: intcounter=%d currenttxskb=%p dnld_sent=%d\n", - priv->intcounter, priv->currenttxskb, priv->dnld_sent); + lbs_deb_thread("3: currenttxskb %p, dnld_sent %d\n", + priv->currenttxskb, priv->dnld_sent); - if (kthread_should_stop() || priv->surpriseremoved) { - lbs_deb_thread("main-thread: break from main thread: surpriseremoved=0x%x\n", - priv->surpriseremoved); + if (kthread_should_stop()) { + lbs_deb_thread("break from main thread\n"); break; } + if (priv->surpriseremoved) { + lbs_deb_thread("adapter removed; waiting to die...\n"); + continue; + } + + lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n", + priv->currenttxskb, priv->dnld_sent); spin_lock_irq(&priv->driver_lock); + /* Process any pending command response */ + resp_idx = priv->resp_idx; + if (priv->resp_len[resp_idx]) { + spin_unlock_irq(&priv->driver_lock); + lbs_process_command_response(priv, + priv->resp_buf[resp_idx], + priv->resp_len[resp_idx]); + spin_lock_irq(&priv->driver_lock); + priv->resp_len[resp_idx] = 0; + } + spin_unlock_irq(&priv->driver_lock); - if (priv->intcounter) { - u8 int_status; + /* command timeout stuff */ + if (priv->cmd_timed_out && priv->cur_cmd) { + struct cmd_ctrl_node *cmdnode = priv->cur_cmd; - priv->intcounter = 0; - int_status = priv->hw_get_int_status(priv, &ireg); + if (++priv->nr_retries > 10) { + lbs_pr_info("Excessive timeouts submitting command %x\n", + le16_to_cpu(cmdnode->cmdbuf->command)); + lbs_complete_command(priv, cmdnode, -ETIMEDOUT); + priv->nr_retries = 0; + } else { + priv->cur_cmd = NULL; + lbs_pr_info("requeueing command %x due to timeout (#%d)\n", + le16_to_cpu(cmdnode->cmdbuf->command), priv->nr_retries); - if (int_status) { - lbs_deb_thread("main-thread: reading HOST_INT_STATUS_REG failed\n"); - spin_unlock_irq(&priv->driver_lock); - continue; + /* Stick it back at the _top_ of the pending queue + for immediate resubmission */ + list_add(&cmdnode->list, &priv->cmdpendingq); } - priv->hisregcpy |= ireg; } + priv->cmd_timed_out = 0; - lbs_deb_thread("main-thread 444: intcounter=%d currenttxskb=%p dnld_sent=%d\n", - priv->intcounter, priv->currenttxskb, priv->dnld_sent); - - /* command response? */ - if (priv->hisregcpy & MRVDRV_CMD_UPLD_RDY) { - lbs_deb_thread("main-thread: cmd response ready\n"); + /* Process hardware events, e.g. card removed, link lost */ + spin_lock_irq(&priv->driver_lock); + while (__kfifo_len(priv->event_fifo)) { + u32 event; - priv->hisregcpy &= ~MRVDRV_CMD_UPLD_RDY; + __kfifo_get(priv->event_fifo, (unsigned char *) &event, + sizeof(event)); spin_unlock_irq(&priv->driver_lock); - lbs_process_rx_command(priv); + lbs_process_event(priv, event); spin_lock_irq(&priv->driver_lock); } + spin_unlock_irq(&priv->driver_lock); - /* Any Card Event */ - if (priv->hisregcpy & MRVDRV_CARDEVENT) { - lbs_deb_thread("main-thread: Card Event Activity\n"); - - priv->hisregcpy &= ~MRVDRV_CARDEVENT; - - if (priv->hw_read_event_cause(priv)) { - lbs_pr_alert("main-thread: hw_read_event_cause failed\n"); - spin_unlock_irq(&priv->driver_lock); - continue; - } - spin_unlock_irq(&priv->driver_lock); - lbs_process_event(priv); - } else - spin_unlock_irq(&priv->driver_lock); + if (!priv->fw_ready) + continue; /* Check if we need to confirm Sleep Request received previously */ if (priv->psstate == PS_STATE_PRE_SLEEP && !priv->dnld_sent && !priv->cur_cmd) { if (priv->connect_status == LBS_CONNECTED) { - lbs_deb_thread("main_thread: PRE_SLEEP--intcounter=%d currenttxskb=%p dnld_sent=%d cur_cmd=%p, confirm now\n", - priv->intcounter, priv->currenttxskb, priv->dnld_sent, priv->cur_cmd); + lbs_deb_thread("pre-sleep, currenttxskb %p, " + "dnld_sent %d, cur_cmd %p\n", + priv->currenttxskb, priv->dnld_sent, + priv->cur_cmd); - lbs_ps_confirm_sleep(priv, (u16) priv->psmode); + lbs_ps_confirm_sleep(priv); } else { /* workaround for firmware sending * deauth/linkloss event immediately @@ -816,7 +799,8 @@ static int lbs_thread(void *data) * after firmware fixes it */ priv->psstate = PS_STATE_AWAKE; - lbs_pr_alert("main-thread: ignore PS_SleepConfirm in non-connected state\n"); + lbs_pr_alert("ignore PS_SleepConfirm in " + "non-connected state\n"); } } @@ -836,6 +820,28 @@ static int lbs_thread(void *data) */ if (!list_empty(&priv->cmdpendingq)) wake_up_all(&priv->cmd_pending); + + spin_lock_irq(&priv->driver_lock); + if (!priv->dnld_sent && priv->tx_pending_len > 0) { + int ret = priv->hw_host_to_card(priv, MVMS_DAT, + priv->tx_pending_buf, + priv->tx_pending_len); + if (ret) { + lbs_deb_tx("host_to_card failed %d\n", ret); + priv->dnld_sent = DNLD_RES_RECEIVED; + } + priv->tx_pending_len = 0; + if (!priv->currenttxskb) { + /* We can wake the queues immediately if we aren't + waiting for TX feedback */ + if (priv->connect_status == LBS_CONNECTED) + netif_wake_queue(priv->dev); + if (priv->mesh_dev && + priv->mesh_connect_status == LBS_CONNECTED) + netif_wake_queue(priv->mesh_dev); + } + } + spin_unlock_irq(&priv->driver_lock); } del_timer(&priv->command_timer); @@ -845,6 +851,63 @@ static int lbs_thread(void *data) return 0; } +static int lbs_suspend_callback(struct lbs_private *priv, unsigned long dummy, + struct cmd_header *cmd) +{ + lbs_deb_enter(LBS_DEB_FW); + + netif_device_detach(priv->dev); + if (priv->mesh_dev) + netif_device_detach(priv->mesh_dev); + + priv->fw_ready = 0; + lbs_deb_leave(LBS_DEB_FW); + return 0; +} + +int lbs_suspend(struct lbs_private *priv) +{ + struct cmd_header cmd; + int ret; + + lbs_deb_enter(LBS_DEB_FW); + + if (priv->wol_criteria == 0xffffffff) { + lbs_pr_info("Suspend attempt without configuring wake params!\n"); + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + + ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_ACTIVATE, &cmd, + sizeof(cmd), lbs_suspend_callback, 0); + if (ret) + lbs_pr_info("HOST_SLEEP_ACTIVATE failed: %d\n", ret); + + lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(lbs_suspend); + +void lbs_resume(struct lbs_private *priv) +{ + lbs_deb_enter(LBS_DEB_FW); + + priv->fw_ready = 1; + + /* Firmware doesn't seem to give us RX packets any more + until we send it some command. Might as well update */ + lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0, + 0, 0, NULL); + + netif_device_attach(priv->dev); + if (priv->mesh_dev) + netif_device_attach(priv->mesh_dev); + + lbs_deb_leave(LBS_DEB_FW); +} +EXPORT_SYMBOL_GPL(lbs_resume); + /** * @brief This function downloads firmware image, gets * HW spec from firmware and set basic parameters to @@ -856,7 +919,6 @@ static int lbs_thread(void *data) static int lbs_setup_firmware(struct lbs_private *priv) { int ret = -1; - struct cmd_ds_mesh_access mesh_access; lbs_deb_enter(LBS_DEB_FW); @@ -864,42 +926,20 @@ static int lbs_setup_firmware(struct lbs_private *priv) * Read MAC address from HW */ memset(priv->current_addr, 0xff, ETH_ALEN); - - ret = lbs_prepare_and_send_command(priv, CMD_GET_HW_SPEC, - 0, CMD_OPTION_WAITFORRSP, 0, NULL); - + ret = lbs_update_hw_spec(priv); if (ret) { ret = -1; goto done; } - lbs_set_mac_packet_filter(priv); + lbs_set_mac_control(priv); - /* Get the supported Data rates */ - ret = lbs_prepare_and_send_command(priv, CMD_802_11_DATA_RATE, - CMD_ACT_GET_TX_RATE, - CMD_OPTION_WAITFORRSP, 0, NULL); - - if (ret) { + ret = lbs_get_data_rate(priv); + if (ret < 0) { ret = -1; goto done; } - /* Disable mesh autostart */ - if (priv->mesh_dev) { - memset(&mesh_access, 0, sizeof(mesh_access)); - mesh_access.data[0] = cpu_to_le32(0); - ret = lbs_prepare_and_send_command(priv, - CMD_MESH_ACCESS, - CMD_ACT_MESH_SET_AUTOSTART_ENABLED, - CMD_OPTION_WAITFORRSP, 0, (void *)&mesh_access); - if (ret) { - ret = -1; - goto done; - } - priv->mesh_autostart_enabled = 0; - } - ret = 0; done: lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); @@ -913,44 +953,44 @@ done: static void command_timer_fn(unsigned long data) { struct lbs_private *priv = (struct lbs_private *)data; - struct cmd_ctrl_node *ptempnode; - struct cmd_ds_command *cmd; unsigned long flags; - ptempnode = priv->cur_cmd; - if (ptempnode == NULL) { - lbs_deb_fw("ptempnode empty\n"); - return; - } + lbs_deb_enter(LBS_DEB_CMD); + spin_lock_irqsave(&priv->driver_lock, flags); - cmd = (struct cmd_ds_command *)ptempnode->bufvirtualaddr; - if (!cmd) { - lbs_deb_fw("cmd is NULL\n"); - return; + if (!priv->cur_cmd) { + lbs_pr_info("Command timer expired; no pending command\n"); + goto out; } - lbs_deb_fw("command_timer_fn fired, cmd %x\n", cmd->command); - - if (!priv->fw_ready) - return; + lbs_pr_info("Command %x timed out\n", le16_to_cpu(priv->cur_cmd->cmdbuf->command)); - spin_lock_irqsave(&priv->driver_lock, flags); - priv->cur_cmd = NULL; + priv->cmd_timed_out = 1; + wake_up_interruptible(&priv->waitq); +out: spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_CMD); +} - lbs_deb_fw("re-sending same command because of timeout\n"); - lbs_queue_cmd(priv, ptempnode, 0); - - wake_up_interruptible(&priv->waitq); +static void lbs_sync_channel_worker(struct work_struct *work) +{ + struct lbs_private *priv = container_of(work, struct lbs_private, + sync_channel); - return; + lbs_deb_enter(LBS_DEB_MAIN); + if (lbs_update_channel(priv)) + lbs_pr_info("Channel synchronization failed."); + lbs_deb_leave(LBS_DEB_MAIN); } + static int lbs_init_adapter(struct lbs_private *priv) { size_t bufsize; int i, ret = 0; + lbs_deb_enter(LBS_DEB_MAIN); + /* Allocate buffer to store the BSSID list */ bufsize = MAX_NETWORK_COUNT * sizeof(struct bss_descriptor); priv->networks = kzalloc(bufsize, GFP_KERNEL); @@ -968,14 +1008,6 @@ static int lbs_init_adapter(struct lbs_private *priv) &priv->network_free_list); } - priv->lbs_ps_confirm_sleep.seqnum = cpu_to_le16(++priv->seqnum); - priv->lbs_ps_confirm_sleep.command = - cpu_to_le16(CMD_802_11_PS_MODE); - priv->lbs_ps_confirm_sleep.size = - cpu_to_le16(sizeof(struct PS_CMD_ConfirmSleep)); - priv->lbs_ps_confirm_sleep.action = - cpu_to_le16(CMD_SUBCMD_SLEEP_CONFIRMED); - memset(priv->current_addr, 0xff, ETH_ALEN); priv->connect_status = LBS_DISCONNECTED; @@ -983,7 +1015,7 @@ static int lbs_init_adapter(struct lbs_private *priv) priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; priv->mode = IW_MODE_INFRA; priv->curbssparams.channel = DEFAULT_AD_HOC_CHANNEL; - priv->currentpacketfilter = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; + priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; priv->radioon = RADIO_ON; priv->auto_rate = 1; priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE; @@ -993,7 +1025,7 @@ static int lbs_init_adapter(struct lbs_private *priv) mutex_init(&priv->lock); setup_timer(&priv->command_timer, command_timer_fn, - (unsigned long)priv); + (unsigned long)priv); INIT_LIST_HEAD(&priv->cmdfreeq); INIT_LIST_HEAD(&priv->cmdpendingq); @@ -1004,24 +1036,38 @@ static int lbs_init_adapter(struct lbs_private *priv) /* Allocate the command buffers */ if (lbs_allocate_cmd_buffer(priv)) { lbs_pr_err("Out of memory allocating command buffers\n"); - ret = -1; + ret = -ENOMEM; + goto out; + } + priv->resp_idx = 0; + priv->resp_len[0] = priv->resp_len[1] = 0; + + /* Create the event FIFO */ + priv->event_fifo = kfifo_alloc(sizeof(u32) * 16, GFP_KERNEL, NULL); + if (IS_ERR(priv->event_fifo)) { + lbs_pr_err("Out of memory allocating event FIFO buffer\n"); + ret = -ENOMEM; + goto out; } out: + lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); + return ret; } static void lbs_free_adapter(struct lbs_private *priv) { - lbs_deb_fw("free command buffer\n"); - lbs_free_cmd_buffer(priv); + lbs_deb_enter(LBS_DEB_MAIN); - lbs_deb_fw("free command_timer\n"); + lbs_free_cmd_buffer(priv); + if (priv->event_fifo) + kfifo_free(priv->event_fifo); del_timer(&priv->command_timer); - - lbs_deb_fw("free scan results table\n"); kfree(priv->networks); priv->networks = NULL; + + lbs_deb_leave(LBS_DEB_MAIN); } /** @@ -1036,7 +1082,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) struct net_device *dev = NULL; struct lbs_private *priv = NULL; - lbs_deb_enter(LBS_DEB_NET); + lbs_deb_enter(LBS_DEB_MAIN); /* Allocate an Ethernet device and register it */ dev = alloc_etherdev(sizeof(struct lbs_private)); @@ -1057,9 +1103,9 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) priv->infra_open = 0; /* Setup the OS Interface to our functions */ - dev->open = lbs_open; + dev->open = lbs_dev_open; dev->hard_start_xmit = lbs_hard_start_xmit; - dev->stop = lbs_close; + dev->stop = lbs_eth_stop; dev->set_mac_address = lbs_set_mac_address; dev->tx_timeout = lbs_tx_timeout; dev->get_stats = lbs_get_stats; @@ -1086,7 +1132,13 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) priv->work_thread = create_singlethread_workqueue("lbs_worker"); INIT_DELAYED_WORK(&priv->assoc_work, lbs_association_worker); INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker); - INIT_WORK(&priv->sync_channel, lbs_sync_channel); + INIT_WORK(&priv->sync_channel, lbs_sync_channel_worker); + + sprintf(priv->mesh_ssid, "mesh"); + priv->mesh_ssid_len = 4; + + priv->wol_criteria = 0xffffffff; + priv->wol_gpio = 0xff; goto done; @@ -1096,26 +1148,26 @@ err_init_adapter: priv = NULL; done: - lbs_deb_leave_args(LBS_DEB_NET, "priv %p", priv); + lbs_deb_leave_args(LBS_DEB_MAIN, "priv %p", priv); return priv; } EXPORT_SYMBOL_GPL(lbs_add_card); -int lbs_remove_card(struct lbs_private *priv) +void lbs_remove_card(struct lbs_private *priv) { struct net_device *dev = priv->dev; union iwreq_data wrqu; lbs_deb_enter(LBS_DEB_MAIN); + lbs_remove_mesh(priv); lbs_remove_rtap(priv); dev = priv->dev; - device_remove_file(&dev->dev, &dev_attr_lbs_rtap); - cancel_delayed_work(&priv->scan_work); - cancel_delayed_work(&priv->assoc_work); + cancel_delayed_work_sync(&priv->scan_work); + cancel_delayed_work_sync(&priv->assoc_work); destroy_workqueue(priv->work_thread); if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { @@ -1137,7 +1189,6 @@ int lbs_remove_card(struct lbs_private *priv) free_netdev(dev); lbs_deb_leave(LBS_DEB_MAIN); - return 0; } EXPORT_SYMBOL_GPL(lbs_remove_card); @@ -1164,6 +1215,37 @@ int lbs_start_card(struct lbs_private *priv) if (device_create_file(&dev->dev, &dev_attr_lbs_rtap)) lbs_pr_err("cannot register lbs_rtap attribute\n"); + lbs_update_channel(priv); + + /* 5.0.16p0 is known to NOT support any mesh */ + if (priv->fwrelease > 0x05001000) { + /* Enable mesh, if supported, and work out which TLV it uses. + 0x100 + 291 is an unofficial value used in 5.110.20.pXX + 0x100 + 37 is the official value used in 5.110.21.pXX + but we check them in that order because 20.pXX doesn't + give an error -- it just silently fails. */ + + /* 5.110.20.pXX firmware will fail the command if the channel + doesn't match the existing channel. But only if the TLV + is correct. If the channel is wrong, _BOTH_ versions will + give an error to 0x100+291, and allow 0x100+37 to succeed. + It's just that 5.110.20.pXX will not have done anything + useful */ + + priv->mesh_tlv = 0x100 + 291; + if (lbs_mesh_config(priv, 1, priv->curbssparams.channel)) { + priv->mesh_tlv = 0x100 + 37; + if (lbs_mesh_config(priv, 1, priv->curbssparams.channel)) + priv->mesh_tlv = 0; + } + if (priv->mesh_tlv) { + lbs_add_mesh(priv); + + if (device_create_file(&dev->dev, &dev_attr_lbs_mesh)) + lbs_pr_err("cannot register lbs_mesh attribute\n"); + } + } + lbs_debugfs_init_one(priv, dev); lbs_pr_info("%s: Marvell WLAN 802.11 adapter\n", dev->name); @@ -1177,23 +1259,30 @@ done: EXPORT_SYMBOL_GPL(lbs_start_card); -int lbs_stop_card(struct lbs_private *priv) +void lbs_stop_card(struct lbs_private *priv) { struct net_device *dev = priv->dev; - int ret = -1; struct cmd_ctrl_node *cmdnode; unsigned long flags; lbs_deb_enter(LBS_DEB_MAIN); + if (!priv) + goto out; + netif_stop_queue(priv->dev); netif_carrier_off(priv->dev); lbs_debugfs_remove_one(priv); + device_remove_file(&dev->dev, &dev_attr_lbs_rtap); + if (priv->mesh_tlv) + device_remove_file(&dev->dev, &dev_attr_lbs_mesh); /* Flush pending command nodes */ + del_timer_sync(&priv->command_timer); spin_lock_irqsave(&priv->driver_lock, flags); list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { + cmdnode->result = -ENOENT; cmdnode->cmdwaitqwoken = 1; wake_up_interruptible(&cmdnode->cmdwait_q); } @@ -1201,8 +1290,8 @@ int lbs_stop_card(struct lbs_private *priv) unregister_netdev(dev); - lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); - return ret; +out: + lbs_deb_leave(LBS_DEB_MAIN); } EXPORT_SYMBOL_GPL(lbs_stop_card); @@ -1213,7 +1302,7 @@ EXPORT_SYMBOL_GPL(lbs_stop_card); * @param priv A pointer to the struct lbs_private structure * @return 0 if successful, -X otherwise */ -int lbs_add_mesh(struct lbs_private *priv, struct device *dev) +static int lbs_add_mesh(struct lbs_private *priv) { struct net_device *mesh_dev = NULL; int ret = 0; @@ -1229,16 +1318,16 @@ int lbs_add_mesh(struct lbs_private *priv, struct device *dev) mesh_dev->priv = priv; priv->mesh_dev = mesh_dev; - mesh_dev->open = lbs_mesh_open; + mesh_dev->open = lbs_dev_open; mesh_dev->hard_start_xmit = lbs_hard_start_xmit; - mesh_dev->stop = lbs_mesh_close; + mesh_dev->stop = lbs_mesh_stop; mesh_dev->get_stats = lbs_get_stats; mesh_dev->set_mac_address = lbs_set_mac_address; mesh_dev->ethtool_ops = &lbs_ethtool_ops; memcpy(mesh_dev->dev_addr, priv->dev->dev_addr, sizeof(priv->dev->dev_addr)); - SET_NETDEV_DEV(priv->mesh_dev, dev); + SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent); #ifdef WIRELESS_EXT mesh_dev->wireless_handlers = (struct iw_handler_def *)&mesh_handler_def; @@ -1268,33 +1357,25 @@ done: lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); return ret; } -EXPORT_SYMBOL_GPL(lbs_add_mesh); - -void lbs_remove_mesh(struct lbs_private *priv) +static void lbs_remove_mesh(struct lbs_private *priv) { struct net_device *mesh_dev; - lbs_deb_enter(LBS_DEB_MAIN); - - if (!priv) - goto out; mesh_dev = priv->mesh_dev; + if (!mesh_dev) + return; + lbs_deb_enter(LBS_DEB_MESH); netif_stop_queue(mesh_dev); netif_carrier_off(priv->mesh_dev); - sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); unregister_netdev(mesh_dev); - - priv->mesh_dev = NULL ; + priv->mesh_dev = NULL; free_netdev(mesh_dev); - -out: - lbs_deb_leave(LBS_DEB_MAIN); + lbs_deb_leave(LBS_DEB_MESH); } -EXPORT_SYMBOL_GPL(lbs_remove_mesh); /** * @brief This function finds the CFP in @@ -1305,7 +1386,7 @@ EXPORT_SYMBOL_GPL(lbs_remove_mesh); * @param cfp_no A pointer to CFP number * @return A pointer to CFP */ -struct chan_freq_power *lbs_get_region_cfp_table(u8 region, u8 band, int *cfp_no) +struct chan_freq_power *lbs_get_region_cfp_table(u8 region, int *cfp_no) { int i, end; @@ -1339,76 +1420,68 @@ int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band) memset(priv->region_channel, 0, sizeof(priv->region_channel)); - { - cfp = lbs_get_region_cfp_table(region, band, &cfp_no); - if (cfp != NULL) { - priv->region_channel[i].nrcfp = cfp_no; - priv->region_channel[i].CFP = cfp; - } else { - lbs_deb_main("wrong region code %#x in band B/G\n", - region); - ret = -1; - goto out; - } - priv->region_channel[i].valid = 1; - priv->region_channel[i].region = region; - priv->region_channel[i].band = band; - i++; + cfp = lbs_get_region_cfp_table(region, &cfp_no); + if (cfp != NULL) { + priv->region_channel[i].nrcfp = cfp_no; + priv->region_channel[i].CFP = cfp; + } else { + lbs_deb_main("wrong region code %#x in band B/G\n", + region); + ret = -1; + goto out; } + priv->region_channel[i].valid = 1; + priv->region_channel[i].region = region; + priv->region_channel[i].band = band; + i++; out: lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); return ret; } -/** - * @brief This function handles the interrupt. it will change PS - * state if applicable. it will wake up main_thread to handle - * the interrupt event as well. - * - * @param dev A pointer to net_device structure - * @return n/a - */ -void lbs_interrupt(struct net_device *dev) +void lbs_queue_event(struct lbs_private *priv, u32 event) { - struct lbs_private *priv = dev->priv; + unsigned long flags; lbs_deb_enter(LBS_DEB_THREAD); + spin_lock_irqsave(&priv->driver_lock, flags); - lbs_deb_thread("lbs_interrupt: intcounter=%d\n", - priv->intcounter); - - priv->intcounter++; - - if (priv->psstate == PS_STATE_SLEEP) { + if (priv->psstate == PS_STATE_SLEEP) priv->psstate = PS_STATE_AWAKE; - netif_wake_queue(dev); - if (priv->mesh_dev) - netif_wake_queue(priv->mesh_dev); - } + + __kfifo_put(priv->event_fifo, (unsigned char *) &event, sizeof(u32)); wake_up_interruptible(&priv->waitq); + spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_leave(LBS_DEB_THREAD); } -EXPORT_SYMBOL_GPL(lbs_interrupt); +EXPORT_SYMBOL_GPL(lbs_queue_event); -int lbs_reset_device(struct lbs_private *priv) +void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx) { - int ret; + lbs_deb_enter(LBS_DEB_THREAD); - lbs_deb_enter(LBS_DEB_MAIN); - ret = lbs_prepare_and_send_command(priv, CMD_802_11_RESET, - CMD_ACT_HALT, 0, 0, NULL); - msleep_interruptible(10); + if (priv->psstate == PS_STATE_SLEEP) + priv->psstate = PS_STATE_AWAKE; - lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); - return ret; + /* Swap buffers by flipping the response index */ + BUG_ON(resp_idx > 1); + priv->resp_idx = resp_idx; + + wake_up_interruptible(&priv->waitq); + + lbs_deb_leave(LBS_DEB_THREAD); } -EXPORT_SYMBOL_GPL(lbs_reset_device); +EXPORT_SYMBOL_GPL(lbs_notify_command_response); static int __init lbs_init_module(void) { lbs_deb_enter(LBS_DEB_MAIN); + memset(&confirm_sleep, 0, sizeof(confirm_sleep)); + confirm_sleep.hdr.command = cpu_to_le16(CMD_802_11_PS_MODE); + confirm_sleep.hdr.size = cpu_to_le16(sizeof(confirm_sleep)); + confirm_sleep.action = cpu_to_le16(CMD_SUBCMD_SLEEP_CONFIRMED); lbs_debugfs_init(); lbs_deb_leave(LBS_DEB_MAIN); return 0; @@ -1417,9 +1490,7 @@ static int __init lbs_init_module(void) static void __exit lbs_exit_module(void) { lbs_deb_enter(LBS_DEB_MAIN); - lbs_debugfs_remove(); - lbs_deb_leave(LBS_DEB_MAIN); } @@ -1429,50 +1500,65 @@ static void __exit lbs_exit_module(void) static int lbs_rtap_open(struct net_device *dev) { - netif_carrier_off(dev); - netif_stop_queue(dev); - return 0; + /* Yes, _stop_ the queue. Because we don't support injection */ + lbs_deb_enter(LBS_DEB_MAIN); + netif_carrier_off(dev); + netif_stop_queue(dev); + lbs_deb_leave(LBS_DEB_LEAVE); + return 0; } static int lbs_rtap_stop(struct net_device *dev) { - return 0; + lbs_deb_enter(LBS_DEB_MAIN); + lbs_deb_leave(LBS_DEB_MAIN); + return 0; } static int lbs_rtap_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { - netif_stop_queue(dev); - return -EOPNOTSUPP; + netif_stop_queue(dev); + return NETDEV_TX_BUSY; } static struct net_device_stats *lbs_rtap_get_stats(struct net_device *dev) { struct lbs_private *priv = dev->priv; + lbs_deb_enter(LBS_DEB_NET); return &priv->stats; } -void lbs_remove_rtap(struct lbs_private *priv) +static void lbs_remove_rtap(struct lbs_private *priv) { + lbs_deb_enter(LBS_DEB_MAIN); if (priv->rtap_net_dev == NULL) - return; + goto out; unregister_netdev(priv->rtap_net_dev); free_netdev(priv->rtap_net_dev); priv->rtap_net_dev = NULL; +out: + lbs_deb_leave(LBS_DEB_MAIN); } -int lbs_add_rtap(struct lbs_private *priv) +static int lbs_add_rtap(struct lbs_private *priv) { - int rc = 0; + int ret = 0; struct net_device *rtap_dev; - if (priv->rtap_net_dev) - return -EPERM; + lbs_deb_enter(LBS_DEB_MAIN); + if (priv->rtap_net_dev) { + ret = -EPERM; + goto out; + } rtap_dev = alloc_netdev(0, "rtap%d", ether_setup); - if (rtap_dev == NULL) - return -ENOMEM; + if (rtap_dev == NULL) { + ret = -ENOMEM; + goto out; + } + memcpy(rtap_dev->dev_addr, priv->current_addr, ETH_ALEN); rtap_dev->type = ARPHRD_IEEE80211_RADIOTAP; rtap_dev->open = lbs_rtap_open; rtap_dev->stop = lbs_rtap_stop; @@ -1481,16 +1567,44 @@ int lbs_add_rtap(struct lbs_private *priv) rtap_dev->set_multicast_list = lbs_set_multicast_list; rtap_dev->priv = priv; - rc = register_netdev(rtap_dev); - if (rc) { + ret = register_netdev(rtap_dev); + if (ret) { free_netdev(rtap_dev); - return rc; + goto out; } priv->rtap_net_dev = rtap_dev; - return 0; +out: + lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); + return ret; } +#ifndef CONFIG_IEEE80211 +const char *escape_essid(const char *essid, u8 essid_len) +{ + static char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; + const char *s = essid; + char *d = escaped; + + if (ieee80211_is_empty_essid(essid, essid_len)) { + memcpy(escaped, "", sizeof("")); + return escaped; + } + + essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE); + while (essid_len--) { + if (*s == '\0') { + *d++ = '\\'; + *d++ = '0'; + s++; + } else { + *d++ = *s++; + } + } + *d = '\0'; + return escaped; +} +#endif module_init(lbs_init_module); module_exit(lbs_exit_module);