]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/wireless/ipw2200.c
ipw2200: prevent alloc of unspecified size on stack
[linux-2.6-omap-h63xx.git] / drivers / net / wireless / ipw2200.c
index 2119a79dcc866867c55bc13f041e6676a8eb1079..38ce8ee8d6f95818354ff63722dab13953e6c7ec 100644 (file)
@@ -1233,9 +1233,19 @@ static ssize_t show_event_log(struct device *d,
 {
        struct ipw_priv *priv = dev_get_drvdata(d);
        u32 log_len = ipw_get_event_log_len(priv);
-       struct ipw_event log[log_len];
+       u32 log_size;
+       struct ipw_event *log;
        u32 len = 0, i;
 
+       /* not using min() because of its strict type checking */
+       log_size = PAGE_SIZE / sizeof(*log) > log_len ?
+                       sizeof(*log) * log_len : PAGE_SIZE;
+       log = kzalloc(log_size, GFP_KERNEL);
+       if (!log) {
+               IPW_ERROR("Unable to allocate memory for log\n");
+               return 0;
+       }
+       log_len = log_size / sizeof(*log);
        ipw_capture_event_log(priv, log_len, log);
 
        len += snprintf(buf + len, PAGE_SIZE - len, "%08X", log_len);
@@ -1244,6 +1254,7 @@ static ssize_t show_event_log(struct device *d,
                                "\n%08X%08X%08X",
                                log[i].time, log[i].event, log[i].data);
        len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+       kfree(log);
        return len;
 }
 
@@ -1740,8 +1751,10 @@ static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
        if (disable_radio) {
                priv->status |= STATUS_RF_KILL_SW;
 
-               if (priv->workqueue)
+               if (priv->workqueue) {
                        cancel_delayed_work(&priv->request_scan);
+                       cancel_delayed_work(&priv->scan_event);
+               }
                queue_work(priv->workqueue, &priv->down);
        } else {
                priv->status &= ~STATUS_RF_KILL_SW;
@@ -1751,7 +1764,7 @@ static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
                        /* Make sure the RF_KILL check timer is running */
                        cancel_delayed_work(&priv->rf_kill);
                        queue_delayed_work(priv->workqueue, &priv->rf_kill,
-                                          round_jiffies(2 * HZ));
+                                          round_jiffies_relative(2 * HZ));
                } else
                        queue_work(priv->workqueue, &priv->up);
        }
@@ -1992,6 +2005,7 @@ static void ipw_irq_tasklet(struct ipw_priv *priv)
                wake_up_interruptible(&priv->wait_command_queue);
                priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
                cancel_delayed_work(&priv->request_scan);
+               cancel_delayed_work(&priv->scan_event);
                schedule_work(&priv->link_down);
                queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
                handled |= IPW_INTA_BIT_RF_KILL_DONE;
@@ -4343,6 +4357,37 @@ static void ipw_handle_missed_beacon(struct ipw_priv *priv,
        IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count);
 }
 
+static void ipw_scan_event(struct work_struct *work)
+{
+       union iwreq_data wrqu;
+
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, scan_event.work);
+
+       wrqu.data.length = 0;
+       wrqu.data.flags = 0;
+       wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
+}
+
+static void handle_scan_event(struct ipw_priv *priv)
+{
+       /* Only userspace-requested scan completion events go out immediately */
+       if (!priv->user_requested_scan) {
+               if (!delayed_work_pending(&priv->scan_event))
+                       queue_delayed_work(priv->workqueue, &priv->scan_event,
+                                        round_jiffies_relative(msecs_to_jiffies(4000)));
+       } else {
+               union iwreq_data wrqu;
+
+               priv->user_requested_scan = 0;
+               cancel_delayed_work(&priv->scan_event);
+
+               wrqu.data.length = 0;
+               wrqu.data.flags = 0;
+               wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
+       }
+}
+
 /**
  * Handle host notification packet.
  * Called from interrupt routine
@@ -4694,7 +4739,7 @@ static void ipw_rx_notification(struct ipw_priv *priv,
                                 && priv->status & STATUS_ASSOCIATED)
                                queue_delayed_work(priv->workqueue,
                                                   &priv->request_scan,
-                                                  round_jiffies(HZ));
+                                                  round_jiffies_relative(HZ));
 
                        /* Send an empty event to user space.
                         * We don't send the received data on the event because
@@ -4705,14 +4750,8 @@ static void ipw_rx_notification(struct ipw_priv *priv,
                         * on how the scan was initiated. User space can just
                         * sync on periodic scan to get fresh data...
                         * Jean II */
-                       if (x->status == SCAN_COMPLETED_STATUS_COMPLETE) {
-                               union iwreq_data wrqu;
-
-                               wrqu.data.length = 0;
-                               wrqu.data.flags = 0;
-                               wireless_send_event(priv->net_dev, SIOCGIWSCAN,
-                                                   &wrqu, NULL);
-                       }
+                       if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
+                               handle_scan_event(priv);
                        break;
                }
 
@@ -9490,6 +9529,10 @@ static int ipw_wx_set_scan(struct net_device *dev,
        struct ipw_priv *priv = ieee80211_priv(dev);
        struct iw_scan_req *req = (struct iw_scan_req *)extra;
 
+       mutex_lock(&priv->mutex);
+       priv->user_requested_scan = 1;
+       mutex_unlock(&priv->mutex);
+
        if (wrqu->data.length == sizeof(struct iw_scan_req)) {
                if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
                        ipw_request_direct_scan(priv, req->essid,
@@ -9571,7 +9614,7 @@ static int ipw_wx_set_power(struct net_device *dev,
        switch (wrqu->power.flags & IW_POWER_MODE) {
        case IW_POWER_ON:       /* If not specified */
        case IW_POWER_MODE:     /* If set all mask */
-       case IW_POWER_ALL_R:    /* If explicitely state all */
+       case IW_POWER_ALL_R:    /* If explicitly state all */
                break;
        default:                /* Otherwise we don't support it */
                IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
@@ -10668,6 +10711,7 @@ static void ipw_link_up(struct ipw_priv *priv)
        }
 
        cancel_delayed_work(&priv->request_scan);
+       cancel_delayed_work(&priv->scan_event);
        ipw_reset_stats(priv);
        /* Ensure the rate is updated immediately */
        priv->last_rate = ipw_get_current_rate(priv);
@@ -10705,7 +10749,8 @@ static void ipw_link_down(struct ipw_priv *priv)
        if (!(priv->status & STATUS_EXIT_PENDING)) {
                /* Queue up another scan... */
                queue_delayed_work(priv->workqueue, &priv->request_scan, 0);
-       }
+       } else
+               cancel_delayed_work(&priv->scan_event);
 }
 
 static void ipw_bg_link_down(struct work_struct *work)
@@ -10735,6 +10780,7 @@ static int ipw_setup_deferred_work(struct ipw_priv *priv)
        INIT_WORK(&priv->up, ipw_bg_up);
        INIT_WORK(&priv->down, ipw_bg_down);
        INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan);
+       INIT_DELAYED_WORK(&priv->scan_event, ipw_scan_event);
        INIT_WORK(&priv->request_passive_scan, ipw_request_passive_scan);
        INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats);
        INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan);
@@ -11766,6 +11812,7 @@ static void ipw_pci_remove(struct pci_dev *pdev)
        cancel_delayed_work(&priv->adhoc_check);
        cancel_delayed_work(&priv->gather_stats);
        cancel_delayed_work(&priv->request_scan);
+       cancel_delayed_work(&priv->scan_event);
        cancel_delayed_work(&priv->rf_kill);
        cancel_delayed_work(&priv->scan_check);
        destroy_workqueue(priv->workqueue);