|
@@ -8,7 +8,7 @@
|
|
|
* Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
|
|
|
*
|
|
|
* The driver was extended to the RTL8187B in 2008 by:
|
|
|
- * Herton Ronaldo Krzesinski <herton@mandriva.com.br>
|
|
|
+ * Herton Ronaldo Krzesinski <herton@mandriva.com.br>
|
|
|
* Hin-Tak Leung <htl10@users.sourceforge.net>
|
|
|
* Larry Finger <Larry.Finger@lwfinger.net>
|
|
|
*
|
|
@@ -232,6 +232,7 @@ static void rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
|
|
|
{
|
|
|
struct rtl8187_priv *priv = dev->priv;
|
|
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
|
|
+ struct ieee80211_hdr *tx_hdr = (struct ieee80211_hdr *)(skb->data);
|
|
|
unsigned int ep;
|
|
|
void *buf;
|
|
|
struct urb *urb;
|
|
@@ -249,7 +250,7 @@ static void rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
|
|
|
flags |= RTL818X_TX_DESC_FLAG_NO_ENC;
|
|
|
|
|
|
flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24;
|
|
|
- if (ieee80211_has_morefrags(((struct ieee80211_hdr *)skb->data)->frame_control))
|
|
|
+ if (ieee80211_has_morefrags(tx_hdr->frame_control))
|
|
|
flags |= RTL818X_TX_DESC_FLAG_MOREFRAG;
|
|
|
if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
|
|
|
flags |= RTL818X_TX_DESC_FLAG_RTS;
|
|
@@ -261,6 +262,13 @@ static void rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
|
|
|
flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
|
|
|
}
|
|
|
|
|
|
+ if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
|
|
|
+ if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
|
|
|
+ priv->seqno += 0x10;
|
|
|
+ tx_hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
|
|
|
+ tx_hdr->seq_ctrl |= cpu_to_le16(priv->seqno);
|
|
|
+ }
|
|
|
+
|
|
|
if (!priv->is_rtl8187b) {
|
|
|
struct rtl8187_tx_hdr *hdr =
|
|
|
(struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
|
|
@@ -274,8 +282,6 @@ static void rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
|
|
|
} else {
|
|
|
/* fc needs to be calculated before skb_push() */
|
|
|
unsigned int epmap[4] = { 6, 7, 5, 4 };
|
|
|
- struct ieee80211_hdr *tx_hdr =
|
|
|
- (struct ieee80211_hdr *)(skb->data);
|
|
|
u16 fc = le16_to_cpu(tx_hdr->frame_control);
|
|
|
|
|
|
struct rtl8187b_tx_hdr *hdr =
|
|
@@ -1031,10 +1037,61 @@ static void rtl8187_stop(struct ieee80211_hw *dev)
|
|
|
cancel_delayed_work_sync(&priv->work);
|
|
|
}
|
|
|
|
|
|
+static u64 rtl8187_get_tsf(struct ieee80211_hw *dev, struct ieee80211_vif *vif)
|
|
|
+{
|
|
|
+ struct rtl8187_priv *priv = dev->priv;
|
|
|
+
|
|
|
+ return rtl818x_ioread32(priv, &priv->map->TSFT[0]) |
|
|
|
+ (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void rtl8187_beacon_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct rtl8187_vif *vif_priv =
|
|
|
+ container_of(work, struct rtl8187_vif, beacon_work.work);
|
|
|
+ struct ieee80211_vif *vif =
|
|
|
+ container_of((void *)vif_priv, struct ieee80211_vif, drv_priv);
|
|
|
+ struct ieee80211_hw *dev = vif_priv->dev;
|
|
|
+ struct ieee80211_mgmt *mgmt;
|
|
|
+ struct sk_buff *skb;
|
|
|
+
|
|
|
+ /* don't overflow the tx ring */
|
|
|
+ if (ieee80211_queue_stopped(dev, 0))
|
|
|
+ goto resched;
|
|
|
+
|
|
|
+ /* grab a fresh beacon */
|
|
|
+ skb = ieee80211_beacon_get(dev, vif);
|
|
|
+ if (!skb)
|
|
|
+ goto resched;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * update beacon timestamp w/ TSF value
|
|
|
+ * TODO: make hardware update beacon timestamp
|
|
|
+ */
|
|
|
+ mgmt = (struct ieee80211_mgmt *)skb->data;
|
|
|
+ mgmt->u.beacon.timestamp = cpu_to_le64(rtl8187_get_tsf(dev, vif));
|
|
|
+
|
|
|
+ /* TODO: use actual beacon queue */
|
|
|
+ skb_set_queue_mapping(skb, 0);
|
|
|
+
|
|
|
+ rtl8187_tx(dev, skb);
|
|
|
+
|
|
|
+resched:
|
|
|
+ /*
|
|
|
+ * schedule next beacon
|
|
|
+ * TODO: use hardware support for beacon timing
|
|
|
+ */
|
|
|
+ schedule_delayed_work(&vif_priv->beacon_work,
|
|
|
+ usecs_to_jiffies(1024 * vif->bss_conf.beacon_int));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static int rtl8187_add_interface(struct ieee80211_hw *dev,
|
|
|
struct ieee80211_vif *vif)
|
|
|
{
|
|
|
struct rtl8187_priv *priv = dev->priv;
|
|
|
+ struct rtl8187_vif *vif_priv;
|
|
|
int i;
|
|
|
int ret = -EOPNOTSUPP;
|
|
|
|
|
@@ -1044,6 +1101,7 @@ static int rtl8187_add_interface(struct ieee80211_hw *dev,
|
|
|
|
|
|
switch (vif->type) {
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
+ case NL80211_IFTYPE_ADHOC:
|
|
|
break;
|
|
|
default:
|
|
|
goto exit;
|
|
@@ -1052,6 +1110,13 @@ static int rtl8187_add_interface(struct ieee80211_hw *dev,
|
|
|
ret = 0;
|
|
|
priv->vif = vif;
|
|
|
|
|
|
+ /* Initialize driver private area */
|
|
|
+ vif_priv = (struct rtl8187_vif *)&vif->drv_priv;
|
|
|
+ vif_priv->dev = dev;
|
|
|
+ INIT_DELAYED_WORK(&vif_priv->beacon_work, rtl8187_beacon_work);
|
|
|
+ vif_priv->enable_beacon = false;
|
|
|
+
|
|
|
+
|
|
|
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
|
|
|
for (i = 0; i < ETH_ALEN; i++)
|
|
|
rtl818x_iowrite8(priv, &priv->map->MAC[i],
|
|
@@ -1175,9 +1240,12 @@ static void rtl8187_bss_info_changed(struct ieee80211_hw *dev,
|
|
|
u32 changed)
|
|
|
{
|
|
|
struct rtl8187_priv *priv = dev->priv;
|
|
|
+ struct rtl8187_vif *vif_priv;
|
|
|
int i;
|
|
|
u8 reg;
|
|
|
|
|
|
+ vif_priv = (struct rtl8187_vif *)&vif->drv_priv;
|
|
|
+
|
|
|
if (changed & BSS_CHANGED_BSSID) {
|
|
|
mutex_lock(&priv->conf_mutex);
|
|
|
for (i = 0; i < ETH_ALEN; i++)
|
|
@@ -1189,8 +1257,12 @@ static void rtl8187_bss_info_changed(struct ieee80211_hw *dev,
|
|
|
else
|
|
|
reg = 0;
|
|
|
|
|
|
- if (is_valid_ether_addr(info->bssid))
|
|
|
- reg |= RTL818X_MSR_INFRA;
|
|
|
+ if (is_valid_ether_addr(info->bssid)) {
|
|
|
+ if (vif->type == NL80211_IFTYPE_ADHOC)
|
|
|
+ reg |= RTL818X_MSR_ADHOC;
|
|
|
+ else
|
|
|
+ reg |= RTL818X_MSR_INFRA;
|
|
|
+ }
|
|
|
else
|
|
|
reg |= RTL818X_MSR_NO_LINK;
|
|
|
|
|
@@ -1202,6 +1274,16 @@ static void rtl8187_bss_info_changed(struct ieee80211_hw *dev,
|
|
|
if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE))
|
|
|
rtl8187_conf_erp(priv, info->use_short_slot,
|
|
|
info->use_short_preamble);
|
|
|
+
|
|
|
+ if (changed & BSS_CHANGED_BEACON_ENABLED)
|
|
|
+ vif_priv->enable_beacon = info->enable_beacon;
|
|
|
+
|
|
|
+ if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON)) {
|
|
|
+ cancel_delayed_work_sync(&vif_priv->beacon_work);
|
|
|
+ if (vif_priv->enable_beacon)
|
|
|
+ schedule_work(&vif_priv->beacon_work.work);
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
static u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev,
|
|
@@ -1279,13 +1361,6 @@ static int rtl8187_conf_tx(struct ieee80211_hw *dev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static u64 rtl8187_get_tsf(struct ieee80211_hw *dev, struct ieee80211_vif *vif)
|
|
|
-{
|
|
|
- struct rtl8187_priv *priv = dev->priv;
|
|
|
-
|
|
|
- return rtl818x_ioread32(priv, &priv->map->TSFT[0]) |
|
|
|
- (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32;
|
|
|
-}
|
|
|
|
|
|
static const struct ieee80211_ops rtl8187_ops = {
|
|
|
.tx = rtl8187_tx,
|
|
@@ -1514,12 +1589,9 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
|
|
|
if (reg & 0xFF00)
|
|
|
priv->rfkill_mask = RFKILL_MASK_8198;
|
|
|
}
|
|
|
-
|
|
|
- /*
|
|
|
- * XXX: Once this driver supports anything that requires
|
|
|
- * beacons it must implement IEEE80211_TX_CTL_ASSIGN_SEQ.
|
|
|
- */
|
|
|
- dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
|
|
|
+ dev->vif_data_size = sizeof(struct rtl8187_vif);
|
|
|
+ dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
|
|
|
+ BIT(NL80211_IFTYPE_ADHOC) ;
|
|
|
|
|
|
if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b)
|
|
|
printk(KERN_INFO "rtl8187: inconsistency between id with OEM"
|