diff options
Diffstat (limited to 'drivers/net/wireless/rtlwifi/usb.c')
| -rw-r--r-- | drivers/net/wireless/rtlwifi/usb.c | 320 |
1 files changed, 218 insertions, 102 deletions
diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index f2ecdeb3a90..0398d3ea15b 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -32,6 +32,13 @@ #include "ps.h" #include "rtl8192c/fw_common.h" #include <linux/export.h> +#include <linux/module.h> + +MODULE_AUTHOR("lizhaoming <chaoming_li@realsil.com.cn>"); +MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>"); +MODULE_AUTHOR("Larry Finger <Larry.FInger@lwfinger.net>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("USB basic driver for rtlwifi"); #define REALTEK_USB_VENQT_READ 0xC0 #define REALTEK_USB_VENQT_WRITE 0x40 @@ -42,8 +49,12 @@ 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, @@ -55,39 +66,47 @@ 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); /* data are already in little-endian order */ - 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; } @@ -212,10 +231,9 @@ static void _usb_writeN_sync(struct rtl_priv *rtlpriv, u32 addr, void *data, u8 *buffer; wvalue = (u16)(addr & 0x0000ffff); - buffer = kmalloc(len, GFP_ATOMIC); + buffer = kmemdup(data, len, GFP_ATOMIC); if (!buffer) return; - memcpy(buffer, data, len); usb_control_msg(udev, pipe, request, reqtype, wvalue, index, buffer, len, 50); @@ -297,6 +315,8 @@ static int _rtl_usb_init_tx(struct ieee80211_hw *hw) return 0; } +static void _rtl_rx_work(unsigned long param); + static int _rtl_usb_init_rx(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -313,6 +333,12 @@ static int _rtl_usb_init_rx(struct ieee80211_hw *hw) pr_info("rx_max_size %d, rx_urb_num %d, in_ep %d\n", rtlusb->rx_max_size, rtlusb->rx_urb_num, rtlusb->in_ep); init_usb_anchor(&rtlusb->rx_submitted); + init_usb_anchor(&rtlusb->rx_cleanup_urbs); + + skb_queue_head_init(&rtlusb->rx_queue); + rtlusb->rx_work_tasklet.func = _rtl_rx_work; + rtlusb->rx_work_tasklet.data = (unsigned long)rtlusb; + return 0; } @@ -384,7 +410,7 @@ static void rtl_usb_init_sw(struct ieee80211_hw *hw) mac->current_ampdu_factor = 3; /* QOS */ - rtlusb->acm_method = eAcmWay2_SW; + rtlusb->acm_method = EACMWAY2_SW; /* IRQ */ /* HIMR - turn all on */ @@ -394,40 +420,30 @@ static void rtl_usb_init_sw(struct ieee80211_hw *hw) rtlusb->disableHWSM = true; } -#define __RADIO_TAP_SIZE_RSV 32 - static void _rtl_rx_completed(struct urb *urb); -static struct sk_buff *_rtl_prep_rx_urb(struct ieee80211_hw *hw, - struct rtl_usb *rtlusb, - struct urb *urb, - gfp_t gfp_mask) +static int _rtl_prep_rx_urb(struct ieee80211_hw *hw, struct rtl_usb *rtlusb, + struct urb *urb, gfp_t gfp_mask) { - struct sk_buff *skb; struct rtl_priv *rtlpriv = rtl_priv(hw); + void *buf; - skb = __dev_alloc_skb((rtlusb->rx_max_size + __RADIO_TAP_SIZE_RSV), - gfp_mask); - if (!skb) { + buf = usb_alloc_coherent(rtlusb->udev, rtlusb->rx_max_size, gfp_mask, + &urb->transfer_dma); + if (!buf) { RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, - "Failed to __dev_alloc_skb!!\n"); - return ERR_PTR(-ENOMEM); + "Failed to usb_alloc_coherent!!\n"); + return -ENOMEM; } - /* reserve some space for mac80211's radiotap */ - skb_reserve(skb, __RADIO_TAP_SIZE_RSV); usb_fill_bulk_urb(urb, rtlusb->udev, usb_rcvbulkpipe(rtlusb->udev, rtlusb->in_ep), - skb->data, min(skb_tailroom(skb), - (int)rtlusb->rx_max_size), - _rtl_rx_completed, skb); + buf, rtlusb->rx_max_size, _rtl_rx_completed, rtlusb); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - _rtl_install_trx_info(rtlusb, skb, rtlusb->in_ep); - return skb; + return 0; } -#undef __RADIO_TAP_SIZE_RSV - static void _rtl_usb_rx_process_agg(struct ieee80211_hw *hw, struct sk_buff *skb) { @@ -439,7 +455,6 @@ static void _rtl_usb_rx_process_agg(struct ieee80211_hw *hw, struct ieee80211_rx_status rx_status = {0}; struct rtl_stats stats = { .signal = 0, - .noise = -98, .rate = 0, }; @@ -460,14 +475,14 @@ static void _rtl_usb_rx_process_agg(struct ieee80211_hw *hw, rtlpriv->stats.rxbytesunicast += skb->len; } - rtl_is_special_data(hw, skb, false); - if (ieee80211_is_data(fc)) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX); if (unicast) rtlpriv->link_info.num_rx_inperiod++; } + /* static bcn for roaming */ + rtl_beacon_statistic(hw, skb); } } @@ -482,7 +497,6 @@ static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw, struct ieee80211_rx_status rx_status = {0}; struct rtl_stats stats = { .signal = 0, - .noise = -98, .rate = 0, }; @@ -503,30 +517,20 @@ static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw, rtlpriv->stats.rxbytesunicast += skb->len; } - rtl_is_special_data(hw, skb, false); - if (ieee80211_is_data(fc)) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX); if (unicast) rtlpriv->link_info.num_rx_inperiod++; } - if (likely(rtl_action_proc(hw, skb, false))) { - struct sk_buff *uskb = NULL; - u8 *pdata; - - uskb = dev_alloc_skb(skb->len + 128); - 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); - } else { + + /* static bcn for roaming */ + rtl_beacon_statistic(hw, skb); + + if (likely(rtl_action_proc(hw, skb, false))) + ieee80211_rx(hw, skb); + else dev_kfree_skb_any(skb); - } } } @@ -542,16 +546,75 @@ 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(hw, _skb); + } +} + +#define __RX_SKB_MAX_QUEUED 64 + +static void _rtl_rx_work(unsigned long param) +{ + struct rtl_usb *rtlusb = (struct rtl_usb *)param; + struct ieee80211_hw *hw = usb_get_intfdata(rtlusb->intf); + struct sk_buff *skb; + + while ((skb = skb_dequeue(&rtlusb->rx_queue))) { + if (unlikely(IS_USB_STOP(rtlusb))) { + dev_kfree_skb_any(skb); + continue; + } + + if (likely(!rtlusb->usb_rx_segregate_hdl)) { + _rtl_usb_rx_process_noagg(hw, skb); + } else { + /* TO DO */ + _rtl_rx_pre_process(hw, skb); + pr_err("rx agg not supported\n"); + } + } +} + +static unsigned int _rtl_rx_get_padding(struct ieee80211_hdr *hdr, + unsigned int len) +{ +#if NET_IP_ALIGN != 0 + unsigned int padding = 0; +#endif + + /* make function no-op when possible */ + if (NET_IP_ALIGN == 0 || len < sizeof(*hdr)) + return 0; + +#if NET_IP_ALIGN != 0 + /* alignment calculation as in lbtf_rx() / carl9170_rx_copy_data() */ + /* TODO: deduplicate common code, define helper function instead? */ + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + + padding ^= NET_IP_ALIGN; + + /* Input might be invalid, avoid accessing memory outside + * the buffer. + */ + if ((unsigned long)qc - (unsigned long)hdr < len && + *qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT) + padding ^= NET_IP_ALIGN; } + + if (ieee80211_has_a4(hdr->frame_control)) + padding ^= NET_IP_ALIGN; + + return padding; +#endif } +#define __RADIO_TAP_SIZE_RSV 32 + static void _rtl_rx_completed(struct urb *_urb) { - struct sk_buff *skb = (struct sk_buff *)_urb->context; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct rtl_usb *rtlusb = (struct rtl_usb *)info->rate_driver_data[0]; + struct rtl_usb *rtlusb = (struct rtl_usb *)_urb->context; struct ieee80211_hw *hw = usb_get_intfdata(rtlusb->intf); struct rtl_priv *rtlpriv = rtl_priv(hw); int err = 0; @@ -560,28 +623,50 @@ static void _rtl_rx_completed(struct urb *_urb) goto free; if (likely(0 == _urb->status)) { - /* If this code were moved to work queue, would CPU - * utilization be improved? NOTE: We shall allocate another skb - * and reuse the original one. - */ - skb_put(skb, _urb->actual_length); + unsigned int padding; + struct sk_buff *skb; + unsigned int qlen; + unsigned int size = _urb->actual_length; + struct ieee80211_hdr *hdr; - if (likely(!rtlusb->usb_rx_segregate_hdl)) { - struct sk_buff *_skb; - _rtl_usb_rx_process_noagg(hw, skb); - _skb = _rtl_prep_rx_urb(hw, rtlusb, _urb, GFP_ATOMIC); - if (IS_ERR(_skb)) { - err = PTR_ERR(_skb); - RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, - "Can't allocate skb for bulk IN!\n"); - return; - } - skb = _skb; - } else{ - /* TO DO */ - _rtl_rx_pre_process(hw, skb); - pr_err("rx agg not supported\n"); + if (size < RTL_RX_DESC_SIZE + sizeof(struct ieee80211_hdr)) { + RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, + "Too short packet from bulk IN! (len: %d)\n", + size); + goto resubmit; } + + qlen = skb_queue_len(&rtlusb->rx_queue); + if (qlen >= __RX_SKB_MAX_QUEUED) { + RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, + "Pending RX skbuff queue full! (qlen: %d)\n", + qlen); + goto resubmit; + } + + hdr = (void *)(_urb->transfer_buffer + RTL_RX_DESC_SIZE); + padding = _rtl_rx_get_padding(hdr, size - RTL_RX_DESC_SIZE); + + skb = dev_alloc_skb(size + __RADIO_TAP_SIZE_RSV + padding); + if (!skb) { + RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, + "Can't allocate skb for bulk IN!\n"); + goto resubmit; + } + + _rtl_install_trx_info(rtlusb, skb, rtlusb->in_ep); + + /* Make sure the payload data is 4 byte aligned. */ + skb_reserve(skb, padding); + + /* reserve some space for mac80211's radiotap */ + skb_reserve(skb, __RADIO_TAP_SIZE_RSV); + + memcpy(skb_put(skb, size), _urb->transfer_buffer, size); + + skb_queue_tail(&rtlusb->rx_queue, skb); + tasklet_schedule(&rtlusb->rx_work_tasklet); + goto resubmit; } @@ -597,9 +682,6 @@ static void _rtl_rx_completed(struct urb *_urb) } resubmit: - skb_reset_tail_pointer(skb); - skb_trim(skb, 0); - usb_anchor_urb(_urb, &rtlusb->rx_submitted); err = usb_submit_urb(_urb, GFP_ATOMIC); if (unlikely(err)) { @@ -609,13 +691,34 @@ resubmit: return; free: - dev_kfree_skb_irq(skb); + /* On some architectures, usb_free_coherent must not be called from + * hardirq context. Queue urb to cleanup list. + */ + usb_anchor_urb(_urb, &rtlusb->rx_cleanup_urbs); +} + +#undef __RADIO_TAP_SIZE_RSV + +static void _rtl_usb_cleanup_rx(struct ieee80211_hw *hw) +{ + struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); + struct urb *urb; + + usb_kill_anchored_urbs(&rtlusb->rx_submitted); + + tasklet_kill(&rtlusb->rx_work_tasklet); + skb_queue_purge(&rtlusb->rx_queue); + + while ((urb = usb_get_from_anchor(&rtlusb->rx_cleanup_urbs))) { + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + } } static int _rtl_usb_receive(struct ieee80211_hw *hw) { struct urb *urb; - struct sk_buff *skb; int err; int i; struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -634,11 +737,10 @@ static int _rtl_usb_receive(struct ieee80211_hw *hw) goto err_out; } - skb = _rtl_prep_rx_urb(hw, rtlusb, urb, GFP_KERNEL); - if (IS_ERR(skb)) { + err = _rtl_prep_rx_urb(hw, rtlusb, urb, GFP_KERNEL); + if (err < 0) { RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, "Failed to prep_rx_urb!!\n"); - err = PTR_ERR(skb); usb_free_urb(urb); goto err_out; } @@ -653,6 +755,7 @@ static int _rtl_usb_receive(struct ieee80211_hw *hw) err_out: usb_kill_anchored_urbs(&rtlusb->rx_submitted); + _rtl_usb_cleanup_rx(hw); return err; } @@ -694,7 +797,7 @@ static void rtl_usb_cleanup(struct ieee80211_hw *hw) SET_USB_STOP(rtlusb); /* clean up rx stuff. */ - usb_kill_anchored_urbs(&rtlusb->rx_submitted); + _rtl_usb_cleanup_rx(hw); /* clean up tx stuff */ for (i = 0; i < RTL_USB_MAX_EP_NUM; i++) { @@ -728,6 +831,7 @@ static void rtl_usb_stop(struct ieee80211_hw *hw) /* should after adapter start and interrupt enable. */ set_hal_stop(rtlhal); + cancel_work_sync(&rtlpriv->works.fill_h2c_cmd); /* Enable software */ SET_USB_STOP(rtlusb); rtl_usb_deinit(hw); @@ -825,8 +929,6 @@ static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb, u32 ep_num; struct urb *_urb = NULL; struct sk_buff *_skb = NULL; - struct sk_buff_head *skb_list; - struct usb_anchor *urb_list; WARN_ON(NULL == rtlusb->usb_tx_aggregate_hdl); if (unlikely(IS_USB_STOP(rtlusb))) { @@ -836,15 +938,14 @@ static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb, return; } ep_num = rtlusb->ep_map.ep_mapping[qnum]; - skb_list = &rtlusb->tx_skb_queue[ep_num]; _skb = skb; _urb = _rtl_usb_tx_urb_setup(hw, _skb, ep_num); 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]; _rtl_submit_tx_urb(hw, _urb); } @@ -893,7 +994,7 @@ static void _rtl_usb_tx_preprocess(struct ieee80211_hw *hw, seq_number += 1; seq_number <<= 4; } - rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, info, sta, skb, + rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, NULL, info, sta, skb, hw_queue, &tcb_desc); if (!ieee80211_has_morefrags(hdr->frame_control)) { if (qc) @@ -933,6 +1034,16 @@ static bool rtl_usb_tx_chk_waitq_insert(struct ieee80211_hw *hw, return false; } +static void rtl_fill_h2c_cmd_work_callback(struct work_struct *work) +{ + struct rtl_works *rtlworks = + container_of(work, struct rtl_works, fill_h2c_cmd); + struct ieee80211_hw *hw = rtlworks->hw; + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->cfg->ops->fill_h2c_cmd(hw, H2C_RA_MASK, 5, rtlpriv->rate_mask); +} + static struct rtl_intf_ops rtl_usb_ops = { .adapter_start = rtl_usb_start, .adapter_stop = rtl_usb_stop, @@ -941,7 +1052,8 @@ static struct rtl_intf_ops rtl_usb_ops = { }; int 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; @@ -963,6 +1075,10 @@ int rtl_usb_probe(struct usb_interface *intf, /* this spin lock must be initialized early */ spin_lock_init(&rtlpriv->locks.usb_lock); + INIT_WORK(&rtlpriv->works.fill_h2c_cmd, + rtl_fill_h2c_cmd_work_callback); + INIT_WORK(&rtlpriv->works.lps_change_work, + rtl_lps_change_work_callback); rtlpriv->usb_data_index = 0; init_completion(&rtlpriv->firmware_loading_complete); @@ -976,7 +1092,7 @@ int 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 */ |
