Ejemplo n.º 1
0
static void _rtl_usb_rx_process_agg(struct ieee80211_hw *hw,
				    struct sk_buff *skb)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u8 *rxdesc = skb->data;
	struct ieee80211_hdr *hdr;
	bool unicast = false;
	__le16 fc;
	struct ieee80211_rx_status rx_status = {0};
	struct rtl_stats stats = {
		.signal = 0,
		.rate = 0,
	};

	skb_pull(skb, RTL_RX_DESC_SIZE);
	rtlpriv->cfg->ops->query_rx_desc(hw, &stats, &rx_status, rxdesc, skb);
	skb_pull(skb, (stats.rx_drvinfo_size + stats.rx_bufshift));
	hdr = (struct ieee80211_hdr *)(skb->data);
	fc = hdr->frame_control;

	memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));

	if (is_broadcast_ether_addr(hdr->addr1)) {
		/*TODO*/;
	} else if (is_multicast_ether_addr(hdr->addr1)) {
		/*TODO*/
	} else {
		unicast = true;
	}

	if (!stats.crc) {
		if (unicast) {
			rtlpriv->link_info.num_rx_inperiod++;
			rtlpriv->stats.rxbytesunicast += skb->len;
		}

		/* static bcn for roaming */
		rtl_beacon_statistic(hw, skb);

		if (likely(rtl_action_proc(hw, skb, false)))
			return;
	}
}

static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw,
				      struct sk_buff *skb)
{
	_rtl_usb_rx_process_agg(hw, skb);
	ieee80211_rx(hw, skb);
}
Ejemplo n.º 2
0
static void _rtl_usb_rx_process_agg(struct ieee80211_hw *hw,
                                    struct sk_buff *skb)
{
    struct rtl_priv *rtlpriv = rtl_priv(hw);
    u8 *rxdesc = skb->data;
    struct ieee80211_hdr *hdr;
    bool unicast = false;
    __le16 fc;
    struct ieee80211_rx_status rx_status = {0};
    struct rtl_stats stats = {
        .signal = 0,
        .rate = 0,
    };

    skb_pull(skb, RTL_RX_DESC_SIZE);
    rtlpriv->cfg->ops->query_rx_desc(hw, &stats, &rx_status, rxdesc, skb);
    skb_pull(skb, (stats.rx_drvinfo_size + stats.rx_bufshift));
    hdr = (struct ieee80211_hdr *)(skb->data);
    fc = hdr->frame_control;
    if (!stats.crc) {
        memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));

        if (is_broadcast_ether_addr(hdr->addr1)) {
            /*TODO*/;
        } else if (is_multicast_ether_addr(hdr->addr1)) {
            /*TODO*/
        } else {
            unicast = true;
            rtlpriv->stats.rxbytesunicast +=  skb->len;
        }

        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);
    }
}

static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw,
                                      struct sk_buff *skb)
{
    struct rtl_priv *rtlpriv = rtl_priv(hw);
    u8 *rxdesc = skb->data;
    struct ieee80211_hdr *hdr;
    bool unicast = false;
    __le16 fc;
    struct ieee80211_rx_status rx_status = {0};
    struct rtl_stats stats = {
        .signal = 0,
        .rate = 0,
    };

    skb_pull(skb, RTL_RX_DESC_SIZE);
    rtlpriv->cfg->ops->query_rx_desc(hw, &stats, &rx_status, rxdesc, skb);
    skb_pull(skb, (stats.rx_drvinfo_size + stats.rx_bufshift));
    hdr = (struct ieee80211_hdr *)(skb->data);
    fc = hdr->frame_control;
    if (!stats.crc) {
        memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));

        if (is_broadcast_ether_addr(hdr->addr1)) {
            /*TODO*/;
        } else if (is_multicast_ether_addr(hdr->addr1)) {
            /*TODO*/
        } else {
            unicast = true;
            rtlpriv->stats.rxbytesunicast +=  skb->len;
        }

        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);

        if (likely(rtl_action_proc(hw, skb, false)))
            ieee80211_rx(hw, skb);
        else
            dev_kfree_skb_any(skb);
    }
}

static void _rtl_rx_pre_process(struct ieee80211_hw *hw, struct sk_buff *skb)
{
    struct sk_buff *_skb;
    struct sk_buff_head rx_queue;
    struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw));

    skb_queue_head_init(&rx_queue);
    if (rtlusb->usb_rx_segregate_hdl)
        rtlusb->usb_rx_segregate_hdl(hw, skb, &rx_queue);
    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(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 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;

    if (unlikely(IS_USB_STOP(rtlusb)))
        goto free;

    if (likely(0 == _urb->status)) {
        unsigned int padding;
        struct sk_buff *skb;
        unsigned int qlen;
        unsigned int size = _urb->actual_length;
        struct ieee80211_hdr *hdr;

        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;
    }

    switch (_urb->status) {
    /* disconnect */
    case -ENOENT:
    case -ECONNRESET:
    case -ENODEV:
    case -ESHUTDOWN:
        goto free;
    default:
        break;
    }

resubmit:
    usb_anchor_urb(_urb, &rtlusb->rx_submitted);
    err = usb_submit_urb(_urb, GFP_ATOMIC);
    if (unlikely(err)) {
        usb_unanchor_urb(_urb);
        goto free;
    }
    return;

free:
    /* 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_priv *rtlpriv = rtl_priv(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);
    cancel_work_sync(&rtlpriv->works.lps_change_work);

    flush_workqueue(rtlpriv->works.rtl_wq);
    destroy_workqueue(rtlpriv->works.rtl_wq);

    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;
    int err;
    int i;
    struct rtl_priv *rtlpriv = rtl_priv(hw);
    struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw));

    WARN_ON(0 == rtlusb->rx_urb_num);
    /* 1600 == 1514 + max WLAN header + rtk info */
    WARN_ON(rtlusb->rx_max_size < 1600);

    for (i = 0; i < rtlusb->rx_urb_num; i++) {
        err = -ENOMEM;
        urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!urb) {
            RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG,
                     "Failed to alloc URB!!\n");
            goto err_out;
        }

        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");
            usb_free_urb(urb);
            goto err_out;
        }

        usb_anchor_urb(urb, &rtlusb->rx_submitted);
        err = usb_submit_urb(urb, GFP_KERNEL);
        if (err)
            goto err_out;
        usb_free_urb(urb);
    }
    return 0;

err_out:
    usb_kill_anchored_urbs(&rtlusb->rx_submitted);
    _rtl_usb_cleanup_rx(hw);
    return err;
}

static int rtl_usb_start(struct ieee80211_hw *hw)
{
    int err;
    struct rtl_priv *rtlpriv = rtl_priv(hw);
    struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
    struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw));

    err = rtlpriv->cfg->ops->hw_init(hw);
    if (!err) {
        rtl_init_rx_config(hw);

        /* Enable software */
        SET_USB_START(rtlusb);
        /* should after adapter start and interrupt enable. */
        set_hal_start(rtlhal);

        /* Start bulk IN */
        err = _rtl_usb_receive(hw);
    }

    return err;
}