* 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>
  *
 {
        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;
        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;
                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));
        } 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 =
                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;
 
 
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_ADHOC:
                break;
        default:
                goto exit;
        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],
                                     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++)
                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;
 
        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,
        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,
                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"