18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux device driver for RTL8187 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2007 Michael Wu <flamingice@sourmilk.net> 68c2ecf20Sopenharmony_ci * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on the r8187 driver, which is: 98c2ecf20Sopenharmony_ci * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The driver was extended to the RTL8187B in 2008 by: 128c2ecf20Sopenharmony_ci * Herton Ronaldo Krzesinski <herton@mandriva.com.br> 138c2ecf20Sopenharmony_ci * Hin-Tak Leung <htl10@users.sourceforge.net> 148c2ecf20Sopenharmony_ci * Larry Finger <Larry.Finger@lwfinger.net> 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Magic delays and register offsets below are taken from the original 178c2ecf20Sopenharmony_ci * r8187 driver sources. Thanks to Realtek for their support! 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/usb.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/delay.h> 238c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 248c2ecf20Sopenharmony_ci#include <linux/eeprom_93cx6.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <net/mac80211.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "rtl8187.h" 298c2ecf20Sopenharmony_ci#include "rtl8225.h" 308c2ecf20Sopenharmony_ci#ifdef CONFIG_RTL8187_LEDS 318c2ecf20Sopenharmony_ci#include "leds.h" 328c2ecf20Sopenharmony_ci#endif 338c2ecf20Sopenharmony_ci#include "rfkill.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); 368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>"); 378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Herton Ronaldo Krzesinski <herton@mandriva.com.br>"); 388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hin-Tak Leung <htl10@users.sourceforge.net>"); 398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>"); 408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver"); 418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const struct usb_device_id rtl8187_table[] = { 448c2ecf20Sopenharmony_ci /* Asus */ 458c2ecf20Sopenharmony_ci {USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187}, 468c2ecf20Sopenharmony_ci /* Belkin */ 478c2ecf20Sopenharmony_ci {USB_DEVICE(0x050d, 0x705e), .driver_info = DEVICE_RTL8187B}, 488c2ecf20Sopenharmony_ci /* Realtek */ 498c2ecf20Sopenharmony_ci {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187}, 508c2ecf20Sopenharmony_ci {USB_DEVICE(0x0bda, 0x8189), .driver_info = DEVICE_RTL8187B}, 518c2ecf20Sopenharmony_ci {USB_DEVICE(0x0bda, 0x8197), .driver_info = DEVICE_RTL8187B}, 528c2ecf20Sopenharmony_ci {USB_DEVICE(0x0bda, 0x8198), .driver_info = DEVICE_RTL8187B}, 538c2ecf20Sopenharmony_ci /* Surecom */ 548c2ecf20Sopenharmony_ci {USB_DEVICE(0x0769, 0x11F2), .driver_info = DEVICE_RTL8187}, 558c2ecf20Sopenharmony_ci /* Logitech */ 568c2ecf20Sopenharmony_ci {USB_DEVICE(0x0789, 0x010C), .driver_info = DEVICE_RTL8187}, 578c2ecf20Sopenharmony_ci /* Netgear */ 588c2ecf20Sopenharmony_ci {USB_DEVICE(0x0846, 0x6100), .driver_info = DEVICE_RTL8187}, 598c2ecf20Sopenharmony_ci {USB_DEVICE(0x0846, 0x6a00), .driver_info = DEVICE_RTL8187}, 608c2ecf20Sopenharmony_ci {USB_DEVICE(0x0846, 0x4260), .driver_info = DEVICE_RTL8187B}, 618c2ecf20Sopenharmony_ci /* HP */ 628c2ecf20Sopenharmony_ci {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187}, 638c2ecf20Sopenharmony_ci /* Sitecom */ 648c2ecf20Sopenharmony_ci {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187}, 658c2ecf20Sopenharmony_ci {USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B}, 668c2ecf20Sopenharmony_ci {USB_DEVICE(0x0df6, 0x0029), .driver_info = DEVICE_RTL8187B}, 678c2ecf20Sopenharmony_ci /* Sphairon Access Systems GmbH */ 688c2ecf20Sopenharmony_ci {USB_DEVICE(0x114B, 0x0150), .driver_info = DEVICE_RTL8187}, 698c2ecf20Sopenharmony_ci /* Dick Smith Electronics */ 708c2ecf20Sopenharmony_ci {USB_DEVICE(0x1371, 0x9401), .driver_info = DEVICE_RTL8187}, 718c2ecf20Sopenharmony_ci /* Abocom */ 728c2ecf20Sopenharmony_ci {USB_DEVICE(0x13d1, 0xabe6), .driver_info = DEVICE_RTL8187}, 738c2ecf20Sopenharmony_ci /* Qcom */ 748c2ecf20Sopenharmony_ci {USB_DEVICE(0x18E8, 0x6232), .driver_info = DEVICE_RTL8187}, 758c2ecf20Sopenharmony_ci /* AirLive */ 768c2ecf20Sopenharmony_ci {USB_DEVICE(0x1b75, 0x8187), .driver_info = DEVICE_RTL8187}, 778c2ecf20Sopenharmony_ci /* Linksys */ 788c2ecf20Sopenharmony_ci {USB_DEVICE(0x1737, 0x0073), .driver_info = DEVICE_RTL8187B}, 798c2ecf20Sopenharmony_ci {} 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, rtl8187_table); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic const struct ieee80211_rate rtl818x_rates[] = { 858c2ecf20Sopenharmony_ci { .bitrate = 10, .hw_value = 0, }, 868c2ecf20Sopenharmony_ci { .bitrate = 20, .hw_value = 1, }, 878c2ecf20Sopenharmony_ci { .bitrate = 55, .hw_value = 2, }, 888c2ecf20Sopenharmony_ci { .bitrate = 110, .hw_value = 3, }, 898c2ecf20Sopenharmony_ci { .bitrate = 60, .hw_value = 4, }, 908c2ecf20Sopenharmony_ci { .bitrate = 90, .hw_value = 5, }, 918c2ecf20Sopenharmony_ci { .bitrate = 120, .hw_value = 6, }, 928c2ecf20Sopenharmony_ci { .bitrate = 180, .hw_value = 7, }, 938c2ecf20Sopenharmony_ci { .bitrate = 240, .hw_value = 8, }, 948c2ecf20Sopenharmony_ci { .bitrate = 360, .hw_value = 9, }, 958c2ecf20Sopenharmony_ci { .bitrate = 480, .hw_value = 10, }, 968c2ecf20Sopenharmony_ci { .bitrate = 540, .hw_value = 11, }, 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const struct ieee80211_channel rtl818x_channels[] = { 1008c2ecf20Sopenharmony_ci { .center_freq = 2412 }, 1018c2ecf20Sopenharmony_ci { .center_freq = 2417 }, 1028c2ecf20Sopenharmony_ci { .center_freq = 2422 }, 1038c2ecf20Sopenharmony_ci { .center_freq = 2427 }, 1048c2ecf20Sopenharmony_ci { .center_freq = 2432 }, 1058c2ecf20Sopenharmony_ci { .center_freq = 2437 }, 1068c2ecf20Sopenharmony_ci { .center_freq = 2442 }, 1078c2ecf20Sopenharmony_ci { .center_freq = 2447 }, 1088c2ecf20Sopenharmony_ci { .center_freq = 2452 }, 1098c2ecf20Sopenharmony_ci { .center_freq = 2457 }, 1108c2ecf20Sopenharmony_ci { .center_freq = 2462 }, 1118c2ecf20Sopenharmony_ci { .center_freq = 2467 }, 1128c2ecf20Sopenharmony_ci { .center_freq = 2472 }, 1138c2ecf20Sopenharmony_ci { .center_freq = 2484 }, 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void rtl8187_iowrite_async_cb(struct urb *urb) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci kfree(urb->context); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr, 1228c2ecf20Sopenharmony_ci void *data, u16 len) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct usb_ctrlrequest *dr; 1258c2ecf20Sopenharmony_ci struct urb *urb; 1268c2ecf20Sopenharmony_ci struct rtl8187_async_write_data { 1278c2ecf20Sopenharmony_ci u8 data[4]; 1288c2ecf20Sopenharmony_ci struct usb_ctrlrequest dr; 1298c2ecf20Sopenharmony_ci } *buf; 1308c2ecf20Sopenharmony_ci int rc; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci buf = kmalloc(sizeof(*buf), GFP_ATOMIC); 1338c2ecf20Sopenharmony_ci if (!buf) 1348c2ecf20Sopenharmony_ci return; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 1378c2ecf20Sopenharmony_ci if (!urb) { 1388c2ecf20Sopenharmony_ci kfree(buf); 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci dr = &buf->dr; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci dr->bRequestType = RTL8187_REQT_WRITE; 1458c2ecf20Sopenharmony_ci dr->bRequest = RTL8187_REQ_SET_REG; 1468c2ecf20Sopenharmony_ci dr->wValue = addr; 1478c2ecf20Sopenharmony_ci dr->wIndex = 0; 1488c2ecf20Sopenharmony_ci dr->wLength = cpu_to_le16(len); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci memcpy(buf, data, len); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0), 1538c2ecf20Sopenharmony_ci (unsigned char *)dr, buf, len, 1548c2ecf20Sopenharmony_ci rtl8187_iowrite_async_cb, buf); 1558c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &priv->anchored); 1568c2ecf20Sopenharmony_ci rc = usb_submit_urb(urb, GFP_ATOMIC); 1578c2ecf20Sopenharmony_ci if (rc < 0) { 1588c2ecf20Sopenharmony_ci kfree(buf); 1598c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci usb_free_urb(urb); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv, 1658c2ecf20Sopenharmony_ci __le32 *addr, u32 val) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci __le32 buf = cpu_to_le32(val); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci rtl8187_iowrite_async(priv, cpu_to_le16((unsigned long)addr), 1708c2ecf20Sopenharmony_ci &buf, sizeof(buf)); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_civoid rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci data <<= 8; 1788c2ecf20Sopenharmony_ci data |= addr | 0x80; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->PHY[3], (data >> 24) & 0xFF); 1818c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->PHY[2], (data >> 16) & 0xFF); 1828c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->PHY[1], (data >> 8) & 0xFF); 1838c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->PHY[0], data & 0xFF); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void rtl8187_tx_cb(struct urb *urb) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct sk_buff *skb = (struct sk_buff *)urb->context; 1898c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 1908c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = info->rate_driver_data[0]; 1918c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = hw->priv; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) : 1948c2ecf20Sopenharmony_ci sizeof(struct rtl8187_tx_hdr)); 1958c2ecf20Sopenharmony_ci ieee80211_tx_info_clear_status(info); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (!(urb->status) && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { 1988c2ecf20Sopenharmony_ci if (priv->is_rtl8187b) { 1998c2ecf20Sopenharmony_ci skb_queue_tail(&priv->b_tx_status.queue, skb); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* queue is "full", discard last items */ 2028c2ecf20Sopenharmony_ci while (skb_queue_len(&priv->b_tx_status.queue) > 5) { 2038c2ecf20Sopenharmony_ci struct sk_buff *old_skb; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci dev_dbg(&priv->udev->dev, 2068c2ecf20Sopenharmony_ci "transmit status queue full\n"); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci old_skb = skb_dequeue(&priv->b_tx_status.queue); 2098c2ecf20Sopenharmony_ci ieee80211_tx_status_irqsafe(hw, old_skb); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci return; 2128c2ecf20Sopenharmony_ci } else { 2138c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci if (priv->is_rtl8187b) 2178c2ecf20Sopenharmony_ci ieee80211_tx_status_irqsafe(hw, skb); 2188c2ecf20Sopenharmony_ci else { 2198c2ecf20Sopenharmony_ci /* Retry information for the RTI8187 is only available by 2208c2ecf20Sopenharmony_ci * reading a register in the device. We are in interrupt mode 2218c2ecf20Sopenharmony_ci * here, thus queue the skb and finish on a work queue. */ 2228c2ecf20Sopenharmony_ci skb_queue_tail(&priv->b_tx_status.queue, skb); 2238c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(hw, &priv->work, 0); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic void rtl8187_tx(struct ieee80211_hw *dev, 2288c2ecf20Sopenharmony_ci struct ieee80211_tx_control *control, 2298c2ecf20Sopenharmony_ci struct sk_buff *skb) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 2328c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 2338c2ecf20Sopenharmony_ci struct ieee80211_hdr *tx_hdr = (struct ieee80211_hdr *)(skb->data); 2348c2ecf20Sopenharmony_ci unsigned int ep; 2358c2ecf20Sopenharmony_ci void *buf; 2368c2ecf20Sopenharmony_ci struct urb *urb; 2378c2ecf20Sopenharmony_ci __le16 rts_dur = 0; 2388c2ecf20Sopenharmony_ci u32 flags; 2398c2ecf20Sopenharmony_ci int rc; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 2428c2ecf20Sopenharmony_ci if (!urb) { 2438c2ecf20Sopenharmony_ci kfree_skb(skb); 2448c2ecf20Sopenharmony_ci return; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci flags = skb->len; 2488c2ecf20Sopenharmony_ci flags |= RTL818X_TX_DESC_FLAG_NO_ENC; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24; 2518c2ecf20Sopenharmony_ci if (ieee80211_has_morefrags(tx_hdr->frame_control)) 2528c2ecf20Sopenharmony_ci flags |= RTL818X_TX_DESC_FLAG_MOREFRAG; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* HW will perform RTS-CTS when only RTS flags is set. 2558c2ecf20Sopenharmony_ci * HW will perform CTS-to-self when both RTS and CTS flags are set. 2568c2ecf20Sopenharmony_ci * RTS rate and RTS duration will be used also for CTS-to-self. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { 2598c2ecf20Sopenharmony_ci flags |= RTL818X_TX_DESC_FLAG_RTS; 2608c2ecf20Sopenharmony_ci flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; 2618c2ecf20Sopenharmony_ci rts_dur = ieee80211_rts_duration(dev, priv->vif, 2628c2ecf20Sopenharmony_ci skb->len, info); 2638c2ecf20Sopenharmony_ci } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { 2648c2ecf20Sopenharmony_ci flags |= RTL818X_TX_DESC_FLAG_RTS | RTL818X_TX_DESC_FLAG_CTS; 2658c2ecf20Sopenharmony_ci flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; 2668c2ecf20Sopenharmony_ci rts_dur = ieee80211_ctstoself_duration(dev, priv->vif, 2678c2ecf20Sopenharmony_ci skb->len, info); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { 2718c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) 2728c2ecf20Sopenharmony_ci priv->seqno += 0x10; 2738c2ecf20Sopenharmony_ci tx_hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); 2748c2ecf20Sopenharmony_ci tx_hdr->seq_ctrl |= cpu_to_le16(priv->seqno); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (!priv->is_rtl8187b) { 2788c2ecf20Sopenharmony_ci struct rtl8187_tx_hdr *hdr = skb_push(skb, sizeof(*hdr)); 2798c2ecf20Sopenharmony_ci hdr->flags = cpu_to_le32(flags); 2808c2ecf20Sopenharmony_ci hdr->len = 0; 2818c2ecf20Sopenharmony_ci hdr->rts_duration = rts_dur; 2828c2ecf20Sopenharmony_ci hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); 2838c2ecf20Sopenharmony_ci buf = hdr; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ep = 2; 2868c2ecf20Sopenharmony_ci } else { 2878c2ecf20Sopenharmony_ci /* fc needs to be calculated before skb_push() */ 2888c2ecf20Sopenharmony_ci unsigned int epmap[4] = { 6, 7, 5, 4 }; 2898c2ecf20Sopenharmony_ci u16 fc = le16_to_cpu(tx_hdr->frame_control); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci struct rtl8187b_tx_hdr *hdr = skb_push(skb, sizeof(*hdr)); 2928c2ecf20Sopenharmony_ci struct ieee80211_rate *txrate = 2938c2ecf20Sopenharmony_ci ieee80211_get_tx_rate(dev, info); 2948c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 2958c2ecf20Sopenharmony_ci hdr->flags = cpu_to_le32(flags); 2968c2ecf20Sopenharmony_ci hdr->rts_duration = rts_dur; 2978c2ecf20Sopenharmony_ci hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); 2988c2ecf20Sopenharmony_ci hdr->tx_duration = 2998c2ecf20Sopenharmony_ci ieee80211_generic_frame_duration(dev, priv->vif, 3008c2ecf20Sopenharmony_ci info->band, 3018c2ecf20Sopenharmony_ci skb->len, txrate); 3028c2ecf20Sopenharmony_ci buf = hdr; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) 3058c2ecf20Sopenharmony_ci ep = 12; 3068c2ecf20Sopenharmony_ci else 3078c2ecf20Sopenharmony_ci ep = epmap[skb_get_queue_mapping(skb)]; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci info->rate_driver_data[0] = dev; 3118c2ecf20Sopenharmony_ci info->rate_driver_data[1] = urb; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep), 3148c2ecf20Sopenharmony_ci buf, skb->len, rtl8187_tx_cb, skb); 3158c2ecf20Sopenharmony_ci urb->transfer_flags |= URB_ZERO_PACKET; 3168c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &priv->anchored); 3178c2ecf20Sopenharmony_ci rc = usb_submit_urb(urb, GFP_ATOMIC); 3188c2ecf20Sopenharmony_ci if (rc < 0) { 3198c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 3208c2ecf20Sopenharmony_ci kfree_skb(skb); 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci usb_free_urb(urb); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic void rtl8187_rx_cb(struct urb *urb) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct sk_buff *skb = (struct sk_buff *)urb->context; 3288c2ecf20Sopenharmony_ci struct rtl8187_rx_info *info = (struct rtl8187_rx_info *)skb->cb; 3298c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = info->dev; 3308c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 3318c2ecf20Sopenharmony_ci struct ieee80211_rx_status rx_status = { 0 }; 3328c2ecf20Sopenharmony_ci int rate, signal; 3338c2ecf20Sopenharmony_ci u32 flags; 3348c2ecf20Sopenharmony_ci unsigned long f; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->rx_queue.lock, f); 3378c2ecf20Sopenharmony_ci __skb_unlink(skb, &priv->rx_queue); 3388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->rx_queue.lock, f); 3398c2ecf20Sopenharmony_ci skb_put(skb, urb->actual_length); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (unlikely(urb->status)) { 3428c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 3438c2ecf20Sopenharmony_ci return; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (!priv->is_rtl8187b) { 3478c2ecf20Sopenharmony_ci struct rtl8187_rx_hdr *hdr = 3488c2ecf20Sopenharmony_ci (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); 3498c2ecf20Sopenharmony_ci flags = le32_to_cpu(hdr->flags); 3508c2ecf20Sopenharmony_ci /* As with the RTL8187B below, the AGC is used to calculate 3518c2ecf20Sopenharmony_ci * signal strength. In this case, the scaling 3528c2ecf20Sopenharmony_ci * constants are derived from the output of p54usb. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci signal = -4 - ((27 * hdr->agc) >> 6); 3558c2ecf20Sopenharmony_ci rx_status.antenna = (hdr->signal >> 7) & 1; 3568c2ecf20Sopenharmony_ci rx_status.mactime = le64_to_cpu(hdr->mac_time); 3578c2ecf20Sopenharmony_ci } else { 3588c2ecf20Sopenharmony_ci struct rtl8187b_rx_hdr *hdr = 3598c2ecf20Sopenharmony_ci (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); 3608c2ecf20Sopenharmony_ci /* The Realtek datasheet for the RTL8187B shows that the RX 3618c2ecf20Sopenharmony_ci * header contains the following quantities: signal quality, 3628c2ecf20Sopenharmony_ci * RSSI, AGC, the received power in dB, and the measured SNR. 3638c2ecf20Sopenharmony_ci * In testing, none of these quantities show qualitative 3648c2ecf20Sopenharmony_ci * agreement with AP signal strength, except for the AGC, 3658c2ecf20Sopenharmony_ci * which is inversely proportional to the strength of the 3668c2ecf20Sopenharmony_ci * signal. In the following, the signal strength 3678c2ecf20Sopenharmony_ci * is derived from the AGC. The arbitrary scaling constants 3688c2ecf20Sopenharmony_ci * are chosen to make the results close to the values obtained 3698c2ecf20Sopenharmony_ci * for a BCM4312 using b43 as the driver. The noise is ignored 3708c2ecf20Sopenharmony_ci * for now. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_ci flags = le32_to_cpu(hdr->flags); 3738c2ecf20Sopenharmony_ci signal = 14 - hdr->agc / 2; 3748c2ecf20Sopenharmony_ci rx_status.antenna = (hdr->rssi >> 7) & 1; 3758c2ecf20Sopenharmony_ci rx_status.mactime = le64_to_cpu(hdr->mac_time); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci rx_status.signal = signal; 3798c2ecf20Sopenharmony_ci priv->signal = signal; 3808c2ecf20Sopenharmony_ci rate = (flags >> 20) & 0xF; 3818c2ecf20Sopenharmony_ci skb_trim(skb, flags & 0x0FFF); 3828c2ecf20Sopenharmony_ci rx_status.rate_idx = rate; 3838c2ecf20Sopenharmony_ci rx_status.freq = dev->conf.chandef.chan->center_freq; 3848c2ecf20Sopenharmony_ci rx_status.band = dev->conf.chandef.chan->band; 3858c2ecf20Sopenharmony_ci rx_status.flag |= RX_FLAG_MACTIME_START; 3868c2ecf20Sopenharmony_ci if (flags & RTL818X_RX_DESC_FLAG_SPLCP) 3878c2ecf20Sopenharmony_ci rx_status.enc_flags |= RX_ENC_FLAG_SHORTPRE; 3888c2ecf20Sopenharmony_ci if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) 3898c2ecf20Sopenharmony_ci rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; 3908c2ecf20Sopenharmony_ci memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); 3918c2ecf20Sopenharmony_ci ieee80211_rx_irqsafe(dev, skb); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci skb = dev_alloc_skb(RTL8187_MAX_RX); 3948c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 3958c2ecf20Sopenharmony_ci /* TODO check rx queue length and refill *somewhere* */ 3968c2ecf20Sopenharmony_ci return; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci info = (struct rtl8187_rx_info *)skb->cb; 4008c2ecf20Sopenharmony_ci info->urb = urb; 4018c2ecf20Sopenharmony_ci info->dev = dev; 4028c2ecf20Sopenharmony_ci urb->transfer_buffer = skb_tail_pointer(skb); 4038c2ecf20Sopenharmony_ci urb->context = skb; 4048c2ecf20Sopenharmony_ci skb_queue_tail(&priv->rx_queue, skb); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &priv->anchored); 4078c2ecf20Sopenharmony_ci if (usb_submit_urb(urb, GFP_ATOMIC)) { 4088c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 4098c2ecf20Sopenharmony_ci skb_unlink(skb, &priv->rx_queue); 4108c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int rtl8187_init_urbs(struct ieee80211_hw *dev) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 4178c2ecf20Sopenharmony_ci struct urb *entry = NULL; 4188c2ecf20Sopenharmony_ci struct sk_buff *skb; 4198c2ecf20Sopenharmony_ci struct rtl8187_rx_info *info; 4208c2ecf20Sopenharmony_ci int ret = 0; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci while (skb_queue_len(&priv->rx_queue) < 32) { 4238c2ecf20Sopenharmony_ci skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL); 4248c2ecf20Sopenharmony_ci if (!skb) { 4258c2ecf20Sopenharmony_ci ret = -ENOMEM; 4268c2ecf20Sopenharmony_ci goto err; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci entry = usb_alloc_urb(0, GFP_KERNEL); 4298c2ecf20Sopenharmony_ci if (!entry) { 4308c2ecf20Sopenharmony_ci ret = -ENOMEM; 4318c2ecf20Sopenharmony_ci goto err; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci usb_fill_bulk_urb(entry, priv->udev, 4348c2ecf20Sopenharmony_ci usb_rcvbulkpipe(priv->udev, 4358c2ecf20Sopenharmony_ci priv->is_rtl8187b ? 3 : 1), 4368c2ecf20Sopenharmony_ci skb_tail_pointer(skb), 4378c2ecf20Sopenharmony_ci RTL8187_MAX_RX, rtl8187_rx_cb, skb); 4388c2ecf20Sopenharmony_ci info = (struct rtl8187_rx_info *)skb->cb; 4398c2ecf20Sopenharmony_ci info->urb = entry; 4408c2ecf20Sopenharmony_ci info->dev = dev; 4418c2ecf20Sopenharmony_ci skb_queue_tail(&priv->rx_queue, skb); 4428c2ecf20Sopenharmony_ci usb_anchor_urb(entry, &priv->anchored); 4438c2ecf20Sopenharmony_ci ret = usb_submit_urb(entry, GFP_KERNEL); 4448c2ecf20Sopenharmony_ci if (ret) { 4458c2ecf20Sopenharmony_ci skb_unlink(skb, &priv->rx_queue); 4468c2ecf20Sopenharmony_ci usb_unanchor_urb(entry); 4478c2ecf20Sopenharmony_ci usb_put_urb(entry); 4488c2ecf20Sopenharmony_ci goto err; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci usb_put_urb(entry); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci return ret; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cierr: 4558c2ecf20Sopenharmony_ci kfree_skb(skb); 4568c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&priv->anchored); 4578c2ecf20Sopenharmony_ci return ret; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic void rtl8187b_status_cb(struct urb *urb) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = (struct ieee80211_hw *)urb->context; 4638c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = hw->priv; 4648c2ecf20Sopenharmony_ci u64 val; 4658c2ecf20Sopenharmony_ci unsigned int cmd_type; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (unlikely(urb->status)) 4688c2ecf20Sopenharmony_ci return; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* 4718c2ecf20Sopenharmony_ci * Read from status buffer: 4728c2ecf20Sopenharmony_ci * 4738c2ecf20Sopenharmony_ci * bits [30:31] = cmd type: 4748c2ecf20Sopenharmony_ci * - 0 indicates tx beacon interrupt 4758c2ecf20Sopenharmony_ci * - 1 indicates tx close descriptor 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * In the case of tx beacon interrupt: 4788c2ecf20Sopenharmony_ci * [0:9] = Last Beacon CW 4798c2ecf20Sopenharmony_ci * [10:29] = reserved 4808c2ecf20Sopenharmony_ci * [30:31] = 00b 4818c2ecf20Sopenharmony_ci * [32:63] = Last Beacon TSF 4828c2ecf20Sopenharmony_ci * 4838c2ecf20Sopenharmony_ci * If it's tx close descriptor: 4848c2ecf20Sopenharmony_ci * [0:7] = Packet Retry Count 4858c2ecf20Sopenharmony_ci * [8:14] = RTS Retry Count 4868c2ecf20Sopenharmony_ci * [15] = TOK 4878c2ecf20Sopenharmony_ci * [16:27] = Sequence No 4888c2ecf20Sopenharmony_ci * [28] = LS 4898c2ecf20Sopenharmony_ci * [29] = FS 4908c2ecf20Sopenharmony_ci * [30:31] = 01b 4918c2ecf20Sopenharmony_ci * [32:47] = unused (reserved?) 4928c2ecf20Sopenharmony_ci * [48:63] = MAC Used Time 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci val = le64_to_cpu(priv->b_tx_status.buf); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci cmd_type = (val >> 30) & 0x3; 4978c2ecf20Sopenharmony_ci if (cmd_type == 1) { 4988c2ecf20Sopenharmony_ci unsigned int pkt_rc, seq_no; 4998c2ecf20Sopenharmony_ci bool tok; 5008c2ecf20Sopenharmony_ci struct sk_buff *skb, *iter; 5018c2ecf20Sopenharmony_ci struct ieee80211_hdr *ieee80211hdr; 5028c2ecf20Sopenharmony_ci unsigned long flags; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci pkt_rc = val & 0xFF; 5058c2ecf20Sopenharmony_ci tok = val & (1 << 15); 5068c2ecf20Sopenharmony_ci seq_no = (val >> 16) & 0xFFF; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->b_tx_status.queue.lock, flags); 5098c2ecf20Sopenharmony_ci skb = NULL; 5108c2ecf20Sopenharmony_ci skb_queue_reverse_walk(&priv->b_tx_status.queue, iter) { 5118c2ecf20Sopenharmony_ci ieee80211hdr = (struct ieee80211_hdr *)iter->data; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* 5148c2ecf20Sopenharmony_ci * While testing, it was discovered that the seq_no 5158c2ecf20Sopenharmony_ci * doesn't actually contains the sequence number. 5168c2ecf20Sopenharmony_ci * Instead of returning just the 12 bits of sequence 5178c2ecf20Sopenharmony_ci * number, hardware is returning entire sequence control 5188c2ecf20Sopenharmony_ci * (fragment number plus sequence number) in a 12 bit 5198c2ecf20Sopenharmony_ci * only field overflowing after some time. As a 5208c2ecf20Sopenharmony_ci * workaround, just consider the lower bits, and expect 5218c2ecf20Sopenharmony_ci * it's unlikely we wrongly ack some sent data 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_ci if ((le16_to_cpu(ieee80211hdr->seq_ctrl) 5248c2ecf20Sopenharmony_ci & 0xFFF) == seq_no) { 5258c2ecf20Sopenharmony_ci skb = iter; 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci if (skb) { 5308c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci __skb_unlink(skb, &priv->b_tx_status.queue); 5338c2ecf20Sopenharmony_ci if (tok) 5348c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 5358c2ecf20Sopenharmony_ci info->status.rates[0].count = pkt_rc + 1; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci ieee80211_tx_status_irqsafe(hw, skb); 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags); 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &priv->anchored); 5438c2ecf20Sopenharmony_ci if (usb_submit_urb(urb, GFP_ATOMIC)) 5448c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic int rtl8187b_init_status_urb(struct ieee80211_hw *dev) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 5508c2ecf20Sopenharmony_ci struct urb *entry; 5518c2ecf20Sopenharmony_ci int ret = 0; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci entry = usb_alloc_urb(0, GFP_KERNEL); 5548c2ecf20Sopenharmony_ci if (!entry) 5558c2ecf20Sopenharmony_ci return -ENOMEM; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9), 5588c2ecf20Sopenharmony_ci &priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf), 5598c2ecf20Sopenharmony_ci rtl8187b_status_cb, dev); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci usb_anchor_urb(entry, &priv->anchored); 5628c2ecf20Sopenharmony_ci ret = usb_submit_urb(entry, GFP_KERNEL); 5638c2ecf20Sopenharmony_ci if (ret) 5648c2ecf20Sopenharmony_ci usb_unanchor_urb(entry); 5658c2ecf20Sopenharmony_ci usb_free_urb(entry); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci return ret; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic void rtl8187_set_anaparam(struct rtl8187_priv *priv, bool rfon) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci u32 anaparam, anaparam2; 5738c2ecf20Sopenharmony_ci u8 anaparam3, reg; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (!priv->is_rtl8187b) { 5768c2ecf20Sopenharmony_ci if (rfon) { 5778c2ecf20Sopenharmony_ci anaparam = RTL8187_RTL8225_ANAPARAM_ON; 5788c2ecf20Sopenharmony_ci anaparam2 = RTL8187_RTL8225_ANAPARAM2_ON; 5798c2ecf20Sopenharmony_ci } else { 5808c2ecf20Sopenharmony_ci anaparam = RTL8187_RTL8225_ANAPARAM_OFF; 5818c2ecf20Sopenharmony_ci anaparam2 = RTL8187_RTL8225_ANAPARAM2_OFF; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci } else { 5848c2ecf20Sopenharmony_ci if (rfon) { 5858c2ecf20Sopenharmony_ci anaparam = RTL8187B_RTL8225_ANAPARAM_ON; 5868c2ecf20Sopenharmony_ci anaparam2 = RTL8187B_RTL8225_ANAPARAM2_ON; 5878c2ecf20Sopenharmony_ci anaparam3 = RTL8187B_RTL8225_ANAPARAM3_ON; 5888c2ecf20Sopenharmony_ci } else { 5898c2ecf20Sopenharmony_ci anaparam = RTL8187B_RTL8225_ANAPARAM_OFF; 5908c2ecf20Sopenharmony_ci anaparam2 = RTL8187B_RTL8225_ANAPARAM2_OFF; 5918c2ecf20Sopenharmony_ci anaparam3 = RTL8187B_RTL8225_ANAPARAM3_OFF; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, 5968c2ecf20Sopenharmony_ci RTL818X_EEPROM_CMD_CONFIG); 5978c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); 5988c2ecf20Sopenharmony_ci reg |= RTL818X_CONFIG3_ANAPARAM_WRITE; 5998c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); 6008c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); 6018c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, anaparam2); 6028c2ecf20Sopenharmony_ci if (priv->is_rtl8187b) 6038c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->ANAPARAM3A, anaparam3); 6048c2ecf20Sopenharmony_ci reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE; 6058c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); 6068c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, 6078c2ecf20Sopenharmony_ci RTL818X_EEPROM_CMD_NORMAL); 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int rtl8187_cmd_reset(struct ieee80211_hw *dev) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 6138c2ecf20Sopenharmony_ci u8 reg; 6148c2ecf20Sopenharmony_ci int i; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->CMD); 6178c2ecf20Sopenharmony_ci reg &= (1 << 1); 6188c2ecf20Sopenharmony_ci reg |= RTL818X_CMD_RESET; 6198c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CMD, reg); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci i = 10; 6228c2ecf20Sopenharmony_ci do { 6238c2ecf20Sopenharmony_ci msleep(2); 6248c2ecf20Sopenharmony_ci if (!(rtl818x_ioread8(priv, &priv->map->CMD) & 6258c2ecf20Sopenharmony_ci RTL818X_CMD_RESET)) 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci } while (--i); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (!i) { 6308c2ecf20Sopenharmony_ci wiphy_err(dev->wiphy, "Reset timeout!\n"); 6318c2ecf20Sopenharmony_ci return -ETIMEDOUT; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* reload registers from eeprom */ 6358c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci i = 10; 6388c2ecf20Sopenharmony_ci do { 6398c2ecf20Sopenharmony_ci msleep(4); 6408c2ecf20Sopenharmony_ci if (!(rtl818x_ioread8(priv, &priv->map->EEPROM_CMD) & 6418c2ecf20Sopenharmony_ci RTL818X_EEPROM_CMD_CONFIG)) 6428c2ecf20Sopenharmony_ci break; 6438c2ecf20Sopenharmony_ci } while (--i); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (!i) { 6468c2ecf20Sopenharmony_ci wiphy_err(dev->wiphy, "eeprom reset timeout!\n"); 6478c2ecf20Sopenharmony_ci return -ETIMEDOUT; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci return 0; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int rtl8187_init_hw(struct ieee80211_hw *dev) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 6568c2ecf20Sopenharmony_ci u8 reg; 6578c2ecf20Sopenharmony_ci int res; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* reset */ 6608c2ecf20Sopenharmony_ci rtl8187_set_anaparam(priv, true); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci msleep(200); 6658c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10); 6668c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11); 6678c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00); 6688c2ecf20Sopenharmony_ci msleep(200); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci res = rtl8187_cmd_reset(dev); 6718c2ecf20Sopenharmony_ci if (res) 6728c2ecf20Sopenharmony_ci return res; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci rtl8187_set_anaparam(priv, true); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci /* setup card */ 6778c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); 6788c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); 6818c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->GPIO0, 1); 6828c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, (__le16 *)0xFFF4, 0xFFFF); 6878c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); 6888c2ecf20Sopenharmony_ci reg &= 0x3F; 6898c2ecf20Sopenharmony_ci reg |= 0x80; 6908c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CONFIG1, reg); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); 6958c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); 6968c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci // TODO: set RESP_RATE and BRSR properly 6998c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); 7008c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* host_usb_init */ 7038c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); 7048c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); 7058c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, (u8 *)0xFE53); 7068c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7)); 7078c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); 7088c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x20); 7098c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); 7108c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x80); 7118c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x80); 7128c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x80); 7138c2ecf20Sopenharmony_ci msleep(100); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); 7168c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); 7178c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); 7188c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, 7198c2ecf20Sopenharmony_ci RTL818X_EEPROM_CMD_CONFIG); 7208c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); 7218c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, 7228c2ecf20Sopenharmony_ci RTL818X_EEPROM_CMD_NORMAL); 7238c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FF7); 7248c2ecf20Sopenharmony_ci msleep(100); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci priv->rf->init(dev); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); 7298c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; 7308c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); 7318c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, (__le16 *)0xFFFE, 0x10); 7328c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->TALLY_SEL, 0x80); 7338c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFFFF, 0x60); 7348c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci return 0; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic const u8 rtl8187b_reg_table[][3] = { 7408c2ecf20Sopenharmony_ci {0xF0, 0x32, 0}, {0xF1, 0x32, 0}, {0xF2, 0x00, 0}, {0xF3, 0x00, 0}, 7418c2ecf20Sopenharmony_ci {0xF4, 0x32, 0}, {0xF5, 0x43, 0}, {0xF6, 0x00, 0}, {0xF7, 0x00, 0}, 7428c2ecf20Sopenharmony_ci {0xF8, 0x46, 0}, {0xF9, 0xA4, 0}, {0xFA, 0x00, 0}, {0xFB, 0x00, 0}, 7438c2ecf20Sopenharmony_ci {0xFC, 0x96, 0}, {0xFD, 0xA4, 0}, {0xFE, 0x00, 0}, {0xFF, 0x00, 0}, 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci {0x58, 0x4B, 1}, {0x59, 0x00, 1}, {0x5A, 0x4B, 1}, {0x5B, 0x00, 1}, 7468c2ecf20Sopenharmony_ci {0x60, 0x4B, 1}, {0x61, 0x09, 1}, {0x62, 0x4B, 1}, {0x63, 0x09, 1}, 7478c2ecf20Sopenharmony_ci {0xCE, 0x0F, 1}, {0xCF, 0x00, 1}, {0xF0, 0x4E, 1}, {0xF1, 0x01, 1}, 7488c2ecf20Sopenharmony_ci {0xF2, 0x02, 1}, {0xF3, 0x03, 1}, {0xF4, 0x04, 1}, {0xF5, 0x05, 1}, 7498c2ecf20Sopenharmony_ci {0xF6, 0x06, 1}, {0xF7, 0x07, 1}, {0xF8, 0x08, 1}, 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci {0x4E, 0x00, 2}, {0x0C, 0x04, 2}, {0x21, 0x61, 2}, {0x22, 0x68, 2}, 7528c2ecf20Sopenharmony_ci {0x23, 0x6F, 2}, {0x24, 0x76, 2}, {0x25, 0x7D, 2}, {0x26, 0x84, 2}, 7538c2ecf20Sopenharmony_ci {0x27, 0x8D, 2}, {0x4D, 0x08, 2}, {0x50, 0x05, 2}, {0x51, 0xF5, 2}, 7548c2ecf20Sopenharmony_ci {0x52, 0x04, 2}, {0x53, 0xA0, 2}, {0x54, 0x1F, 2}, {0x55, 0x23, 2}, 7558c2ecf20Sopenharmony_ci {0x56, 0x45, 2}, {0x57, 0x67, 2}, {0x58, 0x08, 2}, {0x59, 0x08, 2}, 7568c2ecf20Sopenharmony_ci {0x5A, 0x08, 2}, {0x5B, 0x08, 2}, {0x60, 0x08, 2}, {0x61, 0x08, 2}, 7578c2ecf20Sopenharmony_ci {0x62, 0x08, 2}, {0x63, 0x08, 2}, {0x64, 0xCF, 2}, 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci {0x5B, 0x40, 0}, {0x84, 0x88, 0}, {0x85, 0x24, 0}, {0x88, 0x54, 0}, 7608c2ecf20Sopenharmony_ci {0x8B, 0xB8, 0}, {0x8C, 0x07, 0}, {0x8D, 0x00, 0}, {0x94, 0x1B, 0}, 7618c2ecf20Sopenharmony_ci {0x95, 0x12, 0}, {0x96, 0x00, 0}, {0x97, 0x06, 0}, {0x9D, 0x1A, 0}, 7628c2ecf20Sopenharmony_ci {0x9F, 0x10, 0}, {0xB4, 0x22, 0}, {0xBE, 0x80, 0}, {0xDB, 0x00, 0}, 7638c2ecf20Sopenharmony_ci {0xEE, 0x00, 0}, {0x4C, 0x00, 2}, 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci {0x9F, 0x00, 3}, {0x8C, 0x01, 0}, {0x8D, 0x10, 0}, {0x8E, 0x08, 0}, 7668c2ecf20Sopenharmony_ci {0x8F, 0x00, 0} 7678c2ecf20Sopenharmony_ci}; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic int rtl8187b_init_hw(struct ieee80211_hw *dev) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 7728c2ecf20Sopenharmony_ci int res, i; 7738c2ecf20Sopenharmony_ci u8 reg; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci rtl8187_set_anaparam(priv, true); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci /* Reset PLL sequence on 8187B. Realtek note: reduces power 7788c2ecf20Sopenharmony_ci * consumption about 30 mA */ 7798c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFF61, 0x10); 7808c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, (u8 *)0xFF62); 7818c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFF62, reg & ~(1 << 5)); 7828c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFF62, reg | (1 << 5)); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci res = rtl8187_cmd_reset(dev); 7858c2ecf20Sopenharmony_ci if (res) 7868c2ecf20Sopenharmony_ci return res; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci rtl8187_set_anaparam(priv, true); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* BRSR (Basic Rate Set Register) on 8187B looks to be the same as 7918c2ecf20Sopenharmony_ci * RESP_RATE on 8187L in Realtek sources: each bit should be each 7928c2ecf20Sopenharmony_ci * one of the 12 rates, all are enabled */ 7938c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, (__le16 *)0xFF34, 0x0FFF); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); 7968c2ecf20Sopenharmony_ci reg |= RTL818X_CW_CONF_PERPACKET_RETRY; 7978c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* Auto Rate Fallback Register (ARFR): 1M-54M setting */ 8008c2ecf20Sopenharmony_ci rtl818x_iowrite16_idx(priv, (__le16 *)0xFFE0, 0x0FFF, 1); 8018c2ecf20Sopenharmony_ci rtl818x_iowrite8_idx(priv, (u8 *)0xFFE2, 0x00, 1); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci rtl818x_iowrite16_idx(priv, (__le16 *)0xFFD4, 0xFFFF, 1); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, 8068c2ecf20Sopenharmony_ci RTL818X_EEPROM_CMD_CONFIG); 8078c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); 8088c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CONFIG1, (reg & 0x3F) | 0x80); 8098c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, 8108c2ecf20Sopenharmony_ci RTL818X_EEPROM_CMD_NORMAL); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); 8138c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rtl8187b_reg_table); i++) { 8148c2ecf20Sopenharmony_ci rtl818x_iowrite8_idx(priv, 8158c2ecf20Sopenharmony_ci (u8 *)(uintptr_t) 8168c2ecf20Sopenharmony_ci (rtl8187b_reg_table[i][0] | 0xFF00), 8178c2ecf20Sopenharmony_ci rtl8187b_reg_table[i][1], 8188c2ecf20Sopenharmony_ci rtl8187b_reg_table[i][2]); 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50); 8228c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF0, 0, 1); 8258c2ecf20Sopenharmony_ci rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF4, 0, 1); 8268c2ecf20Sopenharmony_ci rtl818x_iowrite8_idx(priv, (u8 *)0xFFF8, 0, 1); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00004001); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci /* RFSW_CTRL register */ 8318c2ecf20Sopenharmony_ci rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x569A, 2); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); 8348c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488); 8358c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); 8368c2ecf20Sopenharmony_ci msleep(100); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci priv->rf->init(dev); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci reg = RTL818X_CMD_TX_ENABLE | RTL818X_CMD_RX_ENABLE; 8418c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CMD, reg); 8428c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFE41, 0xF4); 8458c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x00); 8468c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); 8478c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); 8488c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x0F); 8498c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); 8508c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, (u8 *)0xFFDB); 8538c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFFDB, reg | (1 << 2)); 8548c2ecf20Sopenharmony_ci rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x59FA, 3); 8558c2ecf20Sopenharmony_ci rtl818x_iowrite16_idx(priv, (__le16 *)0xFF74, 0x59D2, 3); 8568c2ecf20Sopenharmony_ci rtl818x_iowrite16_idx(priv, (__le16 *)0xFF76, 0x59D2, 3); 8578c2ecf20Sopenharmony_ci rtl818x_iowrite16_idx(priv, (__le16 *)0xFF78, 0x19FA, 3); 8588c2ecf20Sopenharmony_ci rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7A, 0x19FA, 3); 8598c2ecf20Sopenharmony_ci rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7C, 0x00D0, 3); 8608c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFF61, 0); 8618c2ecf20Sopenharmony_ci rtl818x_iowrite8_idx(priv, (u8 *)0xFF80, 0x0F, 1); 8628c2ecf20Sopenharmony_ci rtl818x_iowrite8_idx(priv, (u8 *)0xFF83, 0x03, 1); 8638c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)0xFFDA, 0x10); 8648c2ecf20Sopenharmony_ci rtl818x_iowrite8_idx(priv, (u8 *)0xFF4D, 0x08, 2); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->HSSI_PARA, 0x0600321B); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci rtl818x_iowrite16_idx(priv, (__le16 *)0xFFEC, 0x0800, 1); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci priv->slot_time = 0x9; 8718c2ecf20Sopenharmony_ci priv->aifsn[0] = 2; /* AIFSN[AC_VO] */ 8728c2ecf20Sopenharmony_ci priv->aifsn[1] = 2; /* AIFSN[AC_VI] */ 8738c2ecf20Sopenharmony_ci priv->aifsn[2] = 7; /* AIFSN[AC_BK] */ 8748c2ecf20Sopenharmony_ci priv->aifsn[3] = 3; /* AIFSN[AC_BE] */ 8758c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* ENEDCA flag must always be set, transmit issues? */ 8788c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_ENEDCA); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci return 0; 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic void rtl8187_work(struct work_struct *work) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci /* The RTL8187 returns the retry count through register 0xFFFA. In 8868c2ecf20Sopenharmony_ci * addition, it appears to be a cumulative retry count, not the 8878c2ecf20Sopenharmony_ci * value for the current TX packet. When multiple TX entries are 8888c2ecf20Sopenharmony_ci * waiting in the queue, the retry count will be the total for all. 8898c2ecf20Sopenharmony_ci * The "error" may matter for purposes of rate setting, but there is 8908c2ecf20Sopenharmony_ci * no other choice with this hardware. 8918c2ecf20Sopenharmony_ci */ 8928c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, 8938c2ecf20Sopenharmony_ci work.work); 8948c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info; 8958c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = priv->dev; 8968c2ecf20Sopenharmony_ci static u16 retry; 8978c2ecf20Sopenharmony_ci u16 tmp; 8988c2ecf20Sopenharmony_ci u16 avg_retry; 8998c2ecf20Sopenharmony_ci int length; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 9028c2ecf20Sopenharmony_ci tmp = rtl818x_ioread16(priv, (__le16 *)0xFFFA); 9038c2ecf20Sopenharmony_ci length = skb_queue_len(&priv->b_tx_status.queue); 9048c2ecf20Sopenharmony_ci if (unlikely(!length)) 9058c2ecf20Sopenharmony_ci length = 1; 9068c2ecf20Sopenharmony_ci if (unlikely(tmp < retry)) 9078c2ecf20Sopenharmony_ci tmp = retry; 9088c2ecf20Sopenharmony_ci avg_retry = (tmp - retry) / length; 9098c2ecf20Sopenharmony_ci while (skb_queue_len(&priv->b_tx_status.queue) > 0) { 9108c2ecf20Sopenharmony_ci struct sk_buff *old_skb; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci old_skb = skb_dequeue(&priv->b_tx_status.queue); 9138c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(old_skb); 9148c2ecf20Sopenharmony_ci info->status.rates[0].count = avg_retry + 1; 9158c2ecf20Sopenharmony_ci if (info->status.rates[0].count > RETRY_COUNT) 9168c2ecf20Sopenharmony_ci info->flags &= ~IEEE80211_TX_STAT_ACK; 9178c2ecf20Sopenharmony_ci ieee80211_tx_status_irqsafe(dev, old_skb); 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci retry = tmp; 9208c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic int rtl8187_start(struct ieee80211_hw *dev) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 9268c2ecf20Sopenharmony_ci u32 reg; 9278c2ecf20Sopenharmony_ci int ret; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) : 9328c2ecf20Sopenharmony_ci rtl8187b_init_hw(dev); 9338c2ecf20Sopenharmony_ci if (ret) 9348c2ecf20Sopenharmony_ci goto rtl8187_start_exit; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci init_usb_anchor(&priv->anchored); 9378c2ecf20Sopenharmony_ci priv->dev = dev; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (priv->is_rtl8187b) { 9408c2ecf20Sopenharmony_ci reg = RTL818X_RX_CONF_MGMT | 9418c2ecf20Sopenharmony_ci RTL818X_RX_CONF_DATA | 9428c2ecf20Sopenharmony_ci RTL818X_RX_CONF_BROADCAST | 9438c2ecf20Sopenharmony_ci RTL818X_RX_CONF_NICMAC | 9448c2ecf20Sopenharmony_ci RTL818X_RX_CONF_BSSID | 9458c2ecf20Sopenharmony_ci (7 << 13 /* RX FIFO threshold NONE */) | 9468c2ecf20Sopenharmony_ci (7 << 10 /* MAX RX DMA */) | 9478c2ecf20Sopenharmony_ci RTL818X_RX_CONF_RX_AUTORESETPHY | 9488c2ecf20Sopenharmony_ci RTL818X_RX_CONF_ONLYERLPKT; 9498c2ecf20Sopenharmony_ci priv->rx_conf = reg; 9508c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); 9538c2ecf20Sopenharmony_ci reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN; 9548c2ecf20Sopenharmony_ci reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL; 9558c2ecf20Sopenharmony_ci reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; 9568c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->TX_CONF, 9598c2ecf20Sopenharmony_ci RTL818X_TX_CONF_HW_SEQNUM | 9608c2ecf20Sopenharmony_ci RTL818X_TX_CONF_DISREQQSIZE | 9618c2ecf20Sopenharmony_ci (RETRY_COUNT << 8 /* short retry limit */) | 9628c2ecf20Sopenharmony_ci (RETRY_COUNT << 0 /* long retry limit */) | 9638c2ecf20Sopenharmony_ci (7 << 21 /* MAX TX DMA */)); 9648c2ecf20Sopenharmony_ci ret = rtl8187_init_urbs(dev); 9658c2ecf20Sopenharmony_ci if (ret) 9668c2ecf20Sopenharmony_ci goto rtl8187_start_exit; 9678c2ecf20Sopenharmony_ci ret = rtl8187b_init_status_urb(dev); 9688c2ecf20Sopenharmony_ci if (ret) 9698c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&priv->anchored); 9708c2ecf20Sopenharmony_ci goto rtl8187_start_exit; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); 9768c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci ret = rtl8187_init_urbs(dev); 9798c2ecf20Sopenharmony_ci if (ret) 9808c2ecf20Sopenharmony_ci goto rtl8187_start_exit; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci reg = RTL818X_RX_CONF_ONLYERLPKT | 9838c2ecf20Sopenharmony_ci RTL818X_RX_CONF_RX_AUTORESETPHY | 9848c2ecf20Sopenharmony_ci RTL818X_RX_CONF_BSSID | 9858c2ecf20Sopenharmony_ci RTL818X_RX_CONF_MGMT | 9868c2ecf20Sopenharmony_ci RTL818X_RX_CONF_DATA | 9878c2ecf20Sopenharmony_ci (7 << 13 /* RX FIFO threshold NONE */) | 9888c2ecf20Sopenharmony_ci (7 << 10 /* MAX RX DMA */) | 9898c2ecf20Sopenharmony_ci RTL818X_RX_CONF_BROADCAST | 9908c2ecf20Sopenharmony_ci RTL818X_RX_CONF_NICMAC; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci priv->rx_conf = reg; 9938c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); 9968c2ecf20Sopenharmony_ci reg &= ~RTL818X_CW_CONF_PERPACKET_CW; 9978c2ecf20Sopenharmony_ci reg |= RTL818X_CW_CONF_PERPACKET_RETRY; 9988c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); 10018c2ecf20Sopenharmony_ci reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN; 10028c2ecf20Sopenharmony_ci reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL; 10038c2ecf20Sopenharmony_ci reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; 10048c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci reg = RTL818X_TX_CONF_CW_MIN | 10078c2ecf20Sopenharmony_ci (7 << 21 /* MAX TX DMA */) | 10088c2ecf20Sopenharmony_ci RTL818X_TX_CONF_NO_ICV; 10098c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->CMD); 10128c2ecf20Sopenharmony_ci reg |= RTL818X_CMD_TX_ENABLE; 10138c2ecf20Sopenharmony_ci reg |= RTL818X_CMD_RX_ENABLE; 10148c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CMD, reg); 10158c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&priv->work, rtl8187_work); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_cirtl8187_start_exit: 10188c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 10198c2ecf20Sopenharmony_ci return ret; 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_cistatic void rtl8187_stop(struct ieee80211_hw *dev) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 10258c2ecf20Sopenharmony_ci struct sk_buff *skb; 10268c2ecf20Sopenharmony_ci u32 reg; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 10298c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->CMD); 10328c2ecf20Sopenharmony_ci reg &= ~RTL818X_CMD_TX_ENABLE; 10338c2ecf20Sopenharmony_ci reg &= ~RTL818X_CMD_RX_ENABLE; 10348c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CMD, reg); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci priv->rf->stop(dev); 10378c2ecf20Sopenharmony_ci rtl8187_set_anaparam(priv, false); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); 10408c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); 10418c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); 10428c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&priv->b_tx_status.queue))) 10458c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&priv->anchored); 10488c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (!priv->is_rtl8187b) 10518c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->work); 10528c2ecf20Sopenharmony_ci} 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_cistatic u64 rtl8187_get_tsf(struct ieee80211_hw *dev, struct ieee80211_vif *vif) 10558c2ecf20Sopenharmony_ci{ 10568c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | 10598c2ecf20Sopenharmony_ci (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic void rtl8187_beacon_work(struct work_struct *work) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct rtl8187_vif *vif_priv = 10668c2ecf20Sopenharmony_ci container_of(work, struct rtl8187_vif, beacon_work.work); 10678c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = 10688c2ecf20Sopenharmony_ci container_of((void *)vif_priv, struct ieee80211_vif, drv_priv); 10698c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = vif_priv->dev; 10708c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt; 10718c2ecf20Sopenharmony_ci struct sk_buff *skb; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci /* don't overflow the tx ring */ 10748c2ecf20Sopenharmony_ci if (ieee80211_queue_stopped(dev, 0)) 10758c2ecf20Sopenharmony_ci goto resched; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* grab a fresh beacon */ 10788c2ecf20Sopenharmony_ci skb = ieee80211_beacon_get(dev, vif); 10798c2ecf20Sopenharmony_ci if (!skb) 10808c2ecf20Sopenharmony_ci goto resched; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* 10838c2ecf20Sopenharmony_ci * update beacon timestamp w/ TSF value 10848c2ecf20Sopenharmony_ci * TODO: make hardware update beacon timestamp 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_ci mgmt = (struct ieee80211_mgmt *)skb->data; 10878c2ecf20Sopenharmony_ci mgmt->u.beacon.timestamp = cpu_to_le64(rtl8187_get_tsf(dev, vif)); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* TODO: use actual beacon queue */ 10908c2ecf20Sopenharmony_ci skb_set_queue_mapping(skb, 0); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci rtl8187_tx(dev, NULL, skb); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ciresched: 10958c2ecf20Sopenharmony_ci /* 10968c2ecf20Sopenharmony_ci * schedule next beacon 10978c2ecf20Sopenharmony_ci * TODO: use hardware support for beacon timing 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_ci schedule_delayed_work(&vif_priv->beacon_work, 11008c2ecf20Sopenharmony_ci usecs_to_jiffies(1024 * vif->bss_conf.beacon_int)); 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistatic int rtl8187_add_interface(struct ieee80211_hw *dev, 11058c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 11068c2ecf20Sopenharmony_ci{ 11078c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 11088c2ecf20Sopenharmony_ci struct rtl8187_vif *vif_priv; 11098c2ecf20Sopenharmony_ci int i; 11108c2ecf20Sopenharmony_ci int ret = -EOPNOTSUPP; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 11138c2ecf20Sopenharmony_ci if (priv->vif) 11148c2ecf20Sopenharmony_ci goto exit; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci switch (vif->type) { 11178c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 11188c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 11198c2ecf20Sopenharmony_ci break; 11208c2ecf20Sopenharmony_ci default: 11218c2ecf20Sopenharmony_ci goto exit; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci ret = 0; 11258c2ecf20Sopenharmony_ci priv->vif = vif; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* Initialize driver private area */ 11288c2ecf20Sopenharmony_ci vif_priv = (struct rtl8187_vif *)&vif->drv_priv; 11298c2ecf20Sopenharmony_ci vif_priv->dev = dev; 11308c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&vif_priv->beacon_work, rtl8187_beacon_work); 11318c2ecf20Sopenharmony_ci vif_priv->enable_beacon = false; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); 11358c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 11368c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->MAC[i], 11378c2ecf20Sopenharmony_ci ((u8 *)vif->addr)[i]); 11388c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ciexit: 11418c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 11428c2ecf20Sopenharmony_ci return ret; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistatic void rtl8187_remove_interface(struct ieee80211_hw *dev, 11468c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 11498c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 11508c2ecf20Sopenharmony_ci priv->vif = NULL; 11518c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_cistatic int rtl8187_config(struct ieee80211_hw *dev, u32 changed) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 11578c2ecf20Sopenharmony_ci struct ieee80211_conf *conf = &dev->conf; 11588c2ecf20Sopenharmony_ci u32 reg; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 11618c2ecf20Sopenharmony_ci reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); 11628c2ecf20Sopenharmony_ci /* Enable TX loopback on MAC level to avoid TX during channel 11638c2ecf20Sopenharmony_ci * changes, as this has be seen to causes problems and the 11648c2ecf20Sopenharmony_ci * card will stop work until next reset 11658c2ecf20Sopenharmony_ci */ 11668c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->TX_CONF, 11678c2ecf20Sopenharmony_ci reg | RTL818X_TX_CONF_LOOPBACK_MAC); 11688c2ecf20Sopenharmony_ci priv->rf->set_chan(dev, conf); 11698c2ecf20Sopenharmony_ci msleep(10); 11708c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2); 11738c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->ATIMTR_INTERVAL, 100); 11748c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100); 11758c2ecf20Sopenharmony_ci rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL_TIME, 100); 11768c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 11778c2ecf20Sopenharmony_ci return 0; 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci/* 11818c2ecf20Sopenharmony_ci * With 8187B, AC_*_PARAM clashes with FEMR definition in struct rtl818x_csr for 11828c2ecf20Sopenharmony_ci * example. Thus we have to use raw values for AC_*_PARAM register addresses. 11838c2ecf20Sopenharmony_ci */ 11848c2ecf20Sopenharmony_cistatic __le32 *rtl8187b_ac_addr[4] = { 11858c2ecf20Sopenharmony_ci (__le32 *) 0xFFF0, /* AC_VO */ 11868c2ecf20Sopenharmony_ci (__le32 *) 0xFFF4, /* AC_VI */ 11878c2ecf20Sopenharmony_ci (__le32 *) 0xFFFC, /* AC_BK */ 11888c2ecf20Sopenharmony_ci (__le32 *) 0xFFF8, /* AC_BE */ 11898c2ecf20Sopenharmony_ci}; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci#define SIFS_TIME 0xa 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic void rtl8187_conf_erp(struct rtl8187_priv *priv, bool use_short_slot, 11948c2ecf20Sopenharmony_ci bool use_short_preamble) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci if (priv->is_rtl8187b) { 11978c2ecf20Sopenharmony_ci u8 difs, eifs; 11988c2ecf20Sopenharmony_ci u16 ack_timeout; 11998c2ecf20Sopenharmony_ci int queue; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci if (use_short_slot) { 12028c2ecf20Sopenharmony_ci priv->slot_time = 0x9; 12038c2ecf20Sopenharmony_ci difs = 0x1c; 12048c2ecf20Sopenharmony_ci eifs = 0x53; 12058c2ecf20Sopenharmony_ci } else { 12068c2ecf20Sopenharmony_ci priv->slot_time = 0x14; 12078c2ecf20Sopenharmony_ci difs = 0x32; 12088c2ecf20Sopenharmony_ci eifs = 0x5b; 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); 12118c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->SLOT, priv->slot_time); 12128c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->DIFS, difs); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* 12158c2ecf20Sopenharmony_ci * BRSR+1 on 8187B is in fact EIFS register 12168c2ecf20Sopenharmony_ci * Value in units of 4 us 12178c2ecf20Sopenharmony_ci */ 12188c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *)&priv->map->BRSR + 1, eifs); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci /* 12218c2ecf20Sopenharmony_ci * For 8187B, CARRIER_SENSE_COUNTER is in fact ack timeout 12228c2ecf20Sopenharmony_ci * register. In units of 4 us like eifs register 12238c2ecf20Sopenharmony_ci * ack_timeout = ack duration + plcp + difs + preamble 12248c2ecf20Sopenharmony_ci */ 12258c2ecf20Sopenharmony_ci ack_timeout = 112 + 48 + difs; 12268c2ecf20Sopenharmony_ci if (use_short_preamble) 12278c2ecf20Sopenharmony_ci ack_timeout += 72; 12288c2ecf20Sopenharmony_ci else 12298c2ecf20Sopenharmony_ci ack_timeout += 144; 12308c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 12318c2ecf20Sopenharmony_ci DIV_ROUND_UP(ack_timeout, 4)); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci for (queue = 0; queue < 4; queue++) 12348c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, (u8 *) rtl8187b_ac_addr[queue], 12358c2ecf20Sopenharmony_ci priv->aifsn[queue] * priv->slot_time + 12368c2ecf20Sopenharmony_ci SIFS_TIME); 12378c2ecf20Sopenharmony_ci } else { 12388c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); 12398c2ecf20Sopenharmony_ci if (use_short_slot) { 12408c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); 12418c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); 12428c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x14); 12438c2ecf20Sopenharmony_ci } else { 12448c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); 12458c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); 12468c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x24); 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci} 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_cistatic void rtl8187_bss_info_changed(struct ieee80211_hw *dev, 12528c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 12538c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *info, 12548c2ecf20Sopenharmony_ci u32 changed) 12558c2ecf20Sopenharmony_ci{ 12568c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 12578c2ecf20Sopenharmony_ci struct rtl8187_vif *vif_priv; 12588c2ecf20Sopenharmony_ci int i; 12598c2ecf20Sopenharmony_ci u8 reg; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci vif_priv = (struct rtl8187_vif *)&vif->drv_priv; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci if (changed & BSS_CHANGED_BSSID) { 12648c2ecf20Sopenharmony_ci mutex_lock(&priv->conf_mutex); 12658c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 12668c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->BSSID[i], 12678c2ecf20Sopenharmony_ci info->bssid[i]); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (priv->is_rtl8187b) 12708c2ecf20Sopenharmony_ci reg = RTL818X_MSR_ENEDCA; 12718c2ecf20Sopenharmony_ci else 12728c2ecf20Sopenharmony_ci reg = 0; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci if (is_valid_ether_addr(info->bssid)) { 12758c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_ADHOC) 12768c2ecf20Sopenharmony_ci reg |= RTL818X_MSR_ADHOC; 12778c2ecf20Sopenharmony_ci else 12788c2ecf20Sopenharmony_ci reg |= RTL818X_MSR_INFRA; 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci else 12818c2ecf20Sopenharmony_ci reg |= RTL818X_MSR_NO_LINK; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->MSR, reg); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci mutex_unlock(&priv->conf_mutex); 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE)) 12898c2ecf20Sopenharmony_ci rtl8187_conf_erp(priv, info->use_short_slot, 12908c2ecf20Sopenharmony_ci info->use_short_preamble); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci if (changed & BSS_CHANGED_BEACON_ENABLED) 12938c2ecf20Sopenharmony_ci vif_priv->enable_beacon = info->enable_beacon; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON)) { 12968c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&vif_priv->beacon_work); 12978c2ecf20Sopenharmony_ci if (vif_priv->enable_beacon) 12988c2ecf20Sopenharmony_ci schedule_work(&vif_priv->beacon_work.work); 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cistatic u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev, 13048c2ecf20Sopenharmony_ci struct netdev_hw_addr_list *mc_list) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci return netdev_hw_addr_list_count(mc_list); 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic void rtl8187_configure_filter(struct ieee80211_hw *dev, 13108c2ecf20Sopenharmony_ci unsigned int changed_flags, 13118c2ecf20Sopenharmony_ci unsigned int *total_flags, 13128c2ecf20Sopenharmony_ci u64 multicast) 13138c2ecf20Sopenharmony_ci{ 13148c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci if (changed_flags & FIF_FCSFAIL) 13178c2ecf20Sopenharmony_ci priv->rx_conf ^= RTL818X_RX_CONF_FCS; 13188c2ecf20Sopenharmony_ci if (changed_flags & FIF_CONTROL) 13198c2ecf20Sopenharmony_ci priv->rx_conf ^= RTL818X_RX_CONF_CTRL; 13208c2ecf20Sopenharmony_ci if (*total_flags & FIF_OTHER_BSS || 13218c2ecf20Sopenharmony_ci *total_flags & FIF_ALLMULTI || multicast > 0) 13228c2ecf20Sopenharmony_ci priv->rx_conf |= RTL818X_RX_CONF_MONITOR; 13238c2ecf20Sopenharmony_ci else 13248c2ecf20Sopenharmony_ci priv->rx_conf &= ~RTL818X_RX_CONF_MONITOR; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci *total_flags = 0; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci if (priv->rx_conf & RTL818X_RX_CONF_FCS) 13298c2ecf20Sopenharmony_ci *total_flags |= FIF_FCSFAIL; 13308c2ecf20Sopenharmony_ci if (priv->rx_conf & RTL818X_RX_CONF_CTRL) 13318c2ecf20Sopenharmony_ci *total_flags |= FIF_CONTROL; 13328c2ecf20Sopenharmony_ci if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) { 13338c2ecf20Sopenharmony_ci *total_flags |= FIF_OTHER_BSS; 13348c2ecf20Sopenharmony_ci *total_flags |= FIF_ALLMULTI; 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf); 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic int rtl8187_conf_tx(struct ieee80211_hw *dev, 13418c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, u16 queue, 13428c2ecf20Sopenharmony_ci const struct ieee80211_tx_queue_params *params) 13438c2ecf20Sopenharmony_ci{ 13448c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 13458c2ecf20Sopenharmony_ci u8 cw_min, cw_max; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci if (queue > 3) 13488c2ecf20Sopenharmony_ci return -EINVAL; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci cw_min = fls(params->cw_min); 13518c2ecf20Sopenharmony_ci cw_max = fls(params->cw_max); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci if (priv->is_rtl8187b) { 13548c2ecf20Sopenharmony_ci priv->aifsn[queue] = params->aifs; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* 13578c2ecf20Sopenharmony_ci * This is the structure of AC_*_PARAM registers in 8187B: 13588c2ecf20Sopenharmony_ci * - TXOP limit field, bit offset = 16 13598c2ecf20Sopenharmony_ci * - ECWmax, bit offset = 12 13608c2ecf20Sopenharmony_ci * - ECWmin, bit offset = 8 13618c2ecf20Sopenharmony_ci * - AIFS, bit offset = 0 13628c2ecf20Sopenharmony_ci */ 13638c2ecf20Sopenharmony_ci rtl818x_iowrite32(priv, rtl8187b_ac_addr[queue], 13648c2ecf20Sopenharmony_ci (params->txop << 16) | (cw_max << 12) | 13658c2ecf20Sopenharmony_ci (cw_min << 8) | (params->aifs * 13668c2ecf20Sopenharmony_ci priv->slot_time + SIFS_TIME)); 13678c2ecf20Sopenharmony_ci } else { 13688c2ecf20Sopenharmony_ci if (queue != 0) 13698c2ecf20Sopenharmony_ci return -EINVAL; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->CW_VAL, 13728c2ecf20Sopenharmony_ci cw_min | (cw_max << 4)); 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci return 0; 13758c2ecf20Sopenharmony_ci} 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_cistatic const struct ieee80211_ops rtl8187_ops = { 13798c2ecf20Sopenharmony_ci .tx = rtl8187_tx, 13808c2ecf20Sopenharmony_ci .start = rtl8187_start, 13818c2ecf20Sopenharmony_ci .stop = rtl8187_stop, 13828c2ecf20Sopenharmony_ci .add_interface = rtl8187_add_interface, 13838c2ecf20Sopenharmony_ci .remove_interface = rtl8187_remove_interface, 13848c2ecf20Sopenharmony_ci .config = rtl8187_config, 13858c2ecf20Sopenharmony_ci .bss_info_changed = rtl8187_bss_info_changed, 13868c2ecf20Sopenharmony_ci .prepare_multicast = rtl8187_prepare_multicast, 13878c2ecf20Sopenharmony_ci .configure_filter = rtl8187_configure_filter, 13888c2ecf20Sopenharmony_ci .conf_tx = rtl8187_conf_tx, 13898c2ecf20Sopenharmony_ci .rfkill_poll = rtl8187_rfkill_poll, 13908c2ecf20Sopenharmony_ci .get_tsf = rtl8187_get_tsf, 13918c2ecf20Sopenharmony_ci}; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_cistatic void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom) 13948c2ecf20Sopenharmony_ci{ 13958c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = eeprom->data; 13968c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 13978c2ecf20Sopenharmony_ci u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; 14008c2ecf20Sopenharmony_ci eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; 14018c2ecf20Sopenharmony_ci eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; 14028c2ecf20Sopenharmony_ci eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; 14038c2ecf20Sopenharmony_ci} 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cistatic void rtl8187_eeprom_register_write(struct eeprom_93cx6 *eeprom) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = eeprom->data; 14088c2ecf20Sopenharmony_ci struct rtl8187_priv *priv = dev->priv; 14098c2ecf20Sopenharmony_ci u8 reg = RTL818X_EEPROM_CMD_PROGRAM; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci if (eeprom->reg_data_in) 14128c2ecf20Sopenharmony_ci reg |= RTL818X_EEPROM_CMD_WRITE; 14138c2ecf20Sopenharmony_ci if (eeprom->reg_data_out) 14148c2ecf20Sopenharmony_ci reg |= RTL818X_EEPROM_CMD_READ; 14158c2ecf20Sopenharmony_ci if (eeprom->reg_data_clock) 14168c2ecf20Sopenharmony_ci reg |= RTL818X_EEPROM_CMD_CK; 14178c2ecf20Sopenharmony_ci if (eeprom->reg_chip_select) 14188c2ecf20Sopenharmony_ci reg |= RTL818X_EEPROM_CMD_CS; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); 14218c2ecf20Sopenharmony_ci udelay(10); 14228c2ecf20Sopenharmony_ci} 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_cistatic int rtl8187_probe(struct usb_interface *intf, 14258c2ecf20Sopenharmony_ci const struct usb_device_id *id) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 14288c2ecf20Sopenharmony_ci struct ieee80211_hw *dev; 14298c2ecf20Sopenharmony_ci struct rtl8187_priv *priv; 14308c2ecf20Sopenharmony_ci struct eeprom_93cx6 eeprom; 14318c2ecf20Sopenharmony_ci struct ieee80211_channel *channel; 14328c2ecf20Sopenharmony_ci const char *chip_name; 14338c2ecf20Sopenharmony_ci u16 txpwr, reg; 14348c2ecf20Sopenharmony_ci u16 product_id = le16_to_cpu(udev->descriptor.idProduct); 14358c2ecf20Sopenharmony_ci int err, i; 14368c2ecf20Sopenharmony_ci u8 mac_addr[ETH_ALEN]; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops); 14398c2ecf20Sopenharmony_ci if (!dev) { 14408c2ecf20Sopenharmony_ci printk(KERN_ERR "rtl8187: ieee80211 alloc failed\n"); 14418c2ecf20Sopenharmony_ci return -ENOMEM; 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci priv = dev->priv; 14458c2ecf20Sopenharmony_ci priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci /* allocate "DMA aware" buffer for register accesses */ 14488c2ecf20Sopenharmony_ci priv->io_dmabuf = kmalloc(sizeof(*priv->io_dmabuf), GFP_KERNEL); 14498c2ecf20Sopenharmony_ci if (!priv->io_dmabuf) { 14508c2ecf20Sopenharmony_ci err = -ENOMEM; 14518c2ecf20Sopenharmony_ci goto err_free_dev; 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci mutex_init(&priv->io_mutex); 14548c2ecf20Sopenharmony_ci mutex_init(&priv->conf_mutex); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci SET_IEEE80211_DEV(dev, &intf->dev); 14578c2ecf20Sopenharmony_ci usb_set_intfdata(intf, dev); 14588c2ecf20Sopenharmony_ci priv->udev = udev; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci usb_get_dev(udev); 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci skb_queue_head_init(&priv->rx_queue); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); 14658c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates)); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); 14688c2ecf20Sopenharmony_ci memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); 14698c2ecf20Sopenharmony_ci priv->map = (struct rtl818x_csr *)0xFF00; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci priv->band.band = NL80211_BAND_2GHZ; 14728c2ecf20Sopenharmony_ci priv->band.channels = priv->channels; 14738c2ecf20Sopenharmony_ci priv->band.n_channels = ARRAY_SIZE(rtl818x_channels); 14748c2ecf20Sopenharmony_ci priv->band.bitrates = priv->rates; 14758c2ecf20Sopenharmony_ci priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); 14768c2ecf20Sopenharmony_ci dev->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci ieee80211_hw_set(dev, RX_INCLUDES_FCS); 14808c2ecf20Sopenharmony_ci ieee80211_hw_set(dev, HOST_BROADCAST_PS_BUFFERING); 14818c2ecf20Sopenharmony_ci ieee80211_hw_set(dev, SIGNAL_DBM); 14828c2ecf20Sopenharmony_ci /* Initialize rate-control variables */ 14838c2ecf20Sopenharmony_ci dev->max_rates = 1; 14848c2ecf20Sopenharmony_ci dev->max_rate_tries = RETRY_COUNT; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci eeprom.data = dev; 14878c2ecf20Sopenharmony_ci eeprom.register_read = rtl8187_eeprom_register_read; 14888c2ecf20Sopenharmony_ci eeprom.register_write = rtl8187_eeprom_register_write; 14898c2ecf20Sopenharmony_ci if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) 14908c2ecf20Sopenharmony_ci eeprom.width = PCI_EEPROM_WIDTH_93C66; 14918c2ecf20Sopenharmony_ci else 14928c2ecf20Sopenharmony_ci eeprom.width = PCI_EEPROM_WIDTH_93C46; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); 14958c2ecf20Sopenharmony_ci udelay(10); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci eeprom_93cx6_multiread(&eeprom, RTL8187_EEPROM_MAC_ADDR, 14988c2ecf20Sopenharmony_ci (__le16 __force *)mac_addr, 3); 14998c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(mac_addr)) { 15008c2ecf20Sopenharmony_ci printk(KERN_WARNING "rtl8187: Invalid hwaddr! Using randomly " 15018c2ecf20Sopenharmony_ci "generated MAC address\n"); 15028c2ecf20Sopenharmony_ci eth_random_addr(mac_addr); 15038c2ecf20Sopenharmony_ci } 15048c2ecf20Sopenharmony_ci SET_IEEE80211_PERM_ADDR(dev, mac_addr); 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci channel = priv->channels; 15078c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 15088c2ecf20Sopenharmony_ci eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_1 + i, 15098c2ecf20Sopenharmony_ci &txpwr); 15108c2ecf20Sopenharmony_ci (*channel++).hw_value = txpwr & 0xFF; 15118c2ecf20Sopenharmony_ci (*channel++).hw_value = txpwr >> 8; 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 15148c2ecf20Sopenharmony_ci eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_4 + i, 15158c2ecf20Sopenharmony_ci &txpwr); 15168c2ecf20Sopenharmony_ci (*channel++).hw_value = txpwr & 0xFF; 15178c2ecf20Sopenharmony_ci (*channel++).hw_value = txpwr >> 8; 15188c2ecf20Sopenharmony_ci } 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_BASE, 15218c2ecf20Sopenharmony_ci &priv->txpwr_base); 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; 15248c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); 15258c2ecf20Sopenharmony_ci /* 0 means asic B-cut, we should use SW 3 wire 15268c2ecf20Sopenharmony_ci * bit-by-bit banging for radio. 1 means we can use 15278c2ecf20Sopenharmony_ci * USB specific request to write radio registers */ 15288c2ecf20Sopenharmony_ci priv->asic_rev = rtl818x_ioread8(priv, (u8 *)0xFFFE) & 0x3; 15298c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); 15308c2ecf20Sopenharmony_ci rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci if (!priv->is_rtl8187b) { 15338c2ecf20Sopenharmony_ci u32 reg32; 15348c2ecf20Sopenharmony_ci reg32 = rtl818x_ioread32(priv, &priv->map->TX_CONF); 15358c2ecf20Sopenharmony_ci reg32 &= RTL818X_TX_CONF_HWVER_MASK; 15368c2ecf20Sopenharmony_ci switch (reg32) { 15378c2ecf20Sopenharmony_ci case RTL818X_TX_CONF_R8187vD_B: 15388c2ecf20Sopenharmony_ci /* Some RTL8187B devices have a USB ID of 0x8187 15398c2ecf20Sopenharmony_ci * detect them here */ 15408c2ecf20Sopenharmony_ci chip_name = "RTL8187BvB(early)"; 15418c2ecf20Sopenharmony_ci priv->is_rtl8187b = 1; 15428c2ecf20Sopenharmony_ci priv->hw_rev = RTL8187BvB; 15438c2ecf20Sopenharmony_ci break; 15448c2ecf20Sopenharmony_ci case RTL818X_TX_CONF_R8187vD: 15458c2ecf20Sopenharmony_ci chip_name = "RTL8187vD"; 15468c2ecf20Sopenharmony_ci break; 15478c2ecf20Sopenharmony_ci default: 15488c2ecf20Sopenharmony_ci chip_name = "RTL8187vB (default)"; 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci } else { 15518c2ecf20Sopenharmony_ci /* 15528c2ecf20Sopenharmony_ci * Force USB request to write radio registers for 8187B, Realtek 15538c2ecf20Sopenharmony_ci * only uses it in their sources 15548c2ecf20Sopenharmony_ci */ 15558c2ecf20Sopenharmony_ci /*if (priv->asic_rev == 0) { 15568c2ecf20Sopenharmony_ci printk(KERN_WARNING "rtl8187: Forcing use of USB " 15578c2ecf20Sopenharmony_ci "requests to write to radio registers\n"); 15588c2ecf20Sopenharmony_ci priv->asic_rev = 1; 15598c2ecf20Sopenharmony_ci }*/ 15608c2ecf20Sopenharmony_ci switch (rtl818x_ioread8(priv, (u8 *)0xFFE1)) { 15618c2ecf20Sopenharmony_ci case RTL818X_R8187B_B: 15628c2ecf20Sopenharmony_ci chip_name = "RTL8187BvB"; 15638c2ecf20Sopenharmony_ci priv->hw_rev = RTL8187BvB; 15648c2ecf20Sopenharmony_ci break; 15658c2ecf20Sopenharmony_ci case RTL818X_R8187B_D: 15668c2ecf20Sopenharmony_ci chip_name = "RTL8187BvD"; 15678c2ecf20Sopenharmony_ci priv->hw_rev = RTL8187BvD; 15688c2ecf20Sopenharmony_ci break; 15698c2ecf20Sopenharmony_ci case RTL818X_R8187B_E: 15708c2ecf20Sopenharmony_ci chip_name = "RTL8187BvE"; 15718c2ecf20Sopenharmony_ci priv->hw_rev = RTL8187BvE; 15728c2ecf20Sopenharmony_ci break; 15738c2ecf20Sopenharmony_ci default: 15748c2ecf20Sopenharmony_ci chip_name = "RTL8187BvB (default)"; 15758c2ecf20Sopenharmony_ci priv->hw_rev = RTL8187BvB; 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ci } 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci if (!priv->is_rtl8187b) { 15808c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 15818c2ecf20Sopenharmony_ci eeprom_93cx6_read(&eeprom, 15828c2ecf20Sopenharmony_ci RTL8187_EEPROM_TXPWR_CHAN_6 + i, 15838c2ecf20Sopenharmony_ci &txpwr); 15848c2ecf20Sopenharmony_ci (*channel++).hw_value = txpwr & 0xFF; 15858c2ecf20Sopenharmony_ci (*channel++).hw_value = txpwr >> 8; 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci } else { 15888c2ecf20Sopenharmony_ci eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6, 15898c2ecf20Sopenharmony_ci &txpwr); 15908c2ecf20Sopenharmony_ci (*channel++).hw_value = txpwr & 0xFF; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci eeprom_93cx6_read(&eeprom, 0x0A, &txpwr); 15938c2ecf20Sopenharmony_ci (*channel++).hw_value = txpwr & 0xFF; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci eeprom_93cx6_read(&eeprom, 0x1C, &txpwr); 15968c2ecf20Sopenharmony_ci (*channel++).hw_value = txpwr & 0xFF; 15978c2ecf20Sopenharmony_ci (*channel++).hw_value = txpwr >> 8; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci /* Handle the differing rfkill GPIO bit in different models */ 16008c2ecf20Sopenharmony_ci priv->rfkill_mask = RFKILL_MASK_8187_89_97; 16018c2ecf20Sopenharmony_ci if (product_id == 0x8197 || product_id == 0x8198) { 16028c2ecf20Sopenharmony_ci eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_SELECT_GPIO, ®); 16038c2ecf20Sopenharmony_ci if (reg & 0xFF00) 16048c2ecf20Sopenharmony_ci priv->rfkill_mask = RFKILL_MASK_8198; 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci dev->vif_data_size = sizeof(struct rtl8187_vif); 16078c2ecf20Sopenharmony_ci dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | 16088c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_ADHOC) ; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci wiphy_ext_feature_set(dev->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b) 16138c2ecf20Sopenharmony_ci printk(KERN_INFO "rtl8187: inconsistency between id with OEM" 16148c2ecf20Sopenharmony_ci " info!\n"); 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci priv->rf = rtl8187_detect_rf(dev); 16178c2ecf20Sopenharmony_ci dev->extra_tx_headroom = (!priv->is_rtl8187b) ? 16188c2ecf20Sopenharmony_ci sizeof(struct rtl8187_tx_hdr) : 16198c2ecf20Sopenharmony_ci sizeof(struct rtl8187b_tx_hdr); 16208c2ecf20Sopenharmony_ci if (!priv->is_rtl8187b) 16218c2ecf20Sopenharmony_ci dev->queues = 1; 16228c2ecf20Sopenharmony_ci else 16238c2ecf20Sopenharmony_ci dev->queues = 4; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci err = ieee80211_register_hw(dev); 16268c2ecf20Sopenharmony_ci if (err) { 16278c2ecf20Sopenharmony_ci printk(KERN_ERR "rtl8187: Cannot register device\n"); 16288c2ecf20Sopenharmony_ci goto err_free_dmabuf; 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci skb_queue_head_init(&priv->b_tx_status.queue); 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci wiphy_info(dev->wiphy, "hwaddr %pM, %s V%d + %s, rfkill mask %d\n", 16338c2ecf20Sopenharmony_ci mac_addr, chip_name, priv->asic_rev, priv->rf->name, 16348c2ecf20Sopenharmony_ci priv->rfkill_mask); 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci#ifdef CONFIG_RTL8187_LEDS 16378c2ecf20Sopenharmony_ci eeprom_93cx6_read(&eeprom, 0x3F, ®); 16388c2ecf20Sopenharmony_ci reg &= 0xFF; 16398c2ecf20Sopenharmony_ci rtl8187_leds_init(dev, reg); 16408c2ecf20Sopenharmony_ci#endif 16418c2ecf20Sopenharmony_ci rtl8187_rfkill_init(dev); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci return 0; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci err_free_dmabuf: 16468c2ecf20Sopenharmony_ci kfree(priv->io_dmabuf); 16478c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 16488c2ecf20Sopenharmony_ci usb_put_dev(udev); 16498c2ecf20Sopenharmony_ci err_free_dev: 16508c2ecf20Sopenharmony_ci ieee80211_free_hw(dev); 16518c2ecf20Sopenharmony_ci return err; 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_cistatic void rtl8187_disconnect(struct usb_interface *intf) 16558c2ecf20Sopenharmony_ci{ 16568c2ecf20Sopenharmony_ci struct ieee80211_hw *dev = usb_get_intfdata(intf); 16578c2ecf20Sopenharmony_ci struct rtl8187_priv *priv; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci if (!dev) 16608c2ecf20Sopenharmony_ci return; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci#ifdef CONFIG_RTL8187_LEDS 16638c2ecf20Sopenharmony_ci rtl8187_leds_exit(dev); 16648c2ecf20Sopenharmony_ci#endif 16658c2ecf20Sopenharmony_ci rtl8187_rfkill_exit(dev); 16668c2ecf20Sopenharmony_ci ieee80211_unregister_hw(dev); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci priv = dev->priv; 16698c2ecf20Sopenharmony_ci usb_reset_device(priv->udev); 16708c2ecf20Sopenharmony_ci usb_put_dev(interface_to_usbdev(intf)); 16718c2ecf20Sopenharmony_ci kfree(priv->io_dmabuf); 16728c2ecf20Sopenharmony_ci ieee80211_free_hw(dev); 16738c2ecf20Sopenharmony_ci} 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_cistatic struct usb_driver rtl8187_driver = { 16768c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 16778c2ecf20Sopenharmony_ci .id_table = rtl8187_table, 16788c2ecf20Sopenharmony_ci .probe = rtl8187_probe, 16798c2ecf20Sopenharmony_ci .disconnect = rtl8187_disconnect, 16808c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 16818c2ecf20Sopenharmony_ci}; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_cimodule_usb_driver(rtl8187_driver); 1684