rtlwifi: fix memory leak for USB device
[pandora-kernel.git] / drivers / net / wireless / rtlwifi / usb.c
index 54cb8a6..968137a 100644 (file)
 
 static void usbctrl_async_callback(struct urb *urb)
 {
-       if (urb)
-               kfree(urb->context);
+       if (urb) {
+               /* free dr */
+               kfree(urb->setup_packet);
+               /* free databuf */
+               kfree(urb->transfer_buffer);
+       }
 }
 
 static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request,
@@ -57,38 +61,46 @@ static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request,
        u8 reqtype;
        struct usb_ctrlrequest *dr;
        struct urb *urb;
-       struct rtl819x_async_write_data {
-               u8 data[REALTEK_USB_VENQT_MAX_BUF_SIZE];
-               struct usb_ctrlrequest dr;
-       } *buf;
+       const u16 databuf_maxlen = REALTEK_USB_VENQT_MAX_BUF_SIZE;
+       u8 *databuf;
+
+       if (WARN_ON_ONCE(len > databuf_maxlen))
+               len = databuf_maxlen;
 
        pipe = usb_sndctrlpipe(udev, 0); /* write_out */
        reqtype =  REALTEK_USB_VENQT_WRITE;
 
-       buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
-       if (!buf)
+       dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
+       if (!dr)
+               return -ENOMEM;
+
+       databuf = kmalloc(databuf_maxlen, GFP_ATOMIC);
+       if (!databuf) {
+               kfree(dr);
                return -ENOMEM;
+       }
 
        urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!urb) {
-               kfree(buf);
+               kfree(databuf);
+               kfree(dr);
                return -ENOMEM;
        }
 
-       dr = &buf->dr;
-
        dr->bRequestType = reqtype;
        dr->bRequest = request;
        dr->wValue = cpu_to_le16(value);
        dr->wIndex = cpu_to_le16(index);
        dr->wLength = cpu_to_le16(len);
-       memcpy(buf, pdata, len);
+       memcpy(databuf, pdata, len);
        usb_fill_control_urb(urb, udev, pipe,
-                            (unsigned char *)dr, buf, len,
-                            usbctrl_async_callback, buf);
+                            (unsigned char *)dr, databuf, len,
+                            usbctrl_async_callback, NULL);
        rc = usb_submit_urb(urb, GFP_ATOMIC);
-       if (rc < 0)
-               kfree(buf);
+       if (rc < 0) {
+               kfree(databuf);
+               kfree(dr);
+       }
        usb_free_urb(urb);
        return rc;
 }
@@ -105,7 +117,7 @@ static int _usbctrl_vendorreq_sync_read(struct usb_device *udev, u8 request,
        reqtype =  REALTEK_USB_VENQT_READ;
 
        status = usb_control_msg(udev, pipe, request, reqtype, value, index,
-                                pdata, len, 0); /* max. timeout */
+                                pdata, len, 1000);
 
        if (status < 0)
                pr_err("reg 0x%x, usbctrl_vendorreq TimeOut! status:0x%x value=0x%x\n",
@@ -113,46 +125,42 @@ static int _usbctrl_vendorreq_sync_read(struct usb_device *udev, u8 request,
        return status;
 }
 
-static u32 _usb_read_sync(struct usb_device *udev, u32 addr, u16 len)
+static u32 _usb_read_sync(struct rtl_priv *rtlpriv, u32 addr, u16 len)
 {
+       struct device *dev = rtlpriv->io.dev;
+       struct usb_device *udev = to_usb_device(dev);
        u8 request;
        u16 wvalue;
        u16 index;
-       u32 *data;
-       u32 ret;
-
-       data = kmalloc(sizeof(u32), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
+       __le32 *data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&rtlpriv->locks.usb_lock, flags);
+       if (++rtlpriv->usb_data_index >= RTL_USB_MAX_RX_COUNT)
+               rtlpriv->usb_data_index = 0;
+       data = &rtlpriv->usb_data[rtlpriv->usb_data_index];
+       spin_unlock_irqrestore(&rtlpriv->locks.usb_lock, flags);
        request = REALTEK_USB_VENQT_CMD_REQ;
        index = REALTEK_USB_VENQT_CMD_IDX; /* n/a */
 
        wvalue = (u16)addr;
        _usbctrl_vendorreq_sync_read(udev, request, wvalue, index, data, len);
-       ret = *data;
-       kfree(data);
-       return ret;
+       return le32_to_cpu(*data);
 }
 
 static u8 _usb_read8_sync(struct rtl_priv *rtlpriv, u32 addr)
 {
-       struct device *dev = rtlpriv->io.dev;
-
-       return (u8)_usb_read_sync(to_usb_device(dev), addr, 1);
+       return (u8)_usb_read_sync(rtlpriv, addr, 1);
 }
 
 static u16 _usb_read16_sync(struct rtl_priv *rtlpriv, u32 addr)
 {
-       struct device *dev = rtlpriv->io.dev;
-
-       return (u16)_usb_read_sync(to_usb_device(dev), addr, 2);
+       return (u16)_usb_read_sync(rtlpriv, addr, 2);
 }
 
 static u32 _usb_read32_sync(struct rtl_priv *rtlpriv, u32 addr)
 {
-       struct device *dev = rtlpriv->io.dev;
-
-       return _usb_read_sync(to_usb_device(dev), addr, 4);
+       return _usb_read_sync(rtlpriv, addr, 4);
 }
 
 static void _usb_write_async(struct usb_device *udev, u32 addr, u32 val,
@@ -481,15 +489,19 @@ static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw,
                        u8 *pdata;
 
                        uskb = dev_alloc_skb(skb->len + 128);
-                       memcpy(IEEE80211_SKB_RXCB(uskb), &rx_status,
-                              sizeof(rx_status));
-                       pdata = (u8 *)skb_put(uskb, skb->len);
-                       memcpy(pdata, skb->data, skb->len);
+                       if (uskb) {     /* drop packet on allocation failure */
+                               memcpy(IEEE80211_SKB_RXCB(uskb), &rx_status,
+                                      sizeof(rx_status));
+                               pdata = (u8 *)skb_put(uskb, skb->len);
+                               memcpy(pdata, skb->data, skb->len);
+                               ieee80211_rx_irqsafe(hw, uskb);
+                       }
                        dev_kfree_skb_any(skb);
-                       ieee80211_rx_irqsafe(hw, uskb);
                } else {
                        dev_kfree_skb_any(skb);
                }
+       } else {
+               dev_kfree_skb_any(skb);
        }
 }
 
@@ -505,8 +517,8 @@ static void _rtl_rx_pre_process(struct ieee80211_hw *hw, struct sk_buff *skb)
        WARN_ON(skb_queue_empty(&rx_queue));
        while (!skb_queue_empty(&rx_queue)) {
                _skb = skb_dequeue(&rx_queue);
-               _rtl_usb_rx_process_agg(hw, skb);
-               ieee80211_rx_irqsafe(hw, skb);
+               _rtl_usb_rx_process_agg(hw, _skb);
+               ieee80211_rx_irqsafe(hw, _skb);
        }
 }
 
@@ -802,6 +814,7 @@ static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb,
        if (unlikely(!_urb)) {
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
                         ("Can't allocate urb. Drop skb!\n"));
+               kfree_skb(skb);
                return;
        }
        urb_list = &rtlusb->tx_pending[ep_num];
@@ -896,7 +909,8 @@ static struct rtl_intf_ops rtl_usb_ops = {
 };
 
 int __devinit rtl_usb_probe(struct usb_interface *intf,
-                       const struct usb_device_id *id)
+                           const struct usb_device_id *id,
+                           struct rtl_hal_cfg *rtl_hal_cfg)
 {
        int err;
        struct ieee80211_hw *hw = NULL;
@@ -911,6 +925,15 @@ int __devinit rtl_usb_probe(struct usb_interface *intf,
                return -ENOMEM;
        }
        rtlpriv = hw->priv;
+       rtlpriv->usb_data = kzalloc(RTL_USB_MAX_RX_COUNT * sizeof(u32),
+                                   GFP_KERNEL);
+       if (!rtlpriv->usb_data)
+               return -ENOMEM;
+
+       /* this spin lock must be initialized early */
+       spin_lock_init(&rtlpriv->locks.usb_lock);
+
+       rtlpriv->usb_data_index = 0;
        SET_IEEE80211_DEV(hw, &intf->dev);
        udev = interface_to_usbdev(intf);
        usb_get_dev(udev);
@@ -921,7 +944,7 @@ int __devinit rtl_usb_probe(struct usb_interface *intf,
        usb_set_intfdata(intf, hw);
        /* init cfg & intf_ops */
        rtlpriv->rtlhal.interface = INTF_USB;
-       rtlpriv->cfg = (struct rtl_hal_cfg *)(id->driver_info);
+       rtlpriv->cfg = rtl_hal_cfg;
        rtlpriv->intf_ops = &rtl_usb_ops;
        rtl_dbgp_flag_init(hw);
        /* Init IO handler */
@@ -988,6 +1011,7 @@ void rtl_usb_disconnect(struct usb_interface *intf)
        /* rtl_deinit_rfkill(hw); */
        rtl_usb_deinit(hw);
        rtl_deinit_core(hw);
+       kfree(rtlpriv->usb_data);
        rtlpriv->cfg->ops->deinit_sw_leds(hw);
        rtlpriv->cfg->ops->deinit_sw_vars(hw);
        _rtl_usb_io_handler_release(hw);