Project import
diff --git a/0001-LinuxReleaseAth6kl-compat-wireless-add-11w-support.patch b/0001-LinuxReleaseAth6kl-compat-wireless-add-11w-support.patch
new file mode 100644
index 0000000..e6c7c0c
--- /dev/null
+++ b/0001-LinuxReleaseAth6kl-compat-wireless-add-11w-support.patch
@@ -0,0 +1,893 @@
+From 186f818bcf09ecd1290309d38844a349124c1386 Mon Sep 17 00:00:00 2001
+From: Shijie Zhang <shijiez@qca.qualcomm.com>
+Date: Mon, 20 May 2013 14:00:08 +0800
+Subject: [PATCH] LinuxWapiCcxReleaseAth6kl:compat-wireless: add 11w support
+ add optional support from wpa_supplicant to host driver for
+ linux packages Signed-off-by: Shijie Zhang
+ <shijiez@qca.qualcomm.com>
+
+---
+ config.mk                                     |    1 +
+ drivers/net/wireless/ath/ath6kl/cfg80211.c    |  103 +++++++++++++++++++++++--
+ drivers/net/wireless/ath/ath6kl/common.h      |    3 +
+ drivers/net/wireless/ath/ath6kl/core.h        |   10 +++
+ drivers/net/wireless/ath/ath6kl/debugfs_pri.c |   97 +++++++++++++++++++++++
+ drivers/net/wireless/ath/ath6kl/main.c        |   14 ++++
+ drivers/net/wireless/ath/ath6kl/wmi.c         |   90 +++++++++++++++++++++
+ drivers/net/wireless/ath/ath6kl/wmi.h         |   44 +++++++++++
+ include/linux/ieee80211.h                     |    5 ++
+ include/linux/nl80211.h                       |    8 +-
+ include/net/cfg80211.h                        |    6 +-
+ net/mac80211/ieee80211_i.h                    |    6 +-
+ net/mac80211/mlme.c                           |   12 ++-
+ net/wireless/core.h                           |    4 +-
+ net/wireless/mlme.c                           |    8 +-
+ net/wireless/nl80211.c                        |   31 +++++---
+ net/wireless/sme.c                            |    4 +-
+ 17 files changed, 405 insertions(+), 41 deletions(-)
+
+diff --git a/config.mk b/config.mk
+index 5567d90..daad171 100644
+--- a/config.mk
++++ b/config.mk
+@@ -617,6 +617,7 @@ CONFIG_ATH6KL_SDIO=m
+ CONFIG_ATH6KL_USB=m
+ CONFIG_ATH6KL_DEBUG=y
+ CONFIG_ATH6KL_REGDOMAIN=y
++CONFIG_SUPPORT_11W=y
+ endif #CONFIG_COMPAT_KERNEL_2_6_27
+ 
+ ifndef CONFIG_COMPAT_KERNEL_2_6_29
+diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
+index 7e8c827..8bcd06e 100644
+--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
++++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
+@@ -279,7 +279,24 @@ static void ath6kl_set_key_mgmt(struct ath6kl_vif *vif, u32 key_mgmt)
+ 			vif->auth_mode = WPA_AUTH_CCKM;
+ 		else if (vif->auth_mode == WPA2_AUTH)
+ 			vif->auth_mode = WPA2_AUTH_CCKM;
++#ifdef CONFIG_SUPPORT_11W
++	} else if (key_mgmt == WLAN_AKM_SUITE_8021X_SHA256) {
++		if (vif->auth_mode == WPA_AUTH){
++			ath6kl_err("%s: auth_mode %x not supported key_mgmt %x\n", __func__, vif->auth_mode,key_mgmt);
++			return ;
++		}
++		else if (vif->auth_mode == WPA2_AUTH)
++			vif->auth_mode = WPA2_AUTH_SHA256;
++	} else if (key_mgmt == WLAN_AKM_SUITE_PSK_SHA256) {
++		if (vif->auth_mode == WPA_AUTH){
++			ath6kl_err("%s: auth_mode %x not supported key_mgmt %x\n", __func__, vif->auth_mode,key_mgmt);
++			return ;
++		}
++		else if (vif->auth_mode == WPA2_AUTH)
++			vif->auth_mode = WPA2_PSK_AUTH_SHA256;
++#endif
+ 	} else if (key_mgmt != WLAN_AKM_SUITE_8021X) {
++
+ 		vif->auth_mode = NONE_AUTH;
+ 	}
+ }
+@@ -530,6 +547,21 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ 	if (sme->bssid && !is_broadcast_ether_addr(sme->bssid))
+ 		memcpy(vif->req_bssid, sme->bssid, sizeof(vif->req_bssid));
+ 
++#ifdef CONFIG_SUPPORT_11W
++	{
++		u16 rsn_cap =  0;
++        
++        ath6kl_err("ath6kl_cfg80211_connect: sme->mfp = %d\n", sme->mfp);
++        if ( sme->mfp == NL80211_MFP_REQUIRED ) {
++            rsn_cap = 0xc0;
++        }else if ( sme->mfp == NL80211_MFP_OPTIONAL ) {
++            rsn_cap = 0x80;
++        }
++		ath6kl_wmi_set_rsn_cap_cmd(ar->wmi, vif->fw_vif_idx, rsn_cap);
++	}
++#endif
++
++
+ 	ath6kl_set_wpa_version(vif, sme->crypto.wpa_versions);
+ 
+ 	status = ath6kl_set_auth_type(vif, sme->auth_type);
+@@ -1132,6 +1164,9 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ 	int seq_len;
+ 	u8 key_usage;
+ 	u8 key_type;
++#ifdef CONFIG_SUPPORT_11W
++	u8 max_key_index;
++#endif
+ 
+ 	if (!ath6kl_cfg80211_ready(vif))
+ 		return -EIO;
+@@ -1143,10 +1178,19 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ 					      params->key);
+ 	}
+ 
++
++#ifdef CONFIG_SUPPORT_11W
++	if (WLAN_CIPHER_SUITE_AES_CMAC == params->cipher)
++		max_key_index = WMI_MAX_SUPPORT_11W_KEY_INDEX;
++	else
++		max_key_index = WMI_MAX_KEY_INDEX;
++	if (key_index > max_key_index) {
++#else
+ 	if (key_index > WMI_MAX_KEY_INDEX) {
++#endif
+ 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+-			   "%s: key index %d out of bounds\n", __func__,
+-			   key_index);
++				"%s: key index %d out of bounds\n", __func__,
++				key_index);
+ 		return -ENOENT;
+ 	}
+ 
+@@ -1191,6 +1235,12 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ 		key_type = WAPI_CRYPT;
+ 		break;
+ 
++#ifdef CONFIG_SUPPORT_11W
++	case WLAN_CIPHER_SUITE_AES_CMAC:
++		key_type = AES_128_CMAC_CRYPT;
++		break;
++#endif
++
+ 	default:
+ 		return -ENOTSUPP;
+ 	}
+@@ -1240,11 +1290,24 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ 		return 0;
+ 	}
+ 
+-	return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
+-				     key_type, key_usage, key->key_len,
+-				     key->seq, key->seq_len, key->key,
+-				     KEY_OP_INIT_VAL,
+-				     (u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
++#ifdef CONFIG_SUPPORT_11W
++	if (AES_128_CMAC_CRYPT == key_type){
++		return ath6kl_wmi_addigtk_cmd(ar->wmi, vif->fw_vif_idx, key_index,
++				key_type, key_usage, key->key_len,
++				key->seq, key->seq_len, key->key,
++				KEY_OP_INIT_VAL,
++				(u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
++	}else {
++#endif
++		return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
++				key_type, key_usage, key->key_len,
++				key->seq, key->seq_len, key->key,
++				KEY_OP_INIT_VAL,
++				(u8 *) mac_addr, SYNC_BOTH_WMIFLAG);
++#ifdef CONFIG_SUPPORT_11W
++	}
++#endif
++
+ }
+ 
+ static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+@@ -1259,7 +1322,12 @@ static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+ 	if (!ath6kl_cfg80211_ready(vif))
+ 		return -EIO;
+ 
++#ifdef CONFIG_SUPPORT_11W
++	if (key_index > WMI_MAX_SUPPORT_11W_KEY_INDEX) {
++#else
+ 	if (key_index > WMI_MAX_KEY_INDEX) {
++#endif
++
+ 		ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+ 			   "%s: key index %d out of bounds\n", __func__,
+ 			   key_index);
+@@ -1711,6 +1779,9 @@ static const u32 cipher_suites[] = {
+ 	WLAN_CIPHER_SUITE_CCMP,
+ 	CCKM_KRK_CIPHER_SUITE,
+ 	WLAN_CIPHER_SUITE_SMS4,
++#ifdef CONFIG_SUPPORT_11W
++	WLAN_CIPHER_SUITE_AES_CMAC,
++#endif
+ };
+ 
+ static bool is_rate_legacy(s32 rate)
+@@ -2789,6 +2860,20 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
+ 			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ 				p.auth_mode |= WPA2_PSK_AUTH;
+ 			break;
++#ifdef CONFIG_SUPPORT_11W
++		case WLAN_AKM_SUITE_8021X_SHA256:
++			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
++				ath6kl_err("WLAN_AKM_SUITE_8021X_SHA256 is not supported in wpa_versions %x\n",info->crypto.wpa_versions);
++			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
++				p.auth_mode |= WPA2_AUTH_SHA256;
++			break;
++		case WLAN_AKM_SUITE_PSK_SHA256:
++			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
++				ath6kl_err("WLAN_AKM_SUITE_PSK_SHA256 is not supported in wpa_versions %x\n",info->crypto.wpa_versions);
++			if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
++				p.auth_mode |= WPA2_PSK_AUTH_SHA256;
++			break;
++#endif
+ 		}
+ 	}
+ 	if (p.auth_mode == 0)
+@@ -2858,7 +2943,11 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
+ 	 * advertise the same in beacon/probe response. Send
+ 	 * the complete RSN IE capability field to firmware
+ 	 */
++#ifdef CONFIG_SUPPORT_11W
++	if ((p.auth_mode & (WPA2_AUTH | WPA2_PSK_AUTH | WPA2_AUTH_SHA256 | WPA2_PSK_AUTH_SHA256)) && (info->tail) &&
++#else
+ 	if ((p.auth_mode & (WPA2_AUTH | WPA2_PSK_AUTH)) && (info->tail) &&
++#endif
+ 	    test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
+ 		     ar->fw_capabilities)) {
+ 		rsn_capb = 0;
+diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h
+index f78c59c..622063d 100644
+--- a/drivers/net/wireless/ath/ath6kl/common.h
++++ b/drivers/net/wireless/ath/ath6kl/common.h
+@@ -73,6 +73,9 @@ enum crypto_type {
+ 	TKIP_CRYPT          = 0x04,
+ 	AES_CRYPT           = 0x08,
+ 	WAPI_CRYPT          = 0x10,
++#ifdef CONFIG_SUPPORT_11W
++	AES_128_CMAC_CRYPT  = 0x20,
++#endif
+ };
+ 
+ struct htc_endpoint_credit_dist;
+diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
+index 1a97a1f..ac240df 100644
+--- a/drivers/net/wireless/ath/ath6kl/core.h
++++ b/drivers/net/wireless/ath/ath6kl/core.h
+@@ -599,7 +599,11 @@ struct ath6kl_vif {
+ 	u16 ch_hint;
+ 	u16 bss_ch;
+ 	struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1];
++#ifdef CONFIG_SUPPORT_11W
++	struct ath6kl_key keys[WMI_MAX_SUPPORT_11W_KEY_INDEX + 1];
++#else
+ 	struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1];
++#endif
+ 	struct aggr_info *aggr_cntxt;
+ 	struct ath6kl_htcap htcap[IEEE80211_NUM_BANDS];
+ 
+@@ -628,6 +632,9 @@ struct ath6kl_vif {
+ 	struct wmi_connect_cmd profile;
+ 	u16 rsn_capab;
+ 
++#ifdef CONFIG_SUPPORT_11W
++	u16 rsn_cap;  /* for 802.11w */
++#endif
+ 	struct list_head mc_filter;
+ };
+ 
+@@ -914,6 +921,9 @@ void ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast);
+ void ath6kl_txpwr_rx_evt(void *devt, u8 tx_pwr);
+ void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status);
+ void ath6kl_tgt_stats_event(struct ath6kl_vif *vif, u8 *ptr, u32 len);
++#ifdef CONFIG_SUPPORT_11W
++void ath6kl_get_rsn_cap_event(struct ath6kl_vif *vif, u16 rsn_cap);
++#endif
+ void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active);
+ enum htc_endpoint_id ath6kl_ac2_endpoint_id(void *devt, u8 ac);
+ 
+diff --git a/drivers/net/wireless/ath/ath6kl/debugfs_pri.c b/drivers/net/wireless/ath/ath6kl/debugfs_pri.c
+index 79a4746..34682be 100644
+--- a/drivers/net/wireless/ath/ath6kl/debugfs_pri.c
++++ b/drivers/net/wireless/ath/ath6kl/debugfs_pri.c
+@@ -195,6 +195,99 @@ static const struct file_operations fops_bmisstime = {
+ 	.llseek = default_llseek,
+ };
+ 
++#ifdef CONFIG_SUPPORT_11W
++static ssize_t ath6kl_rsn_cap_write(struct file *file,
++		const char __user *user_buf,
++		size_t count, loff_t *ppos)
++{
++	struct ath6kl *ar = file->private_data;
++	struct ath6kl_vif *vif;
++	u16 rsn_cap;
++	char buf[32];
++	ssize_t len;
++	int ret;
++
++	if (WARN_ON(!test_bit(WMI_READY, &ar->flag)))
++		return -EIO;
++
++	vif = ath6kl_vif_first(ar);
++	if (!vif)
++		return -EIO;
++
++	len = min(count, sizeof(buf) - 1);
++	if (copy_from_user(buf, user_buf, len))
++		return -EFAULT;
++
++	buf[len] = '\0';
++	if (kstrtou16(buf, 0, &rsn_cap))
++		return -EINVAL;
++
++	vif->rsn_cap = rsn_cap;
++
++	ret = ath6kl_wmi_set_rsn_cap_cmd(ar->wmi, vif->fw_vif_idx,
++			vif->rsn_cap);
++	if (ret) {
++		ath6kl_err("failed to set rsn cap:%d\n", ret);
++		return ret;
++	}
++
++	return count;
++}
++
++static ssize_t ath6kl_rsn_cap_read(struct file *file, char __user *user_buf,
++		size_t count, loff_t *ppos)
++{
++	struct ath6kl *ar = file->private_data;
++	struct ath6kl_vif *vif;
++	char buf[32];
++	long left;
++	int ret;
++	ssize_t len;
++
++	if (WARN_ON(!test_bit(WMI_READY, &ar->flag)))
++		return -EIO;
++
++	vif = ath6kl_vif_first(ar);
++	if (!vif)
++		return -EIO;
++
++	if (down_interruptible(&ar->sem)) {
++		return -EBUSY;
++	}
++
++	set_bit(STATS_UPDATE_PEND, &vif->flags);
++
++	ret = ath6kl_wmi_get_rsn_cap_cmd(ar->wmi, vif->fw_vif_idx);
++	if (ret) {
++		up(&ar->sem);
++		ath6kl_err("ath6kl_wmi_get_rsn_cap_cmd failed:%d \n", ret);
++		return -EIO;
++	}
++
++	left = wait_event_interruptible_timeout(ar->event_wq,
++			!test_bit(STATS_UPDATE_PEND,
++				&vif->flags), WMI_TIMEOUT);
++
++	up(&ar->sem);
++
++	if (left <= 0) {
++		ath6kl_err("wait_event_interruptible_timeout\n");
++		return -ETIMEDOUT;
++	}
++
++	len = scnprintf(buf, sizeof(buf), "%u\n", vif->rsn_cap);
++	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static const struct file_operations fops_rsn_cap = {
++	.open = ath6kl_debugfs_open_pri,
++	.read = ath6kl_rsn_cap_read,
++	.write = ath6kl_rsn_cap_write,
++	.owner = THIS_MODULE,
++	.llseek = default_llseek,
++};
++#endif
++
+ int ath6kl_init_debugfs_pri(struct ath6kl *ar)
+ {
+ 	debugfs_create_file("inactivity_period", S_IWUSR, ar->debugfs_phy, ar,
+@@ -202,6 +295,10 @@ int ath6kl_init_debugfs_pri(struct ath6kl *ar)
+ 
+ 	debugfs_create_file("bmiss_time", S_IRUSR | S_IWUSR, ar->debugfs_phy,
+ 			    ar, &fops_bmisstime);
++#ifdef CONFIG_SUPPORT_11W
++	debugfs_create_file("rsn_cap", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
++			&fops_rsn_cap);
++#endif
+ 
+ 	return 0;
+ }
+diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
+index 3280f05..21cc7a9 100644
+--- a/drivers/net/wireless/ath/ath6kl/main.c
++++ b/drivers/net/wireless/ath/ath6kl/main.c
+@@ -910,6 +910,20 @@ void ath6kl_tgt_stats_event(struct ath6kl_vif *vif, u8 *ptr, u32 len)
+ 	}
+ }
+ 
++#ifdef CONFIG_SUPPORT_11W
++void ath6kl_get_rsn_cap_event(struct ath6kl_vif *vif, u16 rsn_cap)
++{
++	struct ath6kl *ar = vif->ar;
++
++	vif->rsn_cap = rsn_cap;
++
++	if (test_bit(STATS_UPDATE_PEND, &vif->flags)) {
++		clear_bit(STATS_UPDATE_PEND, &vif->flags);
++		wake_up(&ar->event_wq);
++	}
++}
++#endif
++
+ void ath6kl_wakeup_event(void *dev)
+ {
+ 	struct ath6kl *ar = (struct ath6kl *) dev;
+diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
+index 8aebf56..49cb0af 100644
+--- a/drivers/net/wireless/ath/ath6kl/wmi.c
++++ b/drivers/net/wireless/ath/ath6kl/wmi.c
+@@ -1338,6 +1338,22 @@ static int ath6kl_wmi_stats_event_rx(struct wmi *wmi, u8 *datap, int len,
+ 	return 0;
+ }
+ 
++#ifdef CONFIG_SUPPORT_11W
++static int ath6kl_wmi_get_rsn_cap_rx(struct wmi *wmi, u8 *datap, int len,
++		struct ath6kl_vif *vif)
++{
++	struct wmi_rsn_cap_cmd *reply;
++
++	if (len < sizeof(struct wmi_rsn_cap_cmd))
++		return -EINVAL;
++
++	reply = (struct wmi_rsn_cap_cmd *) datap;
++	ath6kl_get_rsn_cap_event(vif, le16_to_cpu(reply->rsn_cap));
++
++	return 0;
++}
++#endif
++
+ static u8 ath6kl_wmi_get_upper_threshold(s16 rssi,
+ 					 struct sq_threshold_params *sq_thresh,
+ 					 u32 size)
+@@ -2198,6 +2214,32 @@ int ath6kl_wmi_bmisstime_cmd(struct wmi *wmi, u8 if_idx,
+ 	return ret;
+ }
+ 
++#ifdef CONFIG_SUPPORT_11W
++int ath6kl_wmi_set_rsn_cap_cmd(struct wmi *wmi, u8 if_idx,
++		u16 rsn_cap)
++{
++	struct sk_buff *skb;
++	struct wmi_rsn_cap_cmd *cmd;
++	int ret;
++
++	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
++	if (!skb)
++		return -ENOMEM;
++
++	cmd = (struct wmi_rsn_cap_cmd *) skb->data;
++	cmd->rsn_cap = cpu_to_le16(rsn_cap);
++
++	ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_RSN_CAP_CMDID,
++			NO_SYNC_WMIFLAG);
++	return ret;
++}
++
++int ath6kl_wmi_get_rsn_cap_cmd(struct wmi *wmi, u8 if_idx)
++{
++	return ath6kl_wmi_simple_cmd(wmi, if_idx, WMI_GET_RSN_CAP_CMDID);
++}
++#endif
++
+ int ath6kl_wmi_powermode_cmd(struct wmi *wmi, u8 if_idx, u8 pwr_mode)
+ {
+ 	struct sk_buff *skb;
+@@ -2313,6 +2355,48 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
+ 	return ret;
+ }
+ 
++#ifdef CONFIG_SUPPORT_11W
++int ath6kl_wmi_addigtk_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
++		enum crypto_type key_type,
++		u8 key_usage, u8 key_len,
++		u8 *key_rsc, unsigned int key_rsc_len,
++		u8 *key_material,
++		u8 key_op_ctrl, u8 *mac_addr,
++		enum wmi_sync_flag sync_flag)
++{
++	struct sk_buff *skb;
++	struct wmi_add_igtk_key_cmd *cmd;
++	int ret;
++
++	ath6kl_dbg(ATH6KL_DBG_WMI, "addigtk cmd: key_index=%u key_type=%d "
++			"key_usage=%d key_len=%d key_op_ctrl=%d\n",
++			key_index, key_type, key_usage, key_len, key_op_ctrl);
++
++	if ((key_index > WMI_MAX_SUPPORT_11W_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) ||
++			(key_material == NULL) || key_rsc_len > 6)
++		return -EINVAL;
++
++	if ((WEP_CRYPT != key_type) && (NULL == key_rsc))
++		return -EINVAL;
++
++	skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
++	if (!skb)
++		return -ENOMEM;
++
++	cmd = (struct wmi_add_igtk_key_cmd *) skb->data;
++	cmd->key_index = key_index;
++	cmd->key_len = key_len;
++	memcpy(cmd->key, key_material, key_len);
++	if (key_rsc != NULL)
++		memcpy(cmd->key_rsc, key_rsc, key_rsc_len);
++
++	ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IGTK_CMDID,
++			sync_flag);
++
++	return ret;
++}
++#endif
++
+ int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk)
+ {
+ 	struct sk_buff *skb;
+@@ -3940,6 +4024,12 @@ static int ath6kl_wmi_proc_events_vif(struct net_device *dev, struct wmi *wmi,
+ 		ret = ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len,
+ 							  vif);
+ 		break;
++#ifdef CONFIG_SUPPORT_11W
++	case WMI_GET_RSN_CAP_EVENTID:
++		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n");
++		ret = ath6kl_wmi_get_rsn_cap_rx(wmi, datap, len, vif);
++		break;
++#endif
+ 	case WMI_SCAN_COMPLETE_EVENTID:
+ 		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n");
+ 		ret = ath6kl_wmi_scan_complete_rx(wmi, datap, len, vif);
+diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h
+index da3612c..661e8ad 100644
+--- a/drivers/net/wireless/ath/ath6kl/wmi.h
++++ b/drivers/net/wireless/ath/ath6kl/wmi.h
+@@ -685,10 +685,18 @@ enum auth_mode {
+ 	WPA2_PSK_AUTH = 0x10,
+ 	WPA_AUTH_CCKM = 0x20,
+ 	WPA2_AUTH_CCKM = 0x40,
++#ifdef CONFIG_SUPPORT_11W
++	WPA2_AUTH_SHA256	= 0x80,
++	WPA2_PSK_AUTH_SHA256	= 0x81,
++#endif
+ };
+ 
+ #define WMI_MAX_KEY_INDEX   3
+ 
++#ifdef CONFIG_SUPPORT_11W
++#define WMI_MAX_SUPPORT_11W_KEY_INDEX	5
++#endif
++
+ #define WMI_MAX_KEY_LEN     32
+ 
+ /*
+@@ -797,6 +805,18 @@ struct wmi_delete_cipher_key_cmd {
+ 	u8 key_index;
+ } __packed;
+ 
++#ifdef CONFIG_SUPPORT_11W
++struct wmi_add_igtk_key_cmd {
++	u8     key_index;
++
++	u8     key_len;
++
++	u8     key_rsc[6];/* key replay sequence counter */
++
++	u8     key[WMI_MAX_KEY_LEN];
++} __packed;
++#endif
++
+ #define WMI_KRK_LEN     16
+ 
+ /* WMI_ADD_KRK_CMDID */
+@@ -2159,6 +2179,16 @@ struct wmi_txe_notify_event {
+ 	__le32 pkts;
+ } __packed;
+ 
++#ifdef CONFIG_SUPPORT_11W
++#define RSN_CAP_PREAUTH     0x01
++#define RSN_CAP_MFPR        0x40
++#define RSN_CAP_MFPC        0x80
++
++struct wmi_rsn_cap_cmd {
++	__le16 rsn_cap;
++} __packed;
++#endif
++
+ /* WMI_SET_AKMP_PARAMS_CMD */
+ 
+ struct wmi_pmkid {
+@@ -2656,6 +2686,16 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
+ 			  u8 *key_material,
+ 			  u8 key_op_ctrl, u8 *mac_addr,
+ 			  enum wmi_sync_flag sync_flag);
++#ifdef CONFIG_SUPPORT_11W
++int ath6kl_wmi_addigtk_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
++		enum crypto_type key_type,
++		u8 key_usage, u8 key_len,
++		u8 *key_rsc, unsigned int key_rsc_len,
++		u8 *key_material,
++		u8 key_op_ctrl, u8 *mac_addr,
++		enum wmi_sync_flag sync_flag);
++#endif
++
+ int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk);
+ int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index);
+ int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid,
+@@ -2665,6 +2705,10 @@ int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi, u8 if_idx);
+ int ath6kl_wmi_get_roam_tbl_cmd(struct wmi *wmi);
+ 
+ int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg);
++#ifdef CONFIG_SUPPORT_11W
++int ath6kl_wmi_get_rsn_cap_cmd(struct wmi *wmi, u8 if_idx);
++int ath6kl_wmi_set_rsn_cap_cmd(struct wmi *wmi, u8 if_idx, u16 rsn_cap);
++#endif
+ int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx,
+ 				 u8 keep_alive_intvl);
+ int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx,
+diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
+index 66cedf6..8f680c8 100644
+--- a/include/linux/ieee80211.h
++++ b/include/linux/ieee80211.h
+@@ -1566,6 +1566,11 @@ enum ieee80211_sa_query_action {
+ 
+ #define WLAN_MAX_KEY_LEN		32
+ 
++#ifdef CONFIG_SUPPORT_11W
++#define WLAN_AKM_SUITE_8021X_SHA256	0x000FAC05
++#define WLAN_AKM_SUITE_PSK_SHA256	0x000FAC06
++#endif
++
+ #define WLAN_PMKID_LEN			16
+ 
+ #define WLAN_OUI_WFA			0x506f9a
+diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
+index 85e5051..d9854f4 100644
+--- a/include/linux/nl80211.h
++++ b/include/linux/nl80211.h
+@@ -363,8 +363,8 @@
+  *	requests to connect to a specified network but without separating
+  *	auth and assoc steps. For this, you need to specify the SSID in a
+  *	%NL80211_ATTR_SSID attribute, and can optionally specify the association
+- *	IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC,
+- *	%NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
++ *	IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
++ *	%NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+  *	%NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
+  *	%NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+  *	Background scan period can optionally be
+@@ -914,7 +914,7 @@ enum nl80211_commands {
+  * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
+  *	used for the association (&enum nl80211_mfp, represented as a u32);
+  *	this attribute can be used
+- *	with %NL80211_CMD_ASSOCIATE request
++ *	with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests
+  *
+  * @NL80211_ATTR_STA_FLAGS2: Attribute containing a
+  *	&struct nl80211_sta_flag_update.
+@@ -2380,10 +2380,12 @@ enum nl80211_key_type {
+  * enum nl80211_mfp - Management frame protection state
+  * @NL80211_MFP_NO: Management frame protection not used
+  * @NL80211_MFP_REQUIRED: Management frame protection required
++ * @NL80211_MFP_OPTIONAL: Management frame protection enabled, but not required
+  */
+ enum nl80211_mfp {
+ 	NL80211_MFP_NO,
+ 	NL80211_MFP_REQUIRED,
++    NL80211_MFP_OPTIONAL,
+ };
+ 
+ enum nl80211_wpa_versions {
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index a98830b..f9e658b 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -1056,7 +1056,7 @@ struct cfg80211_auth_request {
+  * @bss: The BSS to associate with.
+  * @ie: Extra IEs to add to (Re)Association Request frame or %NULL
+  * @ie_len: Length of ie buffer in octets
+- * @use_mfp: Use management frame protection (IEEE 802.11w) in this association
++ * @mfp: indicate whether management frame protection is used
+  * @crypto: crypto settings
+  * @prev_bssid: previous BSSID, if not %NULL use reassociate frame
+  */
+@@ -1065,7 +1065,7 @@ struct cfg80211_assoc_request {
+ 	const u8 *ie, *prev_bssid;
+ 	size_t ie_len;
+ 	struct cfg80211_crypto_settings crypto;
+-	bool use_mfp;
++    enum nl80211_mfp mfp;
+ };
+ 
+ /**
+@@ -1160,6 +1160,7 @@ struct cfg80211_ibss_params {
+  * @ie: IEs for association request
+  * @ie_len: Length of assoc_ie in octets
+  * @privacy: indicates whether privacy-enabled APs should be used
++ * @mfp: indicate whether management frame protection is used
+  * @crypto: crypto settings
+  * @key_len: length of WEP key for shared key authentication
+  * @key_idx: index of WEP key for shared key authentication
+@@ -1176,6 +1177,7 @@ struct cfg80211_connect_params {
+ 	u8 *ie;
+ 	size_t ie_len;
+ 	bool privacy;
++	enum nl80211_mfp mfp;
+ 	struct cfg80211_crypto_settings crypto;
+ 	const u8 *key;
+ 	u8 key_len, key_idx;
+diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
+index a01e37f..9292440 100644
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -406,11 +406,7 @@ struct ieee80211_if_managed {
+ 	bool beacon_crc_valid;
+ 	u32 beacon_crc;
+ 
+-	enum {
+-		IEEE80211_MFP_DISABLED,
+-		IEEE80211_MFP_OPTIONAL,
+-		IEEE80211_MFP_REQUIRED
+-	} mfp; /* management frame protection */
++    enum nl80211_mfp mfp; /* management frame protection */
+ 
+ 	int wmm_last_param_set;
+ 
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 9d1fb85..8838816 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2704,13 +2704,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
+ 	else
+ 		wk->type = IEEE80211_WORK_ASSOC;
+ 
+-	if (req->use_mfp) {
+-		ifmgd->mfp = IEEE80211_MFP_REQUIRED;
+-		ifmgd->flags |= IEEE80211_STA_MFP_ENABLED;
+-	} else {
+-		ifmgd->mfp = IEEE80211_MFP_DISABLED;
+-		ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED;
+-	}
++    ifmgd->mfp = req->mfp;
++    if (req->mfp != NL80211_MFP_NO)
++        ifmgd->flags |= IEEE80211_STA_MFP_ENABLED;
++    else
++        ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED;
+ 
+ 	if (req->crypto.control_port)
+ 		ifmgd->flags |= IEEE80211_STA_CONTROL_PORT;
+diff --git a/net/wireless/core.h b/net/wireless/core.h
+index 11ff6bb..61c92d3 100644
+--- a/net/wireless/core.h
++++ b/net/wireless/core.h
+@@ -339,13 +339,13 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ 			  struct ieee80211_channel *chan,
+ 			  const u8 *bssid, const u8 *prev_bssid,
+ 			  const u8 *ssid, int ssid_len,
+-			  const u8 *ie, int ie_len, bool use_mfp,
++			  const u8 *ie, int ie_len, enum nl80211_mfp mfp,
+ 			  struct cfg80211_crypto_settings *crypt);
+ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ 			struct net_device *dev, struct ieee80211_channel *chan,
+ 			const u8 *bssid, const u8 *prev_bssid,
+ 			const u8 *ssid, int ssid_len,
+-			const u8 *ie, int ie_len, bool use_mfp,
++			const u8 *ie, int ie_len, enum nl80211_mfp mfp,
+ 			struct cfg80211_crypto_settings *crypt);
+ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
+ 			   struct net_device *dev, const u8 *bssid,
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index 4d74154..25abf54 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -506,7 +506,7 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ 			  struct ieee80211_channel *chan,
+ 			  const u8 *bssid, const u8 *prev_bssid,
+ 			  const u8 *ssid, int ssid_len,
+-			  const u8 *ie, int ie_len, bool use_mfp,
++			  const u8 *ie, int ie_len, enum nl80211_mfp mfp,
+ 			  struct cfg80211_crypto_settings *crypt)
+ {
+ 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+@@ -535,7 +535,7 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ 	req.ie = ie;
+ 	req.ie_len = ie_len;
+ 	memcpy(&req.crypto, crypt, sizeof(req.crypto));
+-	req.use_mfp = use_mfp;
++    req.mfp = mfp;
+ 	req.prev_bssid = prev_bssid;
+ 	req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
+ 				   WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+@@ -573,7 +573,7 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ 			struct ieee80211_channel *chan,
+ 			const u8 *bssid, const u8 *prev_bssid,
+ 			const u8 *ssid, int ssid_len,
+-			const u8 *ie, int ie_len, bool use_mfp,
++			const u8 *ie, int ie_len, enum nl80211_mfp mfp,
+ 			struct cfg80211_crypto_settings *crypt)
+ {
+ 	struct wireless_dev *wdev = dev->ieee80211_ptr;
+@@ -581,7 +581,7 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ 
+ 	wdev_lock(wdev);
+ 	err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
+-				    ssid, ssid_len, ie, ie_len, use_mfp, crypt);
++				    ssid, ssid_len, ie, ie_len, mfp, crypt);
+ 	wdev_unlock(wdev);
+ 
+ 	return err;
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 896d19b..0e69a75 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -4493,7 +4493,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+ 	struct ieee80211_channel *chan;
+ 	const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
+ 	int err, ssid_len, ie_len = 0;
+-	bool use_mfp = false;
++    enum nl80211_mfp mfp; 
+ 
+ 	if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ 		return -EINVAL;
+@@ -4525,14 +4525,15 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+ 		ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ 	}
+ 
+-	if (info->attrs[NL80211_ATTR_USE_MFP]) {
+-		enum nl80211_mfp mfp =
+-			nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
+-		if (mfp == NL80211_MFP_REQUIRED)
+-			use_mfp = true;
+-		else if (mfp != NL80211_MFP_NO)
+-			return -EINVAL;
+-	}
++    if (info->attrs[NL80211_ATTR_USE_MFP]) {
++        mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
++        if (mfp != NL80211_MFP_REQUIRED &&
++            mfp != NL80211_MFP_OPTIONAL &&
++            mfp != NL80211_MFP_NO)
++        return -EINVAL;
++    }else{
++        mfp = NL80211_MFP_NO;
++    }
+ 
+ 	if (info->attrs[NL80211_ATTR_PREV_BSSID])
+ 		prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
+@@ -4540,7 +4541,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+ 	err = nl80211_crypto_settings(rdev, info, &crypto, 1);
+ 	if (!err)
+ 		err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
+-					  ssid, ssid_len, ie, ie_len, use_mfp,
++					  ssid, ssid_len, ie, ie_len, mfp,
+ 					  &crypto);
+ 
+ 	return err;
+@@ -5022,6 +5023,16 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
+ 		connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ 	}
+ 
++    if (info->attrs[NL80211_ATTR_USE_MFP]) { 
++        connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
++        if (connect.mfp != NL80211_MFP_REQUIRED &&  
++            connect.mfp != NL80211_MFP_OPTIONAL &&   
++            connect.mfp != NL80211_MFP_NO)           
++        return -EINVAL;         
++    } else { 
++        connect.mfp = NL80211_MFP_NO;   
++    }
++
+ 	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ 		connect.channel =
+ 			ieee80211_get_channel(wiphy,
+diff --git a/net/wireless/sme.c b/net/wireless/sme.c
+index 38d3248..f02e0bc 100644
+--- a/net/wireless/sme.c
++++ b/net/wireless/sme.c
+@@ -190,7 +190,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
+ 					    prev_bssid,
+ 					    params->ssid, params->ssid_len,
+ 					    params->ie, params->ie_len,
+-					    false, &params->crypto);
++					    params->mfp,
++					    &params->crypto);
++
+ 		if (err)
+ 			__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
+ 					       NULL, 0,
+-- 
+1.7.9.5
+
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..ca442d3
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,356 @@
+
+   NOTE! This copyright does *not* cover user programs that use kernel
+ services by normal system calls - this is merely considered normal use
+ of the kernel, and does *not* fall under the heading of "derived work".
+ Also note that the GPL below is copyrighted by the Free Software
+ Foundation, but the instance of code that it refers to (the Linux
+ kernel) is copyrighted by me and others who actually wrote it.
+
+ Also note that the only valid version of the GPL as far as the kernel
+ is concerned is _this_ particular version of the license (ie v2, not
+ v2.2 or v3.x or whatever), unless explicitly otherwise stated.
+
+			Linus Torvalds
+
+----------------------------------------
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..499e419
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,471 @@
+export KMODDIR?=       updates
+KMODDIR_ARG:=   "INSTALL_MOD_DIR=$(KMODDIR)"
+ifneq ($(origin KLIB), undefined)
+KMODPATH_ARG:=  "INSTALL_MOD_PATH=$(KLIB)"
+else
+export KLIB:=          /lib/modules/$(shell uname -r)
+endif
+export KLIB_BUILD ?=	$(KLIB)/build
+# Sometimes not available in the path
+MODPROBE := /sbin/modprobe
+MADWIFI=$(shell $(MODPROBE) -l ath_pci)
+OLD_IWL=$(shell $(MODPROBE) -l iwl4965)
+
+DESTDIR?=
+
+ifneq ($(KERNELRELEASE),)
+
+NOSTDINC_FLAGS := -I$(M)/include/ \
+	-include $(M)/include/linux/compat-2.6.h \
+	$(CFLAGS)
+
+obj-y := compat/
+
+obj-$(CONFIG_COMPAT_RFKILL) += net/rfkill/
+
+ifeq ($(BT),)
+obj-$(CONFIG_COMPAT_WIRELESS) += net/wireless/ net/mac80211/
+obj-$(CONFIG_COMPAT_WIRELESS_MODULES) += drivers/net/wireless/
+
+obj-$(CONFIG_COMPAT_NET_USB_MODULES) += drivers/net/usb/
+
+obj-$(CONFIG_COMPAT_NETWORK_MODULES) += drivers/net/ethernet/atheros/
+obj-$(CONFIG_COMPAT_NETWORK_MODULES) += drivers/net/ethernet/broadcom/
+
+obj-$(CONFIG_COMPAT_VAR_MODULES) += drivers/ssb/
+obj-$(CONFIG_COMPAT_VAR_MODULES) += drivers/bcma/
+obj-$(CONFIG_COMPAT_VAR_MODULES) += drivers/misc/eeprom/
+
+ifeq ($(CONFIG_STAGING_EXCLUDE_BUILD),)
+endif
+
+endif
+
+obj-$(CONFIG_COMPAT_BLUETOOTH) += net/bluetooth/
+obj-$(CONFIG_COMPAT_BLUETOOTH_MODULES) += drivers/bluetooth/
+
+else
+
+export PWD :=	$(shell pwd)
+CFLAGS += \
+        -DCOMPAT_BASE_TREE="\"$(shell cat compat_base_tree)\"" \
+        -DCOMPAT_BASE_TREE_VERSION="\"$(shell cat compat_base_tree_version)\"" \
+        -DCOMPAT_PROJECT="\"Compat-wireless\"" \
+        -DCOMPAT_VERSION="\"$(shell cat compat_version)\""
+
+# These exported as they are used by the scripts
+# to check config and compat autoconf
+export COMPAT_CONFIG=config.mk
+export CONFIG_CHECK=.$(COMPAT_CONFIG)_md5sum.txt
+export COMPAT_AUTOCONF=include/linux/compat_autoconf.h
+export CREL=$(shell cat $(PWD)/compat_version)
+export CREL_PRE:=.compat_autoconf_
+export CREL_CHECK:=$(CREL_PRE)$(CREL)
+
+include $(PWD)/$(COMPAT_CONFIG)
+
+all: modules
+
+modules: $(CREL_CHECK)
+	@./scripts/check_config.sh
+	$(MAKE) -C $(KLIB_BUILD) M=$(PWD) modules
+	@touch $@
+
+bt: $(CREL_CHECK)
+	@./scripts/check_config.sh
+	$(MAKE) -C $(KLIB_BUILD) M=$(PWD) BT=TRUE modules
+	@touch $@
+
+# With the above and this we make sure we generate a new compat autoconf per
+# new relase of compat-wireless-2.6 OR when the user updates the 
+# $(COMPAT_CONFIG) file
+$(CREL_CHECK):
+	@# Force to regenerate compat autoconf
+	@rm -f $(CONFIG_CHECK)
+	@./scripts/check_config.sh
+	@touch $@
+	@md5sum $(COMPAT_CONFIG) > $(CONFIG_CHECK)
+
+btinstall: btuninstall bt-install-modules
+
+bt-install-modules: bt
+	$(MAKE) -C $(KLIB_BUILD) M=$(PWD) $(KMODDIR_ARG) $(KMODPATH_ARG) BT=TRUE \
+		modules_install
+	@/sbin/depmod -ae
+	@echo
+	@echo "Currently detected bluetooth subsystem modules:"
+	@echo
+	@$(MODPROBE) -l ath3k       
+	@$(MODPROBE) -l bcm203x
+	@$(MODPROBE) -l bluecard_cs
+	@$(MODPROBE) -l bluetooth
+	@$(MODPROBE) -l bnep
+	@$(MODPROBE) -l bpa10x
+	@$(MODPROBE) -l bt3c_cs
+	@$(MODPROBE) -l btmrvl
+	@$(MODPROBE) -l btmrvl_sdio
+	@$(MODPROBE) -l btsdio
+	@$(MODPROBE) -l btusb
+	@$(MODPROBE) -l btuart_cs
+	@$(MODPROBE) -l	cmtp
+	@$(MODPROBE) -l	dtl1_cs
+	@$(MODPROBE) -l hidp
+	@$(MODPROBE) -l	hci_vhci
+	@$(MODPROBE) -l	hci_uart
+	@$(MODPROBE) -l l2cap
+	@$(MODPROBE) -l rfcomm
+	@$(MODPROBE) -l sco
+	@echo
+	@echo Now run:
+	@echo
+	@echo sudo make btunload:
+	@echo
+	@echo And then load the needed bluetooth modules. If unsure reboot.
+	@echo
+
+btuninstall:
+	@# New location, matches upstream
+	@rm -rf $(KLIB)/$(KMODDIR)/net/bluetooth/
+	@rm -rf $(KLIB)/$(KMODDIR)/drivers/bluetooth/
+	@# Lets only remove the stuff we are sure we are providing
+	@# on the misc directory.
+	@/sbin/depmod -ae
+	@echo
+	@echo "Your old bluetooth subsystem modules were left intact:"
+	@echo
+	@$(MODPROBE) -l ath3k       
+	@$(MODPROBE) -l bcm203x
+	@$(MODPROBE) -l bluecard_cs
+	@$(MODPROBE) -l bluetooth
+	@$(MODPROBE) -l bnep
+	@$(MODPROBE) -l bpa10x
+	@$(MODPROBE) -l bt3c_cs
+	@$(MODPROBE) -l btmrvl
+	@$(MODPROBE) -l btmrvl_sdio
+	@$(MODPROBE) -l btsdio
+	@$(MODPROBE) -l btusb
+	@$(MODPROBE) -l btuart_cs
+	@$(MODPROBE) -l	cmtp
+	@$(MODPROBE) -l	dtl1_cs
+	@$(MODPROBE) -l hidp
+	@$(MODPROBE) -l	hci_vhci
+	@$(MODPROBE) -l	hci_uart
+	@$(MODPROBE) -l l2cap
+	@$(MODPROBE) -l rfcomm
+	@$(MODPROBE) -l sco
+	@echo
+
+btclean:
+	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) BT=TRUE clean
+	@rm -f $(CREL_PRE)*
+
+install: uninstall install-modules install-scripts
+
+install-modules: modules
+	$(MAKE) -C $(KLIB_BUILD) M=$(PWD) $(KMODDIR_ARG) $(KMODPATH_ARG) \
+		modules_install
+	@./scripts/update-initramfs
+
+install-scripts:
+	@# All the scripts we can use
+	@mkdir -p $(DESTDIR)/usr/lib/compat-wireless/
+	@install scripts/modlib.sh	$(DESTDIR)/usr/lib/compat-wireless/
+	@install scripts/madwifi-unload	$(DESTDIR)/usr/sbin/
+	@# This is to allow switching between drivers without blacklisting
+	@install scripts/athenable	$(DESTDIR)/usr/sbin/
+	@install scripts/b43enable	$(DESTDIR)/usr/sbin/
+	@install scripts/iwl-enable	$(DESTDIR)/usr/sbin/
+	@install scripts/athload	$(DESTDIR)/usr/sbin/
+	@install scripts/b43load	$(DESTDIR)/usr/sbin/
+	@install scripts/iwl-load	$(DESTDIR)/usr/sbin/
+	@if [ ! -z "$(MADWIFI)" ] && [ -z "$(DESTDIR)" ]; then \
+		echo ;\
+		echo -n "Note: madwifi detected, we're going to disable it. "  ;\
+		echo "If you would like to enable it later you can run:"  ;\
+		echo "    sudo athenable madwifi"  ;\
+		echo ;\
+		echo Running athenable ath5k...;\
+		/usr/sbin/athenable ath5k ;\
+	fi
+	@if [ ! -z "$(OLD_IWL)" ] && [ -z "$(DESTDIR)" ]; then \
+		echo ;\
+		echo -n "Note: iwl4965 detected, we're going to disable it. "  ;\
+		echo "If you would like to enable it later you can run:"  ;\
+		echo "    sudo iwl-load iwl4965"  ;\
+		echo ;\
+		echo Running iwl-enable iwlagn...;\
+		/usr/sbin/iwl-enable iwlagn ;\
+	fi
+	@# If on distributions like Mandriva which like to
+	@# compress their modules this will find out and do
+	@# it for you. Reason is some old version of modutils
+	@# won't know mac80211.ko should be used instead of
+	@# mac80211.ko.gz
+	@./scripts/compress_modules
+	@# Mandrake doesn't have a depmod.d/ conf file to prefer
+	@# the updates/ dir which is what we use so we add one for it
+	@# (or any other distribution that doens't have this).
+	@./scripts/check_depmod
+	@# Udev stuff needed for the new compat_firmware_class.
+	@./compat/scripts/compat_firmware_install
+	@/sbin/depmod -a
+	@echo
+	@echo "Currently detected wireless subsystem modules:"
+	@echo 
+	@$(MODPROBE) -l mac80211
+	@$(MODPROBE) -l cfg80211
+	@$(MODPROBE) -l lib80211
+	@$(MODPROBE) -l adm8211
+	@$(MODPROBE) -l ar9170usb
+	@$(MODPROBE) -l at76c50x-usb
+	@$(MODPROBE) -l ath
+	@$(MODPROBE) -l ath5k
+	@$(MODPROBE) -l ath6kl
+	@$(MODPROBE) -l ath9k
+	@$(MODPROBE) -l ath9k_htc
+	@$(MODPROBE) -l b43
+	@$(MODPROBE) -l b43legacy
+	@$(MODPROBE) -l b44
+	@$(MODPROBE) -l carl9170
+	@$(MODPROBE) -l brcm80211
+	@$(MODPROBE) -l cdc_ether
+	@$(MODPROBE) -l eeprom_93cx6
+	@$(MODPROBE) -l ipw2100
+	@$(MODPROBE) -l ipw2200
+	@$(MODPROBE) -l iwl3945
+	@$(MODPROBE) -l iwlagn
+	@$(MODPROBE) -l iwlcore
+	@$(MODPROBE) -l iwmc3200wifi
+	@$(MODPROBE) -l lib80211_crypt_ccmp
+	@$(MODPROBE) -l lib80211_crypt_tkip
+	@$(MODPROBE) -l lib80211_crypt_wep
+	@$(MODPROBE) -l libertas
+	@$(MODPROBE) -l libertas_cs
+	@$(MODPROBE) -l libertas_sdio
+	@$(MODPROBE) -l libertas_spi
+	@$(MODPROBE) -l libertas_tf
+	@$(MODPROBE) -l libertas_tf_usb
+	@$(MODPROBE) -l libipw
+	@$(MODPROBE) -l mac80211_hwsim
+	@$(MODPROBE) -l mwl8k
+	@$(MODPROBE) -l orinoco_cs
+	@$(MODPROBE) -l orinoco_nortel
+	@$(MODPROBE) -l orinoco_pci
+	@$(MODPROBE) -l orinoco_plx
+	@$(MODPROBE) -l orinoco_tld
+	@$(MODPROBE) -l orinoco_usb
+	@$(MODPROBE) -l orinoco
+	@$(MODPROBE) -l p54common
+	@$(MODPROBE) -l p54pci
+	@$(MODPROBE) -l p54spi
+	@$(MODPROBE) -l p54usb
+	@$(MODPROBE) -l rndis_host
+	@$(MODPROBE) -l rndis_wlan
+	@$(MODPROBE) -l rt2400pci
+	@$(MODPROBE) -l rt2500pci
+	@$(MODPROBE) -l rt2500usb
+	@$(MODPROBE) -l rt2800pci
+	@$(MODPROBE) -l rt2800usb
+	@$(MODPROBE) -l rt2x00lib
+	@$(MODPROBE) -l rt2x00pci
+	@$(MODPROBE) -l rt2x00usb
+	@$(MODPROBE) -l rt61pci
+	@$(MODPROBE) -l rt73usb
+	@$(MODPROBE) -l rtl8180
+	@$(MODPROBE) -l rtl8187
+	@$(MODPROBE) -l rtlwifi
+	@$(MODPROBE) -l rtl8192ce
+	@$(MODPROBE) -l spectrum_cs
+	@$(MODPROBE) -l ssb
+	@$(MODPROBE) -l usb8xxx
+	@$(MODPROBE) -l usbnet
+	@$(MODPROBE) -l wl1251
+	@$(MODPROBE) -l wl12xx
+	@$(MODPROBE) -l zd1211rw
+	@echo
+	@echo "Currently detected ethernet subsystem modules:"
+	@echo
+	@$(MODPROBE) -l atl1
+	@$(MODPROBE) -l atl2
+	@$(MODPROBE) -l atl1e
+	@$(MODPROBE) -l atl1c
+	@echo
+	@echo "Currently detected bluetooth subsystem modules:"
+	@echo
+	@$(MODPROBE) -l ath3k           
+	@$(MODPROBE) -l bcm203x
+	@$(MODPROBE) -l bluecard_cs
+	@$(MODPROBE) -l bluetooth
+	@$(MODPROBE) -l bnep
+	@$(MODPROBE) -l bpa10x
+	@$(MODPROBE) -l bt3c_cs
+	@$(MODPROBE) -l btmrvl
+	@$(MODPROBE) -l btmrvl_sdio
+	@$(MODPROBE) -l btsdio
+	@$(MODPROBE) -l btusb
+	@$(MODPROBE) -l btuart_cs
+	@$(MODPROBE) -l	cmtp
+	@$(MODPROBE) -l	dtl1_cs
+	@$(MODPROBE) -l hidp
+	@$(MODPROBE) -l	hci_vhci
+	@$(MODPROBE) -l	hci_uart
+	@$(MODPROBE) -l l2cap
+	@$(MODPROBE) -l rfcomm
+	@$(MODPROBE) -l sco
+	@echo 
+	@echo Now run:
+	@echo 
+	@echo sudo make unload to unload all: wireless, bluetooth and ethernet modules
+	@echo sudo make wlunload to unload wireless modules
+	@echo sudo make btunload to unload bluetooth modules
+	@echo
+	@echo Run sudo modprobe 'driver-name' to load your desired driver. 
+	@echo If unsure reboot.
+	@echo
+
+uninstall:
+	@# New location, matches upstream
+	@rm -rf $(KLIB)/$(KMODDIR)/compat/
+	@rm -rf $(KLIB)/$(KMODDIR)/net/mac80211/
+	@rm -rf $(KLIB)/$(KMODDIR)/net/rfkill/
+	@rm -rf $(KLIB)/$(KMODDIR)/net/wireless/
+	@rm -rf $(KLIB)/$(KMODDIR)/drivers/ssb/
+	@rm -rf $(KLIB)/$(KMODDIR)/drivers/net/usb/
+	@rm -rf $(KLIB)/$(KMODDIR)/drivers/net/wireless/
+	@rm -rf $(KLIB)/$(KMODDIR)/drivers/staging/
+	@rm -rf $(KLIB)/$(KMODDIR)/drivers/net/atl*
+	@rm -rf $(KLIB)/$(KMODDIR)/drivers/net/alx.ko*
+	@# Lets only remove the stuff we are sure we are providing
+	@# on the misc directory.
+	@rm -f $(KLIB)/$(KMODDIR)/drivers/misc/eeprom/eeprom_93cx6.ko*
+	@rm -f $(KLIB)/$(KMODDIR)/drivers/misc/eeprom_93cx6.ko*
+	@rm -f $(KLIB)/$(KMODDIR)/drivers/net/b44.ko*
+	@/sbin/depmod -a
+	@echo
+	@echo "Your old wireless subsystem modules were left intact:"
+	@echo 
+	@$(MODPROBE) -l mac80211
+	@$(MODPROBE) -l cfg80211
+	@$(MODPROBE) -l lib80211
+	@$(MODPROBE) -l adm8211
+	@$(MODPROBE) -l ar9170usb
+	@$(MODPROBE) -l at76c50x-usb
+	@$(MODPROBE) -l ath
+	@$(MODPROBE) -l ath5k
+	@$(MODPROBE) -l ath6kl
+	@$(MODPROBE) -l ath9k
+	@$(MODPROBE) -l ath9k_htc
+	@$(MODPROBE) -l b43
+	@$(MODPROBE) -l b43legacy
+	@$(MODPROBE) -l b44
+	@$(MODPROBE) -l carl9170
+	@$(MODPROBE) -l brcm80211
+	@$(MODPROBE) -l cdc_ether
+	@$(MODPROBE) -l eeprom_93cx6
+	@$(MODPROBE) -l ipw2100
+	@$(MODPROBE) -l ipw2200
+	@$(MODPROBE) -l iwl3945
+	@$(MODPROBE) -l iwlagn
+	@$(MODPROBE) -l iwlcore
+	@$(MODPROBE) -l iwmc3200wifi
+	@$(MODPROBE) -l lib80211_crypt_ccmp
+	@$(MODPROBE) -l lib80211_crypt_tkip
+	@$(MODPROBE) -l lib80211_crypt_wep
+	@$(MODPROBE) -l libertas
+	@$(MODPROBE) -l libertas_cs
+	@$(MODPROBE) -l libertas_sdio
+	@$(MODPROBE) -l libertas_spi
+	@$(MODPROBE) -l libertas_tf
+	@$(MODPROBE) -l libertas_tf_usb
+	@$(MODPROBE) -l libipw
+	@$(MODPROBE) -l mac80211_hwsim
+	@$(MODPROBE) -l mwl8k
+	@$(MODPROBE) -l orinoco_cs
+	@$(MODPROBE) -l orinoco_nortel
+	@$(MODPROBE) -l orinoco_pci
+	@$(MODPROBE) -l orinoco_plx
+	@$(MODPROBE) -l orinoco_tld
+	@$(MODPROBE) -l orinoco_usb
+	@$(MODPROBE) -l orinoco
+	@$(MODPROBE) -l p54common
+	@$(MODPROBE) -l p54pci
+	@$(MODPROBE) -l p54spi
+	@$(MODPROBE) -l p54usb
+	@$(MODPROBE) -l rndis_host
+	@$(MODPROBE) -l rndis_wlan
+	@$(MODPROBE) -l rt2400pci
+	@$(MODPROBE) -l rt2500pci
+	@$(MODPROBE) -l rt2500usb
+	@$(MODPROBE) -l rt2800pci
+	@$(MODPROBE) -l rt2800usb
+	@$(MODPROBE) -l rt2x00lib
+	@$(MODPROBE) -l rt2x00pci
+	@$(MODPROBE) -l rt2x00usb
+	@$(MODPROBE) -l rt61pci
+	@$(MODPROBE) -l rt73usb
+	@$(MODPROBE) -l rtl8180
+	@$(MODPROBE) -l rtl8187
+	@$(MODPROBE) -l rtlwifi
+	@$(MODPROBE) -l rtl8192ce
+	@$(MODPROBE) -l spectrum_cs
+	@$(MODPROBE) -l ssb
+	@$(MODPROBE) -l usb8xxx
+	@$(MODPROBE) -l usbnet
+	@$(MODPROBE) -l wl1251
+	@$(MODPROBE) -l wl12xx
+	@$(MODPROBE) -l zd1211rw
+	@echo
+	@echo "Your old ethernet subsystem modules are left intact:"
+	@echo
+	@$(MODPROBE) -l atl1
+	@$(MODPROBE) -l atl2
+	@$(MODPROBE) -l atl1e
+	@$(MODPROBE) -l atl1c
+	@$(MODPORBE) -l alx
+	@echo
+	@echo "Your old bluetooth subsystem modules were left intact:"
+	@echo
+	@$(MODPROBE) -l ath3k           
+	@$(MODPROBE) -l bcm203x
+	@$(MODPROBE) -l bluecard_cs
+	@$(MODPROBE) -l bluetooth
+	@$(MODPROBE) -l bnep
+	@$(MODPROBE) -l bpa10x
+	@$(MODPROBE) -l bt3c_cs
+	@$(MODPROBE) -l btmrvl
+	@$(MODPROBE) -l btmrvl_sdio
+	@$(MODPROBE) -l btsdio
+	@$(MODPROBE) -l btusb
+	@$(MODPROBE) -l btuart_cs
+	@$(MODPROBE) -l	cmtp
+	@$(MODPROBE) -l	dtl1_cs
+	@$(MODPROBE) -l hidp
+	@$(MODPROBE) -l	hci_vhci
+	@$(MODPROBE) -l	hci_uart
+	@$(MODPROBE) -l l2cap
+	@$(MODPROBE) -l rfcomm
+	@$(MODPROBE) -l sco
+	@
+	@echo 
+
+clean:
+	@if [ -d net -a -d $(KLIB_BUILD) ]; then \
+		$(MAKE) -C $(KLIB_BUILD) M=$(PWD) clean ;\
+	fi
+	@rm -f $(CREL_PRE)*
+unload:
+	@./scripts/unload.sh
+
+btunload:
+	@./scripts/btunload.sh
+
+wlunload:
+	@./scripts/wlunload.sh
+
+
+.PHONY: all clean install uninstall unload btunload wlunload modules bt
+
+endif
+
+clean-files += Module.symvers Module.markers modules modules.order
+clean-files += $(CREL_CHECK) $(CONFIG_CHECK)
diff --git a/README b/README
new file mode 100644
index 0000000..09d8612
--- /dev/null
+++ b/README
@@ -0,0 +1,417 @@
+
+Linux Wireless compatibility package
+=====================================
+
+This is a Linux wireless compatibility package which provides the latest
+Linux wireless subsystem enhancements for kernels 2.6.24 and above.
+It is technically possible to support kernels < 2.6.24 but more
+work is required for that.
+It also provides Linux bluetooth subsystem enhancements for kernels 2.6.27 and above.
+
+With a local git repository you can update the compatibility package yourself. 
+For more information on how to do this please refer the Developers section below.
+
+Documentation
+------------
+
+This package is also documented online and has more-up-to date
+information online than on this README file. You should read the wiki page
+and not rely on this README:
+
+http://wireless.kernel.org/en/users/Download
+
+Subscribe to the wiki page to get updates on the documentation.
+
+Where to get the latest
+-----------------------
+
+This package lets you build your own 'latest', all you need is a local git
+repository. The process is documented in the Developers section of this document.
+However since not many users are expected to keep a local git repository we 
+provide daily snapshots of this package + the wireless and bluetooth subsystem
+code. You can find the latest snapshot at:
+
+linux-next.git version:
+http://wireless.kernel.org/en/users/Download
+
+stable version:
+http://wireless.kernel.org/en/users/Download/stable/
+
+Versions
+--------
+
+There are two different versions of this package available. 
+ * Version based on latest linux-next.git tree named compat-wireless-YYYY-MM-DD.tar.bz2
+ * Version based on linux-2.6-stable.git tree named compat-wireless-2.6.CC.DD.tar.bz2
+
+Both versions should work for every kernel > 2.6.24. The differences are the
+code they are containing. The version based on linux-next.git contains the
+wireless and bluetooth subsystem out of linux-next.git and the stable version
+the wireless subsystem out of the corresponding linux stable version.
+
+Selecting your driver
+---------------------
+
+If you know the driver you want you can select it with our
+helper script:
+
+./scripts/driver-select
+
+Run that script to see more information. 
+Not all drivers are currently enabled via driver-select.
+
+Building, and installing
+------------------------
+
+Build: compile the latest linux wireless subsystem
+
+	make
+
+Install:
+
+We use the updates/ directory so your distribution's drivers are left intact.
+
+	sudo make install
+
+Uninstall:
+
+This nukes our changes to updates/ so you can go back to using your
+distribution's supported drivers.
+
+	sudo make uninstall
+
+Load:
+
+Reboot unless you know what you are doing.
+(Usually modprobe 'driver-name' is the way to go)
+
+
+Bluetooth modules can be separately compiled and installed using below commands
+Bluetooth drivers are also available via ./scripts/driver-select
+ 
+Build:
+	make bt
+
+Install:
+	sudo make btinstall
+
+Uninstall:
+	sudo make btuninstall
+
+Unload:
+	sudo make btunload
+
+Load:
+	modprobe driver-name. If unsure, reboot.
+
+Drivers
+-------
+
+This is the list of drivers the package currently provides. It adds
+all new drivers or drivers which keep being updated which you might
+be interested in.
+
+Drivers list:
+
+adm8211
+ar9170usb
+at76c50x-usb
+ath5k
+ath6kl
+ath9k
+ath9k_htc
+b43
+b43legacy
+b44
+carl9170
+brcm80211
+ipw2100
+ipw2200
+iwl3945
+iwlagn
+iwlcore
+iwmc3200wifi
+libertas
+libertas_cs
+libertas_sdio
+libertas_spi
+libertas_tf
+libertas_tf_usb
+libipw
+mwl8k
+orinoco_cs
+orinoco_nortel
+orinoco_pci
+orinoco_plx
+orinoco_tld
+orinoco_usb
+orinoco
+p54common
+p54pci
+p54spi
+p54usb
+rt2400pci
+rt2500pci
+rt2500usb
+rt2800pci
+rt2800usb
+rt61pci
+rt73usb
+rtl8180
+rtl8187
+rtlwifi
+rtl8192ce
+spectrum_cs
+ssb
+wl1251
+wl12xx
+zd1211rw
+
+This package also provides more drivers which may be documented here
+
+For a complete list see:
+
+http://wireless.kernel.org/en/users/Download
+
+Non-wireless drivers
+--------------------
+
+Atheros Ethernet drivers:
+
+atl1
+atl1c
+atl1e
+atl2
+
+To support b43 ssb is also provided, and since ssb is also provided
+we provide b44 (the ethernet driver).
+
+The new rfkill drivers also provided and backported.
+
+Bluetooth drivers:
+
+ath3k           
+bcm203x
+bluecard_cs
+bluetooth
+bnep
+bpa10x
+bt3c_cs
+btmrvl
+btmrvl_sdio
+btsdio
+btusb
+btuart_cs
+cmtp
+dtl1_cs
+hidp
+hci_vhci
+hci_uart
+l2cap
+rfcomm
+sco
+
+Firmware:
+---------
+
+If your driver needs firmware please be sure to check the driver page
+for that driver here:
+
+http://wireless.kernel.org/en/users/Drivers
+
+Why?
+----
+
+For users or developers stuck on older kernels that want to help test or 
+patch wireless work. Additionally if you're on a recent kernel this lets
+you get the latest and greatest linux-next git work without much effort.
+This may mean new drivers for some users. Last but not least we hope this
+will encourage vendors and developers to post patches upstream first
+rather than forking or maintaining their own mac80211 releases with
+their own patches for their own drivers.
+
+Building for external kernels
+----------------------------------
+
+If you have a kernel you do not have installed but yet want to build the
+compat-wireless-2.6 drivers for it you can use this syntax:
+
+make KLIB=/home/mcgrof/kernels/linux-2.6.23.9 KLIB_BUILD=/home/mcgrof/kernels/linux-2.6.23.9
+
+If you have a kernel installed, which is not your currently running kernel (e.g. via
+distro updates; plus its corresponding kernel-dev package), you can use this syntax:
+
+make  KLIB=/lib/modules/2.6.30.6-53.fc11.x86_64
+
+  and to install to your system's root path for the non-running kernel:
+
+make  KLIB=/lib/modules/2.6.30.6-53.fc11.x86_64 KMODPATH_ARG='INSTALL_MOD_PATH=' install
+
+Bugs
+-----
+
+If you've found a bug please report it to our linux-wireless mailing list:
+
+linux-wireless@vger.kernel.org
+
+Report the bug if you are working with the latest and greatest.
+If your bug is compatibility-related then we should still try to fix
+it within the compat.[ch] work.
+
+ChangeLog
+---------
+
+Here you see the list of changes to all wireless drivers, the wireless core and mac80211.
+
+http://git.kernel.org/?p=linux/kernel/git/linville/wireless-testing.git;a=log;
+
+This views all the changes on wireless-testing.git.
+
+License
+-------
+
+This work is a subset of the Linux kernel as such we keep the kernel's
+Copyright practice. Some files have their own copyright and in those
+cases the license is mentioned in the file. All additional work made
+to building this package is licensed under the GPLv2.
+
+Developers
+----------
+
+Compatibility work goes into compat/compat.[ch]. If using those files do 
+not suffice additional actual code changes can go into patches/*.patch.
+
+An extended and more up to date version can be found at:
+
+http://wireless.kernel.org/en/users/Download/hacking
+
+This section deals with development details of compat-wireless and the other
+trees it uses. If you want to make your own compat-wireless tarballs, or if you
+see something busted with compat-wireless or just want to add something new or
+an enhancement this is the guide for you.
+
+Git trees you will need
+-----------------------
+
+compat-wireless backports both the bluetooth and 802.11 subsystems down to older
+kernels. To be able to synchronize backporting the latest and greatest the
+linux-next.git tree is used as its main source for kernel updates. General Linux
+kernel compatibility is addressed through a general kernel compatibility tree,
+compat.git. compat-wireless then has its own tree for specific wireless
+compatibility. You will then need to checkout three trees to start hacking on
+compat-wireless:
+
+git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
+git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/compat.git
+git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/compat-wireless-2.6.git
+
+Linux next
+----------
+
+The linux-next.git tree brings all subsystems being worked on for the next
+kernel release into one tree. So if the current rc kernel is 2.6.33-rc5, this
+means linux-next will have what people today are working on for the 2.6.34
+kernel release.
+
+compat.git
+----------
+
+The compat git tree is a general kernel compatibility layer which can be shared
+amongst different compatibility projects, or drivers. compat-wireless is just
+one of the kernel compatibility projects using compat.git. compat.git builds a
+general compatibility module, compat, and any additional modules to let you get
+new general kernel updates from future kernels on your old kernels.
+
+compat.git modules
+------------------
+
+compat.git provides a few modules and headers to help with general kernel
+compatibility.
+
+compat
+------
+
+Provides all exported symbols implemented in each respective kernel
+compat-2.6.xy.c files. Upon module load it just initializes the Linux kernel's
+''power management Quality Of Service'' (aka '''pm-qos''') Interface interface
+added as of the 2.6.24 kernel. No other things are initialized, the rest of the
+compat module just acts as a library of exported symbols.
+
+compat_firmware_class
+---------------------
+
+Another module which compat.git provides is a backport of the firmware_class
+module which got updated recently newer with a new request_firmware_nowait()
+to allow better asynchronous firmware uploading. This was added as of the 2.6.33
+kernel. The firmware_class module has been backported into a new module called
+compat_firmware_class. A separate module has been defined instead of a direct
+replacement for firmware_class since your system may have old drivers which use
+the old request_firmware_nowait() and would bust if they used the new
+request_firmware_nowait(). The compat_firmware_class module registers its own
+sysfs subsystem and as such also gets udev events sent through a separate
+subsystem. Because of this a new udev rules file is required and provided.
+
+compat-wireless.git
+-------------------
+
+Anything that is not general kernel compatibility but instead specific to 802.11
+or bluetooth goes into compat-wireless.git. After you've cloned all three trees,
+linux-next.git, compat.git and compat-wireless.git you need to change into the
+compat-wireless directory and tell compat-wireless where you linux-next and
+compat.git trees are. You do this with environment variables GIT_TREE and
+GIT_COMPAT_TREE. You can do for example:
+
+export GIT_TREE=/home/user/wireless-testing/
+export GIT_COMPAT_TREE=/home/users/compat.git/
+
+Then you can update your local sources based on these linux-next.git and
+compat.git trees:
+
+scripts/admin-clean.sh   - Cleans the compat-wireless-2.6 tree
+scripts/admin-update.sh  - Updates compat-wireless-2.6 with your git tree
+scripts/admin-refresh.sh - Does the above two
+
+Adding new drivers
+------------------
+
+Most new drivers are enabled for compilation. If see a driver you would like
+enabled try it into the mix, test them and if they work enable them and send
+the respective patches.
+
+Sending patches
+---------------
+
+Remember there are three trees. linux-next itself is a conglomeration of kernel
+git trees itself, so patches for linux-next.git should be sent to each
+respective subsystem for which the patches are targeted for. So for example for
+802.11 you will want to send them to John Linville and cc linux-wireless, for
+further guidelines on this see the Submitting Patches guidelines for 802.11.
+http://wireless.kernel.org/en/developers/Documentation/SubmittingPatches
+As another example, for bluetooth you will want to send them to Marcel
+Holtmann and cc the linux-bluetooth mailing list. If your patch touches on
+others areas of the kernel refer to the MAINTAINERS file on the kernel.
+
+For compat.git and compat-wireless.git please send patches against to:
+
+To: Luis R. Rodriguez <mcgrof@kernel.org>
+CC: linux-wireless@vger.kernel.org, linux-bluetooth@vger.kernel.org
+Subject: [PATCH] compat-2.6: fix foo
+
+For patches for compat.git please use a subject like the following:
+
+Subject: [PATCH] compat: fix foo
+
+For compat-wireless.git please use a subject like the following:
+
+Subject: [PATCH] compat-wireless: fix foo
+
+Patches are preferred sent with a clear commit log entry, if unfamiliar with
+how to send patches please refer to
+http://wireless.kernel.org/en/developers/Documentation/git-guide.
+
+
+TODO
+-----
+ * Dialog (make menuconfig) option for this package
+ * Compatibility work for 2.6.18 --> 2.6.24
+
diff --git a/code-metrics.txt b/code-metrics.txt
new file mode 100644
index 0000000..8fe4dcf
--- /dev/null
+++ b/code-metrics.txt
@@ -0,0 +1,14 @@
+
+compat-wireless code metrics
+
+    825441 - Total upstream lines of code being pulled
+      2418 - backport code changes
+      2087 - backport code additions
+       331 - backport code deletions
+      9096 - backport from compat module
+     11514 - total backport code
+    1.3949 - % of code consists of backport work
+
+Base tree: ath6kl.git
+Base tree version: branching-rel-3.2.3-74-g39f7005
+compat-wireless release: 3.3-OSR-2012-10-11-15-g2bd3ebf
diff --git a/compat/Makefile b/compat/Makefile
new file mode 100644
index 0000000..f4f4493
--- /dev/null
+++ b/compat/Makefile
@@ -0,0 +1,96 @@
+ifndef OLDJENKINS
+obj-m += wireless_compat.o
+
+obj-$(CONFIG_COMPAT_FIRMWARE_CLASS) += compat_firmware_class.o
+
+wireless_compat-y += main.o
+
+# Compat kernel compatibility code
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_14) += compat-2.6.14.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_18) += compat-2.6.18.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_19) += compat-2.6.19.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_21) += compat-2.6.21.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_22) += compat-2.6.22.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_23) += compat-2.6.23.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_24) += compat-2.6.24.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_25) += \
+	compat-2.6.25.o \
+	pm_qos_params.o
+
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_26) += compat-2.6.26.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_27) += compat-2.6.27.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_28) += compat-2.6.28.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_29) += compat-2.6.29.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_32) += compat-2.6.32.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_33) += compat-2.6.33.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_35) += compat-2.6.35.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_36) += compat-2.6.36.o
+
+wireless_compat-$(CONFIG_COMPAT_KFIFO) += kfifo.o
+
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_37) += compat-2.6.37.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_38) += compat-2.6.38.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_2_6_39) += \
+	compat-2.6.39.o \
+	kstrtox.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_3_0) += compat-3.0.o
+wireless_compat-$(CONFIG_COMPAT_KERNEL_3_2) += compat-3.2.o
+
+wireless_compat-$(CONFIG_COMPAT_CORDIC) += cordic.o
+wireless_compat-$(CONFIG_COMPAT_CRC8) += crc8.o
+
+ifndef CONFIG_64BIT
+ifndef CONFIG_GENERIC_ATOMIC64
+  wireless_compat-y += compat_atomic.o
+endif
+endif
+
+else
+obj-m += compat.o
+
+#compat-objs :=
+
+obj-$(CONFIG_COMPAT_FIRMWARE_CLASS) += compat_firmware_class.o
+
+compat-y += main.o
+
+# Compat kernel compatibility code
+compat-$(CONFIG_COMPAT_KERNEL_2_6_14) += compat-2.6.14.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_18) += compat-2.6.18.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_19) += compat-2.6.19.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_21) += compat-2.6.21.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_22) += compat-2.6.22.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_23) += compat-2.6.23.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_24) += compat-2.6.24.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_25) += \
+	compat-2.6.25.o \
+	pm_qos_params.o
+
+compat-$(CONFIG_COMPAT_KERNEL_2_6_26) += compat-2.6.26.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_27) += compat-2.6.27.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_28) += compat-2.6.28.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_29) += compat-2.6.29.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_32) += compat-2.6.32.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_33) += compat-2.6.33.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_35) += compat-2.6.35.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_36) += compat-2.6.36.o
+
+compat-$(CONFIG_COMPAT_KFIFO) += kfifo.o
+
+compat-$(CONFIG_COMPAT_KERNEL_2_6_37) += compat-2.6.37.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_38) += compat-2.6.38.o
+compat-$(CONFIG_COMPAT_KERNEL_2_6_39) += \
+	compat-2.6.39.o \
+	kstrtox.o
+compat-$(CONFIG_COMPAT_KERNEL_3_0) += compat-3.0.o
+compat-$(CONFIG_COMPAT_KERNEL_3_2) += compat-3.2.o
+
+compat-$(CONFIG_COMPAT_CORDIC) += cordic.o
+compat-$(CONFIG_COMPAT_CRC8) += crc8.o
+
+ifndef CONFIG_64BIT
+ifndef CONFIG_GENERIC_ATOMIC64
+  compat-y += compat_atomic.o
+endif
+endif
+endif
diff --git a/compat/compat-2.6.14.c b/compat/compat-2.6.14.c
new file mode 100644
index 0000000..3de847d
--- /dev/null
+++ b/compat/compat-2.6.14.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2007	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.14.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.14 compat code goes here */
+
diff --git a/compat/compat-2.6.18.c b/compat/compat-2.6.18.c
new file mode 100644
index 0000000..c7961ee
--- /dev/null
+++ b/compat/compat-2.6.18.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2007	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.18.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.18 compat code goes here */
+
diff --git a/compat/compat-2.6.19.c b/compat/compat-2.6.19.c
new file mode 100644
index 0000000..60c3404
--- /dev/null
+++ b/compat/compat-2.6.19.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2007	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.19.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.19 compat code goes here */
+
diff --git a/compat/compat-2.6.21.c b/compat/compat-2.6.21.c
new file mode 100644
index 0000000..7cf8861
--- /dev/null
+++ b/compat/compat-2.6.21.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2007	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.21.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.21 compat code goes here */
+
diff --git a/compat/compat-2.6.22.c b/compat/compat-2.6.22.c
new file mode 100644
index 0000000..d4df7b7
--- /dev/null
+++ b/compat/compat-2.6.22.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2007	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.22.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.22 compat code goes here */
+
diff --git a/compat/compat-2.6.23.c b/compat/compat-2.6.23.c
new file mode 100644
index 0000000..67d0075
--- /dev/null
+++ b/compat/compat-2.6.23.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2007	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.23.
+ */
+
+#include <net/compat.h>
+
+/* On net/core/dev.c as of 2.6.24 */
+int __dev_addr_delete(struct dev_addr_list **list, int *count,
+                      void *addr, int alen, int glbl)
+{
+	struct dev_addr_list *da;
+
+	for (; (da = *list) != NULL; list = &da->next) {
+		if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
+			alen == da->da_addrlen) {
+			if (glbl) {
+				int old_glbl = da->da_gusers;
+				da->da_gusers = 0;
+				if (old_glbl == 0)
+					break;
+			}
+			if (--da->da_users)
+				return 0;
+
+			*list = da->next;
+			kfree(da);
+			(*count)--;
+			return 0;
+		}
+	}
+	return -ENOENT;
+}
+EXPORT_SYMBOL(__dev_addr_delete);
+
+/* On net/core/dev.c as of 2.6.24. This is not yet used by mac80211 but
+ * might as well add it */
+int __dev_addr_add(struct dev_addr_list **list, int *count,
+                   void *addr, int alen, int glbl)
+{
+	struct dev_addr_list *da;
+
+	for (da = *list; da != NULL; da = da->next) {
+		if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
+			da->da_addrlen == alen) {
+			if (glbl) {
+				int old_glbl = da->da_gusers;
+				da->da_gusers = 1;
+				if (old_glbl)
+					return 0;
+			}
+			da->da_users++;
+			return 0;
+		}
+	}
+
+	da = kmalloc(sizeof(*da), GFP_ATOMIC);
+	if (da == NULL)
+		return -ENOMEM;
+	memcpy(da->da_addr, addr, alen);
+	da->da_addrlen = alen;
+	da->da_users = 1;
+	da->da_gusers = glbl ? 1 : 0;
+	da->next = *list;
+	*list = da;
+	(*count)++;
+	return 0;
+}
+EXPORT_SYMBOL(__dev_addr_add);
+
+
+/* Part of net/core/dev_mcast.c as of 2.6.23. This is a slightly different version.
+ * Since da->da_synced is not part of 2.6.22 we need to take longer route when
+ * syncing */
+
+/**
+ *	dev_mc_sync	- Synchronize device's multicast list to another device
+ *	@to: destination device
+ *	@from: source device
+ *
+ * 	Add newly added addresses to the destination device and release
+ * 	addresses that have no users left. The source device must be
+ * 	locked by netif_tx_lock_bh.
+ *
+ *	This function is intended to be called from the dev->set_multicast_list
+ *	function of layered software devices.
+ */
+int dev_mc_sync(struct net_device *to, struct net_device *from)
+{
+	struct dev_addr_list *da, *next, *da_to;
+	int err = 0;
+
+	netif_tx_lock_bh(to);
+	da = from->mc_list;
+	while (da != NULL) {
+		int synced = 0;
+		next = da->next;
+		da_to = to->mc_list;
+		/* 2.6.22 does not have da->da_synced so lets take the long route */
+		while (da_to != NULL) {
+			if (memcmp(da_to->da_addr, da->da_addr, da_to->da_addrlen) == 0 &&
+				da->da_addrlen == da_to->da_addrlen)
+				synced = 1;
+				break;
+		}
+		if (!synced) {
+			err = __dev_addr_add(&to->mc_list, &to->mc_count,
+					     da->da_addr, da->da_addrlen, 0);
+			if (err < 0)
+				break;
+			da->da_users++;
+		} else if (da->da_users == 1) {
+			__dev_addr_delete(&to->mc_list, &to->mc_count,
+					  da->da_addr, da->da_addrlen, 0);
+			__dev_addr_delete(&from->mc_list, &from->mc_count,
+					  da->da_addr, da->da_addrlen, 0);
+		}
+		da = next;
+	}
+	if (!err)
+		__dev_set_rx_mode(to);
+	netif_tx_unlock_bh(to);
+
+	return err;
+}
+EXPORT_SYMBOL(dev_mc_sync);
+
+
+/* Part of net/core/dev_mcast.c as of 2.6.23. This is a slighty different version.
+ * Since da->da_synced is not part of 2.6.22 we need to take longer route when
+ * unsyncing */
+
+/**
+ *      dev_mc_unsync   - Remove synchronized addresses from the destination
+ *			  device
+ *	@to: destination device
+ *	@from: source device
+ *
+ *	Remove all addresses that were added to the destination device by
+ *	dev_mc_sync(). This function is intended to be called from the
+ *	dev->stop function of layered software devices.
+ */
+void dev_mc_unsync(struct net_device *to, struct net_device *from)
+{
+	struct dev_addr_list *da, *next, *da_to;
+
+	netif_tx_lock_bh(from);
+	netif_tx_lock_bh(to);
+
+	da = from->mc_list;
+	while (da != NULL) {
+		bool synced = false;
+		next = da->next;
+		da_to = to->mc_list;
+		/* 2.6.22 does not have da->da_synced so lets take the long route */
+		while (da_to != NULL) {
+			if (memcmp(da_to->da_addr, da->da_addr, da_to->da_addrlen) == 0 &&
+				da->da_addrlen == da_to->da_addrlen)
+				synced = true;
+				break;
+		}
+		if (!synced) {
+			da = next;
+			continue;
+		}
+		__dev_addr_delete(&to->mc_list, &to->mc_count,
+			da->da_addr, da->da_addrlen, 0);
+		__dev_addr_delete(&from->mc_list, &from->mc_count,
+			da->da_addr, da->da_addrlen, 0);
+		da = next;
+	}
+	__dev_set_rx_mode(to);
+
+	netif_tx_unlock_bh(to);
+	netif_tx_unlock_bh(from);
+}
+EXPORT_SYMBOL(dev_mc_unsync);
+
+/* Added as of 2.6.23 on net/core/dev.c. Slightly modifed, no dev->set_rx_mode on
+ * 2.6.22 so ignore that. */
+
+/*
+ *	Upload unicast and multicast address lists to device and
+ *	configure RX filtering. When the device doesn't support unicast
+ *	filtering it is put in promiscous mode while unicast addresses
+ *	are present.
+ */
+void __dev_set_rx_mode(struct net_device *dev)
+{
+	/* dev_open will call this function so the list will stay sane. */
+	if (!(dev->flags&IFF_UP))
+		return;
+
+	if (!netif_device_present(dev))
+		return;
+
+/* This needs to be ported to 2.6.22 framework */
+#if 0
+	/* Unicast addresses changes may only happen under the rtnl,
+	 * therefore calling __dev_set_promiscuity here is safe.
+	 */
+	if (dev->uc_count > 0 && !dev->uc_promisc) {
+		__dev_set_promiscuity(dev, 1);
+		dev->uc_promisc = 1;
+	} else if (dev->uc_count == 0 && dev->uc_promisc) {
+		__dev_set_promiscuity(dev, -1);
+		dev->uc_promisc = 0;
+	}
+#endif
+
+	if (dev->set_multicast_list)
+		dev->set_multicast_list(dev);
+}
+
+/**
+ * pci_try_set_mwi - enables memory-write-invalidate PCI transaction
+ * @dev: the PCI device for which MWI is enabled
+ *
+ * Enables the Memory-Write-Invalidate transaction in %PCI_COMMAND.
+ * Callers are not required to check the return value.
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+int pci_try_set_mwi(struct pci_dev *dev)
+{
+	int rc = 0;
+#ifdef HAVE_PCI_SET_MWI
+	rc = pci_set_mwi(dev);
+#endif
+	return rc;
+}
+EXPORT_SYMBOL(pci_try_set_mwi);
+#endif
+
diff --git a/compat/compat-2.6.24.c b/compat/compat-2.6.24.c
new file mode 100644
index 0000000..b9ae16b
--- /dev/null
+++ b/compat/compat-2.6.24.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2007	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.24.
+ */
+
+#include <net/compat.h>
+#include <net/arp.h>
+
+/*
+ * We simply won't use it though, just declare it for our wrappers and
+ * for usage with tons of code that makes mention to it.
+ */
+struct net init_net;
+EXPORT_SYMBOL(init_net);
+
+/* 2.6.22 and 2.6.23 have eth_header_cache_update defined as extern in include/linux/etherdevice.h
+ * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
+
+/**
+ * eth_header_cache_update - update cache entry
+ * @hh: destination cache entry
+ * @dev: network device
+ * @haddr: new hardware address
+ *
+ * Called by Address Resolution module to notify changes in address.
+ */
+void eth_header_cache_update(struct hh_cache *hh,
+                             struct net_device *dev,
+                             unsigned char *haddr)
+{
+	memcpy(((u8 *) hh->hh_data) + HH_DATA_OFF(sizeof(struct ethhdr)),
+		haddr, ETH_ALEN);
+}
+EXPORT_SYMBOL(eth_header_cache_update);
+
+/* 2.6.22 and 2.6.23 have eth_header_cache defined as extern in include/linux/etherdevice.h
+ * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
+
+/**
+ * eth_header_cache - fill cache entry from neighbour
+ * @neigh: source neighbour
+ * @hh: destination cache entry
+ * Create an Ethernet header template from the neighbour.
+ */
+int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh)
+{
+	__be16 type = hh->hh_type;
+	struct ethhdr *eth;
+	const struct net_device *dev = neigh->dev;
+
+	eth = (struct ethhdr *)
+	    (((u8 *) hh->hh_data) + (HH_DATA_OFF(sizeof(*eth))));
+
+	if (type == htons(ETH_P_802_3))
+		return -1;
+
+	eth->h_proto = type;
+	memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
+	memcpy(eth->h_dest, neigh->ha, ETH_ALEN);
+	hh->hh_len = ETH_HLEN;
+	return 0;
+}
+EXPORT_SYMBOL(eth_header_cache);
+
+/* 2.6.22 and 2.6.23 have eth_header() defined as extern in include/linux/etherdevice.h
+ * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
+
+/**
+ * eth_header - create the Ethernet header
+ * @skb:	buffer to alter
+ * @dev:	source device
+ * @type:	Ethernet type field
+ * @daddr: destination address (NULL leave destination address)
+ * @saddr: source address (NULL use device source address)
+ * @len:   packet length (<= skb->len)
+ *
+ *
+ * Set the protocol type. For a packet of type ETH_P_802_3 we put the length
+ * in here instead. It is up to the 802.2 layer to carry protocol information.
+ */
+int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
+	       void *daddr, void *saddr, unsigned len)
+{
+	struct ethhdr *eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
+
+	if (type != ETH_P_802_3)
+		eth->h_proto = htons(type);
+	else
+		eth->h_proto = htons(len);
+
+	/*
+	 *      Set the source hardware address.
+	 */
+
+	if (!saddr)
+		saddr = dev->dev_addr;
+	memcpy(eth->h_source, saddr, dev->addr_len);
+
+	if (daddr) {
+		memcpy(eth->h_dest, daddr, dev->addr_len);
+		return ETH_HLEN;
+	}
+
+	/*
+	 *      Anyway, the loopback-device should never use this function...
+	 */
+
+	if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
+		memset(eth->h_dest, 0, dev->addr_len);
+		return ETH_HLEN;
+	}
+
+	return -ETH_HLEN;
+}
+
+EXPORT_SYMBOL(eth_header);
+
+/* 2.6.22 and 2.6.23 have eth_rebuild_header defined as extern in include/linux/etherdevice.h
+ * and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
+
+/**
+ * eth_rebuild_header- rebuild the Ethernet MAC header.
+ * @skb: socket buffer to update
+ *
+ * This is called after an ARP or IPV6 ndisc it's resolution on this
+ * sk_buff. We now let protocol (ARP) fill in the other fields.
+ *
+ * This routine CANNOT use cached dst->neigh!
+ * Really, it is used only when dst->neigh is wrong.
+ */
+int eth_rebuild_header(struct sk_buff *skb)
+{
+	struct ethhdr *eth = (struct ethhdr *)skb->data;
+	struct net_device *dev = skb->dev;
+
+	switch (eth->h_proto) {
+#ifdef CONFIG_INET
+	case __constant_htons(ETH_P_IP):
+		return arp_find(eth->h_dest, skb);
+#endif
+	default:
+		printk(KERN_DEBUG
+		       "%s: unable to resolve type %X addresses.\n",
+		       dev->name, (int)eth->h_proto);
+
+		memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
+		break;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(eth_rebuild_header);
+
diff --git a/compat/compat-2.6.25.c b/compat/compat-2.6.25.c
new file mode 100644
index 0000000..7cb996b
--- /dev/null
+++ b/compat/compat-2.6.25.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2007-2010	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.25.
+ */
+
+#include <linux/miscdevice.h>
+
+/**
+ * The following things are out of ./lib/vsprintf.c
+ * The new iwlwifi driver is using them.
+ */
+
+/**
+ * strict_strtoul - convert a string to an unsigned long strictly
+ * @cp: The string to be converted
+ * @base: The number base to use
+ * @res: The converted result value
+ *
+ * strict_strtoul converts a string to an unsigned long only if the
+ * string is really an unsigned long string, any string containing
+ * any invalid char at the tail will be rejected and -EINVAL is returned,
+ * only a newline char at the tail is acceptible because people generally
+ * change a module parameter in the following way:
+ *
+ * 	echo 1024 > /sys/module/e1000/parameters/copybreak
+ *
+ * echo will append a newline to the tail.
+ *
+ * It returns 0 if conversion is successful and *res is set to the converted
+ * value, otherwise it returns -EINVAL and *res is set to 0.
+ *
+ * simple_strtoul just ignores the successive invalid characters and
+ * return the converted value of prefix part of the string.
+ */
+int strict_strtoul(const char *cp, unsigned int base, unsigned long *res);
+
+/**
+ * strict_strtol - convert a string to a long strictly
+ * @cp: The string to be converted
+ * @base: The number base to use
+ * @res: The converted result value
+ *
+ * strict_strtol is similiar to strict_strtoul, but it allows the first
+ * character of a string is '-'.
+ *
+ * It returns 0 if conversion is successful and *res is set to the converted
+ * value, otherwise it returns -EINVAL and *res is set to 0.
+ */
+int strict_strtol(const char *cp, unsigned int base, long *res);
+
+#define define_strict_strtoux(type, valtype)				\
+int strict_strtou##type(const char *cp, unsigned int base, valtype *res)\
+{									\
+	char *tail;							\
+	valtype val;							\
+	size_t len;							\
+									\
+	*res = 0;							\
+	len = strlen(cp);						\
+	if (len == 0)							\
+		return -EINVAL;						\
+									\
+	val = simple_strtou##type(cp, &tail, base);			\
+	if ((*tail == '\0') ||						\
+		((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {\
+		*res = val;						\
+		return 0;						\
+	}								\
+									\
+	return -EINVAL;							\
+}									\
+
+#define define_strict_strtox(type, valtype)				\
+int strict_strto##type(const char *cp, unsigned int base, valtype *res)	\
+{									\
+	int ret;							\
+	if (*cp == '-') {						\
+		ret = strict_strtou##type(cp+1, base, res);		\
+		if (!ret)						\
+			*res = -(*res);					\
+	} else								\
+		ret = strict_strtou##type(cp, base, res);		\
+									\
+	return ret;							\
+}									\
+
+define_strict_strtoux(l, unsigned long)
+define_strict_strtox(l, long)
+
+EXPORT_SYMBOL(strict_strtoul);
+EXPORT_SYMBOL(strict_strtol);
+
diff --git a/compat/compat-2.6.26.c b/compat/compat-2.6.26.c
new file mode 100644
index 0000000..f471506
--- /dev/null
+++ b/compat/compat-2.6.26.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2007-2010	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.26.
+ *
+ * Copyright holders from ported work:
+ *
+ * Copyright (c) 2002-2003 Patrick Mochel <mochel@osdl.org>
+ * Copyright (c) 2006-2007 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (c) 2006-2007 Novell Inc.
+ */
+
+#include <net/compat.h>
+
+/* 2.6.24 does not have the struct kobject with a name */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25))
+
+/**
+ * kobject_set_name_vargs - Set the name of an kobject
+ * @kobj: struct kobject to set the name of
+ * @fmt: format string used to build the name
+ * @vargs: vargs to format the string.
+ */
+static
+int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
+				  va_list vargs)
+{
+	const char *old_name = kobj->name;
+	char *s;
+
+	if (kobj->name && !fmt)
+		return 0;
+
+	kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
+	if (!kobj->name)
+		return -ENOMEM;
+
+	/* ewww... some of these buggers have '/' in the name ... */
+	while ((s = strchr(kobj->name, '/')))
+		s[0] = '!';
+
+	kfree(old_name);
+	return 0;
+}
+#else
+static
+int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
+				  va_list vargs)
+{
+	struct device *dev;
+	unsigned int len;
+	va_list aq;
+
+	dev = container_of(kobj, struct device, kobj);
+
+	va_copy(aq, vargs);
+	len = vsnprintf(NULL, 0, fmt, aq);
+	va_end(aq);
+
+	len = len < BUS_ID_SIZE ? (len + 1) : BUS_ID_SIZE;
+
+	vsnprintf(dev->bus_id, len, fmt, vargs);
+	return 0;
+}
+#endif
+
+/**
+ * dev_set_name - set a device name
+ * @dev: device
+ * @fmt: format string for the device's name
+ */
+int dev_set_name(struct device *dev, const char *fmt, ...)
+{
+	va_list vargs;
+	int err;
+
+	va_start(vargs, fmt);
+	err = kobject_set_name_vargs(&dev->kobj, fmt, vargs);
+	va_end(vargs);
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_set_name);
+
diff --git a/compat/compat-2.6.27.c b/compat/compat-2.6.27.c
new file mode 100644
index 0000000..25bee28
--- /dev/null
+++ b/compat/compat-2.6.27.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2007	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.27
+ */
+
+#include <linux/compat.h>
+#include <linux/pci.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#endif
+
+/* rfkill notification chain */
+#define RFKILL_STATE_CHANGED            0x0001  /* state of a normal rfkill
+							switch has changed */
+
+/*
+ * e5899e1b7d73e67de758a32174a859cc2586c0b9 made pci_pme_capable() external,
+ * it was defined internally, some drivers want access to this information.
+ *
+ * Unfortunately the old kernels do not have ->pm_cap or ->pme_support so
+ * we have to call the PCI routines directly.
+ */
+
+/**
+ * pci_pme_capable - check the capability of PCI device to generate PME#
+ * @dev: PCI device to handle.
+ * @state: PCI state from which device will issue PME#.
+ *
+ * This is the backport code for older kernels for compat-wireless, we read stuff
+ * from the initialization stuff from pci_pm_init().
+ */
+bool pci_pme_capable(struct pci_dev *dev, pci_power_t state)
+{
+	int pm;
+	u16 pmc = 0;
+	u16 pme_support; /* as from the pci dev */
+	/* find PCI PM capability in list */
+	pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+	if (!pm)
+		return false;
+
+        if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
+		dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n",
+			pmc & PCI_PM_CAP_VER_MASK);
+		return false;
+        }
+
+	pmc &= PCI_PM_CAP_PME_MASK;
+
+	if (!pmc)
+		return false;
+
+	pme_support = pmc >> PCI_PM_CAP_PME_SHIFT;
+
+	/* Check device's ability to generate PME# */
+
+	return !!(pme_support & (1 << state));
+}
+EXPORT_SYMBOL(pci_pme_capable);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
+/**
+ *	mmc_align_data_size - pads a transfer size to a more optimal value
+ *	@card: the MMC card associated with the data transfer
+ *	@sz: original transfer size
+ *
+ *	Pads the original data size with a number of extra bytes in
+ *	order to avoid controller bugs and/or performance hits
+ *	(e.g. some controllers revert to PIO for certain sizes).
+ *
+ *	Returns the improved size, which might be unmodified.
+ *
+ *	Note that this function is only relevant when issuing a
+ *	single scatter gather entry.
+ */
+unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
+{
+	/*
+	* FIXME: We don't have a system for the controller to tell
+	* the core about its problems yet, so for now we just 32-bit
+	* align the size.
+	*/
+	sz = ((sz + 3) / 4) * 4;
+
+	return sz;
+}
+EXPORT_SYMBOL(mmc_align_data_size);
+
+/*
+ * Calculate the maximum byte mode transfer size
+ */
+static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
+{
+	unsigned int mval = (unsigned int) min(func->card->host->max_seg_size,
+			    func->card->host->max_blk_size);
+	mval = min(mval, func->max_blksize);
+	return min(mval, 512u); /* maximum size for byte mode */
+}
+
+/**
+ *	sdio_align_size - pads a transfer size to a more optimal value
+ *	@func: SDIO function
+ *	@sz: original transfer size
+ *
+ *	Pads the original data size with a number of extra bytes in
+ *	order to avoid controller bugs and/or performance hits
+ *	(e.g. some controllers revert to PIO for certain sizes).
+ *
+ *	If possible, it will also adjust the size so that it can be
+ *	handled in just a single request.
+ *
+ *	Returns the improved size, which might be unmodified.
+ */
+unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
+{
+	unsigned int orig_sz;
+	unsigned int blk_sz, byte_sz;
+	unsigned chunk_sz;
+
+	orig_sz = sz;
+
+	/*
+	 * Do a first check with the controller, in case it
+	 * wants to increase the size up to a point where it
+	 * might need more than one block.
+	 */
+	sz = mmc_align_data_size(func->card, sz);
+
+	/*
+	 * If we can still do this with just a byte transfer, then
+	 * we're done.
+	 */
+	if (sz <= sdio_max_byte_size(func))
+		return sz;
+
+	if (func->card->cccr.multi_block) {
+		/*
+		 * Check if the transfer is already block aligned
+		 */
+		if ((sz % func->cur_blksize) == 0)
+			return sz;
+
+		/*
+		 * Realign it so that it can be done with one request,
+		 * and recheck if the controller still likes it.
+		 */
+		blk_sz = ((sz + func->cur_blksize - 1) /
+			func->cur_blksize) * func->cur_blksize;
+		blk_sz = mmc_align_data_size(func->card, blk_sz);
+
+		/*
+		 * This value is only good if it is still just
+		 * one request.
+		 */
+		if ((blk_sz % func->cur_blksize) == 0)
+			return blk_sz;
+
+		/*
+		 * We failed to do one request, but at least try to
+		 * pad the remainder properly.
+		 */
+		byte_sz = mmc_align_data_size(func->card,
+				sz % func->cur_blksize);
+		if (byte_sz <= sdio_max_byte_size(func)) {
+			blk_sz = sz / func->cur_blksize;
+			return blk_sz * func->cur_blksize + byte_sz;
+		}
+	} else {
+		/*
+		 * We need multiple requests, so first check that the
+		 * controller can handle the chunk size;
+		 */
+		chunk_sz = mmc_align_data_size(func->card,
+				sdio_max_byte_size(func));
+		if (chunk_sz == sdio_max_byte_size(func)) {
+			/*
+			 * Fix up the size of the remainder (if any)
+			 */
+			byte_sz = orig_sz % chunk_sz;
+			if (byte_sz) {
+				byte_sz = mmc_align_data_size(func->card,
+						byte_sz);
+			}
+
+			return (orig_sz / chunk_sz) * chunk_sz + byte_sz;
+		}
+	}
+
+	/*
+	 * The controller is simply incapable of transferring the size
+	 * we want in decent manner, so just return the original size.
+	 */
+	return orig_sz;
+}
+EXPORT_SYMBOL_GPL(sdio_align_size);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) */
+
+#ifdef CONFIG_DEBUG_FS
+/*
+ * Backport of debugfs_remove_recursive() without using the internals globals
+ * which are used by the kernel's version with:
+ * simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+ */
+void debugfs_remove_recursive(struct dentry *dentry)
+{
+	struct dentry *last = NULL;
+
+	/* Sanity checks */
+	if (!dentry || !dentry->d_parent || !dentry->d_parent->d_inode)
+		return;
+
+	while (dentry != last) {
+		struct dentry *child = dentry;
+
+		/* Find a child without children */
+		while (!list_empty(&child->d_subdirs))
+			child = list_entry(child->d_subdirs.next,
+					   struct dentry,
+					   d_u.d_child);
+
+		/* Bail out if we already tried to remove that entry */
+		if (child == last)
+			return;
+
+		last = child;
+		debugfs_remove(child);
+	}
+}
+EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
+#endif /* CONFIG_DEBUG_FS */
+
diff --git a/compat/compat-2.6.28.c b/compat/compat-2.6.28.c
new file mode 100644
index 0000000..72c9e09
--- /dev/null
+++ b/compat/compat-2.6.28.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright 2007	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.28.
+ */
+
+#include <linux/compat.h>
+#include <linux/usb.h>
+#include <linux/tty.h>
+#include <asm/poll.h>
+
+/* 2.6.28 compat code goes here */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23))
+#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE)
+/*
+ * Compat-wireless notes for USB backport stuff:
+ *
+ * urb->reject exists on 2.6.27, the poison/unpoison helpers
+ * did not though. The anchor poison does not exist so we cannot use them.
+ *
+ * USB anchor poising seems to exist to prevent future driver sumbissions
+ * of usb_anchor_urb() to an anchor marked as poisoned. For older kernels
+ * we cannot use that, so new usb_anchor_urb()s will be anchored. The down
+ * side to this should be submission of URBs will continue being anchored
+ * on an anchor instead of having them being rejected immediately when the
+ * driver realized we needed to stop. For ar9170 we poison URBs upon the
+ * ar9170 mac80211 stop callback(), don't think this should be so bad.
+ * It mean there is period of time in older kernels for which we continue
+ * to anchor new URBs to a known stopped anchor. We have two anchors
+ * (TX, and RX)
+ */
+
+#if 0
+/**
+ * usb_poison_urb - reliably kill a transfer and prevent further use of an URB
+ * @urb: pointer to URB describing a previously submitted request,
+ *	may be NULL
+ *
+ * This routine cancels an in-progress request.  It is guaranteed that
+ * upon return all completion handlers will have finished and the URB
+ * will be totally idle and cannot be reused.  These features make
+ * this an ideal way to stop I/O in a disconnect() callback.
+ * If the request has not already finished or been unlinked
+ * the completion handler will see urb->status == -ENOENT.
+ *
+ * After and while the routine runs, attempts to resubmit the URB will fail
+ * with error -EPERM.  Thus even if the URB's completion handler always
+ * tries to resubmit, it will not succeed and the URB will become idle.
+ *
+ * This routine may not be used in an interrupt context (such as a bottom
+ * half or a completion handler), or when holding a spinlock, or in other
+ * situations where the caller can't schedule().
+ *
+ * This routine should not be called by a driver after its disconnect
+ * method has returned.
+ */
+void usb_poison_urb(struct urb *urb)
+{
+	might_sleep();
+	if (!(urb && urb->dev && urb->ep))
+		return;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
+	spin_lock_irq(&usb_reject_lock);
+#endif
+	++urb->reject;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
+	spin_unlock_irq(&usb_reject_lock);
+#endif
+	/*
+	 * XXX: usb_hcd_unlink_urb() needs backporting... this is defined
+	 * on usb hcd.c but urb.c gets access to it. That is, older kernels
+	 * have usb_hcd_unlink_urb() but its not exported, nor can we
+	 * re-implement it exactly. This essentially dequeues the urb from
+	 * hw, we need to figure out a way to backport this.
+	 */
+	//usb_hcd_unlink_urb(urb, -ENOENT);
+
+	wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
+}
+EXPORT_SYMBOL_GPL(usb_poison_urb);
+#endif
+#endif /* CONFIG_USB */
+
+#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
+
+#include <pcmcia/ds.h>
+struct pcmcia_cfg_mem {
+	tuple_t tuple;
+	cisparse_t parse;
+	u8 buf[256];
+	cistpl_cftable_entry_t dflt;
+};
+/**
+ * pcmcia_loop_config() - loop over configuration options
+ * @p_dev:	the struct pcmcia_device which we need to loop for.
+ * @conf_check:	function to call for each configuration option.
+ *		It gets passed the struct pcmcia_device, the CIS data
+ *		describing the configuration option, and private data
+ *		being passed to pcmcia_loop_config()
+ * @priv_data:	private data to be passed to the conf_check function.
+ *
+ * pcmcia_loop_config() loops over all configuration options, and calls
+ * the driver-specific conf_check() for each one, checking whether
+ * it is a valid one. Returns 0 on success or errorcode otherwise.
+ */
+int pcmcia_loop_config(struct pcmcia_device *p_dev,
+		       int	(*conf_check)	(struct pcmcia_device *p_dev,
+						 cistpl_cftable_entry_t *cfg,
+						 cistpl_cftable_entry_t *dflt,
+						 unsigned int vcc,
+						 void *priv_data),
+		       void *priv_data)
+{
+	struct pcmcia_cfg_mem *cfg_mem;
+
+	tuple_t *tuple;
+	int ret;
+	unsigned int vcc;
+
+	cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL);
+	if (cfg_mem == NULL)
+		return -ENOMEM;
+
+	/* get the current Vcc setting */
+	vcc = p_dev->socket->socket.Vcc;
+
+	tuple = &cfg_mem->tuple;
+	tuple->TupleData = cfg_mem->buf;
+	tuple->TupleDataMax = 255;
+	tuple->TupleOffset = 0;
+	tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	tuple->Attributes = 0;
+
+	ret = pcmcia_get_first_tuple(p_dev, tuple);
+	while (!ret) {
+		cistpl_cftable_entry_t *cfg = &cfg_mem->parse.cftable_entry;
+
+		if (pcmcia_get_tuple_data(p_dev, tuple))
+			goto next_entry;
+
+		if (pcmcia_parse_tuple(tuple, &cfg_mem->parse))
+			goto next_entry;
+
+		/* default values */
+		p_dev->conf.ConfigIndex = cfg->index;
+		if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+			cfg_mem->dflt = *cfg;
+
+		ret = conf_check(p_dev, cfg, &cfg_mem->dflt, vcc, priv_data);
+		if (!ret)
+			break;
+
+next_entry:
+		ret = pcmcia_get_next_tuple(p_dev, tuple);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(pcmcia_loop_config);
+
+#endif /* CONFIG_PCMCIA */
+
+#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE)
+
+void usb_unpoison_urb(struct urb *urb)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
+	unsigned long flags;
+#endif
+
+	if (!urb)
+		return;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
+	spin_lock_irqsave(&usb_reject_lock, flags);
+#endif
+	--urb->reject;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
+	spin_unlock_irqrestore(&usb_reject_lock, flags);
+#endif
+}
+EXPORT_SYMBOL_GPL(usb_unpoison_urb);
+
+
+#if 0
+/**
+ * usb_poison_anchored_urbs - cease all traffic from an anchor
+ * @anchor: anchor the requests are bound to
+ *
+ * this allows all outstanding URBs to be poisoned starting
+ * from the back of the queue. Newly added URBs will also be
+ * poisoned
+ *
+ * This routine should not be called by a driver after its disconnect
+ * method has returned.
+ */
+void usb_poison_anchored_urbs(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+
+	spin_lock_irq(&anchor->lock);
+	// anchor->poisoned = 1; /* XXX: Cannot backport */
+	while (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.prev, struct urb,
+				    anchor_list);
+		/* we must make sure the URB isn't freed before we kill it*/
+		usb_get_urb(victim);
+		spin_unlock_irq(&anchor->lock);
+		/* this will unanchor the URB */
+		usb_poison_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irq(&anchor->lock);
+	}
+	spin_unlock_irq(&anchor->lock);
+}
+EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs);
+#endif
+
+/**
+ * usb_anchor_empty - is an anchor empty
+ * @anchor: the anchor you want to query
+ *
+ * returns 1 if the anchor has no urbs associated with it
+ */
+int usb_anchor_empty(struct usb_anchor *anchor)
+{
+	return list_empty(&anchor->urb_list);
+}
+
+EXPORT_SYMBOL_GPL(usb_anchor_empty);
+#endif /* CONFIG_USB */
+#endif
+
+void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar)
+{
+	/*
+	 * Make sure the BAR is actually a memory resource, not an IO resource
+	 */
+	if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
+		WARN_ON(1);
+		return NULL;
+	}
+	return ioremap_nocache(pci_resource_start(pdev, bar),
+				     pci_resource_len(pdev, bar));
+}
+EXPORT_SYMBOL_GPL(pci_ioremap_bar);
+
+static unsigned long round_jiffies_common(unsigned long j, int cpu,
+		bool force_up)
+{
+	int rem;
+	unsigned long original = j;
+
+	/*
+	 * We don't want all cpus firing their timers at once hitting the
+	 * same lock or cachelines, so we skew each extra cpu with an extra
+	 * 3 jiffies. This 3 jiffies came originally from the mm/ code which
+	 * already did this.
+	 * The skew is done by adding 3*cpunr, then round, then subtract this
+	 * extra offset again.
+	 */
+	j += cpu * 3;
+
+	rem = j % HZ;
+
+	/*
+	 * If the target jiffie is just after a whole second (which can happen
+	 * due to delays of the timer irq, long irq off times etc etc) then
+	 * we should round down to the whole second, not up. Use 1/4th second
+	 * as cutoff for this rounding as an extreme upper bound for this.
+	 * But never round down if @force_up is set.
+	 */
+	if (rem < HZ/4 && !force_up) /* round down */
+		j = j - rem;
+	else /* round up */
+		j = j - rem + HZ;
+
+	/* now that we have rounded, subtract the extra skew again */
+	j -= cpu * 3;
+
+	if (j <= jiffies) /* rounding ate our timeout entirely; */
+		return original;
+	return j;
+}
+
+/**
+ * round_jiffies_up - function to round jiffies up to a full second
+ * @j: the time in (absolute) jiffies that should be rounded
+ *
+ * This is the same as round_jiffies() except that it will never
+ * round down.  This is useful for timeouts for which the exact time
+ * of firing does not matter too much, as long as they don't fire too
+ * early.
+ */
+unsigned long round_jiffies_up(unsigned long j)
+{
+	return round_jiffies_common(j, raw_smp_processor_id(), true);
+}
+EXPORT_SYMBOL_GPL(round_jiffies_up);
+
+void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off,
+		int size)
+{
+	skb_fill_page_desc(skb, i, page, off, size);
+	skb->len += size;
+	skb->data_len += size;
+	skb->truesize += size;
+}
+EXPORT_SYMBOL(skb_add_rx_frag);
+
+void tty_write_unlock(struct tty_struct *tty)
+{
+	mutex_unlock(&tty->atomic_write_lock);
+	wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
+}
+
+int tty_write_lock(struct tty_struct *tty, int ndelay)
+{
+	if (!mutex_trylock(&tty->atomic_write_lock)) {
+		if (ndelay)
+			return -EAGAIN;
+		if (mutex_lock_interruptible(&tty->atomic_write_lock))
+			return -ERESTARTSYS;
+	}
+	return 0;
+}
+
+/**
+ *	send_prio_char		-	send priority character
+ *
+ *	Send a high priority character to the tty even if stopped
+ *
+ *	Locking: none for xchar method, write ordering for write method.
+ */
+
+static int send_prio_char(struct tty_struct *tty, char ch)
+{
+	int	was_stopped = tty->stopped;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
+	if (tty->ops->send_xchar) {
+		tty->ops->send_xchar(tty, ch);
+#else
+	if (tty->driver->send_xchar) {
+		tty->driver->send_xchar(tty, ch);
+#endif
+		return 0;
+	}
+
+	if (tty_write_lock(tty, 0) < 0)
+		return -ERESTARTSYS;
+
+	if (was_stopped)
+		start_tty(tty);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
+	tty->ops->write(tty, &ch, 1);
+#else
+	tty->driver->write(tty, &ch, 1);
+#endif
+	if (was_stopped)
+		stop_tty(tty);
+	tty_write_unlock(tty);
+	return 0;
+}
+
+int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
+	unsigned long flags;
+#endif
+	int retval;
+
+	switch (cmd) {
+	case TCXONC:
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		switch (arg) {
+		case TCOOFF:
+			if (!tty->flow_stopped) {
+				tty->flow_stopped = 1;
+				stop_tty(tty);
+			}
+			break;
+		case TCOON:
+			if (tty->flow_stopped) {
+				tty->flow_stopped = 0;
+				start_tty(tty);
+			}
+			break;
+		case TCIOFF:
+			if (STOP_CHAR(tty) != __DISABLED_CHAR)
+				return send_prio_char(tty, STOP_CHAR(tty));
+			break;
+		case TCION:
+			if (START_CHAR(tty) != __DISABLED_CHAR)
+				return send_prio_char(tty, START_CHAR(tty));
+			break;
+		default:
+			return -EINVAL;
+		}
+		return 0;
+	case TCFLSH:
+		return tty_perform_flush(tty, arg);
+	case TIOCPKT:
+	{
+		int pktmode;
+
+		if (tty->driver->type != TTY_DRIVER_TYPE_PTY ||
+		    tty->driver->subtype != PTY_TYPE_MASTER)
+			return -ENOTTY;
+		if (get_user(pktmode, (int __user *) arg))
+			return -EFAULT;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
+		spin_lock_irqsave(&tty->ctrl_lock, flags);
+#endif
+		if (pktmode) {
+			if (!tty->packet) {
+				tty->packet = 1;
+				tty->link->ctrl_status = 0;
+			}
+		} else
+			tty->packet = 0;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
+		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+#endif
+		return 0;
+	}
+	default:
+		/* Try the mode commands */
+		return tty_mode_ioctl(tty, file, cmd, arg);
+	}
+}
+EXPORT_SYMBOL(n_tty_ioctl_helper);
+
+/**
+ * pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold
+ * @dev: PCI device to prepare
+ * @enable: True to enable wake-up event generation; false to disable
+ *
+ * Many drivers want the device to wake up the system from D3_hot or D3_cold
+ * and this function allows them to set that up cleanly - pci_enable_wake()
+ * should not be called twice in a row to enable wake-up due to PCI PM vs ACPI
+ * ordering constraints.
+ *
+ * This function only returns error code if the device is not capable of
+ * generating PME# from both D3_hot and D3_cold, and the platform is unable to
+ * enable wake-up power for it.
+ */
+int pci_wake_from_d3(struct pci_dev *dev, bool enable)
+{
+	return pci_pme_capable(dev, PCI_D3cold) ?
+			pci_enable_wake(dev, PCI_D3cold, enable) :
+			pci_enable_wake(dev, PCI_D3hot, enable);
+}
+EXPORT_SYMBOL(pci_wake_from_d3);
+
diff --git a/compat/compat-2.6.29.c b/compat/compat-2.6.29.c
new file mode 100644
index 0000000..14f5e90
--- /dev/null
+++ b/compat/compat-2.6.29.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2007-2010	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.29.
+ */
+
+#include <linux/compat.h>
+#include <linux/usb.h>
+#include <linux/etherdevice.h>
+
+/*
+ * If you don't see your net_device_ops implemented on
+ * netdev_attach_ops() then you are shit out of luck and
+ * you must do the nasty ifdef magic, unless you figure
+ * out a way to squeze your hacks into this routine :)
+ */
+void netdev_attach_ops(struct net_device *dev,
+		       const struct net_device_ops *ops)
+{
+	dev->open = ops->ndo_open;
+	dev->init = ops->ndo_init;
+	dev->stop = ops->ndo_stop;
+	dev->hard_start_xmit = ops->ndo_start_xmit;
+	dev->change_rx_flags = ops->ndo_change_rx_flags;
+	dev->set_multicast_list = ops->ndo_set_multicast_list;
+	dev->validate_addr = ops->ndo_validate_addr;
+	dev->do_ioctl = ops->ndo_do_ioctl;
+	dev->set_config = ops->ndo_set_config;
+	dev->change_mtu = ops->ndo_change_mtu;
+	dev->set_mac_address = ops->ndo_set_mac_address;
+	dev->tx_timeout = ops->ndo_tx_timeout;
+	if (ops->ndo_get_stats)
+		dev->get_stats = ops->ndo_get_stats;
+	dev->vlan_rx_register = ops->ndo_vlan_rx_register;
+	dev->vlan_rx_add_vid = ops->ndo_vlan_rx_add_vid;
+	dev->vlan_rx_kill_vid = ops->ndo_vlan_rx_kill_vid;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ops->ndo_poll_controller;
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
+	dev->select_queue = ops->ndo_select_queue;
+#endif
+}
+EXPORT_SYMBOL(netdev_attach_ops);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23))
+#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE)
+/**
+ * usb_unpoison_anchored_urbs - let an anchor be used successfully again
+ * @anchor: anchor the requests are bound to
+ *
+ * Reverses the effect of usb_poison_anchored_urbs
+ * the anchor can be used normally after it returns
+ */
+void usb_unpoison_anchored_urbs(struct usb_anchor *anchor)
+{
+	unsigned long flags;
+	struct urb *lazarus;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	list_for_each_entry(lazarus, &anchor->urb_list, anchor_list) {
+		usb_unpoison_urb(lazarus);
+	}
+	//anchor->poisoned = 0; /* XXX: cannot backport */
+	spin_unlock_irqrestore(&anchor->lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
+#endif /* CONFIG_USB */
+#endif
+
+/**
+ * eth_mac_addr - set new Ethernet hardware address
+ * @dev: network device
+ * @p: socket address
+ * Change hardware address of device.
+ *
+ * This doesn't change hardware matching, so needs to be overridden
+ * for most real devices.
+ */
+int eth_mac_addr(struct net_device *dev, void *p)
+{
+	struct sockaddr *addr = p;
+
+	if (netif_running(dev))
+		return -EBUSY;
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+	return 0;
+}
+EXPORT_SYMBOL(eth_mac_addr);
+
+/**
+ * eth_change_mtu - set new MTU size
+ * @dev: network device
+ * @new_mtu: new Maximum Transfer Unit
+ *
+ * Allow changing MTU size. Needs to be overridden for devices
+ * supporting jumbo frames.
+ */
+int eth_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)
+		return -EINVAL;
+	dev->mtu = new_mtu;
+	return 0;
+}
+EXPORT_SYMBOL(eth_change_mtu);
+
+int eth_validate_addr(struct net_device *dev)
+{
+	if (!is_valid_ether_addr(dev->dev_addr))
+		return -EADDRNOTAVAIL;
+
+	return 0;
+}
+EXPORT_SYMBOL(eth_validate_addr);
+/* Source: net/ethernet/eth.c */
+
+#define NETREG_DUMMY 5
+/**
+ *	init_dummy_netdev	- init a dummy network device for NAPI
+ *	@dev: device to init
+ *
+ *	This takes a network device structure and initialize the minimum
+ *	amount of fields so it can be used to schedule NAPI polls without
+ *	registering a full blown interface. This is to be used by drivers
+ *	that need to tie several hardware interfaces to a single NAPI
+ *	poll scheduler due to HW limitations.
+ */
+int init_dummy_netdev(struct net_device *dev)
+{
+	/* Clear everything. Note we don't initialize spinlocks
+	 * are they aren't supposed to be taken by any of the
+	 * NAPI code and this dummy netdev is supposed to be
+	 * only ever used for NAPI polls
+	 */
+	memset(dev, 0, sizeof(struct net_device));
+
+	/* make sure we BUG if trying to hit standard
+	 * register/unregister code path
+	 */
+	dev->reg_state = NETREG_DUMMY;
+
+	/* initialize the ref count */
+	atomic_set(&dev->refcnt, 1);
+
+#ifdef CONFIG_NETPOLL
+	/* NAPI wants this */
+	INIT_LIST_HEAD(&dev->napi_list);
+#endif
+
+	/* a dummy interface is started by default */
+	set_bit(__LINK_STATE_PRESENT, &dev->state);
+	set_bit(__LINK_STATE_START, &dev->state);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(init_dummy_netdev);
+/* Source: net/core/dev.c */
+
diff --git a/compat/compat-2.6.32.c b/compat/compat-2.6.32.c
new file mode 100644
index 0000000..0b3af07
--- /dev/null
+++ b/compat/compat-2.6.32.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2007	Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.32.
+ */
+
+#include <linux/compat.h>
+#include <linux/netdevice.h>
+
+int __dev_addr_add(struct dev_addr_list **list, int *count,
+		   void *addr, int alen, int glbl)
+{
+	struct dev_addr_list *da;
+
+	for (da = *list; da != NULL; da = da->next) {
+		if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
+		    da->da_addrlen == alen) {
+			if (glbl) {
+				int old_glbl = da->da_gusers;
+				da->da_gusers = 1;
+				if (old_glbl)
+					return 0;
+			}
+			da->da_users++;
+			return 0;
+		}
+	}
+
+	da = kzalloc(sizeof(*da), GFP_ATOMIC);
+	if (da == NULL)
+		return -ENOMEM;
+	memcpy(da->da_addr, addr, alen);
+	da->da_addrlen = alen;
+	da->da_users = 1;
+	da->da_gusers = glbl ? 1 : 0;
+	da->next = *list;
+	*list = da;
+	(*count)++;
+	return 0;
+}
+
+int __dev_addr_delete(struct dev_addr_list **list, int *count,
+		      void *addr, int alen, int glbl)
+{
+	struct dev_addr_list *da;
+
+	for (; (da = *list) != NULL; list = &da->next) {
+		if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
+		    alen == da->da_addrlen) {
+			if (glbl) {
+				int old_glbl = da->da_gusers;
+				da->da_gusers = 0;
+				if (old_glbl == 0)
+					break;
+			}
+			if (--da->da_users)
+				return 0;
+
+			*list = da->next;
+			kfree(da);
+			(*count)--;
+			return 0;
+		}
+	}
+	return -ENOENT;
+}
+
+int __dev_addr_sync(struct dev_addr_list **to, int *to_count,
+		    struct dev_addr_list **from, int *from_count)
+{
+	struct dev_addr_list *da, *next;
+	int err = 0;
+
+	da = *from;
+	while (da != NULL) {
+		next = da->next;
+		if (!da->da_synced) {
+			err = __dev_addr_add(to, to_count,
+					     da->da_addr, da->da_addrlen, 0);
+			if (err < 0)
+				break;
+			da->da_synced = 1;
+			da->da_users++;
+		} else if (da->da_users == 1) {
+			__dev_addr_delete(to, to_count,
+					  da->da_addr, da->da_addrlen, 0);
+			__dev_addr_delete(from, from_count,
+					  da->da_addr, da->da_addrlen, 0);
+		}
+		da = next;
+	}
+	return err;
+}
+EXPORT_SYMBOL_GPL(__dev_addr_sync);
+
+void __dev_addr_unsync(struct dev_addr_list **to, int *to_count,
+		       struct dev_addr_list **from, int *from_count)
+{
+	struct dev_addr_list *da, *next;
+
+	da = *from;
+	while (da != NULL) {
+		next = da->next;
+		if (da->da_synced) {
+			__dev_addr_delete(to, to_count,
+					  da->da_addr, da->da_addrlen, 0);
+			da->da_synced = 0;
+			__dev_addr_delete(from, from_count,
+					  da->da_addr, da->da_addrlen, 0);
+		}
+		da = next;
+	}
+}
+EXPORT_SYMBOL_GPL(__dev_addr_unsync);
+
+/*
+ * Nonzero if YEAR is a leap year (every 4 years,
+ * except every 100th isn't, and every 400th is).
+ */
+static int __isleap(long year)
+{
+	return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
+}
+
+/* do a mathdiv for long type */
+static long math_div(long a, long b)
+{
+	return a / b - (a % b < 0);
+}
+
+/* How many leap years between y1 and y2, y1 must less or equal to y2 */
+static long leaps_between(long y1, long y2)
+{
+	long leaps1 = math_div(y1 - 1, 4) - math_div(y1 - 1, 100)
+		+ math_div(y1 - 1, 400);
+	long leaps2 = math_div(y2 - 1, 4) - math_div(y2 - 1, 100)
+		+ math_div(y2 - 1, 400);
+	return leaps2 - leaps1;
+}
+
+/* How many days come before each month (0-12). */
+static const unsigned short __mon_yday[2][13] = {
+	/* Normal years. */
+	{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
+	/* Leap years. */
+	{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
+};
+
+#define SECS_PER_HOUR	(60 * 60)
+#define SECS_PER_DAY	(SECS_PER_HOUR * 24)
+
+/**
+ * time_to_tm - converts the calendar time to local broken-down time
+ *
+ * @totalsecs	the number of seconds elapsed since 00:00:00 on January 1, 1970,
+ *		Coordinated Universal Time (UTC).
+ * @offset	offset seconds adding to totalsecs.
+ * @result	pointer to struct tm variable to receive broken-down time
+ */
+void time_to_tm(time_t totalsecs, int offset, struct tm *result)
+{
+	long days, rem, y;
+	const unsigned short *ip;
+
+	days = totalsecs / SECS_PER_DAY;
+	rem = totalsecs % SECS_PER_DAY;
+	rem += offset;
+	while (rem < 0) {
+		rem += SECS_PER_DAY;
+		--days;
+	}
+	while (rem >= SECS_PER_DAY) {
+		rem -= SECS_PER_DAY;
+		++days;
+	}
+
+	result->tm_hour = rem / SECS_PER_HOUR;
+	rem %= SECS_PER_HOUR;
+	result->tm_min = rem / 60;
+	result->tm_sec = rem % 60;
+
+	/* January 1, 1970 was a Thursday. */
+	result->tm_wday = (4 + days) % 7;
+	if (result->tm_wday < 0)
+		result->tm_wday += 7;
+
+	y = 1970;
+
+	while (days < 0 || days >= (__isleap(y) ? 366 : 365)) {
+		/* Guess a corrected year, assuming 365 days per year. */
+		long yg = y + math_div(days, 365);
+
+		/* Adjust DAYS and Y to match the guessed year. */
+		days -= (yg - y) * 365 + leaps_between(y, yg);
+		y = yg;
+	}
+
+	result->tm_year = y - 1900;
+
+	result->tm_yday = days;
+
+	ip = __mon_yday[__isleap(y)];
+	for (y = 11; days < ip[y]; y--)
+		continue;
+	days -= ip[y];
+
+	result->tm_mon = y;
+	result->tm_mday = days + 1;
+}
+EXPORT_SYMBOL(time_to_tm);
+/* source: kernel/time/timeconv.c*/
+
diff --git a/compat/compat-2.6.33.c b/compat/compat-2.6.33.c
new file mode 100644
index 0000000..c8e568d
--- /dev/null
+++ b/compat/compat-2.6.33.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2009	Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.33.
+ */
+
+#include <linux/compat.h>
+#include <linux/autoconf.h>
+
+#if defined(CONFIG_PCCARD) || defined(CONFIG_PCCARD_MODULE)
+
+/**
+ * pccard_loop_tuple() - loop over tuples in the CIS
+ * @s:		the struct pcmcia_socket where the card is inserted
+ * @function:	the device function we loop for
+ * @code:	which CIS code shall we look for?
+ * @parse:	buffer where the tuple shall be parsed (or NULL, if no parse)
+ * @priv_data:	private data to be passed to the loop_tuple function.
+ * @loop_tuple:	function to call for each CIS entry of type @function. IT
+ *		gets passed the raw tuple, the paresed tuple (if @parse is
+ *		set) and @priv_data.
+ *
+ * pccard_loop_tuple() loops over all CIS entries of type @function, and
+ * calls the @loop_tuple function for each entry. If the call to @loop_tuple
+ * returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
+ */
+int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
+		      cisdata_t code, cisparse_t *parse, void *priv_data,
+		      int (*loop_tuple) (tuple_t *tuple,
+					 cisparse_t *parse,
+					 void *priv_data))
+{
+	tuple_t tuple;
+	cisdata_t *buf;
+	int ret;
+
+	buf = kzalloc(256, GFP_KERNEL);
+	if (buf == NULL) {
+		dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
+		return -ENOMEM;
+	}
+
+	tuple.TupleData = buf;
+	tuple.TupleDataMax = 255;
+	tuple.TupleOffset = 0;
+	tuple.DesiredTuple = code;
+	tuple.Attributes = 0;
+
+	ret = pccard_get_first_tuple(s, function, &tuple);
+	while (!ret) {
+		if (pccard_get_tuple_data(s, &tuple))
+			goto next_entry;
+
+		if (parse)
+			if (pcmcia_parse_tuple(&tuple, parse))
+				goto next_entry;
+
+		ret = loop_tuple(&tuple, parse, priv_data);
+		if (!ret)
+			break;
+
+next_entry:
+		ret = pccard_get_next_tuple(s, function, &tuple);
+	}
+
+	kfree(buf);
+	return ret;
+}
+EXPORT_SYMBOL(pccard_loop_tuple);
+/* Source: drivers/pcmcia/cistpl.c */
+
+#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
+
+struct pcmcia_loop_mem {
+	struct pcmcia_device *p_dev;
+	void *priv_data;
+	int (*loop_tuple) (struct pcmcia_device *p_dev,
+			   tuple_t *tuple,
+			   void *priv_data);
+};
+
+/**
+ * pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config()
+ *
+ * pcmcia_do_loop_tuple() is the internal callback for the call from
+ * pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred
+ * by a struct pcmcia_cfg_mem.
+ */
+static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv)
+{
+	struct pcmcia_loop_mem *loop = priv;
+
+	return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data);
+};
+
+/**
+ * pcmcia_loop_tuple() - loop over tuples in the CIS
+ * @p_dev:	the struct pcmcia_device which we need to loop for.
+ * @code:	which CIS code shall we look for?
+ * @priv_data:	private data to be passed to the loop_tuple function.
+ * @loop_tuple:	function to call for each CIS entry of type @function. IT
+ *		gets passed the raw tuple and @priv_data.
+ *
+ * pcmcia_loop_tuple() loops over all CIS entries of type @function, and
+ * calls the @loop_tuple function for each entry. If the call to @loop_tuple
+ * returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
+ */
+int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code,
+		      int (*loop_tuple) (struct pcmcia_device *p_dev,
+					 tuple_t *tuple,
+					 void *priv_data),
+		      void *priv_data)
+{
+	struct pcmcia_loop_mem loop = {
+		.p_dev = p_dev,
+		.loop_tuple = loop_tuple,
+		.priv_data = priv_data};
+
+	return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL,
+				 &loop, pcmcia_do_loop_tuple);
+}
+EXPORT_SYMBOL(pcmcia_loop_tuple);
+/* Source: drivers/pcmcia/pcmcia_resource.c */
+
+#endif /* CONFIG_PCMCIA */
+
+#endif /* CONFIG_PCCARD */
+
diff --git a/compat/compat-2.6.35.c b/compat/compat-2.6.35.c
new file mode 100644
index 0000000..7a31a54
--- /dev/null
+++ b/compat/compat-2.6.35.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2010    Kshitij Kulshreshtha <kkhere.geo@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.35.
+ */
+
+#include <linux/compat.h>
+#include <linux/ctype.h>
+
+/**
+ * hex_to_bin - convert a hex digit to its real value
+ * @ch: ascii character represents hex digit
+ *
+ * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
+ * input.
+ */
+int compat_hex_to_bin(char ch)
+{
+	if ((ch >= '0') && (ch <= '9'))
+		return ch - '0';
+	ch = tolower(ch);
+	if ((ch >= 'a') && (ch <= 'f'))
+		return ch - 'a' + 10;
+	return -1;
+}
+EXPORT_SYMBOL(compat_hex_to_bin);
+
+/**
+ * noop_llseek - No Operation Performed llseek implementation
+ * @file:	file structure to seek on
+ * @offset:	file offset to seek to
+ * @origin:	type of seek
+ *
+ * This is an implementation of ->llseek useable for the rare special case when
+ * userspace expects the seek to succeed but the (device) file is actually not
+ * able to perform the seek. In this case you use noop_llseek() instead of
+ * falling back to the default implementation of ->llseek.
+ */
+loff_t noop_llseek(struct file *file, loff_t offset, int origin)
+{
+	return file->f_pos;
+}
+EXPORT_SYMBOL(noop_llseek);
+
diff --git a/compat/compat-2.6.36.c b/compat/compat-2.6.36.c
new file mode 100644
index 0000000..dfab2ea
--- /dev/null
+++ b/compat/compat-2.6.36.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2010    Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.36.
+ */
+
+#include <linux/compat.h>
+#include <linux/usb.h>
+
+#ifdef CONFIG_COMPAT_USB_URB_THREAD_FIX
+/* Callers must hold anchor->lock */
+static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
+{
+	urb->anchor = NULL;
+	list_del(&urb->anchor_list);
+	usb_put_urb(urb);
+	if (list_empty(&anchor->urb_list))
+		wake_up(&anchor->wait);
+}
+
+/**
+ * usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse
+ * @anchor: anchor the requests are bound to
+ *
+ * this allows all outstanding URBs to be unlinked starting
+ * from the back of the queue. This function is asynchronous.
+ * The unlinking is just tiggered. It may happen after this
+ * function has returned.
+ *
+ * This routine should not be called by a driver after its disconnect
+ * method has returned.
+ */
+void compat_usb_unlink_anchored_urbs(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+
+	while ((victim = usb_get_from_anchor(anchor)) != NULL) {
+		usb_unlink_urb(victim);
+		usb_put_urb(victim);
+	}
+}
+EXPORT_SYMBOL_GPL(compat_usb_unlink_anchored_urbs);
+
+/**
+ * usb_get_from_anchor - get an anchor's oldest urb
+ * @anchor: the anchor whose urb you want
+ *
+ * this will take the oldest urb from an anchor,
+ * unanchor and return it
+ */
+struct urb *compat_usb_get_from_anchor(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	if (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.next, struct urb,
+				    anchor_list);
+		usb_get_urb(victim);
+		__usb_unanchor_urb(victim, anchor);
+	} else {
+		victim = NULL;
+	}
+	spin_unlock_irqrestore(&anchor->lock, flags);
+
+	return victim;
+}
+EXPORT_SYMBOL_GPL(compat_usb_get_from_anchor);
+
+/**
+ * usb_scuttle_anchored_urbs - unanchor all an anchor's urbs
+ * @anchor: the anchor whose urbs you want to unanchor
+ *
+ * use this to get rid of all an anchor's urbs
+ */
+void compat_usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	while (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.prev, struct urb,
+				    anchor_list);
+		__usb_unanchor_urb(victim, anchor);
+	}
+	spin_unlock_irqrestore(&anchor->lock, flags);
+}
+EXPORT_SYMBOL_GPL(compat_usb_scuttle_anchored_urbs);
+
+#endif /* CONFIG_COMPAT_USB_URB_THREAD_FIX */
+
+struct workqueue_struct *system_nrt_wq __read_mostly;
+EXPORT_SYMBOL_GPL(system_nrt_wq);
+
+void compat_system_workqueue_create()
+{
+	system_nrt_wq = create_singlethread_workqueue("events_nrt");
+	WARN_ON(!system_nrt_wq);
+}
+
+void compat_system_workqueue_destroy()
+{
+	destroy_workqueue(system_nrt_wq);
+}
diff --git a/compat/compat-2.6.37.c b/compat/compat-2.6.37.c
new file mode 100644
index 0000000..d7259bb
--- /dev/null
+++ b/compat/compat-2.6.37.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2010    Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.37.
+ */
+
+#include <linux/compat.h>
+#include <linux/netdevice.h>
+#include <net/sock.h>
+#include <linux/nsproxy.h>
+#include <linux/vmalloc.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+static const void *net_current_ns(void)
+{
+	return current->nsproxy->net_ns;
+}
+
+static const void *net_initial_ns(void)
+{
+	return &init_net;
+}
+
+static const void *net_netlink_ns(struct sock *sk)
+{
+	return sock_net(sk);
+}
+
+struct kobj_ns_type_operations net_ns_type_operations = {
+	.type = KOBJ_NS_TYPE_NET,
+	.current_ns = net_current_ns,
+	.netlink_ns = net_netlink_ns,
+	.initial_ns = net_initial_ns,
+};
+EXPORT_SYMBOL_GPL(net_ns_type_operations);
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)*/ 
+
+#undef genl_info
+#undef genl_unregister_family
+
+static LIST_HEAD(compat_nl_fam);
+
+static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
+{
+	struct genl_ops *ops;
+
+	list_for_each_entry(ops, &family->family.ops_list, ops.ops_list)
+		if (ops->cmd == cmd)
+			return ops;
+
+	return NULL;
+}
+
+
+static int nl_doit_wrapper(struct sk_buff *skb, struct genl_info *info)
+{
+	struct compat_genl_info compat_info;
+	struct genl_family *family;
+	struct genl_ops *ops;
+	int err;
+
+	list_for_each_entry(family, &compat_nl_fam, list) {
+		if (family->id == info->nlhdr->nlmsg_type)
+			goto found;
+	}
+	return -ENOENT;
+
+found:
+	ops = genl_get_cmd(info->genlhdr->cmd, family);
+	if (!ops)
+		return -ENOENT;
+
+	memset(&compat_info.user_ptr, 0, sizeof(compat_info.user_ptr));
+	compat_info.info = info;
+#define __copy(_field) compat_info._field = info->_field
+	__copy(snd_seq);
+	__copy(snd_pid);
+	__copy(genlhdr);
+	__copy(attrs);
+#undef __copy
+	if (family->pre_doit) {
+		err = family->pre_doit(ops, skb, &compat_info);
+		if (err)
+			return err;
+	}
+
+	err = ops->doit(skb, &compat_info);
+
+	if (family->post_doit)
+		family->post_doit(ops, skb, &compat_info);
+
+	return err;
+}
+
+int compat_genl_register_family_with_ops(struct genl_family *family,
+					 struct genl_ops *ops, size_t n_ops)
+{
+	int i, ret;
+
+#define __copy(_field) family->family._field = family->_field
+	__copy(id);
+	__copy(hdrsize);
+	__copy(version);
+	__copy(maxattr);
+	strncpy(family->family.name, family->name, sizeof(family->family.name));
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+	__copy(netnsok);
+#endif
+#undef __copy
+
+	ret = genl_register_family(&family->family);
+	if (ret < 0)
+		return ret;
+
+	family->attrbuf = family->family.attrbuf;
+	family->id = family->family.id;
+
+	for (i = 0; i < n_ops; i++) {
+#define __copy(_field) ops[i].ops._field = ops[i]._field
+		__copy(cmd);
+		__copy(flags);
+		__copy(policy);
+		__copy(dumpit);
+		__copy(done);
+#undef __copy
+		if (ops[i].doit)
+			ops[i].ops.doit = nl_doit_wrapper;
+		ret = genl_register_ops(&family->family, &ops[i].ops);
+		if (ret < 0)
+			goto error_ops;
+	}
+	list_add(&family->list, &compat_nl_fam);
+
+	return ret;
+
+error_ops:
+	compat_genl_unregister_family(family);
+	return ret;
+}
+EXPORT_SYMBOL(compat_genl_register_family_with_ops);
+
+int compat_genl_unregister_family(struct genl_family *family)
+{
+	int err;
+	err = genl_unregister_family(&family->family);
+	list_del(&family->list);
+	return err;
+}
+EXPORT_SYMBOL(compat_genl_unregister_family);
+
+#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+
+#undef led_brightness_set
+#undef led_classdev_unregister
+
+static DEFINE_SPINLOCK(led_lock);
+static LIST_HEAD(led_timers);
+
+struct led_timer {
+	struct list_head list;
+	struct led_classdev *cdev;
+	struct timer_list blink_timer;
+	unsigned long blink_delay_on;
+	unsigned long blink_delay_off;
+	int blink_brightness;
+};
+
+static void led_brightness_set(struct led_classdev *led_cdev,
+			       enum led_brightness brightness)
+{
+	led_cdev->brightness = brightness;
+	led_cdev->brightness_set(led_cdev, brightness);
+}
+
+static struct led_timer *led_get_timer(struct led_classdev *led_cdev)
+{
+	struct led_timer *p;
+	unsigned long flags;
+
+	spin_lock_irqsave(&led_lock, flags);
+	list_for_each_entry(p, &led_timers, list) {
+		if (p->cdev == led_cdev)
+			goto found;
+	}
+	p = NULL;
+found:
+	spin_unlock_irqrestore(&led_lock, flags);
+	return p;
+}
+
+static void led_stop_software_blink(struct led_timer *led)
+{
+	del_timer_sync(&led->blink_timer);
+	led->blink_delay_on = 0;
+	led->blink_delay_off = 0;
+}
+
+static void led_timer_function(unsigned long data)
+{
+	struct led_timer *led = (struct led_timer *)data;
+	unsigned long brightness;
+	unsigned long delay;
+
+	if (!led->blink_delay_on || !led->blink_delay_off) {
+		led->cdev->brightness_set(led->cdev, LED_OFF);
+		return;
+	}
+
+	brightness = led->cdev->brightness;
+	if (!brightness) {
+		/* Time to switch the LED on. */
+		brightness = led->blink_brightness;
+		delay = led->blink_delay_on;
+	} else {
+		/* Store the current brightness value to be able
+		 * to restore it when the delay_off period is over.
+		 */
+		led->blink_brightness = brightness;
+		brightness = LED_OFF;
+		delay = led->blink_delay_off;
+	}
+
+	led_brightness_set(led->cdev, brightness);
+	mod_timer(&led->blink_timer, jiffies + msecs_to_jiffies(delay));
+}
+
+static struct led_timer *led_new_timer(struct led_classdev *led_cdev)
+{
+	struct led_timer *led;
+	unsigned long flags;
+
+	led = kzalloc(sizeof(struct led_timer), GFP_ATOMIC);
+	if (!led)
+		return NULL;
+
+	led->cdev = led_cdev;
+	init_timer(&led->blink_timer);
+	led->blink_timer.function = led_timer_function;
+	led->blink_timer.data = (unsigned long) led;
+
+	spin_lock_irqsave(&led_lock, flags);
+	list_add(&led->list, &led_timers);
+	spin_unlock_irqrestore(&led_lock, flags);
+
+	return led;
+}
+
+void led_blink_set(struct led_classdev *led_cdev,
+		   unsigned long *delay_on,
+		   unsigned long *delay_off)
+{
+	struct led_timer *led;
+	int current_brightness;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25))
+	if (led_cdev->blink_set &&
+	    !led_cdev->blink_set(led_cdev, delay_on, delay_off))
+		return;
+#endif
+
+	led = led_get_timer(led_cdev);
+	if (!led) {
+		led = led_new_timer(led_cdev);
+		if (!led)
+			return;
+	}
+
+	/* blink with 1 Hz as default if nothing specified */
+	if (!*delay_on && !*delay_off)
+		*delay_on = *delay_off = 500;
+
+	if (led->blink_delay_on == *delay_on &&
+	    led->blink_delay_off == *delay_off)
+		return;
+
+	current_brightness = led_cdev->brightness;
+	if (current_brightness)
+		led->blink_brightness = current_brightness;
+	if (!led->blink_brightness)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+		led->blink_brightness = led_cdev->max_brightness;
+#else
+		led->blink_brightness = LED_FULL;
+#endif
+
+	led_stop_software_blink(led);
+	led->blink_delay_on = *delay_on;
+	led->blink_delay_off = *delay_off;
+
+	/* never on - don't blink */
+	if (!*delay_on)
+		return;
+
+	/* never off - just set to brightness */
+	if (!*delay_off) {
+		led_brightness_set(led_cdev, led->blink_brightness);
+		return;
+	}
+
+	mod_timer(&led->blink_timer, jiffies + 1);
+}
+EXPORT_SYMBOL(led_blink_set);
+
+void compat_led_brightness_set(struct led_classdev *led_cdev,
+			       enum led_brightness brightness)
+{
+	struct led_timer *led = led_get_timer(led_cdev);
+
+	if (led)
+		led_stop_software_blink(led);
+
+	return led_cdev->brightness_set(led_cdev, brightness);
+}
+EXPORT_SYMBOL(compat_led_brightness_set);
+
+void compat_led_classdev_unregister(struct led_classdev *led_cdev)
+{
+	struct led_timer *led = led_get_timer(led_cdev);
+	unsigned long flags;
+
+	if (led) {
+		del_timer_sync(&led->blink_timer);
+		spin_lock_irqsave(&led_lock, flags);
+		list_del(&led->list);
+		spin_unlock_irqrestore(&led_lock, flags);
+		kfree(led);
+	}
+
+	led_classdev_unregister(led_cdev);
+}
+EXPORT_SYMBOL(compat_led_classdev_unregister);
+
+/**
+ *	vzalloc - allocate virtually contiguous memory with zero fill
+ *	@size:	allocation size
+ *	Allocate enough pages to cover @size from the page level
+ *	allocator and map them into contiguous kernel virtual space.
+ *	The memory allocated is set to zero.
+ *
+ *	For tight control over page level allocator and protection flags
+ *	use __vmalloc() instead.
+ */
+void *compat_vzalloc(unsigned long size)
+{
+	void *buf;
+	buf = vmalloc(size);
+	if (buf)
+		memset(buf, 0, size);
+	return buf;
+}
+EXPORT_SYMBOL(compat_vzalloc);
+
+#endif
diff --git a/compat/compat-2.6.38.c b/compat/compat-2.6.38.c
new file mode 100644
index 0000000..172aa19
--- /dev/null
+++ b/compat/compat-2.6.38.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010    Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.38.
+ */
+
+#include <linux/compat.h>
+#include <linux/module.h>
+#include <linux/bug.h>
+
+/**
+ * ewma_init() - Initialize EWMA parameters
+ * @avg: Average structure
+ * @factor: Factor to use for the scaled up internal value. The maximum value
+ *	of averages can be ULONG_MAX/(factor*weight).
+ * @weight: Exponential weight, or decay rate. This defines how fast the
+ *	influence of older values decreases. Has to be bigger than 1.
+ *
+ * Initialize the EWMA parameters for a given struct ewma @avg.
+ */
+void ewma_init(struct ewma *avg, unsigned long factor, unsigned long weight)
+{
+	WARN_ON(weight <= 1 || factor == 0);
+	avg->internal = 0;
+	avg->weight = weight;
+	avg->factor = factor;
+}
+EXPORT_SYMBOL(ewma_init);
+
+/**
+ * ewma_add() - Exponentially weighted moving average (EWMA)
+ * @avg: Average structure
+ * @val: Current value
+ *
+ * Add a sample to the average.
+ */
+struct ewma *ewma_add(struct ewma *avg, unsigned long val)
+{
+	avg->internal = avg->internal  ?
+		(((avg->internal * (avg->weight - 1)) +
+			(val * avg->factor)) / avg->weight) :
+		(val * avg->factor);
+	return avg;
+}
+EXPORT_SYMBOL(ewma_add);
+
diff --git a/compat/compat-2.6.39.c b/compat/compat-2.6.39.c
new file mode 100644
index 0000000..5bb9322
--- /dev/null
+++ b/compat/compat-2.6.39.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2011    Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 2.6.39.
+ */
+
+#include <linux/compat.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
+/*
+ *		Termios Helper Methods
+ */
+static void unset_locked_termios(struct ktermios *termios,
+				 struct ktermios *old,
+				 struct ktermios *locked)
+{
+	int	i;
+
+#define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z)))
+
+	if (!locked) {
+		printk(KERN_WARNING "Warning?!? termios_locked is NULL.\n");
+		return;
+	}
+
+	NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
+	NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
+	NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
+	NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
+	termios->c_line = locked->c_line ? old->c_line : termios->c_line;
+	for (i = 0; i < NCCS; i++)
+		termios->c_cc[i] = locked->c_cc[i] ?
+			old->c_cc[i] : termios->c_cc[i];
+	/* FIXME: What should we do for i/ospeed */
+}
+
+/**
+ *	tty_set_termios		-	update termios values
+ *	@tty: tty to update
+ *	@new_termios: desired new value
+ *
+ *	Perform updates to the termios values set on this terminal. There
+ *	is a bit of layering violation here with n_tty in terms of the
+ *	internal knowledge of this function.
+ *
+ *	Locking: termios_mutex
+ */
+int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
+{
+	struct ktermios old_termios;
+	struct tty_ldisc *ld;
+	unsigned long flags;
+
+	/*
+	 *	Perform the actual termios internal changes under lock.
+	 */
+
+
+	/* FIXME: we need to decide on some locking/ordering semantics
+	   for the set_termios notification eventually */
+	mutex_lock(&tty->termios_mutex);
+	old_termios = *tty->termios;
+	*tty->termios = *new_termios;
+	unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
+
+	/* See if packet mode change of state. */
+	if (tty->link && tty->link->packet) {
+		int extproc = (old_termios.c_lflag & EXTPROC) |
+				(tty->termios->c_lflag & EXTPROC);
+		int old_flow = ((old_termios.c_iflag & IXON) &&
+				(old_termios.c_cc[VSTOP] == '\023') &&
+				(old_termios.c_cc[VSTART] == '\021'));
+		int new_flow = (I_IXON(tty) &&
+				STOP_CHAR(tty) == '\023' &&
+				START_CHAR(tty) == '\021');
+		if ((old_flow != new_flow) || extproc) {
+			spin_lock_irqsave(&tty->ctrl_lock, flags);
+			if (old_flow != new_flow) {
+				tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
+				if (new_flow)
+					tty->ctrl_status |= TIOCPKT_DOSTOP;
+				else
+					tty->ctrl_status |= TIOCPKT_NOSTOP;
+			}
+			if (extproc)
+				tty->ctrl_status |= TIOCPKT_IOCTL;
+			spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+			wake_up_interruptible(&tty->link->read_wait);
+		}
+	}
+
+	if (tty->ops->set_termios)
+		(*tty->ops->set_termios)(tty, &old_termios);
+	else
+		tty_termios_copy_hw(tty->termios, &old_termios);
+
+	ld = tty_ldisc_ref(tty);
+	if (ld != NULL) {
+		if (ld->ops->set_termios)
+			(ld->ops->set_termios)(tty, &old_termios);
+		tty_ldisc_deref(ld);
+	}
+	mutex_unlock(&tty->termios_mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tty_set_termios);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) */
+
diff --git a/compat/compat-3.0.c b/compat/compat-3.0.c
new file mode 100644
index 0000000..f517e9f
--- /dev/null
+++ b/compat/compat-3.0.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011    Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright 2011    Alexey Dobriyan <adobriyan@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 3.0.
+ */
+
+#include <linux/compat.h>
+#include <linux/if_ether.h>
+
+int mac_pton(const char *s, u8 *mac)
+{
+	int i;
+
+	/* XX:XX:XX:XX:XX:XX */
+	if (strlen(s) < 3 * ETH_ALEN - 1)
+		return 0;
+
+	/* Don't dirty result unless string is valid MAC. */
+	for (i = 0; i < ETH_ALEN; i++) {
+		if (!strchr("0123456789abcdefABCDEF", s[i * 3]))
+			return 0;
+		if (!strchr("0123456789abcdefABCDEF", s[i * 3 + 1]))
+			return 0;
+		if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':')
+			return 0;
+	}
+	for (i = 0; i < ETH_ALEN; i++) {
+		mac[i] = (hex_to_bin(s[i * 3]) << 4) | hex_to_bin(s[i * 3 + 1]);
+	}
+	return 1;
+}
+EXPORT_SYMBOL(mac_pton);
+
+#define kstrto_from_user(f, g, type)					\
+int f(const char __user *s, size_t count, unsigned int base, type *res)	\
+{									\
+	/* sign, base 2 representation, newline, terminator */		\
+	char buf[1 + sizeof(type) * 8 + 1 + 1];				\
+									\
+	count = min(count, sizeof(buf) - 1);				\
+	if (copy_from_user(buf, s, count))				\
+		return -EFAULT;						\
+	buf[count] = '\0';						\
+	return g(buf, base, res);					\
+}									\
+EXPORT_SYMBOL(f)
+
+kstrto_from_user(kstrtoull_from_user,	kstrtoull,	unsigned long long);
+kstrto_from_user(kstrtoll_from_user,	kstrtoll,	long long);
+kstrto_from_user(kstrtoul_from_user,	kstrtoul,	unsigned long);
+kstrto_from_user(kstrtol_from_user,	kstrtol,	long);
+kstrto_from_user(kstrtouint_from_user,	kstrtouint,	unsigned int);
+kstrto_from_user(kstrtoint_from_user,	kstrtoint,	int);
+kstrto_from_user(kstrtou16_from_user,	kstrtou16,	u16);
+kstrto_from_user(kstrtos16_from_user,	kstrtos16,	s16);
+kstrto_from_user(kstrtou8_from_user,	kstrtou8,	u8);
+kstrto_from_user(kstrtos8_from_user,	kstrtos8,	s8);
diff --git a/compat/compat-3.2.c b/compat/compat-3.2.c
new file mode 100644
index 0000000..ab5bd1f
--- /dev/null
+++ b/compat/compat-3.2.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012  Luis R. Rodriguez <mcgrof@frijolero.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Compatibility file for Linux wireless for kernels 3.2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/device.h>
+
+int __netdev_printk(const char *level, const struct net_device *dev,
+			   struct va_format *vaf)
+{
+	int r;
+
+	if (dev && dev->dev.parent)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35))
+		r = dev_printk(level, dev->dev.parent, "%s: %pV",
+			       netdev_name(dev), vaf);
+#else
+		/* XXX: this could likely be done better but I'm lazy */
+		r = printk("%s%s: %pV", level, netdev_name(dev), vaf);
+#endif
+	else if (dev)
+		r = printk("%s%s: %pV", level, netdev_name(dev), vaf);
+	else
+		r = printk("%s(NULL net_device): %pV", level, vaf);
+
+	return r;
+}
+EXPORT_SYMBOL(__netdev_printk);
diff --git a/compat/compat_atomic.c b/compat/compat_atomic.c
new file mode 100644
index 0000000..747e275
--- /dev/null
+++ b/compat/compat_atomic.c
@@ -0,0 +1,33 @@
+#include <linux/spinlock.h>
+#include <linux/module.h>
+
+#if !((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) && (defined(CONFIG_UML) || defined(CONFIG_X86))) && !((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) && defined(CONFIG_ARM) && !defined(CONFIG_GENERIC_ATOMIC64))
+
+static DEFINE_SPINLOCK(lock);
+
+long long atomic64_read(const atomic64_t *v)
+{
+    unsigned long flags;
+    long long val;
+
+    spin_lock_irqsave(&lock, flags);
+    val = v->counter;
+    spin_unlock_irqrestore(&lock, flags);
+    return val;
+}
+EXPORT_SYMBOL(atomic64_read);
+
+long long atomic64_add_return(long long a, atomic64_t *v)
+{
+    unsigned long flags;
+    long long val;
+
+    spin_lock_irqsave(&lock, flags);
+    val = v->counter += a;
+    spin_unlock_irqrestore(&lock, flags);
+    return val;
+}
+EXPORT_SYMBOL(atomic64_add_return);
+
+#endif
+
diff --git a/compat/compat_firmware_class.c b/compat/compat_firmware_class.c
new file mode 100644
index 0000000..4eb5ba7
--- /dev/null
+++ b/compat/compat_firmware_class.c
@@ -0,0 +1,759 @@
+/*
+ * firmware_class.c - Multi purpose firmware loading support
+ *
+ * Copyright (c) 2003 Manuel Estrada Sainz
+ *
+ * Please see Documentation/firmware_class/ for more information.
+ *
+ */
+
+#include <linux/capability.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/kthread.h>
+#include <linux/highmem.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+
+#define to_dev(obj) container_of(obj, struct device, kobj)
+
+MODULE_AUTHOR("Manuel Estrada Sainz");
+MODULE_DESCRIPTION("Multi purpose firmware loading support");
+MODULE_LICENSE("GPL");
+
+/* Builtin firmware support */
+
+//#ifdef CONFIG_FW_LOADER
+#if 0
+
+extern struct builtin_fw __start_builtin_fw[];
+extern struct builtin_fw __end_builtin_fw[];
+
+static bool fw_get_builtin_firmware(struct firmware *fw, const char *name)
+{
+	struct builtin_fw *b_fw;
+
+	for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
+		if (strcmp(name, b_fw->name) == 0) {
+			fw->size = b_fw->size;
+			fw->data = b_fw->data;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool fw_is_builtin_firmware(const struct firmware *fw)
+{
+	struct builtin_fw *b_fw;
+
+	for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++)
+		if (fw->data == b_fw->data)
+			return true;
+
+	return false;
+}
+
+#else /* Module case - no builtin firmware support */
+
+static inline bool fw_get_builtin_firmware(struct firmware *fw, const char *name)
+{
+	return false;
+}
+
+static inline bool fw_is_builtin_firmware(const struct firmware *fw)
+{
+	return false;
+}
+#endif
+
+enum {
+	FW_STATUS_LOADING,
+	FW_STATUS_DONE,
+	FW_STATUS_ABORT,
+};
+
+static int loading_timeout = 60;	/* In seconds */
+
+/* fw_lock could be moved to 'struct firmware_priv' but since it is just
+ * guarding for corner cases a global lock should be OK */
+static DEFINE_MUTEX(fw_lock);
+
+struct firmware_priv {
+	struct completion completion;
+	struct firmware *fw;
+	unsigned long status;
+	struct page **pages;
+	int nr_pages;
+	int page_array_size;
+	struct timer_list timeout;
+	struct device dev;
+	bool nowait;
+	char fw_id[];
+};
+
+static struct firmware_priv *to_firmware_priv(struct device *dev)
+{
+	return container_of(dev, struct firmware_priv, dev);
+}
+
+static void fw_load_abort(struct firmware_priv *fw_priv)
+{
+	set_bit(FW_STATUS_ABORT, &fw_priv->status);
+	wmb();
+	complete(&fw_priv->completion);
+}
+
+static ssize_t firmware_timeout_show(struct class *class,
+				     char *buf)
+{
+	return sprintf(buf, "%d\n", loading_timeout);
+}
+
+/**
+ * firmware_timeout_store - set number of seconds to wait for firmware
+ * @class: device class pointer
+ * @buf: buffer to scan for timeout value
+ * @count: number of bytes in @buf
+ *
+ *	Sets the number of seconds to wait for the firmware.  Once
+ *	this expires an error will be returned to the driver and no
+ *	firmware will be provided.
+ *
+ *	Note: zero means 'wait forever'.
+ **/
+static ssize_t firmware_timeout_store(struct class *class,
+				      const char *buf, size_t count)
+{
+	loading_timeout = simple_strtol(buf, NULL, 10);
+	if (loading_timeout < 0)
+		loading_timeout = 0;
+
+	return count;
+}
+
+static struct class_attribute firmware_class_attrs[] = {
+	__ATTR(timeout, S_IWUSR | S_IRUGO,
+		firmware_timeout_show, firmware_timeout_store),
+	__ATTR_NULL
+};
+
+static void fw_dev_release(struct device *dev)
+{
+	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+	int i;
+
+	for (i = 0; i < fw_priv->nr_pages; i++)
+		__free_page(fw_priv->pages[i]);
+	kfree(fw_priv->pages);
+	kfree(fw_priv);
+
+	module_put(THIS_MODULE);
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
+static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+
+	if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))
+		return -ENOMEM;
+	if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
+		return -ENOMEM;
+	if (add_uevent_var(env, "ASYNC=%d", fw_priv->nowait))
+		return -ENOMEM;
+
+	return 0;
+}
+#else
+static int firmware_uevent(struct device *dev, char **envp,
+			   int num_envp, char *buf, int size)
+{
+	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+	int error, len = 0, i = 0;
+
+	error = add_uevent_var(envp, num_envp, &i,
+			       buf, size, &len,
+			       "FIRMWARE=%s", fw_priv->fw_id);
+	if (error)
+		goto exit;
+
+	error = add_uevent_var(envp, num_envp, &i,
+			       buf, size, &len,
+			       "TIMEOUT=%i", loading_timeout);
+	if (error)
+		goto exit;
+	error = add_uevent_var(envp, num_envp, &i,
+			       buf, size, &len,
+			       "ASYNC=%i", fw_priv->nowait);
+	if (error)
+		goto exit;
+
+	return 0;
+exit:
+	envp[i] = NULL;
+	return error;
+}
+#endif
+
+static struct class firmware_class = {
+	.name		= "compat_firmware",
+	.class_attrs	= firmware_class_attrs,
+	.dev_uevent	= firmware_uevent,
+	.dev_release	= fw_dev_release,
+};
+
+static ssize_t firmware_loading_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+	int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
+
+	return sprintf(buf, "%d\n", loading);
+}
+
+static void firmware_free_data(const struct firmware *fw)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+	int i;
+	vunmap(fw->data);
+	if (fw->pages) {
+		for (i = 0; i < PFN_UP(fw->size); i++)
+			__free_page(fw->pages[i]);
+		kfree(fw->pages);
+	}
+#else
+	vunmap(fw->data);
+#endif
+}
+
+/* Some architectures don't have PAGE_KERNEL_RO */
+#ifndef PAGE_KERNEL_RO
+#define PAGE_KERNEL_RO PAGE_KERNEL
+#endif
+/**
+ * firmware_loading_store - set value in the 'loading' control file
+ * @dev: device pointer
+ * @buf: buffer to scan for loading control value
+ * @count: number of bytes in @buf
+ *
+ *	The relevant values are:
+ *
+ *	 1: Start a load, discarding any previous partial load.
+ *	 0: Conclude the load and hand the data to the driver code.
+ *	-1: Conclude the load with an error and discard any written data.
+ **/
+static ssize_t firmware_loading_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count)
+{
+	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+	int loading = simple_strtol(buf, NULL, 10);
+	int i;
+
+	switch (loading) {
+	case 1:
+		mutex_lock(&fw_lock);
+		if (!fw_priv->fw) {
+			mutex_unlock(&fw_lock);
+			break;
+		}
+		firmware_free_data(fw_priv->fw);
+		memset(fw_priv->fw, 0, sizeof(struct firmware));
+		/* If the pages are not owned by 'struct firmware' */
+		for (i = 0; i < fw_priv->nr_pages; i++)
+			__free_page(fw_priv->pages[i]);
+		kfree(fw_priv->pages);
+		fw_priv->pages = NULL;
+		fw_priv->page_array_size = 0;
+		fw_priv->nr_pages = 0;
+		set_bit(FW_STATUS_LOADING, &fw_priv->status);
+		mutex_unlock(&fw_lock);
+		break;
+	case 0:
+		if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
+			vunmap(fw_priv->fw->data);
+			fw_priv->fw->data = vmap(fw_priv->pages,
+						 fw_priv->nr_pages,
+						 0, PAGE_KERNEL_RO);
+			if (!fw_priv->fw->data) {
+				dev_err(dev, "%s: vmap() failed\n", __func__);
+				goto err;
+			}
+			/* Pages are now owned by 'struct firmware' */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+			fw_priv->fw->pages = fw_priv->pages;
+			fw_priv->pages = NULL;
+#endif
+
+			fw_priv->page_array_size = 0;
+			fw_priv->nr_pages = 0;
+			complete(&fw_priv->completion);
+			clear_bit(FW_STATUS_LOADING, &fw_priv->status);
+			break;
+		}
+		/* fallthrough */
+	default:
+		dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
+		/* fallthrough */
+	case -1:
+	err:
+		fw_load_abort(fw_priv);
+		break;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
+
+#if defined(CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP)
+static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
+				  struct bin_attribute *bin_attr,
+				  char *buffer, loff_t offset, size_t count)
+#else
+static ssize_t firmware_data_read(struct kobject *kobj,
+				  struct bin_attribute *bin_attr,
+				  char *buffer, loff_t offset, size_t count)
+#endif
+{
+	struct device *dev = to_dev(kobj);
+	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+	struct firmware *fw;
+	ssize_t ret_count;
+
+	mutex_lock(&fw_lock);
+	fw = fw_priv->fw;
+	if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+		ret_count = -ENODEV;
+		goto out;
+	}
+	if (offset > fw->size) {
+		ret_count = 0;
+		goto out;
+	}
+	if (count > fw->size - offset)
+		count = fw->size - offset;
+
+	ret_count = count;
+
+	while (count) {
+		void *page_data;
+		int page_nr = offset >> PAGE_SHIFT;
+		int page_ofs = offset & (PAGE_SIZE-1);
+		int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+
+		page_data = kmap(fw_priv->pages[page_nr]);
+
+		memcpy(buffer, page_data + page_ofs, page_cnt);
+
+		kunmap(fw_priv->pages[page_nr]);
+		buffer += page_cnt;
+		offset += page_cnt;
+		count -= page_cnt;
+	}
+out:
+	mutex_unlock(&fw_lock);
+	return ret_count;
+}
+
+static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
+{
+	int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
+
+	/* If the array of pages is too small, grow it... */
+	if (fw_priv->page_array_size < pages_needed) {
+		int new_array_size = max(pages_needed,
+					 fw_priv->page_array_size * 2);
+		struct page **new_pages;
+
+		new_pages = kmalloc(new_array_size * sizeof(void *),
+				    GFP_KERNEL);
+		if (!new_pages) {
+			fw_load_abort(fw_priv);
+			return -ENOMEM;
+		}
+		memcpy(new_pages, fw_priv->pages,
+		       fw_priv->page_array_size * sizeof(void *));
+		memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
+		       (new_array_size - fw_priv->page_array_size));
+		kfree(fw_priv->pages);
+		fw_priv->pages = new_pages;
+		fw_priv->page_array_size = new_array_size;
+	}
+
+	while (fw_priv->nr_pages < pages_needed) {
+		fw_priv->pages[fw_priv->nr_pages] =
+			alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
+
+		if (!fw_priv->pages[fw_priv->nr_pages]) {
+			fw_load_abort(fw_priv);
+			return -ENOMEM;
+		}
+		fw_priv->nr_pages++;
+	}
+	return 0;
+}
+
+/**
+ * firmware_data_write - write method for firmware
+ * @kobj: kobject for the device
+ * @bin_attr: bin_attr structure
+ * @buffer: buffer being written
+ * @offset: buffer offset for write in total data store area
+ * @count: buffer size
+ *
+ *	Data written to the 'data' attribute will be later handed to
+ *	the driver as a firmware image.
+ **/
+#if defined(CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP)
+static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
+				   struct bin_attribute *bin_attr,
+				   char *buffer, loff_t offset, size_t count)
+#else
+static ssize_t firmware_data_write(struct kobject *kobj,
+				   struct bin_attribute *bin_attr,
+				   char *buffer, loff_t offset, size_t count)
+#endif
+{
+	struct device *dev = to_dev(kobj);
+	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+	struct firmware *fw;
+	ssize_t retval;
+
+	if (!capable(CAP_SYS_RAWIO))
+		return -EPERM;
+
+	mutex_lock(&fw_lock);
+	fw = fw_priv->fw;
+	if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+		retval = -ENODEV;
+		goto out;
+	}
+	retval = fw_realloc_buffer(fw_priv, offset + count);
+	if (retval)
+		goto out;
+
+	retval = count;
+
+	while (count) {
+		void *page_data;
+		int page_nr = offset >> PAGE_SHIFT;
+		int page_ofs = offset & (PAGE_SIZE - 1);
+		int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+
+		page_data = kmap(fw_priv->pages[page_nr]);
+
+		memcpy(page_data + page_ofs, buffer, page_cnt);
+
+		kunmap(fw_priv->pages[page_nr]);
+		buffer += page_cnt;
+		offset += page_cnt;
+		count -= page_cnt;
+	}
+
+	fw->size = max_t(size_t, offset, fw->size);
+out:
+	mutex_unlock(&fw_lock);
+	return retval;
+}
+
+static struct bin_attribute firmware_attr_data = {
+	.attr = { .name = "data", .mode = 0644 },
+	.size = 0,
+	.read = firmware_data_read,
+	.write = firmware_data_write,
+};
+
+static void firmware_class_timeout(u_long data)
+{
+	struct firmware_priv *fw_priv = (struct firmware_priv *) data;
+
+	fw_load_abort(fw_priv);
+}
+
+static struct firmware_priv *
+fw_create_instance(struct firmware *firmware, const char *fw_name,
+		   struct device *device, bool uevent, bool nowait)
+{
+	struct firmware_priv *fw_priv;
+	struct device *f_dev;
+	int error;
+
+	fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
+	if (!fw_priv) {
+		dev_err(device, "%s: kmalloc failed\n", __func__);
+		error = -ENOMEM;
+		goto err_out;
+	}
+
+	fw_priv->fw = firmware;
+	fw_priv->nowait = nowait;
+	strcpy(fw_priv->fw_id, fw_name);
+	init_completion(&fw_priv->completion);
+	setup_timer(&fw_priv->timeout,
+		    firmware_class_timeout, (u_long) fw_priv);
+
+	f_dev = &fw_priv->dev;
+
+	device_initialize(f_dev);
+	dev_set_name(f_dev, "%s", dev_name(device));
+	f_dev->parent = device;
+	f_dev->class = &firmware_class;
+
+	dev_set_uevent_suppress(f_dev, true);
+
+	/* Need to pin this module until class device is destroyed */
+	__module_get(THIS_MODULE);
+
+	error = device_add(f_dev);
+	if (error) {
+		dev_err(device, "%s: device_register failed\n", __func__);
+		goto err_put_dev;
+	}
+
+	error = device_create_bin_file(f_dev, &firmware_attr_data);
+	if (error) {
+		dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__);
+		goto err_del_dev;
+	}
+
+	error = device_create_file(f_dev, &dev_attr_loading);
+	if (error) {
+		dev_err(device, "%s: device_create_file failed\n", __func__);
+		goto err_del_bin_attr;
+	}
+
+	if (uevent)
+		dev_set_uevent_suppress(f_dev, false);
+
+	return fw_priv;
+
+err_del_bin_attr:
+	device_remove_bin_file(f_dev, &firmware_attr_data);
+err_del_dev:
+	device_del(f_dev);
+err_put_dev:
+	put_device(f_dev);
+err_out:
+	return ERR_PTR(error);
+}
+
+static void fw_destroy_instance(struct firmware_priv *fw_priv)
+{
+	struct device *f_dev = &fw_priv->dev;
+
+	device_remove_file(f_dev, &dev_attr_loading);
+	device_remove_bin_file(f_dev, &firmware_attr_data);
+	device_unregister(f_dev);
+}
+
+static int _request_firmware(const struct firmware **firmware_p,
+			     const char *name, struct device *device,
+			     bool uevent, bool nowait)
+{
+	struct firmware_priv *fw_priv;
+	struct firmware *firmware;
+	int retval = 0;
+
+	if (!firmware_p)
+		return -EINVAL;
+
+	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
+	if (!firmware) {
+		dev_err(device, "%s: kmalloc(struct firmware) failed\n",
+			__func__);
+		retval = -ENOMEM;
+		goto out;
+	}
+
+	if (fw_get_builtin_firmware(firmware, name)) {
+		dev_dbg(device, "firmware: using built-in firmware %s\n", name);
+		return 0;
+	}
+
+	if (uevent)
+		dev_dbg(device, "firmware: requesting %s\n", name);
+
+	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
+	if (IS_ERR(fw_priv)) {
+		retval = PTR_ERR(fw_priv);
+		goto out;
+	}
+
+	if (uevent) {
+		if (loading_timeout > 0)
+			mod_timer(&fw_priv->timeout,
+				  round_jiffies_up(jiffies +
+						   loading_timeout * HZ));
+
+		kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
+	}
+
+	wait_for_completion(&fw_priv->completion);
+
+	set_bit(FW_STATUS_DONE, &fw_priv->status);
+	del_timer_sync(&fw_priv->timeout);
+
+	mutex_lock(&fw_lock);
+	if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status))
+		retval = -ENOENT;
+	fw_priv->fw = NULL;
+	mutex_unlock(&fw_lock);
+
+	fw_destroy_instance(fw_priv);
+
+out:
+	if (retval) {
+		release_firmware(firmware);
+		*firmware_p = NULL;
+	}
+
+	return retval;
+}
+
+/**
+ * request_firmware: - send firmware request and wait for it
+ * @firmware_p: pointer to firmware image
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded
+ *
+ *      @firmware_p will be used to return a firmware image by the name
+ *      of @name for device @device.
+ *
+ *      Should be called from user context where sleeping is allowed.
+ *
+ *      @name will be used as $FIRMWARE in the uevent environment and
+ *      should be distinctive enough not to be confused with any other
+ *      firmware image for this or any other device.
+ **/
+int
+request_firmware(const struct firmware **firmware_p, const char *name,
+                 struct device *device)
+{
+        int uevent = 1;
+        return _request_firmware(firmware_p, name, device, uevent, false);
+}
+
+/**
+ * release_firmware: - release the resource associated with a firmware image
+ * @fw: firmware resource to release
+ **/
+void release_firmware(const struct firmware *fw)
+{
+	if (fw) {
+		if (!fw_is_builtin_firmware(fw))
+			firmware_free_data(fw);
+		kfree(fw);
+	}
+}
+
+/* Async support */
+struct firmware_work {
+	struct work_struct work;
+	struct module *module;
+	const char *name;
+	struct device *device;
+	void *context;
+	void (*cont)(const struct firmware *fw, void *context);
+	int uevent;
+};
+
+static int request_firmware_work_func(void *arg)
+{
+	struct firmware_work *fw_work = arg;
+	const struct firmware *fw;
+	int ret;
+
+	if (!arg) {
+		WARN_ON(1);
+		return 0;
+	}
+
+	ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+				fw_work->uevent, true);
+	fw_work->cont(fw, fw_work->context);
+
+	module_put(fw_work->module);
+	kfree(fw_work);
+
+	return ret;
+}
+
+/**
+ * request_firmware_nowait - asynchronous version of request_firmware
+ * @module: module requesting the firmware
+ * @uevent: sends uevent to copy the firmware image if this flag
+ *	is non-zero else the firmware copy must be done manually.
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded
+ * @gfp: allocation flags
+ * @context: will be passed over to @cont, and
+ *	@fw may be %NULL if firmware request fails.
+ * @cont: function will be called asynchronously when the firmware
+ *	request is over.
+ *
+ *	Asynchronous variant of request_firmware() for user contexts where
+ *	it is not possible to sleep for long time. It can't be called
+ *	in atomic contexts.
+ **/
+int
+request_firmware_nowait(
+	struct module *module, int uevent,
+	const char *name, struct device *device, gfp_t gfp, void *context,
+	void (*cont)(const struct firmware *fw, void *context))
+{
+	struct task_struct *task;
+	struct firmware_work *fw_work;
+
+	fw_work = kzalloc(sizeof (struct firmware_work), gfp);
+	if (!fw_work)
+		return -ENOMEM;
+
+	fw_work->module = module;
+	fw_work->name = name;
+	fw_work->device = device;
+	fw_work->context = context;
+	fw_work->cont = cont;
+	fw_work->uevent = uevent;
+
+	if (!try_module_get(module)) {
+		kfree(fw_work);
+		return -EFAULT;
+	}
+
+	task = kthread_run(request_firmware_work_func, fw_work,
+			    "firmware/%s", name);
+	if (IS_ERR(task)) {
+		fw_work->cont(NULL, fw_work->context);
+		module_put(fw_work->module);
+		kfree(fw_work);
+		return PTR_ERR(task);
+	}
+
+	return 0;
+}
+
+static int __init firmware_class_init(void)
+{
+	return class_register(&firmware_class);
+}
+
+static void __exit firmware_class_exit(void)
+{
+	class_unregister(&firmware_class);
+}
+
+fs_initcall(firmware_class_init);
+module_exit(firmware_class_exit);
+
+EXPORT_SYMBOL(release_firmware);
+EXPORT_SYMBOL(request_firmware);
+EXPORT_SYMBOL(request_firmware_nowait);
diff --git a/compat/cordic.c b/compat/cordic.c
new file mode 100644
index 0000000..aa27a88
--- /dev/null
+++ b/compat/cordic.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/module.h>
+#include <linux/cordic.h>
+
+#define CORDIC_ANGLE_GEN	39797
+#define CORDIC_PRECISION_SHIFT	16
+#define	CORDIC_NUM_ITER		(CORDIC_PRECISION_SHIFT + 2)
+
+#define	FIXED(X)	((s32)((X) << CORDIC_PRECISION_SHIFT))
+#define	FLOAT(X)	(((X) >= 0) \
+		? ((((X) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1) \
+		: -((((-(X)) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1))
+
+static const s32 arctan_table[] = {
+	2949120,
+	1740967,
+	919879,
+	466945,
+	234379,
+	117304,
+	58666,
+	29335,
+	14668,
+	7334,
+	3667,
+	1833,
+	917,
+	458,
+	229,
+	115,
+	57,
+	29
+};
+
+/*
+ * cordic_calc_iq() - calculates the i/q coordinate for given angle
+ *
+ * theta: angle in degrees for which i/q coordinate is to be calculated
+ * coord: function output parameter holding the i/q coordinate
+ */
+struct cordic_iq cordic_calc_iq(s32 theta)
+{
+	struct cordic_iq coord;
+	s32 angle, valtmp;
+	unsigned iter;
+	int signx = 1;
+	int signtheta;
+
+	coord.i = CORDIC_ANGLE_GEN;
+	coord.q = 0;
+	angle = 0;
+
+	theta = FIXED(theta);
+	signtheta = (theta < 0) ? -1 : 1;
+	theta = ((theta + FIXED(180) * signtheta) % FIXED(360)) -
+		FIXED(180) * signtheta;
+
+	if (FLOAT(theta) > 90) {
+		theta -= FIXED(180);
+		signx = -1;
+	} else if (FLOAT(theta) < -90) {
+		theta += FIXED(180);
+		signx = -1;
+	}
+
+	for (iter = 0; iter < CORDIC_NUM_ITER; iter++) {
+		if (theta > angle) {
+			valtmp = coord.i - (coord.q >> iter);
+			coord.q += (coord.i >> iter);
+			angle += arctan_table[iter];
+		} else {
+			valtmp = coord.i + (coord.q >> iter);
+			coord.q -= (coord.i >> iter);
+			angle -= arctan_table[iter];
+		}
+		coord.i = valtmp;
+	}
+
+	coord.i *= signx;
+	coord.q *= signx;
+	return coord;
+}
+EXPORT_SYMBOL(cordic_calc_iq);
+
+MODULE_DESCRIPTION("Cordic functions");
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/compat/crc8.c b/compat/crc8.c
new file mode 100644
index 0000000..87b59ca
--- /dev/null
+++ b/compat/crc8.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/crc8.h>
+#include <linux/printk.h>
+
+/*
+ * crc8_populate_msb - fill crc table for given polynomial in reverse bit order.
+ *
+ * table:	table to be filled.
+ * polynomial:	polynomial for which table is to be filled.
+ */
+void crc8_populate_msb(u8 table[CRC8_TABLE_SIZE], u8 polynomial)
+{
+	int i, j;
+	const u8 msbit = 0x80;
+	u8 t = msbit;
+
+	table[0] = 0;
+
+	for (i = 1; i < CRC8_TABLE_SIZE; i *= 2) {
+		t = (t << 1) ^ (t & msbit ? polynomial : 0);
+		for (j = 0; j < i; j++)
+			table[i+j] = table[j] ^ t;
+	}
+}
+EXPORT_SYMBOL(crc8_populate_msb);
+
+/*
+ * crc8_populate_lsb - fill crc table for given polynomial in regular bit order.
+ *
+ * table:	table to be filled.
+ * polynomial:	polynomial for which table is to be filled.
+ */
+void crc8_populate_lsb(u8 table[CRC8_TABLE_SIZE], u8 polynomial)
+{
+	int i, j;
+	u8 t = 1;
+
+	table[0] = 0;
+
+	for (i = (CRC8_TABLE_SIZE >> 1); i; i >>= 1) {
+		t = (t >> 1) ^ (t & 1 ? polynomial : 0);
+		for (j = 0; j < CRC8_TABLE_SIZE; j += 2*i)
+			table[i+j] = table[j] ^ t;
+	}
+}
+EXPORT_SYMBOL(crc8_populate_lsb);
+
+/*
+ * crc8 - calculate a crc8 over the given input data.
+ *
+ * table: crc table used for calculation.
+ * pdata: pointer to data buffer.
+ * nbytes: number of bytes in data buffer.
+ * crc:	previous returned crc8 value.
+ */
+u8 crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes, u8 crc)
+{
+	/* loop over the buffer data */
+	while (nbytes-- > 0)
+		crc = table[(crc ^ *pdata++) & 0xff];
+
+	return crc;
+}
+EXPORT_SYMBOL(crc8);
+
+MODULE_DESCRIPTION("CRC8 (by Williams, Ross N.) function");
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/compat/kfifo.c b/compat/kfifo.c
new file mode 100644
index 0000000..01a0700
--- /dev/null
+++ b/compat/kfifo.c
@@ -0,0 +1,608 @@
+/*
+ * A generic kernel FIFO implementation
+ *
+ * Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/log2.h>
+#include <linux/uaccess.h>
+#include <linux/kfifo.h>
+
+/*
+ * internal helper to calculate the unused elements in a fifo
+ */
+static inline unsigned int kfifo_unused(struct __kfifo *fifo)
+{
+	return (fifo->mask + 1) - (fifo->in - fifo->out);
+}
+
+int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
+		size_t esize, gfp_t gfp_mask)
+{
+	/*
+	 * round down to the next power of 2, since our 'let the indices
+	 * wrap' technique works only in this case.
+	 */
+	if (!is_power_of_2(size))
+		size = rounddown_pow_of_two(size);
+
+	fifo->in = 0;
+	fifo->out = 0;
+	fifo->esize = esize;
+
+	if (size < 2) {
+		fifo->data = NULL;
+		fifo->mask = 0;
+		return -EINVAL;
+	}
+
+	fifo->data = kmalloc(size * esize, gfp_mask);
+
+	if (!fifo->data) {
+		fifo->mask = 0;
+		return -ENOMEM;
+	}
+	fifo->mask = size - 1;
+
+	return 0;
+}
+EXPORT_SYMBOL(__kfifo_alloc);
+
+void __kfifo_free(struct __kfifo *fifo)
+{
+	kfree(fifo->data);
+	fifo->in = 0;
+	fifo->out = 0;
+	fifo->esize = 0;
+	fifo->data = NULL;
+	fifo->mask = 0;
+}
+EXPORT_SYMBOL(__kfifo_free);
+
+int __kfifo_init(struct __kfifo *fifo, void *buffer,
+		unsigned int size, size_t esize)
+{
+	size /= esize;
+
+	if (!is_power_of_2(size))
+		size = rounddown_pow_of_two(size);
+
+	fifo->in = 0;
+	fifo->out = 0;
+	fifo->esize = esize;
+	fifo->data = buffer;
+
+	if (size < 2) {
+		fifo->mask = 0;
+		return -EINVAL;
+	}
+	fifo->mask = size - 1;
+
+	return 0;
+}
+EXPORT_SYMBOL(__kfifo_init);
+
+static void kfifo_copy_in(struct __kfifo *fifo, const void *src,
+		unsigned int len, unsigned int off)
+{
+	unsigned int size = fifo->mask + 1;
+	unsigned int esize = fifo->esize;
+	unsigned int l;
+
+	off &= fifo->mask;
+	if (esize != 1) {
+		off *= esize;
+		size *= esize;
+		len *= esize;
+	}
+	l = min(len, size - off);
+
+	memcpy(fifo->data + off, src, l);
+	memcpy(fifo->data, src + l, len - l);
+	/*
+	 * make sure that the data in the fifo is up to date before
+	 * incrementing the fifo->in index counter
+	 */
+	smp_wmb();
+}
+
+unsigned int __kfifo_in(struct __kfifo *fifo,
+		const void *buf, unsigned int len)
+{
+	unsigned int l;
+
+	l = kfifo_unused(fifo);
+	if (len > l)
+		len = l;
+
+	kfifo_copy_in(fifo, buf, len, fifo->in);
+	fifo->in += len;
+	return len;
+}
+EXPORT_SYMBOL(__kfifo_in);
+
+static void kfifo_copy_out(struct __kfifo *fifo, void *dst,
+		unsigned int len, unsigned int off)
+{
+	unsigned int size = fifo->mask + 1;
+	unsigned int esize = fifo->esize;
+	unsigned int l;
+
+	off &= fifo->mask;
+	if (esize != 1) {
+		off *= esize;
+		size *= esize;
+		len *= esize;
+	}
+	l = min(len, size - off);
+
+	memcpy(dst, fifo->data + off, l);
+	memcpy(dst + l, fifo->data, len - l);
+	/*
+	 * make sure that the data is copied before
+	 * incrementing the fifo->out index counter
+	 */
+	smp_wmb();
+}
+
+unsigned int __kfifo_out_peek(struct __kfifo *fifo,
+		void *buf, unsigned int len)
+{
+	unsigned int l;
+
+	l = fifo->in - fifo->out;
+	if (len > l)
+		len = l;
+
+	kfifo_copy_out(fifo, buf, len, fifo->out);
+	return len;
+}
+EXPORT_SYMBOL(__kfifo_out_peek);
+
+unsigned int __kfifo_out(struct __kfifo *fifo,
+		void *buf, unsigned int len)
+{
+	len = __kfifo_out_peek(fifo, buf, len);
+	fifo->out += len;
+	return len;
+}
+EXPORT_SYMBOL(__kfifo_out);
+
+static unsigned long kfifo_copy_from_user(struct __kfifo *fifo,
+	const void __user *from, unsigned int len, unsigned int off,
+	unsigned int *copied)
+{
+	unsigned int size = fifo->mask + 1;
+	unsigned int esize = fifo->esize;
+	unsigned int l;
+	unsigned long ret;
+
+	off &= fifo->mask;
+	if (esize != 1) {
+		off *= esize;
+		size *= esize;
+		len *= esize;
+	}
+	l = min(len, size - off);
+
+	ret = copy_from_user(fifo->data + off, from, l);
+	if (unlikely(ret))
+		ret = DIV_ROUND_UP(ret + len - l, esize);
+	else {
+		ret = copy_from_user(fifo->data, from + l, len - l);
+		if (unlikely(ret))
+			ret = DIV_ROUND_UP(ret, esize);
+	}
+	/*
+	 * make sure that the data in the fifo is up to date before
+	 * incrementing the fifo->in index counter
+	 */
+	smp_wmb();
+	*copied = len - ret;
+	/* return the number of elements which are not copied */
+	return ret;
+}
+
+int __kfifo_from_user(struct __kfifo *fifo, const void __user *from,
+		unsigned long len, unsigned int *copied)
+{
+	unsigned int l;
+	unsigned long ret;
+	unsigned int esize = fifo->esize;
+	int err;
+
+	if (esize != 1)
+		len /= esize;
+
+	l = kfifo_unused(fifo);
+	if (len > l)
+		len = l;
+
+	ret = kfifo_copy_from_user(fifo, from, len, fifo->in, copied);
+	if (unlikely(ret)) {
+		len -= ret;
+		err = -EFAULT;
+	} else
+		err = 0;
+	fifo->in += len;
+	return err;
+}
+EXPORT_SYMBOL(__kfifo_from_user);
+
+static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to,
+		unsigned int len, unsigned int off, unsigned int *copied)
+{
+	unsigned int l;
+	unsigned long ret;
+	unsigned int size = fifo->mask + 1;
+	unsigned int esize = fifo->esize;
+
+	off &= fifo->mask;
+	if (esize != 1) {
+		off *= esize;
+		size *= esize;
+		len *= esize;
+	}
+	l = min(len, size - off);
+
+	ret = copy_to_user(to, fifo->data + off, l);
+	if (unlikely(ret))
+		ret = DIV_ROUND_UP(ret + len - l, esize);
+	else {
+		ret = copy_to_user(to + l, fifo->data, len - l);
+		if (unlikely(ret))
+			ret = DIV_ROUND_UP(ret, esize);
+	}
+	/*
+	 * make sure that the data is copied before
+	 * incrementing the fifo->out index counter
+	 */
+	smp_wmb();
+	*copied = len - ret;
+	/* return the number of elements which are not copied */
+	return ret;
+}
+
+int __kfifo_to_user(struct __kfifo *fifo, void __user *to,
+		unsigned long len, unsigned int *copied)
+{
+	unsigned int l;
+	unsigned long ret;
+	unsigned int esize = fifo->esize;
+	int err;
+
+	if (esize != 1)
+		len /= esize;
+
+	l = fifo->in - fifo->out;
+	if (len > l)
+		len = l;
+	ret = kfifo_copy_to_user(fifo, to, len, fifo->out, copied);
+	if (unlikely(ret)) {
+		len -= ret;
+		err = -EFAULT;
+	} else
+		err = 0;
+	fifo->out += len;
+	return err;
+}
+EXPORT_SYMBOL(__kfifo_to_user);
+
+static int setup_sgl_buf(struct scatterlist *sgl, void *buf,
+		int nents, unsigned int len)
+{
+	int n;
+	unsigned int l;
+	unsigned int off;
+	struct page *page;
+
+	if (!nents)
+		return 0;
+
+	if (!len)
+		return 0;
+
+	n = 0;
+	page = virt_to_page(buf);
+	off = offset_in_page(buf);
+	l = 0;
+
+	while (len >= l + PAGE_SIZE - off) {
+		struct page *npage;
+
+		l += PAGE_SIZE;
+		buf += PAGE_SIZE;
+		npage = virt_to_page(buf);
+		if (page_to_phys(page) != page_to_phys(npage) - l) {
+			sg_set_page(sgl, page, l - off, off);
+			sgl = sg_next(sgl);
+			if (++n == nents || sgl == NULL)
+				return n;
+			page = npage;
+			len -= l - off;
+			l = off = 0;
+		}
+	}
+	sg_set_page(sgl, page, len, off);
+	return n + 1;
+}
+
+static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
+		int nents, unsigned int len, unsigned int off)
+{
+	unsigned int size = fifo->mask + 1;
+	unsigned int esize = fifo->esize;
+	unsigned int l;
+	unsigned int n;
+
+	off &= fifo->mask;
+	if (esize != 1) {
+		off *= esize;
+		size *= esize;
+		len *= esize;
+	}
+	l = min(len, size - off);
+
+	n = setup_sgl_buf(sgl, fifo->data + off, nents, l);
+	n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - l);
+
+	return n;
+}
+
+unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo,
+		struct scatterlist *sgl, int nents, unsigned int len)
+{
+	unsigned int l;
+
+	l = kfifo_unused(fifo);
+	if (len > l)
+		len = l;
+
+	return setup_sgl(fifo, sgl, nents, len, fifo->in);
+}
+EXPORT_SYMBOL(__kfifo_dma_in_prepare);
+
+unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
+		struct scatterlist *sgl, int nents, unsigned int len)
+{
+	unsigned int l;
+
+	l = fifo->in - fifo->out;
+	if (len > l)
+		len = l;
+
+	return setup_sgl(fifo, sgl, nents, len, fifo->out);
+}
+EXPORT_SYMBOL(__kfifo_dma_out_prepare);
+
+unsigned int __kfifo_max_r(unsigned int len, size_t recsize)
+{
+	unsigned int max = (1 << (recsize << 3)) - 1;
+
+	if (len > max)
+		return max;
+	return len;
+}
+
+#define	__KFIFO_PEEK(data, out, mask) \
+	((data)[(out) & (mask)])
+/*
+ * __kfifo_peek_n internal helper function for determinate the length of
+ * the next record in the fifo
+ */
+static unsigned int __kfifo_peek_n(struct __kfifo *fifo, size_t recsize)
+{
+	unsigned int l;
+	unsigned int mask = fifo->mask;
+	unsigned char *data = fifo->data;
+
+	l = __KFIFO_PEEK(data, fifo->out, mask);
+
+	if (--recsize)
+		l |= __KFIFO_PEEK(data, fifo->out + 1, mask) << 8;
+
+	return l;
+}
+
+#define	__KFIFO_POKE(data, in, mask, val) \
+	( \
+	(data)[(in) & (mask)] = (unsigned char)(val) \
+	)
+
+/*
+ * __kfifo_poke_n internal helper function for storeing the length of
+ * the record into the fifo
+ */
+static void __kfifo_poke_n(struct __kfifo *fifo, unsigned int n, size_t recsize)
+{
+	unsigned int mask = fifo->mask;
+	unsigned char *data = fifo->data;
+
+	__KFIFO_POKE(data, fifo->in, mask, n);
+
+	if (recsize > 1)
+		__KFIFO_POKE(data, fifo->in + 1, mask, n >> 8);
+}
+
+unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize)
+{
+	return __kfifo_peek_n(fifo, recsize);
+}
+EXPORT_SYMBOL(__kfifo_len_r);
+
+unsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf,
+		unsigned int len, size_t recsize)
+{
+	if (len + recsize > kfifo_unused(fifo))
+		return 0;
+
+	__kfifo_poke_n(fifo, len, recsize);
+
+	kfifo_copy_in(fifo, buf, len, fifo->in + recsize);
+	fifo->in += len + recsize;
+	return len;
+}
+EXPORT_SYMBOL(__kfifo_in_r);
+
+static unsigned int kfifo_out_copy_r(struct __kfifo *fifo,
+	void *buf, unsigned int len, size_t recsize, unsigned int *n)
+{
+	*n = __kfifo_peek_n(fifo, recsize);
+
+	if (len > *n)
+		len = *n;
+
+	kfifo_copy_out(fifo, buf, len, fifo->out + recsize);
+	return len;
+}
+
+unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf,
+		unsigned int len, size_t recsize)
+{
+	unsigned int n;
+
+	if (fifo->in == fifo->out)
+		return 0;
+
+	return kfifo_out_copy_r(fifo, buf, len, recsize, &n);
+}
+EXPORT_SYMBOL(__kfifo_out_peek_r);
+
+unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf,
+		unsigned int len, size_t recsize)
+{
+	unsigned int n;
+
+	if (fifo->in == fifo->out)
+		return 0;
+
+	len = kfifo_out_copy_r(fifo, buf, len, recsize, &n);
+	fifo->out += n + recsize;
+	return len;
+}
+EXPORT_SYMBOL(__kfifo_out_r);
+
+void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize)
+{
+	unsigned int n;
+
+	n = __kfifo_peek_n(fifo, recsize);
+	fifo->out += n + recsize;
+}
+EXPORT_SYMBOL(__kfifo_skip_r);
+
+int __kfifo_from_user_r(struct __kfifo *fifo, const void __user *from,
+	unsigned long len, unsigned int *copied, size_t recsize)
+{
+	unsigned long ret;
+
+	len = __kfifo_max_r(len, recsize);
+
+	if (len + recsize > kfifo_unused(fifo)) {
+		*copied = 0;
+		return 0;
+	}
+
+	__kfifo_poke_n(fifo, len, recsize);
+
+	ret = kfifo_copy_from_user(fifo, from, len, fifo->in + recsize, copied);
+	if (unlikely(ret)) {
+		*copied = 0;
+		return -EFAULT;
+	}
+	fifo->in += len + recsize;
+	return 0;
+}
+EXPORT_SYMBOL(__kfifo_from_user_r);
+
+int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to,
+	unsigned long len, unsigned int *copied, size_t recsize)
+{
+	unsigned long ret;
+	unsigned int n;
+
+	if (fifo->in == fifo->out) {
+		*copied = 0;
+		return 0;
+	}
+
+	n = __kfifo_peek_n(fifo, recsize);
+	if (len > n)
+		len = n;
+
+	ret = kfifo_copy_to_user(fifo, to, len, fifo->out + recsize, copied);
+	if (unlikely(ret)) {
+		*copied = 0;
+		return -EFAULT;
+	}
+	fifo->out += n + recsize;
+	return 0;
+}
+EXPORT_SYMBOL(__kfifo_to_user_r);
+
+unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo,
+	struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
+{
+	if (!nents)
+		BUG();
+
+	len = __kfifo_max_r(len, recsize);
+
+	if (len + recsize > kfifo_unused(fifo))
+		return 0;
+
+	return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize);
+}
+EXPORT_SYMBOL(__kfifo_dma_in_prepare_r);
+
+void __kfifo_dma_in_finish_r(struct __kfifo *fifo,
+	unsigned int len, size_t recsize)
+{
+	len = __kfifo_max_r(len, recsize);
+	__kfifo_poke_n(fifo, len, recsize);
+	fifo->in += len + recsize;
+}
+EXPORT_SYMBOL(__kfifo_dma_in_finish_r);
+
+unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
+	struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
+{
+	if (!nents)
+		BUG();
+
+	len = __kfifo_max_r(len, recsize);
+
+	if (len + recsize > fifo->in - fifo->out)
+		return 0;
+
+	return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize);
+}
+EXPORT_SYMBOL(__kfifo_dma_out_prepare_r);
+
+void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize)
+{
+	unsigned int len;
+
+	len = __kfifo_peek_n(fifo, recsize);
+	fifo->out += len + recsize;
+}
+EXPORT_SYMBOL(__kfifo_dma_out_finish_r);
diff --git a/compat/kstrtox.c b/compat/kstrtox.c
new file mode 100644
index 0000000..10aa91b
--- /dev/null
+++ b/compat/kstrtox.c
@@ -0,0 +1,236 @@
+/*
+ * Convert integer string representation to an integer.
+ * If an integer doesn't fit into specified type, -E is returned.
+ *
+ * Integer starts with optional sign.
+ * kstrtou*() functions do not accept sign "-".
+ *
+ * Radix 0 means autodetection: leading "0x" implies radix 16,
+ * leading "0" implies radix 8, otherwise radix is 10.
+ * Autodetection hints work after optional sign, but not before.
+ *
+ * If -E is returned, result is not touched.
+ */
+#include <linux/kernel.h>
+/* 
+ * kstrto* was included in kernel 2.6.38.4 and causes conflicts with the
+ * version included in compat-wireless. We use strict_strtol to check if
+ * kstrto* is already available.
+ */
+#ifndef strict_strtol
+
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+static inline char _tolower(const char c)
+{
+	return c | 0x20;
+}
+
+static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
+{
+	unsigned long long acc;
+	int ok;
+
+	if (base == 0) {
+		if (s[0] == '0') {
+			if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
+				base = 16;
+			else
+				base = 8;
+		} else
+			base = 10;
+	}
+	if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
+		s += 2;
+
+	acc = 0;
+	ok = 0;
+	while (*s) {
+		unsigned int val;
+
+		if ('0' <= *s && *s <= '9')
+			val = *s - '0';
+		else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
+			val = _tolower(*s) - 'a' + 10;
+		else if (*s == '\n') {
+			if (*(s + 1) == '\0')
+				break;
+			else
+				return -EINVAL;
+		} else
+			return -EINVAL;
+
+		if (val >= base)
+			return -EINVAL;
+		if (acc > div_u64(ULLONG_MAX - val, base))
+			return -ERANGE;
+		acc = acc * base + val;
+		ok = 1;
+
+		s++;
+	}
+	if (!ok)
+		return -EINVAL;
+	*res = acc;
+	return 0;
+}
+
+int kstrtoull(const char *s, unsigned int base, unsigned long long *res)
+{
+	if (s[0] == '+')
+		s++;
+	return _kstrtoull(s, base, res);
+}
+EXPORT_SYMBOL(kstrtoull);
+
+int kstrtoll(const char *s, unsigned int base, long long *res)
+{
+	unsigned long long tmp;
+	int rv;
+
+	if (s[0] == '-') {
+		rv = _kstrtoull(s + 1, base, &tmp);
+		if (rv < 0)
+			return rv;
+		if ((long long)(-tmp) >= 0)
+			return -ERANGE;
+		*res = -tmp;
+	} else {
+		rv = kstrtoull(s, base, &tmp);
+		if (rv < 0)
+			return rv;
+		if ((long long)tmp < 0)
+			return -ERANGE;
+		*res = tmp;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(kstrtoll);
+
+/* Internal, do not use. */
+int _kstrtoul(const char *s, unsigned int base, unsigned long *res)
+{
+	unsigned long long tmp;
+	int rv;
+
+	rv = kstrtoull(s, base, &tmp);
+	if (rv < 0)
+		return rv;
+	if (tmp != (unsigned long long)(unsigned long)tmp)
+		return -ERANGE;
+	*res = tmp;
+	return 0;
+}
+EXPORT_SYMBOL(_kstrtoul);
+
+/* Internal, do not use. */
+int _kstrtol(const char *s, unsigned int base, long *res)
+{
+	long long tmp;
+	int rv;
+
+	rv = kstrtoll(s, base, &tmp);
+	if (rv < 0)
+		return rv;
+	if (tmp != (long long)(long)tmp)
+		return -ERANGE;
+	*res = tmp;
+	return 0;
+}
+EXPORT_SYMBOL(_kstrtol);
+
+int kstrtouint(const char *s, unsigned int base, unsigned int *res)
+{
+	unsigned long long tmp;
+	int rv;
+
+	rv = kstrtoull(s, base, &tmp);
+	if (rv < 0)
+		return rv;
+	if (tmp != (unsigned long long)(unsigned int)tmp)
+		return -ERANGE;
+	*res = tmp;
+	return 0;
+}
+EXPORT_SYMBOL(kstrtouint);
+
+int kstrtoint(const char *s, unsigned int base, int *res)
+{
+	long long tmp;
+	int rv;
+
+	rv = kstrtoll(s, base, &tmp);
+	if (rv < 0)
+		return rv;
+	if (tmp != (long long)(int)tmp)
+		return -ERANGE;
+	*res = tmp;
+	return 0;
+}
+EXPORT_SYMBOL(kstrtoint);
+
+int kstrtou16(const char *s, unsigned int base, u16 *res)
+{
+	unsigned long long tmp;
+	int rv;
+
+	rv = kstrtoull(s, base, &tmp);
+	if (rv < 0)
+		return rv;
+	if (tmp != (unsigned long long)(u16)tmp)
+		return -ERANGE;
+	*res = tmp;
+	return 0;
+}
+EXPORT_SYMBOL(kstrtou16);
+
+int kstrtos16(const char *s, unsigned int base, s16 *res)
+{
+	long long tmp;
+	int rv;
+
+	rv = kstrtoll(s, base, &tmp);
+	if (rv < 0)
+		return rv;
+	if (tmp != (long long)(s16)tmp)
+		return -ERANGE;
+	*res = tmp;
+	return 0;
+}
+EXPORT_SYMBOL(kstrtos16);
+
+int kstrtou8(const char *s, unsigned int base, u8 *res)
+{
+	unsigned long long tmp;
+	int rv;
+
+	rv = kstrtoull(s, base, &tmp);
+	if (rv < 0)
+		return rv;
+	if (tmp != (unsigned long long)(u8)tmp)
+		return -ERANGE;
+	*res = tmp;
+	return 0;
+}
+EXPORT_SYMBOL(kstrtou8);
+
+int kstrtos8(const char *s, unsigned int base, s8 *res)
+{
+	long long tmp;
+	int rv;
+
+	rv = kstrtoll(s, base, &tmp);
+	if (rv < 0)
+		return rv;
+	if (tmp != (long long)(s8)tmp)
+		return -ERANGE;
+	*res = tmp;
+	return 0;
+}
+EXPORT_SYMBOL(kstrtos8);
+#endif /* #ifndef strict_strtol */
diff --git a/compat/main.c b/compat/main.c
new file mode 100644
index 0000000..f830488
--- /dev/null
+++ b/compat/main.c
@@ -0,0 +1,61 @@
+#include <linux/module.h>
+
+MODULE_AUTHOR("Luis R. Rodriguez");
+MODULE_DESCRIPTION("Kernel compatibility module");
+MODULE_LICENSE("GPL");
+
+#ifndef COMPAT_BASE_TREE
+#error "You need a COMPAT_BASE_TREE"
+#endif
+
+#ifndef COMPAT_BASE_TREE_VERSION
+#error "You need a COMPAT_BASE_TREE_VERSION"
+#endif
+
+#ifndef COMPAT_VERSION
+#error "You need a COMPAT_VERSION"
+#endif
+
+static char *compat_base_tree = COMPAT_BASE_TREE;
+static char *compat_base_tree_version = COMPAT_BASE_TREE_VERSION;
+static char *compat_version = COMPAT_VERSION;
+
+module_param(compat_base_tree, charp, 0400);
+MODULE_PARM_DESC(compat_base_tree,
+		 "The upstream tree used as base for this backport");
+
+module_param(compat_base_tree_version, charp, 0400);
+MODULE_PARM_DESC(compat_base_tree_version,
+		 "The git-describe of the upstream base tree");
+
+module_param(compat_version, charp, 0400);
+MODULE_PARM_DESC(compat_version,
+		 "Version of the kernel compat backport work");
+
+static int __init compat_init(void)
+{
+	/* pm-qos for kernels <= 2.6.24, this is a no-op on newer kernels */
+	compat_pm_qos_power_init();
+	compat_system_workqueue_create();
+
+	printk(KERN_INFO
+	       COMPAT_PROJECT " backport release: "
+	       COMPAT_VERSION
+	       "\n");
+	printk(KERN_INFO "Backport based on "
+	       COMPAT_BASE_TREE " " COMPAT_BASE_TREE_VERSION
+	       "\n");
+
+        return 0;
+}
+module_init(compat_init);
+
+static void __exit compat_exit(void)
+{
+	compat_pm_qos_power_deinit();
+	compat_system_workqueue_destroy();
+
+        return;
+}
+module_exit(compat_exit);
+
diff --git a/compat/pm_qos_params.c b/compat/pm_qos_params.c
new file mode 100644
index 0000000..833d98c
--- /dev/null
+++ b/compat/pm_qos_params.c
@@ -0,0 +1,477 @@
+#include <net/compat.h>
+
+/* This is the backport of pm-qos params for kernels <= 2.6.25 */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25))
+
+/*
+ * This module exposes the interface to kernel space for specifying
+ * QoS dependencies.  It provides infrastructure for registration of:
+ *
+ * Dependents on a QoS value : register requirements
+ * Watchers of QoS value : get notified when target QoS value changes
+ *
+ * This QoS design is best effort based.  Dependents register their QoS needs.
+ * Watchers register to keep track of the current QoS needs of the system.
+ *
+ * There are 3 basic classes of QoS parameter: latency, timeout, throughput
+ * each have defined units:
+ * latency: usec
+ * timeout: usec <-- currently not used.
+ * throughput: kbs (kilo byte / sec)
+ *
+ * There are lists of pm_qos_objects each one wrapping requirements, notifiers
+ *
+ * User mode requirements on a QOS parameter register themselves to the
+ * subsystem by opening the device node /dev/... and writing there request to
+ * the node.  As long as the process holds a file handle open to the node the
+ * client continues to be accounted for.  Upon file release the usermode
+ * requirement is removed and a new qos target is computed.  This way when the
+ * requirement that the application has is cleaned up when closes the file
+ * pointer or exits the pm_qos_object will get an opportunity to clean up.
+ *
+ * Mark Gross <mgross@linux.intel.com>
+ */
+
+#include <linux/pm_qos_params.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+
+#include <linux/uaccess.h>
+
+/*
+ * locking rule: all changes to requirements or notifiers lists
+ * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
+ * held, taken with _irqsave.  One lock to rule them all
+ */
+struct requirement_list {
+	struct list_head list;
+	union {
+		s32 value;
+		s32 usec;
+		s32 kbps;
+	};
+	char *name;
+};
+
+static s32 max_compare(s32 v1, s32 v2);
+static s32 min_compare(s32 v1, s32 v2);
+
+struct pm_qos_object {
+	struct requirement_list requirements;
+	struct blocking_notifier_head *notifiers;
+	struct miscdevice pm_qos_power_miscdev;
+	char *name;
+	s32 default_value;
+	atomic_t target_value;
+	s32 (*comparitor)(s32, s32);
+};
+
+static struct pm_qos_object null_pm_qos;
+static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
+static struct pm_qos_object cpu_dma_pm_qos = {
+	.requirements = {LIST_HEAD_INIT(cpu_dma_pm_qos.requirements.list)},
+	.notifiers = &cpu_dma_lat_notifier,
+	.name = "cpu_dma_latency",
+	.default_value = 2000 * USEC_PER_SEC,
+	.target_value = ATOMIC_INIT(2000 * USEC_PER_SEC),
+	.comparitor = min_compare
+};
+
+static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
+static struct pm_qos_object network_lat_pm_qos = {
+	.requirements = {LIST_HEAD_INIT(network_lat_pm_qos.requirements.list)},
+	.notifiers = &network_lat_notifier,
+	.name = "network_latency",
+	.default_value = 2000 * USEC_PER_SEC,
+	.target_value = ATOMIC_INIT(2000 * USEC_PER_SEC),
+	.comparitor = min_compare
+};
+
+
+static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
+static struct pm_qos_object network_throughput_pm_qos = {
+	.requirements =
+		{LIST_HEAD_INIT(network_throughput_pm_qos.requirements.list)},
+	.notifiers = &network_throughput_notifier,
+	.name = "network_throughput",
+	.default_value = 0,
+	.target_value = ATOMIC_INIT(0),
+	.comparitor = max_compare
+};
+
+static BLOCKING_NOTIFIER_HEAD(system_bus_freq_notifier);
+static struct pm_qos_object system_bus_freq_pm_qos = {
+	.requirements =
+		{LIST_HEAD_INIT(system_bus_freq_pm_qos.requirements.list)},
+	.notifiers = &system_bus_freq_notifier,
+	.name = "system_bus_freq",
+	.default_value = 0,
+	.target_value = ATOMIC_INIT(0),
+	.comparitor = max_compare
+};
+
+
+static struct pm_qos_object *pm_qos_array[PM_QOS_NUM_CLASSES] = {
+	[PM_QOS_RESERVED] = &null_pm_qos,
+	[PM_QOS_CPU_DMA_LATENCY] = &cpu_dma_pm_qos,
+	[PM_QOS_NETWORK_LATENCY] = &network_lat_pm_qos,
+	[PM_QOS_NETWORK_THROUGHPUT] = &network_throughput_pm_qos,
+	[PM_QOS_SYSTEM_BUS_FREQ] = &system_bus_freq_pm_qos,
+};
+
+static DEFINE_SPINLOCK(pm_qos_lock);
+
+static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
+		size_t count, loff_t *f_pos);
+static int pm_qos_power_open(struct inode *inode, struct file *filp);
+static int pm_qos_power_release(struct inode *inode, struct file *filp);
+
+static const struct file_operations pm_qos_power_fops = {
+	.write = pm_qos_power_write,
+	.open = pm_qos_power_open,
+	.release = pm_qos_power_release,
+};
+
+/* static helper functions */
+static s32 max_compare(s32 v1, s32 v2)
+{
+	return max(v1, v2);
+}
+
+static s32 min_compare(s32 v1, s32 v2)
+{
+	return min(v1, v2);
+}
+
+
+static void update_target(int target)
+{
+	s32 extreme_value;
+	struct requirement_list *node;
+	unsigned long flags;
+	int call_notifier = 0;
+
+	spin_lock_irqsave(&pm_qos_lock, flags);
+	extreme_value = pm_qos_array[target]->default_value;
+	list_for_each_entry(node,
+			&pm_qos_array[target]->requirements.list, list) {
+		extreme_value = pm_qos_array[target]->comparitor(
+				extreme_value, node->value);
+	}
+	if (atomic_read(&pm_qos_array[target]->target_value) != extreme_value) {
+		call_notifier = 1;
+		atomic_set(&pm_qos_array[target]->target_value, extreme_value);
+		pr_debug(KERN_ERR "new target for qos %d is %d\n", target,
+			atomic_read(&pm_qos_array[target]->target_value));
+	}
+	spin_unlock_irqrestore(&pm_qos_lock, flags);
+
+	if (call_notifier)
+		blocking_notifier_call_chain(pm_qos_array[target]->notifiers,
+			(unsigned long) extreme_value, NULL);
+}
+
+static int register_pm_qos_misc(struct pm_qos_object *qos)
+{
+	qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
+	qos->pm_qos_power_miscdev.name = qos->name;
+	qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
+
+	return misc_register(&qos->pm_qos_power_miscdev);
+}
+
+static int find_pm_qos_object_by_minor(int minor)
+{
+	int pm_qos_class;
+
+	for (pm_qos_class = 0;
+		pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
+		if (minor ==
+			pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
+			return pm_qos_class;
+	}
+	return -1;
+}
+
+/**
+ * pm_qos_requirement - returns current system wide qos expectation
+ * @pm_qos_class: identification of which qos value is requested
+ *
+ * This function returns the current target value in an atomic manner.
+ */
+int pm_qos_requirement(int pm_qos_class)
+{
+	return atomic_read(&pm_qos_array[pm_qos_class]->target_value);
+}
+EXPORT_SYMBOL_GPL(pm_qos_requirement);
+
+/**
+ * pm_qos_add_requirement - inserts new qos request into the list
+ * @pm_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ * @value: defines the qos request
+ *
+ * This function inserts a new entry in the pm_qos_class list of requested qos
+ * performance characteristics.  It recomputes the aggregate QoS expectations
+ * for the pm_qos_class of parameters.
+ */
+int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value)
+{
+	struct requirement_list *dep;
+	unsigned long flags;
+
+	dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL);
+	if (dep) {
+		if (value == PM_QOS_DEFAULT_VALUE)
+			dep->value = pm_qos_array[pm_qos_class]->default_value;
+		else
+			dep->value = value;
+		dep->name = kstrdup(name, GFP_KERNEL);
+		if (!dep->name)
+			goto cleanup;
+
+		spin_lock_irqsave(&pm_qos_lock, flags);
+		list_add(&dep->list,
+			&pm_qos_array[pm_qos_class]->requirements.list);
+		spin_unlock_irqrestore(&pm_qos_lock, flags);
+		update_target(pm_qos_class);
+
+		return 0;
+	}
+
+cleanup:
+	kfree(dep);
+	return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(pm_qos_add_requirement);
+
+/**
+ * pm_qos_update_requirement - modifies an existing qos request
+ * @pm_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ * @value: defines the qos request
+ *
+ * Updates an existing qos requirement for the pm_qos_class of parameters along
+ * with updating the target pm_qos_class value.
+ *
+ * If the named request isn't in the list then no change is made.
+ */
+int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value)
+{
+	unsigned long flags;
+	struct requirement_list *node;
+	int pending_update = 0;
+
+	spin_lock_irqsave(&pm_qos_lock, flags);
+	list_for_each_entry(node,
+		&pm_qos_array[pm_qos_class]->requirements.list, list) {
+		if (strcmp(node->name, name) == 0) {
+			if (new_value == PM_QOS_DEFAULT_VALUE)
+				node->value =
+				pm_qos_array[pm_qos_class]->default_value;
+			else
+				node->value = new_value;
+			pending_update = 1;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&pm_qos_lock, flags);
+	if (pending_update)
+		update_target(pm_qos_class);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm_qos_update_requirement);
+
+/**
+ * pm_qos_remove_requirement - modifies an existing qos request
+ * @pm_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ *
+ * Will remove named qos request from pm_qos_class list of parameters and
+ * recompute the current target value for the pm_qos_class.
+ */
+void pm_qos_remove_requirement(int pm_qos_class, char *name)
+{
+	unsigned long flags;
+	struct requirement_list *node;
+	int pending_update = 0;
+
+	spin_lock_irqsave(&pm_qos_lock, flags);
+	list_for_each_entry(node,
+		&pm_qos_array[pm_qos_class]->requirements.list, list) {
+		if (strcmp(node->name, name) == 0) {
+			kfree(node->name);
+			list_del(&node->list);
+			kfree(node);
+			pending_update = 1;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&pm_qos_lock, flags);
+	if (pending_update)
+		update_target(pm_qos_class);
+}
+EXPORT_SYMBOL_GPL(pm_qos_remove_requirement);
+
+/**
+ * pm_qos_add_notifier - sets notification entry for changes to target value
+ * @pm_qos_class: identifies which qos target changes should be notified.
+ * @notifier: notifier block managed by caller.
+ *
+ * will register the notifier into a notification chain that gets called
+ * upon changes to the pm_qos_class target value.
+ */
+int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
+{
+	int retval;
+
+	retval = blocking_notifier_chain_register(
+			pm_qos_array[pm_qos_class]->notifiers, notifier);
+
+	return retval;
+}
+EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
+
+/**
+ * pm_qos_remove_notifier - deletes notification entry from chain.
+ * @pm_qos_class: identifies which qos target changes are notified.
+ * @notifier: notifier block to be removed.
+ *
+ * will remove the notifier from the notification chain that gets called
+ * upon changes to the pm_qos_class target value.
+ */
+int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
+{
+	int retval;
+
+	retval = blocking_notifier_chain_unregister(
+			pm_qos_array[pm_qos_class]->notifiers, notifier);
+
+	return retval;
+}
+EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
+
+#define PID_NAME_LEN 32
+
+static int pm_qos_power_open(struct inode *inode, struct file *filp)
+{
+	int ret;
+	long pm_qos_class;
+	char name[PID_NAME_LEN];
+
+	pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
+	if (pm_qos_class >= 0) {
+		filp->private_data = (void *)pm_qos_class;
+		snprintf(name, PID_NAME_LEN, "process_%d", current->pid);
+		ret = pm_qos_add_requirement(pm_qos_class, name,
+					PM_QOS_DEFAULT_VALUE);
+		if (ret >= 0)
+			return 0;
+	}
+	return -EPERM;
+}
+
+static int pm_qos_power_release(struct inode *inode, struct file *filp)
+{
+	int pm_qos_class;
+	char name[PID_NAME_LEN];
+
+	pm_qos_class = (long)filp->private_data;
+	snprintf(name, PID_NAME_LEN, "process_%d", current->pid);
+	pm_qos_remove_requirement(pm_qos_class, name);
+
+	return 0;
+}
+
+static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
+		size_t count, loff_t *f_pos)
+{
+	s32 value;
+	int pm_qos_class;
+	char name[PID_NAME_LEN];
+
+	pm_qos_class = (long)filp->private_data;
+	if (count != sizeof(s32))
+		return -EINVAL;
+	if (copy_from_user(&value, buf, sizeof(s32)))
+		return -EFAULT;
+	snprintf(name, PID_NAME_LEN, "process_%d", current->pid);
+	pm_qos_update_requirement(pm_qos_class, name, value);
+
+	return  sizeof(s32);
+}
+
+
+/*
+ * This initializes pm-qos for older kernels.
+ */
+int compat_pm_qos_power_init(void)
+{
+	int ret = 0;
+
+	ret = register_pm_qos_misc(&cpu_dma_pm_qos);
+	if (ret < 0) {
+		printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
+		return ret;
+	}
+	ret = register_pm_qos_misc(&network_lat_pm_qos);
+	if (ret < 0) {
+		printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
+		return ret;
+	}
+	ret = register_pm_qos_misc(&network_throughput_pm_qos);
+	if (ret < 0) {
+		printk(KERN_ERR
+			"pm_qos_param: network_throughput setup failed\n");
+		return ret;
+	}
+	ret = register_pm_qos_misc(&system_bus_freq_pm_qos);
+	if (ret < 0)
+		printk(KERN_ERR
+			"pm_qos_param: system_bus_freq setup failed\n");
+
+	return ret;
+}
+
+int compat_pm_qos_power_deinit(void)
+{
+	int ret = 0;
+
+	ret = misc_deregister(&cpu_dma_pm_qos.pm_qos_power_miscdev);
+	if (ret < 0) {
+		printk(KERN_ERR "pm_qos_param: cpu_dma_latency deinit failed\n");
+		return ret;
+	}
+
+	ret = misc_deregister(&network_lat_pm_qos.pm_qos_power_miscdev);
+	if (ret < 0) {
+		printk(KERN_ERR "pm_qos_param: network_latency deinit failed\n");
+		return ret;
+	}
+
+	ret = misc_deregister(&network_throughput_pm_qos.pm_qos_power_miscdev);
+	if (ret < 0) {
+		printk(KERN_ERR
+			"pm_qos_param: network_throughput deinit failed\n");
+		return ret;
+	}
+
+	ret = misc_deregister(&system_bus_freq_pm_qos.pm_qos_power_miscdev);
+	if (ret < 0) {
+		printk(KERN_ERR
+			"pm_qos_param: system_bus_freq deinit failed\n");
+		return ret;
+	}
+
+	return ret;
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) */
diff --git a/compat/scripts/compat_firmware_install b/compat/scripts/compat_firmware_install
new file mode 100755
index 0000000..33e4fde
--- /dev/null
+++ b/compat/scripts/compat_firmware_install
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+if [ -f /usr/bin/lsb_release ]; then
+	LSB_RED_ID=$(/usr/bin/lsb_release -i -s)
+else
+	LSB_RED_ID="Unknown"
+fi
+
+case $LSB_RED_ID in
+"Ubuntu")
+	mkdir -p /lib/udev/ /lib/udev/rules.d/
+	cp udev/ubuntu/compat_firmware.sh /lib/udev/
+	cp udev/ubuntu/50-compat_firmware.rules /lib/udev/rules.d/
+        ;;
+*)
+	mkdir -p /lib/udev/ /lib/udev/rules.d/
+	cp udev/compat_firmware.sh /lib/udev/
+	cp udev/50-compat_firmware.rules /lib/udev/rules.d/
+        ;;
+esac
+
diff --git a/compat_base_tree b/compat_base_tree
new file mode 100644
index 0000000..d6f97f5
--- /dev/null
+++ b/compat_base_tree
@@ -0,0 +1 @@
+ath6kl.git
diff --git a/compat_base_tree_version b/compat_base_tree_version
new file mode 100644
index 0000000..5d95780
--- /dev/null
+++ b/compat_base_tree_version
@@ -0,0 +1 @@
+branching-rel-3.2.3-74-g39f7005
diff --git a/compat_version b/compat_version
new file mode 100644
index 0000000..df5977e
--- /dev/null
+++ b/compat_version
@@ -0,0 +1 @@
+3.3-OSR-2012-10-11-15-g2bd3ebf
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..daad171
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,699 @@
+export
+
+## NOTE
+## Make sure to have each variable declaration start
+## in the first column, no whitespace allowed.
+
+ifeq ($(wildcard $(KLIB_BUILD)/.config),)
+# These will be ignored by compat autoconf
+ CONFIG_PCI=y
+ CONFIG_USB=y
+ CONFIG_PCMCIA=y
+ CONFIG_SSB=m
+else
+include $(KLIB_BUILD)/.config
+endif
+
+ifneq ($(wildcard $(KLIB_BUILD)/Makefile),)
+
+COMPAT_LATEST_VERSION = 3
+
+KERNEL_VERSION := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^\([0-9]\)\..*/\1/p')
+
+ifneq ($(KERNEL_VERSION),2)
+KERNEL_SUBLEVEL := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^3\.\([0-9]\+\).*/\1/p')
+else
+COMPAT_26LATEST_VERSION = 39
+KERNEL_26SUBLEVEL := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^2\.6\.\([0-9]\+\).*/\1/p')
+COMPAT_26VERSIONS := $(shell I=$(COMPAT_26LATEST_VERSION); while [ "$$I" -gt $(KERNEL_26SUBLEVEL) ]; do echo $$I; I=$$(($$I - 1)); done)
+$(foreach ver,$(COMPAT_26VERSIONS),$(eval CONFIG_COMPAT_KERNEL_2_6_$(ver)=y))
+KERNEL_SUBLEVEL := -1
+endif
+
+COMPAT_VERSIONS := $(shell I=$(COMPAT_LATEST_VERSION); while [ "$$I" -gt $(KERNEL_SUBLEVEL) ]; do echo $$I; I=$$(($$I - 1)); done)
+$(foreach ver,$(COMPAT_VERSIONS),$(eval CONFIG_COMPAT_KERNEL_3_$(ver)=y))
+
+RHEL_MAJOR := $(shell grep ^RHEL_MAJOR $(KLIB_BUILD)/Makefile | sed -n 's/.*= *\(.*\)/\1/p')
+
+ifneq ($(RHEL_MAJOR),)
+RHEL_MINOR := $(shell grep ^RHEL_MINOR $(KLIB_BUILD)/Makefile | sed -n 's/.*= *\(.*\)/\1/p')
+COMPAT_RHEL_VERSIONS := $(shell I=$(RHEL_MINOR); while [ "$$I" -ge 0 ]; do echo $$I; I=$$(($$I - 1)); done)
+$(foreach ver,$(COMPAT_RHEL_VERSIONS),$(eval CONFIG_COMPAT_RHEL_$(RHEL_MAJOR)_$(ver)=y))
+endif
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_24
+$(error "ERROR: compat-wireless by default supports kernels >= 2.6.24, try enabling only one driver though")
+endif #CONFIG_COMPAT_KERNEL_2_6_24
+
+ifeq ($(CONFIG_CFG80211),y)
+$(error "ERROR: your kernel has CONFIG_CFG80211=y, you should have it CONFIG_CFG80211=m if you want to use this thing.")
+endif
+
+
+# 2.6.27 has FTRACE_DYNAMIC borked, so we will complain if
+# you have it enabled, otherwise you will very likely run into
+# a kernel panic.
+ifeq ($(shell test $(KERNEL_VERSION) -eq 2 -a $(KERNEL_SUBLEVEL) -eq 27 && echo yes),yes)
+ifeq ($(CONFIG_DYNAMIC_FTRACE),y)
+$(error "ERROR: Your 2.6.27 kernel has CONFIG_DYNAMIC_FTRACE, please upgrade your distribution kernel as newer ones should not have this enabled (and if so report a bug) or remove this warning if you know what you are doing")
+endif
+endif
+
+# This is because with CONFIG_MAC80211 include/linux/skbuff.h will
+# enable on 2.6.27 a new attribute:
+#
+# skb->do_not_encrypt
+#
+# and on 2.6.28 another new attribute:
+#
+# skb->requeue
+#
+# In kernel 2.6.32 both attributes were removed.
+#
+ifeq ($(shell test $(KERNEL_VERSION) -eq 2 -a $(KERNEL_SUBLEVEL) -ge 27 -a $(KERNEL_SUBLEVEL) -le 31 && echo yes),yes)
+ifeq ($(CONFIG_MAC80211),)
+$(error "ERROR: Your >=2.6.27 and <= 2.6.31 kernel has CONFIG_MAC80211 disabled, you should have it CONFIG_MAC80211=m if you want to use this thing.")
+endif
+endif
+
+ifneq ($(KERNELRELEASE),) # This prevents a warning
+
+# We will warn when you don't have MQ support or NET_SCHED enabled.
+#
+# We could consider just quiting if MQ and NET_SCHED is disabled
+# as I suspect all users of this package want 802.11e (WME) and
+# 802.11n (HT) support.
+ifeq ($(CONFIG_NET_SCHED),)
+ QOS_REQS_MISSING+=CONFIG_NET_SCHED
+endif
+
+ifneq ($(QOS_REQS_MISSING),) # Complain about our missing dependencies
+$(warning "WARNING: You are running a kernel >= 2.6.23, you should enable in it $(QOS_REQS_MISSING) for 802.11[ne] support")
+endif
+
+endif # build check
+endif # kernel Makefile check
+
+# These both are needed by compat-wireless || compat-bluetooth so enable them
+ CONFIG_COMPAT_RFKILL=y
+
+ifeq ($(CONFIG_MAC80211),y)
+$(error "ERROR: you have MAC80211 compiled into the kernel, CONFIG_MAC80211=y, as such you cannot replace its mac80211 driver. You need this set to CONFIG_MAC80211=m. If you are using Fedora upgrade your kernel as later version should this set as modular. For further information on Fedora see https://bugzilla.redhat.com/show_bug.cgi?id=470143. If you are using your own kernel recompile it and make mac80211 modular")
+else
+ CONFIG_COMPAT_WIRELESS=y
+ CONFIG_COMPAT_WIRELESS_MODULES=m
+ CONFIG_COMPAT_VAR_MODULES=m
+# We could technically separate these but not yet, we only have b44
+# Note that we don't intend on backporting network drivers that
+# use Multiqueue as that was a pain to backport to kernels older than
+# 2.6.27. But -- we could just disable those drivers from kernels
+# older than 2.6.27
+ CONFIG_COMPAT_NETWORK_MODULES=m
+ CONFIG_COMPAT_NET_USB_MODULES=m
+endif
+
+# The Bluetooth compatibility only builds on kernels >= 2.6.27 for now
+ifndef CONFIG_COMPAT_KERNEL_2_6_27
+ifeq ($(CONFIG_BT),y)
+# we'll ignore compiling bluetooth
+else
+ CONFIG_COMPAT_BLUETOOTH=y
+ CONFIG_COMPAT_BLUETOOTH_MODULES=m
+endif
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+#
+# CONFIG_COMPAT_FIRMWARE_CLASS definition has no leading whitespace,
+# because it gets passed-on through compat_autoconf.h.
+#
+ifdef CONFIG_COMPAT_KERNEL_2_6_33
+ifndef CONFIG_COMPAT_RHEL_6_1
+ifdef CONFIG_FW_LOADER
+CONFIG_COMPAT_FIRMWARE_CLASS=m
+endif #CONFIG_FW_LOADER
+endif #CONFIG_COMPAT_RHEL_6_1
+endif #CONFIG_COMPAT_KERNEL_2_6_33
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_36
+ifndef CONFIG_COMPAT_RHEL_6_1
+ CONFIG_COMPAT_KFIFO=y
+endif #CONFIG_COMPAT_RHEL_6_1
+endif #CONFIG_COMPAT_KERNEL_2_6_36
+
+#
+# CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN definitions have no leading
+# whitespace, because they get passed-on through compat_autoconf.h.
+#
+ifndef CONFIG_COMPAT_KERNEL_2_6_33
+CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN=y
+endif #CONFIG_COMPAT_KERNEL_2_6_33
+ifdef CONFIG_COMPAT_RHEL_6_0
+CONFIG_COMPAT_BT_SOCK_CREATE_NEEDS_KERN=y
+endif #CONFIG_COMPAT_RHEL_6_0
+
+#
+# CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP definition has no leading
+# whitespace, because it gets passed-on through compat_autoconf.h.
+#
+ifdef CONFIG_COMPAT_RHEL_6_0
+CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP=y
+endif #CONFIG_COMPAT_RHEL_6_0
+
+# Wireless subsystem stuff
+CONFIG_MAC80211=m
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_33
+CONFIG_MAC80211_DRIVER_API_TRACER=y
+endif #CONFIG_COMPAT_KERNEL_2_6_33
+
+# CONFIG_MAC80211_DEBUGFS=y
+# CONFIG_MAC80211_NOINLINE=y
+# CONFIG_MAC80211_VERBOSE_DEBUG=y
+# CONFIG_MAC80211_HT_DEBUG=y
+# CONFIG_MAC80211_TKIP_DEBUG=y
+# CONFIG_MAC80211_IBSS_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_PS_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_MPL_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_TDLS_DEBUG
+# CONFIG_MAC80211_DEBUG_COUNTERS=y
+
+# choose between pid and minstrel as default rate control algorithm
+CONFIG_MAC80211_RC_DEFAULT=minstrel_ht
+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
+# CONFIG_MAC80211_RC_DEFAULT_PID=y
+# This is the one used by our compat-wireless net/mac80211/rate.c
+# in case you have and old kernel which is overriding this to pid.
+CONFIG_COMPAT_MAC80211_RC_DEFAULT=minstrel_ht
+CONFIG_MAC80211_RC_PID=y
+CONFIG_MAC80211_RC_MINSTREL=y
+CONFIG_MAC80211_RC_MINSTREL_HT=y
+ifdef CONFIG_LEDS_TRIGGERS
+CONFIG_MAC80211_LEDS=y
+endif #CONFIG_LEDS_TRIGGERS
+
+# enable mesh networking too
+CONFIG_MAC80211_MESH=y
+
+CONFIG_CFG80211=m
+CONFIG_CFG80211_DEFAULT_PS=y
+# CONFIG_CFG80211_DEBUGFS=y
+CONFIG_NL80211_TESTMODE=y
+# CONFIG_CFG80211_DEVELOPER_WARNINGS=y
+# CONFIG_CFG80211_REG_DEBUG=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
+# See below for wext stuff
+
+CONFIG_LIB80211=m
+CONFIG_LIB80211_CRYPT_WEP=m
+CONFIG_LIB80211_CRYPT_CCMP=m
+CONFIG_LIB80211_CRYPT_TKIP=m
+# CONFIG_LIB80211_DEBUG=y
+
+CONFIG_BT=m
+CONFIG_COMPAT_BT_L2CAP=y
+CONFIG_COMPAT_BT_SCO=y
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+# CONFIG_BT_CMTP depends on ISDN_CAPI
+ifdef CONFIG_ISDN_CAPI
+CONFIG_BT_CMTP=m
+endif #CONFIG_ISDN_CAPI
+ifndef CONFIG_COMPAT_KERNEL_2_6_28
+CONFIG_COMPAT_BT_HIDP=m
+endif #CONFIG_COMPAT_KERNEL_2_6_28
+
+CONFIG_BT_HCIUART=M
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_ATH3K=y
+CONFIG_BT_HCIUART_LL=y
+
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_MRVL=m
+
+ifdef CONFIG_PCMCIA
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+endif #CONFIG_PCMCIA
+
+
+# We need CONFIG_WIRELESS_EXT for CONFIG_CFG80211_WEXT for every kernel 
+# version. The new way CONFIG_CFG80211_WEXT is called from the kernel 
+# does not work with compat-wireless because it calls some callback 
+# function on struct wiphy. This struct is shipped with compat-wireless 
+# and changes from kernel version to version. We are using the 
+# wireless_handlers attribute which will be activated by 
+# CONFIG_WIRELESS_EXT. 
+ifdef CONFIG_WIRELESS_EXT
+CONFIG_CFG80211_WEXT=y
+else #CONFIG_CFG80211_WEXT
+$(warning "WARNING: CONFIG_CFG80211_WEXT will be deactivated or not working because kernel was compiled with CONFIG_WIRELESS_EXT=n. Tools using wext interface like iwconfig will not work. To activate it build your kernel e.g. with CONFIG_LIBIPW=m.")
+endif #CONFIG_WIRELESS_EXT
+
+ifdef CONFIG_STAGING
+CONFIG_COMPAT_STAGING=m
+endif #CONFIG_STAGING
+
+# mac80211 test driver
+CONFIG_MAC80211_HWSIM=m
+
+CONFIG_ATH5K=m
+# CONFIG_ATH5K_DEBUG=y
+# CONFIG_ATH5K_TRACER=y
+# CONFIG_ATH5K_AHB=y
+
+CONFIG_ATH9K=m
+CONFIG_ATH9K_HW=m
+CONFIG_ATH9K_COMMON=m
+# CONFIG_ATH9K_DEBUGFS=y
+# CONFIG_ATH9K_AHB=y
+# CONFIG_ATH9K_PKTLOG=y
+
+# Disable this to get minstrel as default, we leave the ath9k
+# rate control algorithm as the default for now as that is also
+# default upstream on the kernel. We will move this to minstrel
+# as default once we get minstrel properly tested and blessed by
+# our systems engineering team. CCK rates also need to be used
+# for long range considerations.
+CONFIG_ATH9K_RATE_CONTROL=y
+
+# PCI Drivers
+ifdef CONFIG_PCI
+
+CONFIG_ATH5K_PCI=y
+CONFIG_ATH9K_PCI=y
+
+CONFIG_IWLWIFI=m
+# CONFIG_IWLWIFI_DEBUG=y
+# CONFIG_IWLWIFI_DEBUGFS=y
+# CONFIG_IWLWIFI_DEVICE_TRACING=y
+# CONFIG_IWLWIFI_DEVICE_SVTOOL=y
+# CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE=y
+
+CONFIG_IWLEGACY=m
+CONFIG_COMPAT_IWL4965=m
+CONFIG_IWL3945=m
+# CONFIG_IWLEGACY_DEBUG=y
+# CONFIG_IWLEGACY_DEBUGFS=y
+
+
+CONFIG_B43=m
+CONFIG_B43_HWRNG=y
+CONFIG_B43_PCI_AUTOSELECT=y
+ifdef CONFIG_PCMCIA
+CONFIG_B43_PCMCIA=y
+endif #CONFIG_PCMCIA
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_B43_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+CONFIG_B43_PHY_LP=y
+CONFIG_B43_PHY_N=y
+CONFIG_B43_PHY_HT=y
+# CONFIG_B43_PHY_LCN=y
+# CONFIG_B43_FORCE_PIO=y
+# CONFIG_B43_DEBUG=y
+
+CONFIG_B43LEGACY=m
+CONFIG_B43LEGACY_HWRNG=y
+CONFIG_B43LEGACY_PCI_AUTOSELECT=y
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_B43LEGACY_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+# CONFIG_B43LEGACY_DEBUG=y
+CONFIG_B43LEGACY_DMA=y
+CONFIG_B43LEGACY_PIO=y
+
+ifdef CONFIG_WIRELESS_EXT
+# The Intel ipws
+CONFIG_LIBIPW=m
+# CONFIG_LIBIPW_DEBUG=y
+
+CONFIG_IPW2100=m
+CONFIG_IPW2100_MONITOR=y
+# CONFIG_IPW2100_DEBUG=y
+CONFIG_IPW2200=m
+CONFIG_IPW2200_MONITOR=y
+CONFIG_IPW2200_RADIOTAP=y
+CONFIG_IPW2200_PROMISCUOUS=y
+CONFIG_IPW2200_QOS=y
+# CONFIG_IPW2200_DEBUG=y
+# The above enables use a second interface prefixed 'rtap'.
+#           Example usage:
+#
+# % modprobe ipw2200 rtap_iface=1
+# % ifconfig rtap0 up
+# % tethereal -i rtap0
+#
+# If you do not specify 'rtap_iface=1' as a module parameter then
+# the rtap interface will not be created and you will need to turn
+# it on via sysfs:
+#
+# % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface
+endif #CONFIG_WIRELESS_EXT
+
+ifdef CONFIG_SSB
+# Sonics Silicon Backplane
+CONFIG_SSB_SPROM=y
+
+CONFIG_SSB_BLOCKIO=y
+CONFIG_SSB_PCIHOST=y
+CONFIG_SSB_B43_PCI_BRIDGE=y
+ifdef CONFIG_PCMCIA
+CONFIG_SSB_PCMCIAHOST=y
+endif #CONFIG_PCMCIA
+# CONFIG_SSB_DEBUG=y
+CONFIG_SSB_DRIVER_PCICORE=y
+CONFIG_B43_SSB=y
+endif #CONFIG_SSB
+
+CONFIG_BCMA=m
+CONFIG_BCMA_BLOCKIO=y
+CONFIG_BCMA_HOST_PCI=y
+# CONFIG_BCMA_DEBUG=y
+CONFIG_B43_BCMA=y
+CONFIG_B43_BCMA_PIO=y
+
+CONFIG_P54_PCI=m
+
+CONFIG_B44=m
+CONFIG_B44_PCI=y
+
+CONFIG_RTL8180=m
+
+CONFIG_ADM8211=m
+
+CONFIG_RT2X00_LIB_PCI=m
+CONFIG_RT2400PCI=m
+CONFIG_RT2500PCI=m
+ifdef CONFIG_CRC_CCITT
+CONFIG_RT2800PCI=m
+CONFIG_RT2800PCI_RT33XX=y
+CONFIG_RT2800PCI_RT35XX=y
+# CONFIG_RT2800PCI_RT53XX=y
+endif #CONFIG_CRC_CCITT
+NEED_RT2X00=y
+
+# Two rt2x00 drivers require firmware: rt61pci and rt73usb. They depend on
+# CRC to check the firmware. We check here first for the PCI
+# driver as we're in the PCI section.
+ifdef CONFIG_CRC_ITU_T
+CONFIG_RT61PCI=m
+endif #CONFIG_CRC_ITU_T
+
+CONFIG_MWL8K=m
+
+# Ethernet drivers go here
+CONFIG_ATL1=m
+CONFIG_ATL2=m
+CONFIG_ATL1E=m
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_ATL1C=m
+CONFIG_ALX=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_ATL1C=n
+CONFIG_ALX=m
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+ifdef CONFIG_WIRELESS_EXT
+CONFIG_HERMES=m
+CONFIG_HERMES_CACHE_FW_ON_INIT=y
+ifdef CONFIG_PPC_PMAC
+CONFIG_APPLE_AIRPORT=m
+endif #CONFIG_PPC_PMAC
+CONFIG_PLX_HERMES=m
+CONFIG_TMD_HERMES=m
+CONFIG_NORTEL_HERMES=m
+CONFIG_PCI_HERMES=m
+ifdef CONFIG_PCMCIA
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+endif #CONFIG_PCMCIA
+endif #CONFIG_WIRELESS_EXT
+
+CONFIG_RTL8192CE=m
+CONFIG_RTL8192SE=m
+CONFIG_RTL8192DE=m
+
+CONFIG_BRCMSMAC=m
+
+CONFIG_MWIFIEX_PCIE=m
+
+endif #CONFIG_PCI
+## end of PCI
+
+ifdef CONFIG_PCMCIA
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS=n
+CONFIG_LIBERTAS_CS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_CS=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+endif #CONFIG_PCMCIA
+## end of PCMCIA
+
+# This is required for some cards
+CONFIG_EEPROM_93CX6=m
+
+# USB Drivers
+ifdef CONFIG_USB
+ifndef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_COMPAT_ZD1211RW=m
+# CONFIG_ZD1211RW_DEBUG=y
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+# Sorry, rndis_wlan uses cancel_work_sync which is new and can't be done in compat...
+
+# Wireless RNDIS USB support (RTL8185 802.11g) A-Link WL54PC
+# All of these devices are based on Broadcom 4320 chip which
+# is only wireless RNDIS chip known to date.
+# Note: this depends on CONFIG_USB_NET_RNDIS_HOST and CONFIG_USB_NET_CDCETHER
+# it also requires new RNDIS_HOST and CDC_ETHER modules which we add
+ifdef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_USB_COMPAT_USBNET=n
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=n
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=n
+CONFIG_USB_NET_COMPAT_CDCETHER=n
+else #CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_USB_COMPAT_USBNET=m
+ifdef CONFIG_USB_NET_CDCETHER
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=m
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=m
+endif #CONFIG_USB_NET_CDCETHER
+ifdef CONFIG_USB_NET_CDCETHER_MODULE
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=m
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=m
+endif #CONFIG_USB_NET_CDCETHER
+CONFIG_USB_NET_COMPAT_CDCETHER=m
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+
+CONFIG_P54_USB=m
+CONFIG_RTL8187=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_RTL8187_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+
+CONFIG_AT76C50X_USB=m
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_CARL9170=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_CARL9170_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+# CONFIG_CARL9170_DEBUGFS=y
+CONFIG_CARL9170_WPC=y
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+# This activates a threading fix for usb urb.
+# this is mainline commit: b3e670443b7fb8a2d29831b62b44a039c283e351
+# This fix will be included in some stable releases.
+CONFIG_COMPAT_USB_URB_THREAD_FIX=y
+
+CONFIG_ATH9K_HTC=m
+# CONFIG_ATH9K_HTC_DEBUGFS=y
+
+# RT2500USB does not require firmware
+CONFIG_RT2500USB=m
+ifdef CONFIG_CRC_CCITT
+CONFIG_RT2800USB=m
+CONFIG_RT2800USB_RT33XX=y
+CONFIG_RT2800USB_RT35XX=y
+# CONFIG_RT2800USB_RT53XX=y
+CONFIG_RT2800USB_UNKNOWN=y
+endif #CONFIG_CRC_CCITT
+CONFIG_RT2X00_LIB_USB=m
+NEED_RT2X00=y
+# RT73USB requires firmware
+ifdef CONFIG_CRC_ITU_T
+CONFIG_RT73USB=m
+endif #CONFIG_CRC_ITU_T
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_THINFIRM_USB=n
+CONFIG_LIBERTAS_USB=n
+NEED_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_THINFIRM_USB=m
+CONFIG_LIBERTAS_USB=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+CONFIG_ORINOCO_USB=m
+
+CONFIG_BT_HCIBTUSB=m
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_ATH3K=m
+
+CONFIG_RTL8192CU=m
+
+endif #CONFIG_USB end of USB driver list
+
+ifdef CONFIG_SPI_MASTER
+ifndef CONFIG_COMPAT_KERNEL_2_6_25
+
+ifdef CONFIG_CRC7
+CONFIG_WL1251_SPI=m
+CONFIG_WL12XX_SPI=m
+endif #CONFIG_CRC7
+CONFIG_P54_SPI=m
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SPI=n
+NEED_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SPI=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+endif #CONFIG_COMPAT_KERNEL_2_6_25
+endif #CONFIG_SPI_MASTER end of SPI driver list
+
+ifdef CONFIG_MMC
+
+CONFIG_SSB_SDIOHOST=y
+CONFIG_B43_SDIO=y
+
+ifdef CONFIG_CRC7
+ifdef CONFIG_WL12XX_PLATFORM_DATA
+CONFIG_COMPAT_WL1251_SDIO=m
+endif #CONFIG_WL12XX_PLATFORM_DATA
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_32
+ifdef CONFIG_WL12XX_PLATFORM_DATA
+CONFIG_COMPAT_WL12XX_SDIO=m
+endif #CONFIG_WL12XX_PLATFORM_DATA
+endif #CONFIG_COMPAT_KERNEL_2_6_32
+
+endif #CONFIG_CRC7
+
+CONFIG_MWIFIEX_SDIO=m
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_32
+CONFIG_COMPAT_LIBERTAS_SDIO=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_32
+
+CONFIG_IWM=m
+# CONFIG_IWM_DEBUG=y
+# CONFIG_IWM_TRACING=y
+
+CONFIG_BT_HCIBTSDIO=m
+CONFIG_BT_MRVL_SDIO=m
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_SDIO=m
+CONFIG_ATH6KL_USB=m
+CONFIG_ATH6KL_DEBUG=y
+CONFIG_ATH6KL_REGDOMAIN=y
+CONFIG_SUPPORT_11W=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_BRCMFMAC=m
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+endif #CONFIG_MMC
+
+CONFIG_RTLWIFI=m
+CONFIG_RTL8192C_COMMON=m
+
+# Common rt2x00 requirements
+ifeq ($(NEED_RT2X00),y)
+CONFIG_RT2X00=y
+CONFIG_RT2X00_LIB=m
+CONFIG_RT2800_LIB=m
+CONFIG_RT2X00_LIB_FIRMWARE=y
+CONFIG_RT2X00_LIB_CRYPTO=y
+# CONFIG_RT2X00_LIB_SOC=y
+ifdef CONFIG_COMPAT_KERNEL_2_6_25
+CONFIG_RT2X00_LIB_LEDS=n
+else #CONFIG_COMPAT_KERNEL_2_6_25
+ifdef CONFIG_LEDS_CLASS
+CONFIG_RT2X00_LIB_LEDS=y
+endif #CONFIG_LEDS_CLASS
+endif #CONFIG_COMPAT_KERNEL_2_6_25
+# CONFIG_RT2X00_DEBUG=y
+# CONFIG_RT2X00_LIB_DEBUGFS
+endif
+
+# p54
+CONFIG_P54_COMMON=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_P54_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+
+# Atheros
+CONFIG_ATH_COMMON=m
+# CONFIG_ATH_DEBUG=y
+
+CONFIG_BRCMUTIL=m
+# CONFIG_BRCMDBG=y
+
+ifdef CONFIG_CRC7
+CONFIG_WL1251=m
+CONFIG_WL12XX=m
+endif #CONFIG_CRC7
+
+CONFIG_MWIFIEX=m
+
+ifndef CONFIG_CORDIC
+CONFIG_COMPAT_CORDIC=y
+endif #CONFIG_CORDIC
+
+ifndef CONFIG_CRC8
+CONFIG_COMPAT_CRC8=y
+endif #CONFIG_CRC8
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+ifeq ($(NEED_LIBERTAS),y)
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_MESH=y
+# CONFIG_LIBERTAS_DEBUG=y
+endif
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+# We need the backported rfkill module on kernel < 2.6.31.
+# In more recent kernel versions use the in kernel rfkill module.
+ifdef CONFIG_COMPAT_KERNEL_2_6_31
+CONFIG_RFKILL_BACKPORT=m
+ifdef CONFIG_LEDS_TRIGGERS
+CONFIG_RFKILL_BACKPORT_LEDS=y
+endif #CONFIG_LEDS_TRIGGERS
+CONFIG_RFKILL_BACKPORT_INPUT=y
+endif #CONFIG_COMPAT_KERNEL_2_6_31
+
diff --git a/crap/0002-ath9k-Add-pktlog-support.patch b/crap/0002-ath9k-Add-pktlog-support.patch
new file mode 100644
index 0000000..67cf684
--- /dev/null
+++ b/crap/0002-ath9k-Add-pktlog-support.patch
@@ -0,0 +1,1411 @@
+Reason for not yet publishing: This code needs more testing and
+enhancements.
+
+From 067eeff8bf0ddb90ea77bf088f924c2a165a98d1 Mon Sep 17 00:00:00 2001
+From: Vasanthakumar Thiagarajan <vasanth@atheros.com>
+Date: Wed, 14 Apr 2010 11:36:44 -0700
+Subject: [PATCH 2/3] ath9k: Add pktlog support
+
+This adds packet log support for all of the supported
+Atheros hardware families under ath9k, AR5008, AR9001, AR9002
+and AR9003. Packet log is used to extract specific descriptor
+and rate control data into a binary file parsed for analysis
+by our systems team.
+
+Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
+---
+ drivers/net/wireless/ath/ath9k/Kconfig      |    8 +
+ drivers/net/wireless/ath/ath9k/Makefile     |    1 +
+ drivers/net/wireless/ath/ath9k/ar9002_mac.c |    3 +-
+ drivers/net/wireless/ath/ath9k/ar9003_mac.c |    4 +-
+ drivers/net/wireless/ath/ath9k/ath9k.h      |    6 +
+ drivers/net/wireless/ath/ath9k/debug.c      |    4 +
+ drivers/net/wireless/ath/ath9k/hw-ops.h     |    5 +-
+ drivers/net/wireless/ath/ath9k/hw.c         |    2 +-
+ drivers/net/wireless/ath/ath9k/hw.h         |    4 +-
+ drivers/net/wireless/ath/ath9k/pktlog.c     |  783 +++++++++++++++++++++++++++
+ drivers/net/wireless/ath/ath9k/pktlog.h     |  242 +++++++++
+ drivers/net/wireless/ath/ath9k/rc.c         |   22 +-
+ drivers/net/wireless/ath/ath9k/recv.c       |   15 +-
+ drivers/net/wireless/ath/ath9k/xmit.c       |   24 +-
+ 14 files changed, 1103 insertions(+), 20 deletions(-)
+ create mode 100644 drivers/net/wireless/ath/ath9k/pktlog.c
+ create mode 100644 drivers/net/wireless/ath/ath9k/pktlog.h
+
+--- a/drivers/net/wireless/ath/ath9k/Kconfig
++++ b/drivers/net/wireless/ath/ath9k/Kconfig
+@@ -40,6 +40,13 @@ config ATH9K_RATE_CONTROL
+ 	  Say Y, if you want to use the ath9k specific rate control
+ 	  module instead of minstrel_ht.
+ 
++config ATH9K_PKTLOG
++	bool "ath9k packet logging support"
++	depends on ATH9K_DEBUGFS
++	---help---
++	Say Y to dump frame information during tx/rx, rate information
++	and ani state.
++
+ config ATH9K_HTC
+        tristate "Atheros HTC based wireless cards support"
+        depends on USB && MAC80211
+@@ -61,3 +68,4 @@ config ATH9K_HTC_DEBUGFS
+ 	depends on ATH9K_HTC && DEBUG_FS
+ 	---help---
+ 	  Say Y, if you need access to ath9k_htc's statistics.
++
+--- a/drivers/net/wireless/ath/ath9k/Makefile
++++ b/drivers/net/wireless/ath/ath9k/Makefile
+@@ -9,6 +9,7 @@ ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc
+ ath9k-$(CONFIG_PCI) += pci.o
+ ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o
+ ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
++ath9k-$(CONFIG_ATH9K_PKTLOG) += pktlog.o
+ 
+ obj-$(CONFIG_ATH9K) += ath9k.o
+ 
+--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
++++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+@@ -205,7 +205,8 @@ static void ar9002_hw_fill_txdesc(struct
+ }
+ 
+ static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
+-				 struct ath_tx_status *ts)
++				 struct ath_tx_status *ts,
++				 void *txs_desc)
+ {
+ 	struct ar5416_desc *ads = AR5416DESC(ds);
+ 	u32 status;
+--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+@@ -234,7 +234,8 @@ static void ar9003_hw_fill_txdesc(struct
+ }
+ 
+ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
+-				 struct ath_tx_status *ts)
++				 struct ath_tx_status *ts,
++				 void *txs_desc)
+ {
+ 	struct ar9003_txs *ads;
+ 	u32 status;
+@@ -308,6 +309,7 @@ static int ar9003_hw_proc_txdesc(struct
+ 	ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11);
+ 	ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12);
+ 
++	memcpy(txs_desc, ads, sizeof(*ads));
+ 	memset(ads, 0, sizeof(*ads));
+ 
+ 	return 0;
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -24,6 +24,7 @@
+ 
+ #include "debug.h"
+ #include "common.h"
++#include "pktlog.h"
+ 
+ /*
+  * Header for the ath9k.ko driver core *only* -- hw code nor any other driver
+@@ -550,6 +551,7 @@ struct ath_ant_comb {
+ #define SC_OP_BT_SCAN		     BIT(13)
+ #define SC_OP_ANI_RUN		     BIT(14)
+ #define SC_OP_ENABLE_APM	     BIT(15)
++#define SC_OP_PKTLOGGING	     BIT(16)
+ 
+ /* Powersave flags */
+ #define PS_WAIT_FOR_BEACON        BIT(0)
+@@ -629,6 +631,10 @@ struct ath_softc {
+ 	struct list_head nodes; /* basically, stations */
+ 	unsigned int tx_complete_poll_work_seen;
+ #endif
++#ifdef CONFIG_ATH9K_PKTLOG
++	struct ath_pktlog_debugfs pktlog;
++#endif
++	bool is_pkt_logging;
+ 	struct ath_beacon_config cur_beacon_conf;
+ 	struct delayed_work tx_complete_work;
+ 	struct delayed_work hw_pll_work;
+--- a/drivers/net/wireless/ath/ath9k/debug.c
++++ b/drivers/net/wireless/ath/ath9k/debug.c
+@@ -1066,6 +1066,9 @@ static int open_file_regdump(struct inod
+ 
+ 	file->private_data = buf;
+ 
++	if (ath9k_init_pktlog(sc) != 0)
++		return -EINVAL;
++
+ 	return 0;
+ }
+ 
+@@ -1148,6 +1151,7 @@ int ath9k_init_debug(struct ath_hw *ah)
+ 	sc->debug.regidx = 0;
+ 	return 0;
+ err:
++	ath9k_deinit_pktlog(sc);
+ 	debugfs_remove_recursive(sc->debug.debugfs_phy);
+ 	sc->debug.debugfs_phy = NULL;
+ 	return -ENOMEM;
+--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
++++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
+@@ -67,9 +67,10 @@ static inline void ath9k_hw_filltxdesc(s
+ }
+ 
+ static inline int ath9k_hw_txprocdesc(struct ath_hw *ah, void *ds,
+-				      struct ath_tx_status *ts)
++				      struct ath_tx_status *ts,
++				      void *txs_desc)
+ {
+-	return ath9k_hw_ops(ah)->proc_txdesc(ah, ds, ts);
++	return ath9k_hw_ops(ah)->proc_txdesc(ah, ds, ts, txs_desc);
+ }
+ 
+ static inline void ath9k_hw_set11n_txdesc(struct ath_hw *ah, void *ds,
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -2192,7 +2192,7 @@ void ath9k_hw_setrxfilter(struct ath_hw
+ 		phybits |= AR_PHY_ERR_RADAR;
+ 	if (bits & ATH9K_RX_FILTER_PHYERR)
+ 		phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING;
+-	REG_WRITE(ah, AR_PHY_ERR, phybits);
++	REG_WRITE(ah, AR_PHY_ERR, 0xffffffff);
+ 
+ 	if (phybits)
+ 		REG_WRITE(ah, AR_RXCFG,
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -620,7 +620,7 @@ struct ath_hw_ops {
+ 			    const void *ds0, dma_addr_t buf_addr,
+ 			    unsigned int qcu);
+ 	int (*proc_txdesc)(struct ath_hw *ah, void *ds,
+-			   struct ath_tx_status *ts);
++			   struct ath_tx_status *ts, void* txs_desc);
+ 	void (*set11n_txdesc)(struct ath_hw *ah, void *ds,
+ 			      u32 pktLen, enum ath9k_pkt_type type,
+ 			      u32 txPower, u32 keyIx,
+@@ -856,6 +856,8 @@ struct ath_hw {
+ 
+ 	/* Enterprise mode cap */
+ 	u32 ent_mode;
++
++	bool is_pkt_logging;
+ };
+ 
+ static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah)
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath9k/pktlog.c
+@@ -0,0 +1,783 @@
++
++#include <linux/vmalloc.h>
++#include <linux/highmem.h>
++#include "ath9k.h"
++
++static int ath9k_debugfs_open(struct inode *inode, struct file *file)
++{
++	file->private_data = inode->i_private;
++	return 0;
++}
++
++static struct page *pktlog_virt_to_logical(void *addr)
++{
++	struct page *page;
++	unsigned long vpage = 0UL;
++
++	page = vmalloc_to_page(addr);
++	if (page) {
++		vpage = (unsigned long) page_address(page);
++		vpage |= ((unsigned long) addr & (PAGE_SIZE - 1));
++	}
++	return virt_to_page((void *) vpage);
++}
++
++static void ath_pktlog_release(struct ath_pktlog *pktlog)
++{
++	unsigned long page_cnt, vaddr;
++	struct page *page;
++
++	page_cnt =
++		((sizeof(*(pktlog->pktlog_buf)) +
++		pktlog->pktlog_buf_size) / PAGE_SIZE) + 1;
++
++	for (vaddr = (unsigned long) (pktlog->pktlog_buf); vaddr <
++			(unsigned long) (pktlog->pktlog_buf) +
++			(page_cnt * PAGE_SIZE);
++			vaddr += PAGE_SIZE) {
++		page = pktlog_virt_to_logical((void *) vaddr);
++		clear_bit(PG_reserved, &page->flags);
++	}
++
++	vfree(pktlog->pktlog_buf);
++	pktlog->pktlog_buf = NULL;
++}
++
++static int ath_alloc_pktlog_buf(struct ath_softc *sc)
++{
++	u32 page_cnt;
++	unsigned long vaddr;
++	struct page *page;
++	struct ath_pktlog *pktlog = &sc->pktlog.pktlog;
++
++	if (pktlog->pktlog_buf_size == 0)
++		return -EINVAL;
++
++	page_cnt = (sizeof(*(pktlog->pktlog_buf)) +
++		    pktlog->pktlog_buf_size) / PAGE_SIZE;
++
++	pktlog->pktlog_buf =  vmalloc((page_cnt + 2) * PAGE_SIZE);
++	if (pktlog->pktlog_buf == NULL) {
++		printk(KERN_ERR "Failed to allocate memory  for pktlog");
++		return -ENOMEM;
++	}
++
++	pktlog->pktlog_buf = (struct ath_pktlog_buf *)
++				     (((unsigned long)
++				      (pktlog->pktlog_buf)
++				     + PAGE_SIZE - 1) & PAGE_MASK);
++
++	for (vaddr = (unsigned long) (pktlog->pktlog_buf);
++		      vaddr < ((unsigned long) (pktlog->pktlog_buf)
++		      + (page_cnt * PAGE_SIZE)); vaddr += PAGE_SIZE) {
++		page = pktlog_virt_to_logical((void *)vaddr);
++		set_bit(PG_reserved, &page->flags);
++	}
++
++	return 0;
++}
++
++static void ath_init_pktlog_buf(struct ath_pktlog *pktlog)
++{
++	pktlog->pktlog_buf->bufhdr.magic_num = PKTLOG_MAGIC_NUM;
++	pktlog->pktlog_buf->bufhdr.version = CUR_PKTLOG_VER;
++	pktlog->pktlog_buf->rd_offset = -1;
++	pktlog->pktlog_buf->wr_offset = 0;
++	if (pktlog->pktlog_filter == 0)
++		pktlog->pktlog_filter = ATH_PKTLOG_FILTER_DEFAULT;
++}
++
++static char *ath_pktlog_getbuf(struct ath_pktlog *pl_info,
++			       u16 log_type, size_t log_size,
++		               u32 flags)
++{
++	struct ath_pktlog_buf *log_buf;
++	struct ath_pktlog_hdr *log_hdr;
++	int32_t cur_wr_offset, buf_size;
++	char *log_ptr;
++
++	log_buf = pl_info->pktlog_buf;
++	buf_size = pl_info->pktlog_buf_size;
++
++	spin_lock_bh(&pl_info->pktlog_lock);
++	cur_wr_offset = log_buf->wr_offset;
++	/* Move read offset to the next entry if there is a buffer overlap */
++	if (log_buf->rd_offset >= 0) {
++		if ((cur_wr_offset <= log_buf->rd_offset)
++				&& (cur_wr_offset +
++				sizeof(struct ath_pktlog_hdr)) >
++				log_buf->rd_offset)
++			PKTLOG_MOV_RD_IDX(log_buf->rd_offset, log_buf,
++					  buf_size);
++	} else {
++		log_buf->rd_offset = cur_wr_offset;
++	}
++
++	log_hdr =
++		(struct ath_pktlog_hdr *) (log_buf->log_data + cur_wr_offset);
++	log_hdr->log_type = log_type;
++	log_hdr->flags = flags;
++	log_hdr->timestamp = jiffies;
++	log_hdr->size = (u16) log_size;
++
++	cur_wr_offset += sizeof(*log_hdr);
++
++	if ((buf_size - cur_wr_offset) < log_size) {
++		while ((cur_wr_offset <= log_buf->rd_offset)
++				&& (log_buf->rd_offset < buf_size))
++			PKTLOG_MOV_RD_IDX(log_buf->rd_offset, log_buf,
++					  buf_size);
++		cur_wr_offset = 0;
++	}
++
++	while ((cur_wr_offset <= log_buf->rd_offset)
++			&& (cur_wr_offset + log_size) > log_buf->rd_offset)
++		PKTLOG_MOV_RD_IDX(log_buf->rd_offset, log_buf, buf_size);
++
++	log_ptr = &(log_buf->log_data[cur_wr_offset]);
++
++	cur_wr_offset += log_hdr->size;
++
++	log_buf->wr_offset =
++		((buf_size - cur_wr_offset) >=
++		 sizeof(struct ath_pktlog_hdr)) ? cur_wr_offset : 0;
++	spin_unlock_bh(&pl_info->pktlog_lock);
++
++	return log_ptr;
++}
++
++static void ath9k_hw_get_descinfo(struct ath_hw *ah, struct ath_desc_info *desc_info)
++{
++       desc_info->txctl_numwords = TXCTL_NUMWORDS(ah);
++       desc_info->txctl_offset = TXCTL_OFFSET(ah);
++       desc_info->txstatus_numwords = TXSTATUS_NUMWORDS(ah);
++       desc_info->txstatus_offset = TXSTATUS_OFFSET(ah);
++
++       desc_info->rxctl_numwords = RXCTL_NUMWORDS(ah);
++       desc_info->rxctl_offset = RXCTL_OFFSET(ah);
++       desc_info->rxstatus_numwords = RXSTATUS_NUMWORDS(ah);
++       desc_info->rxstatus_offset = RXSTATUS_OFFSET(ah);
++}
++
++static int  pktlog_pgfault(struct vm_area_struct *vma, struct vm_fault *vmf)
++{
++	unsigned long address = (unsigned long) vmf->virtual_address;
++
++	if (address == 0UL)
++		return VM_FAULT_NOPAGE;
++
++	if (vmf->pgoff > vma->vm_end)
++		return VM_FAULT_SIGBUS;
++
++	get_page(virt_to_page(address));
++	vmf->page = virt_to_page(address);
++	return VM_FAULT_MINOR;
++}
++
++static struct vm_operations_struct pktlog_vmops = {
++	.fault = pktlog_pgfault
++};
++
++static int ath_pktlog_mmap(struct file *file, struct vm_area_struct *vma)
++{
++	struct ath_softc *sc = file->private_data;
++
++	/* entire buffer should be mapped */
++	if (vma->vm_pgoff != 0)
++		return -EINVAL;
++
++	if (!sc->pktlog.pktlog.pktlog_buf) {
++		printk(KERN_ERR "Can't allocate pktlog buf");
++		return -ENOMEM;
++	}
++
++	vma->vm_flags |= VM_LOCKED;
++	vma->vm_ops = &pktlog_vmops;
++
++	return 0;
++}
++
++static ssize_t ath_pktlog_read(struct file *file, char __user *userbuf,
++			       size_t count, loff_t *ppos)
++{
++	size_t bufhdr_size;
++	size_t nbytes = 0, ret_val = 0;
++	int rem_len;
++	int start_offset, end_offset;
++	int fold_offset, ppos_data, cur_rd_offset;
++	struct ath_softc *sc = file->private_data;
++	struct ath_pktlog *pktlog_info = &sc->pktlog.pktlog;
++	struct ath_pktlog_buf *log_buf = pktlog_info->pktlog_buf;
++
++	if (log_buf == NULL)
++		return 0;
++
++	bufhdr_size = sizeof(log_buf->bufhdr);
++
++	/* copy valid log entries from circular buffer into user space */
++	rem_len = count;
++
++	nbytes = 0;
++
++	if (*ppos < bufhdr_size) {
++		nbytes = min((int) (bufhdr_size -  *ppos), rem_len);
++		if (copy_to_user(userbuf,
++		    ((char *) &log_buf->bufhdr) + *ppos, nbytes))
++			return -EFAULT;
++		rem_len -= nbytes;
++		ret_val += nbytes;
++	}
++
++	start_offset = log_buf->rd_offset;
++
++	if ((rem_len == 0) || (start_offset < 0))
++		goto read_done;
++
++	fold_offset = -1;
++	cur_rd_offset = start_offset;
++
++	/* Find the last offset and fold-offset if the buffer is folded */
++	do {
++		struct ath_pktlog_hdr *log_hdr;
++		int log_data_offset;
++
++		log_hdr =
++			(struct ath_pktlog_hdr *) (log_buf->log_data +
++							cur_rd_offset);
++
++		log_data_offset = cur_rd_offset + sizeof(struct ath_pktlog_hdr);
++
++		if ((fold_offset == -1)
++				&& ((pktlog_info->pktlog_buf_size -
++				    log_data_offset) <= log_hdr->size))
++			fold_offset = log_data_offset - 1;
++
++		PKTLOG_MOV_RD_IDX(cur_rd_offset, log_buf,
++				  pktlog_info->pktlog_buf_size);
++
++		if ((fold_offset == -1) && (cur_rd_offset == 0)
++				&& (cur_rd_offset != log_buf->wr_offset))
++			fold_offset = log_data_offset + log_hdr->size - 1;
++
++		end_offset = log_data_offset + log_hdr->size - 1;
++	} while (cur_rd_offset != log_buf->wr_offset);
++
++	ppos_data = *ppos + ret_val - bufhdr_size + start_offset;
++
++	if (fold_offset == -1) {
++		if (ppos_data > end_offset)
++			goto read_done;
++
++		nbytes = min(rem_len, end_offset - ppos_data + 1);
++		if (copy_to_user(userbuf + ret_val,
++				 log_buf->log_data + ppos_data, nbytes))
++			return -EFAULT;
++		ret_val += nbytes;
++		rem_len -= nbytes;
++	} else {
++		if (ppos_data <= fold_offset) {
++			nbytes = min(rem_len, fold_offset - ppos_data + 1);
++			if (copy_to_user(userbuf + ret_val,
++						log_buf->log_data + ppos_data,
++						nbytes))
++				return -EFAULT;
++			ret_val += nbytes;
++			rem_len -= nbytes;
++		}
++
++		if (rem_len == 0)
++			goto read_done;
++
++		ppos_data =
++			*ppos + ret_val - (bufhdr_size +
++					(fold_offset - start_offset + 1));
++
++		if (ppos_data <= end_offset) {
++			nbytes = min(rem_len, end_offset - ppos_data + 1);
++			if (copy_to_user(userbuf + ret_val, log_buf->log_data
++					 + ppos_data,
++					 nbytes))
++				return -EFAULT;
++			ret_val += nbytes;
++			rem_len -= nbytes;
++		}
++	}
++
++read_done:
++		*ppos += ret_val;
++
++		return ret_val;
++}
++
++static const struct file_operations fops_pktlog_dump = {
++	.read = ath_pktlog_read,
++	.mmap = ath_pktlog_mmap,
++	.open = ath9k_debugfs_open
++};
++
++static ssize_t write_pktlog_start(struct file *file, const char __user *ubuf,
++				   size_t count, loff_t *ppos)
++{
++	struct ath_softc *sc = file->private_data;
++	struct ath_pktlog *pktlog = &sc->pktlog.pktlog;
++	char buf[32];
++	int buf_size;
++	int start_pktlog, err;
++
++	buf_size = min(count, sizeof(buf) - 1);
++	if (copy_from_user(buf, ubuf, buf_size))
++		return -EFAULT;
++
++	sscanf(buf, "%d", &start_pktlog);
++	if (start_pktlog) {
++		if (pktlog->pktlog_buf != NULL)
++			ath_pktlog_release(pktlog);
++
++		err = ath_alloc_pktlog_buf(sc);
++		if (err != 0)
++			return err;
++
++		ath_init_pktlog_buf(pktlog);
++		pktlog->pktlog_buf->rd_offset = -1;
++		pktlog->pktlog_buf->wr_offset = 0;
++		sc->is_pkt_logging = 1;
++	} else {
++		sc->is_pkt_logging = 0;
++	}
++
++	sc->sc_ah->is_pkt_logging = sc->is_pkt_logging;
++	return count;
++}
++
++static ssize_t read_pktlog_start(struct file *file, char __user *ubuf,
++				  size_t count, loff_t *ppos)
++{
++	char buf[32];
++	struct ath_softc *sc = file->private_data;
++	int len = 0;
++
++	len = scnprintf(buf, sizeof(buf) - len, "%d", sc->is_pkt_logging);
++	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
++}
++
++static const struct file_operations fops_pktlog_start = {
++	.read = read_pktlog_start,
++	.write = write_pktlog_start,
++	.open = ath9k_debugfs_open
++};
++
++static ssize_t pktlog_size_write(struct file *file, const char __user *ubuf,
++				 size_t count, loff_t *ppos)
++{
++	struct ath_softc *sc = file->private_data;
++	char buf[32];
++	u32 pktlog_size;
++	int buf_size;
++
++	buf_size = min(count, sizeof(buf) - 1);
++	if (copy_from_user(buf, ubuf, buf_size))
++		return -EFAULT;
++
++	sscanf(buf, "%d", &pktlog_size);
++
++	if (pktlog_size == sc->pktlog.pktlog.pktlog_buf_size)
++		return count;
++
++	if (sc->is_pkt_logging) {
++		printk(KERN_DEBUG "Stop packet logging before"
++			" changing the pktlog size \n");
++		return -EINVAL;
++	}
++
++	sc->pktlog.pktlog.pktlog_buf_size = pktlog_size;
++
++	return count;
++}
++
++static ssize_t pktlog_size_read(struct file *file, char __user *ubuf,
++				size_t count, loff_t *ppos)
++{
++	char buf[32];
++	struct ath_softc *sc = file->private_data;
++	int len = 0;
++
++	len = scnprintf(buf, sizeof(buf) - len, "%ul",
++			    sc->pktlog.pktlog.pktlog_buf_size);
++	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
++}
++
++static const struct file_operations fops_pktlog_size = {
++	.read = pktlog_size_read,
++	.write = pktlog_size_write,
++	.open = ath9k_debugfs_open
++};
++
++static ssize_t pktlog_filter_write(struct file *file, const char __user *ubuf,
++				   size_t count, loff_t *ppos)
++{
++	char buf[32];
++	struct ath_softc *sc = file->private_data;
++	u32 filter;
++	int buf_count;
++
++	buf_count = min(count, sizeof(buf) - 1);
++	if (copy_from_user(buf, ubuf, buf_count))
++		return -EFAULT;
++
++	if (sscanf(buf, "%x", &filter))
++		sc->pktlog.pktlog.pktlog_filter = filter;
++	else
++		sc->pktlog.pktlog.pktlog_filter = 0;
++
++	return count;
++}
++
++static ssize_t  pktlog_filter_read(struct file *file, char __user *ubuf,
++				   size_t count, loff_t *ppos)
++{
++	char buf[32];
++	struct ath_softc *sc = file->private_data;
++	int len = 0;
++
++	len = scnprintf(buf, sizeof(buf) - len, "%ul",
++			    sc->pktlog.pktlog.pktlog_filter);
++
++	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
++}
++
++static const struct file_operations fops_pktlog_filter = {
++	.read = pktlog_filter_read,
++	.write = pktlog_filter_write,
++	.open = ath9k_debugfs_open
++};
++
++void ath_pktlog_txctl(struct ath_softc *sc, struct ath_buf *bf)
++{
++	struct ath_pktlog_txctl *tx_log;
++	struct ath_pktlog *pl_info;
++	struct ieee80211_hdr *hdr;
++	struct ath_desc_info desc_info;
++	int i;
++	u32 *ds_words, flags = 0;
++
++	pl_info = &sc->pktlog.pktlog;
++
++	if ((pl_info->pktlog_filter & ATH_PKTLOG_TX) == 0 ||
++	    bf->bf_mpdu == NULL || !sc->is_pkt_logging)
++		return;
++
++	flags |= (((sc->sc_ah->hw_version.macRev <<
++			PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
++			((sc->sc_ah->hw_version.macVersion << PHFLAGS_MACVERSION_SFT)
++			& PHFLAGS_MACVERSION_MASK));
++
++	tx_log = (struct ath_pktlog_txctl *)ath_pktlog_getbuf(pl_info,
++			PKTLOG_TYPE_TXCTL, sizeof(*tx_log), flags);
++
++	memset(tx_log, 0, sizeof(*tx_log));
++
++	hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data;
++	tx_log->framectrl = hdr->frame_control;
++	tx_log->seqctrl   = hdr->seq_ctrl;
++
++	if (ieee80211_has_tods(tx_log->framectrl)) {
++		tx_log->bssid_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
++				     (hdr->addr1[ETH_ALEN - 1]);
++		tx_log->sa_tail    = (hdr->addr2[ETH_ALEN - 2] << 8) |
++				     (hdr->addr2[ETH_ALEN - 1]);
++		tx_log->da_tail    = (hdr->addr3[ETH_ALEN - 2] << 8) |
++				     (hdr->addr3[ETH_ALEN - 1]);
++	} else if (ieee80211_has_fromds(tx_log->framectrl)) {
++		tx_log->bssid_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
++				     (hdr->addr2[ETH_ALEN - 1]);
++		tx_log->sa_tail    = (hdr->addr3[ETH_ALEN - 2] << 8) |
++				     (hdr->addr3[ETH_ALEN - 1]);
++		tx_log->da_tail    = (hdr->addr1[ETH_ALEN - 2] << 8) |
++				     (hdr->addr1[ETH_ALEN - 1]);
++	} else {
++		tx_log->bssid_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
++				     (hdr->addr3[ETH_ALEN - 1]);
++		tx_log->sa_tail	   = (hdr->addr2[ETH_ALEN - 2] << 8) |
++				     (hdr->addr2[ETH_ALEN - 1]);
++		tx_log->da_tail    = (hdr->addr1[ETH_ALEN - 2] << 8) |
++				     (hdr->addr1[ETH_ALEN - 1]);
++	}
++
++	ath9k_hw_get_descinfo(sc->sc_ah, &desc_info);
++
++	ds_words = (u32 *)(bf->bf_desc) + desc_info.txctl_offset;
++	for (i = 0; i < desc_info.txctl_numwords; i++)
++		tx_log->txdesc_ctl[i] = ds_words[i];
++}
++
++void ath_pktlog_txstatus(struct ath_softc *sc, void *ds)
++{
++	struct ath_pktlog_txstatus *tx_log;
++	struct ath_pktlog *pl_info;
++	struct ath_desc_info desc_info;
++	int i;
++	u32 *ds_words, flags = 0;
++
++	pl_info = &sc->pktlog.pktlog;
++
++	if ((pl_info->pktlog_filter & ATH_PKTLOG_TX) == 0 ||
++	    !sc->is_pkt_logging)
++		return;
++
++	flags |= (((sc->sc_ah->hw_version.macRev <<
++		  PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
++		  ((sc->sc_ah->hw_version.macVersion << PHFLAGS_MACVERSION_SFT)
++		  & PHFLAGS_MACVERSION_MASK));
++	tx_log = (struct ath_pktlog_txstatus *)ath_pktlog_getbuf(pl_info,
++			PKTLOG_TYPE_TXSTATUS, sizeof(*tx_log), flags);
++
++	memset(tx_log, 0, sizeof(*tx_log));
++
++	ath9k_hw_get_descinfo(sc->sc_ah, &desc_info);
++
++	ds_words = (u32 *)(ds) + desc_info.txstatus_offset;
++
++	for (i = 0; i < desc_info.txstatus_numwords; i++)
++		tx_log->txdesc_status[i] = ds_words[i];
++}
++
++void ath_pktlog_rx(struct ath_softc *sc, void *desc, struct sk_buff *skb)
++{
++	struct ath_pktlog_rx *rx_log;
++	struct ath_pktlog *pl_info;
++	struct ieee80211_hdr *hdr;
++	struct ath_desc_info desc_info;
++	int i;
++	u32 *ds_words, flags = 0;
++
++	pl_info = &sc->pktlog.pktlog;
++
++	if ((pl_info->pktlog_filter & ATH_PKTLOG_RX) == 0 ||
++	    !sc->is_pkt_logging)
++		return;
++
++	flags |= (((sc->sc_ah->hw_version.macRev <<
++		  PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
++		  ((sc->sc_ah->hw_version.macVersion <<
++		 PHFLAGS_MACVERSION_SFT) & PHFLAGS_MACVERSION_MASK));
++
++	rx_log = (struct ath_pktlog_rx *)ath_pktlog_getbuf(pl_info, PKTLOG_TYPE_RX,
++			sizeof(*rx_log), flags);
++
++	memset(rx_log, 0, sizeof(*rx_log));
++
++	if (skb->len > sizeof(struct ieee80211_hdr)) {
++		hdr = (struct ieee80211_hdr *) skb->data;
++		rx_log->framectrl = hdr->frame_control;
++		rx_log->seqctrl   = hdr->seq_ctrl;
++
++		if (ieee80211_has_tods(rx_log->framectrl)) {
++			rx_log->bssid_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
++					     (hdr->addr1[ETH_ALEN - 1]);
++			rx_log->sa_tail    = (hdr->addr2[ETH_ALEN - 2] << 8) |
++					     (hdr->addr2[ETH_ALEN - 1]);
++			rx_log->da_tail    = (hdr->addr3[ETH_ALEN - 2] << 8) |
++					     (hdr->addr3[ETH_ALEN - 1]);
++		} else if (ieee80211_has_fromds(rx_log->framectrl)) {
++			rx_log->bssid_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
++					     (hdr->addr2[ETH_ALEN - 1]);
++			rx_log->sa_tail    = (hdr->addr3[ETH_ALEN - 2] << 8) |
++					     (hdr->addr3[ETH_ALEN - 1]);
++			rx_log->da_tail    = (hdr->addr1[ETH_ALEN - 2] << 8) |
++					     (hdr->addr1[ETH_ALEN - 1]);
++		} else {
++			rx_log->bssid_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
++					     (hdr->addr3[ETH_ALEN - 1]);
++			rx_log->sa_tail    = (hdr->addr2[ETH_ALEN - 2] << 8) |
++					     (hdr->addr2[ETH_ALEN - 1]);
++			rx_log->da_tail    = (hdr->addr1[ETH_ALEN - 2] << 8) |
++					     (hdr->addr1[ETH_ALEN - 1]);
++		}
++	} else {
++		hdr = (struct ieee80211_hdr *) skb->data;
++
++		if (ieee80211_is_ctl(hdr->frame_control)) {
++			rx_log->framectrl = hdr->frame_control;
++			rx_log->da_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
++					     (hdr->addr1[ETH_ALEN - 1]);
++			if (skb->len < sizeof(struct ieee80211_rts)) {
++				rx_log->sa_tail = 0;
++			} else {
++				rx_log->sa_tail = (hdr->addr2[ETH_ALEN - 2]
++						  << 8) |
++						  (hdr->addr2[ETH_ALEN - 1]);
++			}
++		} else {
++			rx_log->framectrl = 0xFFFF;
++			rx_log->da_tail = 0;
++			rx_log->sa_tail = 0;
++		}
++
++		rx_log->seqctrl   = 0;
++		rx_log->bssid_tail = 0;
++	}
++
++	ath9k_hw_get_descinfo(sc->sc_ah, &desc_info);
++
++	ds_words = (u32 *)(desc) + desc_info.rxstatus_offset;
++
++	for (i = 0; i < desc_info.rxstatus_numwords; i++)
++		rx_log->rxdesc_status[i] = ds_words[i];
++}
++
++void ath9k_pktlog_rc(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv,
++		     int8_t ratecode, u8 rate, int8_t is_probing, u16 ac)
++{
++	struct ath_pktlog_rcfind *rcf_log;
++	struct ath_pktlog *pl_info;
++	u32 flags = 0;
++
++	pl_info = &sc->pktlog.pktlog;
++
++	if ((pl_info->pktlog_filter & ATH_PKTLOG_RCFIND) == 0 ||
++	    !sc->is_pkt_logging)
++		return;
++
++	flags |= (((sc->sc_ah->hw_version.macRev <<
++		 PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
++		 ((sc->sc_ah->hw_version.macVersion <<
++		 PHFLAGS_MACVERSION_SFT) & PHFLAGS_MACVERSION_MASK));
++	rcf_log = (struct ath_pktlog_rcfind *)ath_pktlog_getbuf(pl_info,
++		  PKTLOG_TYPE_RCFIND, sizeof(*rcf_log), flags);
++
++	rcf_log->rate = rate;
++	rcf_log->rateCode = ratecode;
++	rcf_log->rcProbeRate = is_probing ? ath_rc_priv->probe_rate : 0;
++	rcf_log->isProbing = is_probing;
++	rcf_log->ac = ac;
++	rcf_log->rcRateMax = ath_rc_priv->rate_max_phy;
++	rcf_log->rcRateTableSize = ath_rc_priv->rate_table_size;
++}
++
++void ath9k_pktlog_rcupdate(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, u8 tx_rate,
++			   u8 rate_code, u8 xretries, u8 retries, int8_t rssi, u16 ac)
++{
++	struct ath_pktlog_rcupdate *rcu_log;
++	struct ath_pktlog *pl_info;
++	int i;
++	u32 flags = 0;
++
++	pl_info = &sc->pktlog.pktlog;
++
++	if ((pl_info->pktlog_filter & ATH_PKTLOG_RCUPDATE) == 0 ||
++	    !sc->is_pkt_logging)
++		return;
++
++	flags |= (((sc->sc_ah->hw_version.macRev <<
++		 PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
++		 ((sc->sc_ah->hw_version.macVersion <<
++		 PHFLAGS_MACVERSION_SFT) & PHFLAGS_MACVERSION_MASK));
++	rcu_log = (struct ath_pktlog_rcupdate *)ath_pktlog_getbuf(pl_info,
++						PKTLOG_TYPE_RCUPDATE,
++						sizeof(*rcu_log), flags);
++
++	memset(rcu_log, 0, sizeof(*rcu_log));
++
++	rcu_log->txRate = tx_rate;
++	rcu_log->rateCode = rate_code;
++	rcu_log->Xretries = xretries;
++	rcu_log->retries = retries;
++	rcu_log->rssiAck = rssi;
++	rcu_log->ac = ac;
++	rcu_log->rcProbeRate = ath_rc_priv->probe_rate;
++	rcu_log->rcRateMax = ath_rc_priv->rate_max_phy;
++
++	for (i = 0; i < RATE_TABLE_SIZE; i++) {
++		rcu_log->rcPer[i] = ath_rc_priv->per[i];
++	}
++}
++
++void ath9k_pktlog_txcomplete(struct ath_softc *sc, struct list_head *bf_head,
++			     struct ath_buf *bf, struct ath_buf *bf_last)
++{
++	struct log_tx ;
++	struct ath_buf *tbf;
++
++	list_for_each_entry(tbf, bf_head, list)
++		ath_pktlog_txctl(sc, tbf);
++
++	if (bf->bf_next == NULL && bf_last->bf_stale)
++		ath_pktlog_txctl(sc, bf_last);
++}
++
++void ath9k_pktlog_txctrl(struct ath_softc *sc, struct list_head *bf_head, struct ath_buf *lastbf)
++{
++	struct log_tx ;
++	struct ath_buf *tbf;
++
++	list_for_each_entry(tbf, bf_head, list)
++		ath_pktlog_txctl(sc, tbf);
++
++	/* log the last descriptor. */
++	ath_pktlog_txctl(sc, lastbf);
++}
++
++static void pktlog_init(struct ath_softc *sc)
++{
++	spin_lock_init(&sc->pktlog.pktlog.pktlog_lock);
++	sc->pktlog.pktlog.pktlog_buf_size = ATH_DEBUGFS_PKTLOG_SIZE_DEFAULT;
++	sc->pktlog.pktlog.pktlog_buf = NULL;
++	sc->pktlog.pktlog.pktlog_filter = 0;
++}
++
++int ath9k_init_pktlog(struct ath_softc *sc)
++{
++	sc->pktlog.debugfs_pktlog = debugfs_create_dir("pktlog",
++			sc->debug.debugfs_phy);
++	if (!sc->pktlog.debugfs_pktlog)
++		goto err;
++
++	sc->pktlog.pktlog_start = debugfs_create_file("pktlog_start",
++			S_IRUGO | S_IWUSR,
++			sc->pktlog.debugfs_pktlog,
++			sc, &fops_pktlog_start);
++	if (!sc->pktlog.pktlog_start)
++		goto err;
++
++	sc->pktlog.pktlog_size = debugfs_create_file("pktlog_size",
++			S_IRUGO | S_IWUSR,
++			sc->pktlog.debugfs_pktlog,
++			sc, &fops_pktlog_size);
++	if (!sc->pktlog.pktlog_size)
++		goto err;
++
++	sc->pktlog.pktlog_filter = debugfs_create_file("pktlog_filter",
++			S_IRUGO | S_IWUSR,
++			sc->pktlog.debugfs_pktlog,
++			sc, &fops_pktlog_filter);
++	if (!sc->pktlog.pktlog_filter)
++		goto err;
++
++	sc->pktlog.pktlog_dump = debugfs_create_file("pktlog_dump",
++			S_IRUGO,
++			sc->pktlog.debugfs_pktlog,
++			sc, &fops_pktlog_dump);
++	if (!sc->pktlog.pktlog_dump)
++		goto err;
++
++	pktlog_init(sc);
++
++	return 0;
++
++err:
++	return -ENOMEM;
++}
++
++void ath9k_deinit_pktlog(struct ath_softc *sc)
++{
++	struct ath_pktlog *pktlog = &sc->pktlog.pktlog;
++
++	if (pktlog->pktlog_buf != NULL)
++		ath_pktlog_release(pktlog);
++
++	debugfs_remove(sc->pktlog.pktlog_start);
++	debugfs_remove(sc->pktlog.pktlog_size);
++	debugfs_remove(sc->pktlog.pktlog_filter);
++	debugfs_remove(sc->pktlog.pktlog_dump);
++	debugfs_remove(sc->pktlog.debugfs_pktlog);
++}
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath9k/pktlog.h
+@@ -0,0 +1,242 @@
++#ifndef PKTLOG_H
++#define PKTLOG_H
++
++#ifdef CONFIG_ATH9K_PKTLOG
++#define CUR_PKTLOG_VER          10010  /* Packet log version */
++#define PKTLOG_MAGIC_NUM        7735225
++#define ATH_PKTLOG_TX		0x000000001
++#define ATH_PKTLOG_RX		0x000000002
++#define ATH_PKTLOG_RCFIND	0x000000004
++#define ATH_PKTLOG_RCUPDATE	0x000000008
++
++#define ATH_DEBUGFS_PKTLOG_SIZE_DEFAULT (1024 * 1024)
++#define ATH_PKTLOG_FILTER_DEFAULT (ATH_PKTLOG_TX | ATH_PKTLOG_RX |	\
++		ATH_PKTLOG_RCFIND | ATH_PKTLOG_RCUPDATE)
++
++#define PHFLAGS_MACVERSION_MASK 0x00ff0000
++#define PHFLAGS_MACVERSION_SFT  16
++#define PHFLAGS_MACREV_MASK 0xff0  /* MAC revision */
++#define PHFLAGS_MACREV_SFT  4
++
++struct ath_pktlog_hdr {
++	u32 flags;
++	u16 log_type; /* Type of log information foll this header */
++	int16_t size; /* Size of variable length log information in bytes */
++	u32 timestamp;
++}  __packed;
++
++/* Types of packet log events */
++#define PKTLOG_TYPE_TXCTL    0
++#define PKTLOG_TYPE_TXSTATUS 1
++#define PKTLOG_TYPE_RX       2
++#define PKTLOG_TYPE_RCFIND   3
++#define PKTLOG_TYPE_RCUPDATE 4
++
++#define PKTLOG_MAX_TXCTL_WORDS 12
++#define PKTLOG_MAX_TXSTATUS_WORDS 10
++#define PKTLOG_MAX_PROTO_WORDS  16
++
++struct ath_pktlog_txctl {
++	__le16 framectrl;       /* frame control field from header */
++	__le16 seqctrl;         /* frame control field from header */
++	u16 bssid_tail;      /* last two octets of bssid */
++	u16 sa_tail;         /* last two octets of SA */
++	u16 da_tail;         /* last two octets of DA */
++	u16 resvd;
++	u32 txdesc_ctl[PKTLOG_MAX_TXCTL_WORDS];     /* Tx descriptor words */
++	unsigned long proto_hdr;   /* protocol header (variable length!) */
++	int32_t misc[0]; /* Can be used for HT specific or other misc info */
++}  __packed;
++
++struct ath_pktlog_txstatus {
++	/* Tx descriptor status words */
++	u32 txdesc_status[PKTLOG_MAX_TXSTATUS_WORDS];
++	int32_t misc[0]; /* Can be used for HT specific or other misc info */
++}  __packed;
++
++#define PKTLOG_MAX_RXSTATUS_WORDS 11
++
++struct ath_pktlog_rx {
++	u16 framectrl;       /* frame control field from header */
++	u16 seqctrl;         /* sequence control field */
++	u16 bssid_tail;      /* last two octets of bssid */
++	u16 sa_tail;         /* last two octets of SA */
++	u16 da_tail;         /* last two octets of DA */
++	u16 resvd;
++	u32 rxdesc_status[PKTLOG_MAX_RXSTATUS_WORDS];  /* Rx descriptor words */
++	unsigned long proto_hdr;   /* protocol header (variable length!) */
++	int32_t misc[0];    /* Can be used for HT specific or other misc info */
++}  __packed;
++
++struct ath_pktlog_rcfind {
++	u8 rate;
++	u8 rateCode;
++	s8 rcRssiLast;
++	s8 rcRssiLastPrev;
++	s8 rcRssiLastPrev2;
++	s8 rssiReduce;
++	u8 rcProbeRate;
++	s8 isProbing;
++	s8 primeInUse;
++	s8 currentPrimeState;
++	u8 rcRateTableSize;
++	u8 rcRateMax;
++	u8 ac;
++	int32_t misc[0]; /* Can be used for HT specific or other misc info */
++}  __packed;
++
++struct ath_pktlog_rcupdate {
++	u8 txRate;
++	u8 rateCode;
++	s8 rssiAck;
++	u8 Xretries;
++	u8 retries;
++	s8 rcRssiLast;
++	s8 rcRssiLastLkup;
++	s8 rcRssiLastPrev;
++	s8 rcRssiLastPrev2;
++	u8 rcProbeRate;
++	u8 rcRateMax;
++	s8 useTurboPrime;
++	s8 currentBoostState;
++	u8 rcHwMaxRetryRate;
++	u8 ac;
++	u8 resvd[2];
++	s8 rcRssiThres[RATE_TABLE_SIZE];
++	u8 rcPer[RATE_TABLE_SIZE];
++	u8 resv2[RATE_TABLE_SIZE + 5];
++	int32_t misc[0]; /* Can be used for HT specific or other misc info */
++};
++
++#define TXCTL_OFFSET(ah)      (AR_SREV_9300_20_OR_LATER(ah) ? 11 : 2)
++#define TXCTL_NUMWORDS(ah)    (AR_SREV_5416_20_OR_LATER(ah) ? 12 : 8)
++#define TXSTATUS_OFFSET(ah)   (AR_SREV_9300_20_OR_LATER(ah) ? 2 : 14)
++#define TXSTATUS_NUMWORDS(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 7 : 10)
++
++#define RXCTL_OFFSET(ah)      (AR_SREV_9300_20_OR_LATER(ah) ? 0 : 3)
++#define RXCTL_NUMWORDS(ah)    (AR_SREV_9300_20_OR_LATER(ah) ? 0 : 1)
++#define RXSTATUS_OFFSET(ah)   (AR_SREV_9300_20_OR_LATER(ah) ? 1 : 4)
++#define RXSTATUS_NUMWORDS(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 11 : 9)
++
++struct ath_desc_info {
++	u8 txctl_offset;
++	u8 txctl_numwords;
++	u8 txstatus_offset;
++	u8 txstatus_numwords;
++	u8 rxctl_offset;
++	u8 rxctl_numwords;
++	u8 rxstatus_offset;
++	u8 rxstatus_numwords;
++};
++
++#define PKTLOG_MOV_RD_IDX(_rd_offset, _log_buf, _log_size)  \
++	do { \
++		if ((_rd_offset + sizeof(struct ath_pktlog_hdr) + \
++		    ((struct ath_pktlog_hdr *)((_log_buf)->log_data + \
++			    (_rd_offset)))->size) <= _log_size) { \
++			_rd_offset = ((_rd_offset) + \
++					sizeof(struct ath_pktlog_hdr) + \
++					((struct ath_pktlog_hdr *) \
++					 ((_log_buf)->log_data + \
++					  (_rd_offset)))->size); \
++		} else { \
++			_rd_offset = ((struct ath_pktlog_hdr *) \
++					((_log_buf)->log_data +  \
++					 (_rd_offset)))->size;  \
++		} \
++		(_rd_offset) = (((_log_size) - (_rd_offset)) >= \
++				sizeof(struct ath_pktlog_hdr)) ? \
++		_rd_offset : 0; \
++	} while (0);
++
++struct ath_pktlog_bufhdr {
++	u32 magic_num;  /* Used by post processing scripts */
++	u32 version;    /* Set to CUR_PKTLOG_VER */
++};
++
++struct ath_pktlog_buf {
++	struct ath_pktlog_bufhdr bufhdr;
++	int32_t rd_offset;
++	int32_t wr_offset;
++	char log_data[0];
++};
++
++struct ath_pktlog {
++	struct ath_pktlog_buf *pktlog_buf;
++	u32 pktlog_filter;
++	u32 pktlog_buf_size;           /* Size of buffer in bytes */
++	spinlock_t pktlog_lock;
++};
++
++struct ath_pktlog_debugfs {
++	struct dentry *debugfs_pktlog;
++	struct dentry *pktlog_enable;
++	struct dentry *pktlog_start;
++	struct dentry *pktlog_filter;
++	struct dentry *pktlog_size;
++	struct dentry *pktlog_dump;
++	struct ath_pktlog pktlog;
++};
++
++void ath_pktlog_txctl(struct ath_softc *sc, struct ath_buf *bf);
++void ath_pktlog_txstatus(struct ath_softc *sc, void *ds);
++void ath_pktlog_rx(struct ath_softc *sc, void *ds, struct sk_buff *skb);
++void ath9k_pktlog_rc(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv,
++		int8_t ratecode, u8 rate, int8_t is_probing, u16 ac);
++void ath9k_pktlog_rcupdate(struct ath_softc *sc,
++			   struct ath_rate_priv *ath_rc_priv, u8 tx_rate,
++			   u8 rate_code, u8 xretries, u8 retries, int8_t rssi,
++			   u16 ac);
++void ath9k_pktlog_txcomplete(struct ath_softc *sc ,struct list_head *bf_head,
++			     struct ath_buf *bf, struct ath_buf *bf_last);
++void ath9k_pktlog_txctrl(struct ath_softc *sc, struct list_head *bf_head,
++			 struct ath_buf *lastbf);
++int ath9k_init_pktlog(struct ath_softc *sc);
++void ath9k_deinit_pktlog(struct ath_softc *sc);
++#else /* CONFIG_ATH9K_PKTLOG */
++static inline void ath_pktlog_txstatus(struct ath_softc *sc, void *ds)
++{
++}
++
++static inline void ath_pktlog_rx(struct ath_softc *sc, void *ds,
++				 struct sk_buff *skb)
++{
++}
++
++static inline void ath9k_pktlog_rc(struct ath_softc *sc,
++				   struct ath_rate_priv *ath_rc_priv,
++				   int8_t ratecode, u8 rate,
++				   int8_t is_probing, u16 ac)
++{
++}
++
++static inline void ath9k_pktlog_rcupdate(struct ath_softc *sc,
++					 struct ath_rate_priv *ath_rc_priv,
++					 u8 tx_rate, u8 rate_code,
++					 u8 xretries, u8 retries,
++					 int8_t rssi, u16 ac)
++{
++}
++
++static inline void ath9k_pktlog_txcomplete(struct ath_softc *sc,
++					   struct list_head *bf_head,
++					   struct ath_buf *bf,
++					   struct ath_buf *bf_last)
++{
++}
++
++static inline void ath9k_pktlog_txctrl(struct ath_softc *sc,
++				       struct list_head *bf_head,
++				       struct ath_buf *lastbf)
++{
++}
++static inline int ath9k_init_pktlog(struct ath_softc *sc)
++{
++	return 0;
++}
++static inline void ath9k_deinit_pktlog(struct ath_softc *sc)
++{
++}
++#endif /* CONFIG_ATH9K_PKTLOG */
++
++#endif
+--- a/drivers/net/wireless/ath/ath9k/rc.c
++++ b/drivers/net/wireless/ath/ath9k/rc.c
+@@ -580,7 +580,7 @@ static u8 ath_rc_setvalid_htrates(struct
+ static u8 ath_rc_get_highest_rix(struct ath_softc *sc,
+ 			         struct ath_rate_priv *ath_rc_priv,
+ 				 const struct ath_rate_table *rate_table,
+-				 int *is_probing)
++				 int *is_probing, u16 ac)
+ {
+ 	u32 best_thruput, this_thruput, now_msec;
+ 	u8 rate, next_rate, best_rate, maxindex, minindex;
+@@ -671,6 +671,8 @@ static u8 ath_rc_get_highest_rix(struct
+ 
+ 	rate = ath_rc_priv->valid_rate_index[0];
+ 
++      ath9k_pktlog_rc(sc, ath_rc_priv,  rate_table->info[rate].ratecode,
++		      rate, *is_probing, ac);
+ 	return rate;
+ }
+ 
+@@ -762,7 +764,7 @@ static void ath_get_rate(void *priv, str
+ 	try_per_rate = 4;
+ 
+ 	rate_table = ath_rc_priv->rate_table;
+-	rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe);
++	rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe, skb_get_queue_mapping(skb));
+ 
+ 	/*
+ 	 * If we're in HT mode and both us and our peer supports LDPC.
+@@ -1012,7 +1014,8 @@ static void ath_debug_stat_retries(struc
+ static void ath_rc_update_ht(struct ath_softc *sc,
+ 			     struct ath_rate_priv *ath_rc_priv,
+ 			     struct ieee80211_tx_info *tx_info,
+-			     int tx_rate, int xretries, int retries)
++			     int tx_rate, int xretries, int retries,
++			     u16 ac)
+ {
+ 	u32 now_msec = jiffies_to_msecs(jiffies);
+ 	int rate;
+@@ -1081,6 +1084,9 @@ static void ath_rc_update_ht(struct ath_
+ 	ath_debug_stat_retries(ath_rc_priv, tx_rate, xretries, retries,
+ 			       ath_rc_priv->per[tx_rate]);
+ 
++	ath9k_pktlog_rcupdate(sc, ath_rc_priv, tx_rate,
++			rate_table->info[tx_rate].ratecode,
++			xretries, retries, tx_info->status.ack_signal, ac);
+ }
+ 
+ static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table,
+@@ -1113,7 +1119,8 @@ static int ath_rc_get_rateindex(const st
+ static void ath_rc_tx_status(struct ath_softc *sc,
+ 			     struct ath_rate_priv *ath_rc_priv,
+ 			     struct ieee80211_tx_info *tx_info,
+-			     int final_ts_idx, int xretries, int long_retry)
++			     int final_ts_idx, int xretries, int long_retry,
++			     u16 ac)
+ {
+ 	const struct ath_rate_table *rate_table;
+ 	struct ieee80211_tx_rate *rates = tx_info->status.rates;
+@@ -1142,7 +1149,7 @@ static void ath_rc_tx_status(struct ath_
+ 				rix = ath_rc_get_rateindex(rate_table, &rates[i]);
+ 				ath_rc_update_ht(sc, ath_rc_priv, tx_info,
+ 						rix, xretries ? 1 : 2,
+-						rates[i].count);
++						rates[i].count, ac);
+ 			}
+ 		}
+ 	} else {
+@@ -1164,7 +1171,7 @@ static void ath_rc_tx_status(struct ath_
+ 		return;
+ 
+ 	rix = ath_rc_get_rateindex(rate_table, &rates[i]);
+-	ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry);
++	ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry, ac);
+ }
+ 
+ static const
+@@ -1358,7 +1365,7 @@ static void ath_tx_status(void *priv, st
+ 		tx_status = 1;
+ 
+ 	ath_rc_tx_status(sc, ath_rc_priv, tx_info, final_ts_idx, tx_status,
+-			 long_retry);
++			 long_retry, skb_get_queue_mapping(skb));
+ 
+ 	/* Check if aggregation has to be enabled for this tid */
+ 	if (conf_is_ht(&sc->hw->conf) &&
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -1577,6 +1577,7 @@ int ath_rx_tasklet(struct ath_softc *sc,
+ 	struct ieee80211_rx_status *rxs;
+ 	struct ath_hw *ah = sc->sc_ah;
+ 	struct ath_common *common = ath9k_hw_common(ah);
++	u32 *rx_desc = NULL;
+ 	/*
+ 	 * The hw can technically differ from common->hw when using ath9k
+ 	 * virtual wiphy so to account for that we iterate over the active
+@@ -1676,13 +1677,24 @@ int ath_rx_tasklet(struct ath_softc *sc,
+ 				 dma_type);
+ 
+ 		skb_put(skb, rs.rs_datalen + ah->caps.rx_status_len);
+-		if (ah->caps.rx_status_len)
++		if (ah->caps.rx_status_len) {
++			rx_desc = kzalloc(ah->caps.rx_status_len, GFP_ATOMIC);
++			if (rx_desc == NULL)
++				BUG_ON(1);
++			memcpy(rx_desc, skb->data, ah->caps.rx_status_len);
+ 			skb_pull(skb, ah->caps.rx_status_len);
++}
+ 
+ 		if (!rs.rs_more)
+ 			ath9k_rx_skb_postprocess(common, hdr_skb, &rs,
+ 						 rxs, decrypt_error);
+ 
++		if (rx_desc) {
++			ath_pktlog_rx(sc, (void *) rx_desc, skb);
++			kfree(rx_desc);
++		} else
++			ath_pktlog_rx(sc, bf->bf_desc, skb);
++
+ 		/* We will now give hardware our shiny new allocated skb */
+ 		bf->bf_mpdu = requeue_skb;
+ 		bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data,
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -475,6 +475,8 @@ static void ath_tx_complete_aggr(struct
+ 			list_move_tail(&bf->list, &bf_head);
+ 		}
+ 
++		ath9k_pktlog_txcomplete(sc, &bf_head, bf, bf_last);
++
+ 		if (!txpending || (tid->state & AGGR_CLEANUP)) {
+ 			/*
+ 			 * complete the acked-ones/xretried ones; update
+@@ -2043,7 +2045,7 @@ static void ath_tx_processq(struct ath_s
+ 		ds = lastbf->bf_desc;
+ 
+ 		memset(&ts, 0, sizeof(ts));
+-		status = ath9k_hw_txprocdesc(ah, ds, &ts);
++		status = ath9k_hw_txprocdesc(ah, ds, &ts, NULL);
+ 		if (status == -EINPROGRESS) {
+ 			spin_unlock_bh(&txq->axq_lock);
+ 			break;
+@@ -2085,11 +2087,15 @@ static void ath_tx_processq(struct ath_s
+ 			ath_tx_rc_status(sc, bf, &ts, 1, txok ? 0 : 1, txok, true);
+ 		}
+ 
+-		if (bf_isampdu(bf))
++		if (bf_isampdu(bf)) {
+ 			ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, txok,
+ 					     true);
+-		else
++		} else {
++			ath9k_pktlog_txctrl(sc, &bf_head, lastbf);
+ 			ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, txok, 0);
++		}
++
++		ath_pktlog_txstatus(sc, lastbf->bf_desc);
+ 
+ 		spin_lock_bh(&txq->axq_lock);
+ 
+@@ -2210,9 +2216,11 @@ void ath_tx_edma_tasklet(struct ath_soft
+ 	struct list_head bf_head;
+ 	int status;
+ 	int txok;
++	u32 txs_desc[9];
+ 
+ 	for (;;) {
+-		status = ath9k_hw_txprocdesc(ah, NULL, (void *)&txs);
++		status = ath9k_hw_txprocdesc(ah, NULL, (void *)&txs,
++					     (void *) txs_desc);
+ 		if (status == -EINPROGRESS)
+ 			break;
+ 		if (status == -EIO) {
+@@ -2258,9 +2266,13 @@ void ath_tx_edma_tasklet(struct ath_soft
+ 		if (bf_isampdu(bf))
+ 			ath_tx_complete_aggr(sc, txq, bf, &bf_head, &txs,
+ 					     txok, true);
+-		else
++		else {
++			ath9k_pktlog_txctrl(sc, &bf_head, lastbf);
+ 			ath_tx_complete_buf(sc, bf, txq, &bf_head,
+ 					    &txs, txok, 0);
++		}
++
++		ath_pktlog_txstatus(sc, txs_desc);
+ 
+ 		spin_lock_bh(&txq->axq_lock);
+ 
diff --git a/crap/README b/crap/README
new file mode 100644
index 0000000..c1b512e
--- /dev/null
+++ b/crap/README
@@ -0,0 +1,17 @@
+
+compat-wireless crap patches
+============================
+
+If you are including patches into this directory you
+must be fixing some critical bug for a customer which needs
+immediate release or immediate testing.
+
+Alternatively you would use this to apply some sort of
+crap code you are maintaining.
+
+You must have a really good reason to be adding files
+in this directory. If possible you should explain your
+reasoning of why the patch is getting included here and
+not upstream and why it hasn't even yet been posted.
+
+You should avoid these patches at all costs.
diff --git a/defconfigs/README b/defconfigs/README
new file mode 100644
index 0000000..5dcd84a
--- /dev/null
+++ b/defconfigs/README
@@ -0,0 +1,2 @@
+You can stuff into this directory default config.mk files which are suited
+for debugging or other testing purposes.
diff --git a/defconfigs/atheros-debug.mk b/defconfigs/atheros-debug.mk
new file mode 100644
index 0000000..285b231
--- /dev/null
+++ b/defconfigs/atheros-debug.mk
@@ -0,0 +1,649 @@
+export
+
+## NOTE
+## Make sure to have each variable declaration start
+## in the first column, no whitespace allowed.
+
+ifeq ($(wildcard $(KLIB_BUILD)/.config),)
+# These will be ignored by compat autoconf
+ CONFIG_PCI=y
+ CONFIG_USB=y
+ CONFIG_PCMCIA=y
+ CONFIG_SSB=m
+else
+include $(KLIB_BUILD)/.config
+endif
+
+ifneq ($(wildcard $(KLIB_BUILD)/Makefile),)
+
+COMPAT_LATEST_VERSION = 1
+
+KERNEL_VERSION := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^\([0-9]\)\..*/\1/p')
+
+ifneq ($(KERNEL_VERSION),2)
+KERNEL_SUBLEVEL := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^3\.\([0-9]\+\).*/\1/p')
+else
+COMPAT_26LATEST_VERSION = 39
+KERNEL_26SUBLEVEL := $(shell $(MAKE) -C $(KLIB_BUILD) kernelversion | sed -n 's/^2\.6\.\([0-9]\+\).*/\1/p')
+COMPAT_26VERSIONS := $(shell I=$(COMPAT_26LATEST_VERSION); while [ "$$I" -gt $(KERNEL_26SUBLEVEL) ]; do echo $$I; I=$$(($$I - 1)); done)
+$(foreach ver,$(COMPAT_26VERSIONS),$(eval CONFIG_COMPAT_KERNEL_2_6_$(ver)=y))
+KERNEL_SUBLEVEL := -1
+endif
+
+COMPAT_VERSIONS := $(shell I=$(COMPAT_LATEST_VERSION); while [ "$$I" -gt $(KERNEL_SUBLEVEL) ]; do echo $$I; I=$$(($$I - 1)); done)
+$(foreach ver,$(COMPAT_VERSIONS),$(eval CONFIG_COMPAT_KERNEL_3_$(ver)=y))
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_24
+$(error "ERROR: compat-wireless by default supports kernels >= 2.6.24, try enabling only one driver though")
+endif #CONFIG_COMPAT_KERNEL_2_6_24
+
+ifeq ($(CONFIG_CFG80211),y)
+$(error "ERROR: your kernel has CONFIG_CFG80211=y, you should have it CONFIG_CFG80211=m if you want to use this thing.")
+endif
+
+
+# 2.6.27 has FTRACE_DYNAMIC borked, so we will complain if
+# you have it enabled, otherwise you will very likely run into
+# a kernel panic.
+ifeq ($(shell test $(KERNEL_VERSION) -eq 2 -a $(KERNEL_SUBLEVEL) -eq 27 && echo yes),yes)
+ifeq ($(CONFIG_DYNAMIC_FTRACE),y)
+$(error "ERROR: Your 2.6.27 kernel has CONFIG_DYNAMIC_FTRACE, please upgrade your distribution kernel as newer ones should not have this enabled (and if so report a bug) or remove this warning if you know what you are doing")
+endif
+endif
+
+# This is because with CONFIG_MAC80211 include/linux/skbuff.h will
+# enable on 2.6.27 a new attribute:
+#
+# skb->do_not_encrypt
+#
+# and on 2.6.28 another new attribute:
+#
+# skb->requeue
+#
+# In kernel 2.6.32 both attributes were removed.
+#
+ifeq ($(shell test $(KERNEL_VERSION) -eq 2 -a $(KERNEL_SUBLEVEL) -ge 27 -a $(KERNEL_SUBLEVEL) -le 31 && echo yes),yes)
+ifeq ($(CONFIG_MAC80211),)
+$(error "ERROR: Your >=2.6.27 and <= 2.6.31 kernel has CONFIG_MAC80211 disabled, you should have it CONFIG_MAC80211=m if you want to use this thing.")
+endif
+endif
+
+ifneq ($(KERNELRELEASE),) # This prevents a warning
+
+# We will warn when you don't have MQ support or NET_SCHED enabled.
+#
+# We could consider just quiting if MQ and NET_SCHED is disabled
+# as I suspect all users of this package want 802.11e (WME) and
+# 802.11n (HT) support.
+ifeq ($(CONFIG_NET_SCHED),)
+ QOS_REQS_MISSING+=CONFIG_NET_SCHED
+endif
+
+ifneq ($(QOS_REQS_MISSING),) # Complain about our missing dependencies
+$(warning "WARNING: You are running a kernel >= 2.6.23, you should enable in it $(QOS_REQS_MISSING) for 802.11[ne] support")
+endif
+
+endif # build check
+endif # kernel Makefile check
+
+# These both are needed by compat-wireless || compat-bluetooth so enable them
+ CONFIG_COMPAT_RFKILL=y
+
+ifeq ($(CONFIG_MAC80211),y)
+$(error "ERROR: you have MAC80211 compiled into the kernel, CONFIG_MAC80211=y, as such you cannot replace its mac80211 driver. You need this set to CONFIG_MAC80211=m. If you are using Fedora upgrade your kernel as later version should this set as modular. For further information on Fedora see https://bugzilla.redhat.com/show_bug.cgi?id=470143. If you are using your own kernel recompile it and make mac80211 modular")
+else
+ CONFIG_COMPAT_WIRELESS=y
+ CONFIG_COMPAT_WIRELESS_MODULES=m
+ CONFIG_COMPAT_VAR_MODULES=m
+# We could technically separate these but not yet, we only have b44
+# Note that we don't intend on backporting network drivers that
+# use Multiqueue as that was a pain to backport to kernels older than
+# 2.6.27. But -- we could just disable those drivers from kernels
+# older than 2.6.27
+ CONFIG_COMPAT_NETWORK_MODULES=m
+ CONFIG_COMPAT_NET_USB_MODULES=m
+endif
+
+# The Bluetooth compatibility only builds on kernels >= 2.6.27 for now
+ifndef CONFIG_COMPAT_KERNEL_2_6_27
+ifeq ($(CONFIG_BT),y)
+# we'll ignore compiling bluetooth
+else
+ CONFIG_COMPAT_BLUETOOTH=y
+ CONFIG_COMPAT_BLUETOOTH_MODULES=m
+endif
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_33
+ifdef CONFIG_FW_LOADER
+ CONFIG_COMPAT_FIRMWARE_CLASS=m
+endif #CONFIG_FW_LOADER
+endif #CONFIG_COMPAT_KERNEL_2_6_33
+
+# Wireless subsystem stuff
+CONFIG_MAC80211=m
+
+CONFIG_MAC80211_DEBUGFS=y
+CONFIG_MAC80211_NOINLINE=y
+CONFIG_MAC80211_VERBOSE_DEBUG=y
+CONFIG_MAC80211_HT_DEBUG=y
+# CONFIG_MAC80211_TKIP_DEBUG=y
+# CONFIG_MAC80211_IBSS_DEBUG=y
+CONFIG_MAC80211_VERBOSE_PS_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_MPL_DEBUG=y
+# CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG=y
+CONFIG_MAC80211_DEBUG_COUNTERS=y
+# CONFIG_MAC80211_DRIVER_API_TRACER=y
+
+# choose between pid and minstrel as default rate control algorithm
+CONFIG_MAC80211_RC_DEFAULT=minstrel_ht
+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
+# CONFIG_MAC80211_RC_DEFAULT_PID=y
+# This is the one used by our compat-wireless net/mac80211/rate.c
+# in case you have and old kernel which is overriding this to pid.
+CONFIG_COMPAT_MAC80211_RC_DEFAULT=minstrel_ht
+CONFIG_MAC80211_RC_PID=y
+CONFIG_MAC80211_RC_MINSTREL=y
+CONFIG_MAC80211_RC_MINSTREL_HT=y
+ifdef CONFIG_LEDS_TRIGGERS
+CONFIG_MAC80211_LEDS=y
+endif #CONFIG_LEDS_TRIGGERS
+
+# enable mesh networking too
+CONFIG_MAC80211_MESH=y
+
+CONFIG_CFG80211=m
+CONFIG_CFG80211_DEFAULT_PS=y
+CONFIG_CFG80211_DEBUGFS=y
+# CONFIG_NL80211_TESTMODE=y
+CONFIG_CFG80211_DEVELOPER_WARNINGS=y
+CONFIG_CFG80211_REG_DEBUG=y
+# See below for wext stuff
+
+CONFIG_LIB80211=m
+CONFIG_LIB80211_CRYPT_WEP=m
+CONFIG_LIB80211_CRYPT_CCMP=m
+CONFIG_LIB80211_CRYPT_TKIP=m
+# CONFIG_LIB80211_DEBUG=y
+
+CONFIG_BT=m
+CONFIG_COMPAT_BT_L2CAP=y
+CONFIG_COMPAT_BT_SCO=y
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+# CONFIG_BT_CMTP depends on ISDN_CAPI
+ifdef CONFIG_ISDN_CAPI
+CONFIG_BT_CMTP=m
+endif #CONFIG_ISDN_CAPI
+ifndef CONFIG_COMPAT_KERNEL_2_6_28
+CONFIG_COMPAT_BT_HIDP=m
+endif #CONFIG_COMPAT_KERNEL_2_6_28
+
+CONFIG_BT_HCIUART=M
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_ATH3K=y
+CONFIG_BT_HCIUART_LL=y
+
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_MRVL=m
+
+ifdef CONFIG_PCMCIA
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+endif #CONFIG_PCMCIA
+
+
+# We need CONFIG_WIRELESS_EXT for CONFIG_CFG80211_WEXT for every kernel 
+# version. The new way CONFIG_CFG80211_WEXT is called from the kernel 
+# does not work with compat-wireless because it calls some callback 
+# function on struct wiphy. This struct is shipped with compat-wireless 
+# and changes from kernel version to version. We are using the 
+# wireless_handlers attribute which will be activated by 
+# CONFIG_WIRELESS_EXT. 
+ifdef CONFIG_WIRELESS_EXT
+CONFIG_CFG80211_WEXT=y
+else #CONFIG_CFG80211_WEXT
+$(warning "WARNING: CONFIG_CFG80211_WEXT will be deactivated or not working because kernel was compiled with CONFIG_WIRELESS_EXT=n. Tools using wext interface like iwconfig will not work. To activate it build your kernel e.g. with CONFIG_LIBIPW=m.")
+endif #CONFIG_WIRELESS_EXT
+
+ifdef CONFIG_STAGING
+CONFIG_COMPAT_STAGING=m
+endif #CONFIG_STAGING
+
+# mac80211 test driver
+CONFIG_MAC80211_HWSIM=m
+
+CONFIG_ATH5K=m
+CONFIG_ATH5K_DEBUG=y
+# CONFIG_ATH5K_AHB=y
+
+CONFIG_ATH9K=m
+CONFIG_ATH9K_HW=m
+CONFIG_ATH9K_COMMON=m
+CONFIG_ATH9K_DEBUGFS=y
+# CONFIG_ATH9K_AHB=y
+CONFIG_ATH9K_PKTLOG=y
+
+# Disable this to get minstrel as default, we leave the ath9k
+# rate control algorithm as the default for now as that is also
+# default upstream on the kernel. We will move this to minstrel
+# as default once we get minstrel properly tested and blessed by
+# our systems engineering team. CCK rates also need to be used
+# for long range considerations.
+CONFIG_ATH9K_RATE_CONTROL=y
+
+# PCI Drivers
+ifdef CONFIG_PCI
+
+CONFIG_ATH5K_PCI=y
+CONFIG_ATH9K_PCI=y
+
+CONFIG_IWLAGN=m
+# CONFIG_IWLWIFI_DEBUG=y
+# CONFIG_IWLWIFI_DEBUGFS=y
+# CONFIG_IWLWIFI_DEVICE_TRACING=y
+# CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE=y
+CONFIG_IWL_P2P=y
+
+CONFIG_IWLWIFI_LEGACY=m
+CONFIG_COMPAT_IWL4965=m
+CONFIG_IWL3945=m
+# CONFIG_IWLWIFI_LEGACY_DEBUG=y
+# CONFIG_IWLWIFI_LEGACY_DEBUGFS=y
+# CONFIG_IWLWIFI_LEGACY_DEVICE_TRACING=y
+
+
+CONFIG_B43=m
+CONFIG_B43_HWRNG=y
+CONFIG_B43_PCI_AUTOSELECT=y
+ifdef CONFIG_PCMCIA
+CONFIG_B43_PCMCIA=y
+endif #CONFIG_PCMCIA
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_B43_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+CONFIG_B43_PHY_LP=y
+CONFIG_B43_PHY_N=y
+# CONFIG_B43_FORCE_PIO=y
+# CONFIG_B43_DEBUG=y
+
+CONFIG_B43LEGACY=m
+CONFIG_B43LEGACY_HWRNG=y
+CONFIG_B43LEGACY_PCI_AUTOSELECT=y
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_B43LEGACY_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+# CONFIG_B43LEGACY_DEBUG=y
+CONFIG_B43LEGACY_DMA=y
+CONFIG_B43LEGACY_PIO=y
+
+ifdef CONFIG_WIRELESS_EXT
+# The Intel ipws
+CONFIG_LIBIPW=m
+# CONFIG_LIBIPW_DEBUG=y
+
+CONFIG_IPW2100=m
+CONFIG_IPW2100_MONITOR=y
+# CONFIG_IPW2100_DEBUG=y
+CONFIG_IPW2200=m
+CONFIG_IPW2200_MONITOR=y
+CONFIG_IPW2200_RADIOTAP=y
+CONFIG_IPW2200_PROMISCUOUS=y
+CONFIG_IPW2200_QOS=y
+# CONFIG_IPW2200_DEBUG=y
+# The above enables use a second interface prefixed 'rtap'.
+#           Example usage:
+#
+# % modprobe ipw2200 rtap_iface=1
+# % ifconfig rtap0 up
+# % tethereal -i rtap0
+#
+# If you do not specify 'rtap_iface=1' as a module parameter then
+# the rtap interface will not be created and you will need to turn
+# it on via sysfs:
+#
+# % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface
+endif #CONFIG_WIRELESS_EXT
+
+ifdef CONFIG_SSB
+# Sonics Silicon Backplane
+CONFIG_SSB_SPROM=y
+
+CONFIG_SSB_BLOCKIO=y
+CONFIG_SSB_PCIHOST=y
+CONFIG_SSB_B43_PCI_BRIDGE=y
+ifdef CONFIG_PCMCIA
+CONFIG_SSB_PCMCIAHOST=y
+endif #CONFIG_PCMCIA
+# CONFIG_SSB_DEBUG=y
+CONFIG_SSB_DRIVER_PCICORE=y
+endif #CONFIG_SSB
+
+CONFIG_BCMA=m
+CONFIG_BCMA_BLOCKIO=y
+CONFIG_BCMA_HOST_PCI=y
+# CONFIG_BCMA_DEBUG=y
+
+CONFIG_P54_PCI=m
+
+CONFIG_B44=m
+CONFIG_B44_PCI=y
+
+CONFIG_RTL8180=m
+
+CONFIG_ADM8211=m
+
+CONFIG_RT2X00_LIB_PCI=m
+CONFIG_RT2400PCI=m
+CONFIG_RT2500PCI=m
+ifdef CONFIG_CRC_CCITT
+CONFIG_RT2800PCI=m
+CONFIG_RT2800PCI_RT33XX=y
+CONFIG_RT2800PCI_RT35XX=y
+# CONFIG_RT2800PCI_RT53XX=y
+endif #CONFIG_CRC_CCITT
+NEED_RT2X00=y
+
+# Two rt2x00 drivers require firmware: rt61pci and rt73usb. They depend on
+# CRC to check the firmware. We check here first for the PCI
+# driver as we're in the PCI section.
+ifdef CONFIG_CRC_ITU_T
+CONFIG_RT61PCI=m
+endif #CONFIG_CRC_ITU_T
+
+CONFIG_MWL8K=m
+
+# Ethernet drivers go here
+CONFIG_ATL1=m
+CONFIG_ATL2=m
+CONFIG_ATL1E=m
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_ATL1C=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_ATL1C=m
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+ifdef CONFIG_WIRELESS_EXT
+CONFIG_HERMES=m
+CONFIG_HERMES_CACHE_FW_ON_INIT=y
+ifdef CONFIG_PPC_PMAC
+CONFIG_APPLE_AIRPORT=m
+endif #CONFIG_PPC_PMAC
+CONFIG_PLX_HERMES=m
+CONFIG_TMD_HERMES=m
+CONFIG_NORTEL_HERMES=m
+CONFIG_PCI_HERMES=m
+ifdef CONFIG_PCMCIA
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+endif #CONFIG_PCMCIA
+endif #CONFIG_WIRELESS_EXT
+
+CONFIG_RTL8192CE=m
+CONFIG_RTL8192SE=m
+
+ifdef CONFIG_COMPAT_STAGING
+CONFIG_BRCMSMAC=m
+endif #CONFIG_COMPAT_STAGING
+
+endif #CONFIG_PCI
+## end of PCI
+
+ifdef CONFIG_PCMCIA
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS=n
+CONFIG_LIBERTAS_CS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_CS=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+endif #CONFIG_PCMCIA
+## end of PCMCIA
+
+# This is required for some cards
+CONFIG_EEPROM_93CX6=m
+
+# USB Drivers
+ifdef CONFIG_USB
+ifndef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_COMPAT_ZD1211RW=m
+# CONFIG_ZD1211RW_DEBUG=y
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+# Sorry, rndis_wlan uses cancel_work_sync which is new and can't be done in compat...
+
+# Wireless RNDIS USB support (RTL8185 802.11g) A-Link WL54PC
+# All of these devices are based on Broadcom 4320 chip which
+# is only wireless RNDIS chip known to date.
+# Note: this depends on CONFIG_USB_NET_RNDIS_HOST and CONFIG_USB_NET_CDCETHER
+# it also requires new RNDIS_HOST and CDC_ETHER modules which we add
+ifdef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_USB_COMPAT_USBNET=n
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=n
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=n
+CONFIG_USB_NET_COMPAT_CDCETHER=n
+else #CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_USB_COMPAT_USBNET=m
+ifdef CONFIG_USB_NET_CDCETHER
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=m
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=m
+endif #CONFIG_USB_NET_CDCETHER
+ifdef CONFIG_USB_NET_CDCETHER_MODULE
+CONFIG_USB_NET_COMPAT_RNDIS_HOST=m
+CONFIG_USB_NET_COMPAT_RNDIS_WLAN=m
+endif #CONFIG_USB_NET_CDCETHER
+CONFIG_USB_NET_COMPAT_CDCETHER=m
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+
+CONFIG_P54_USB=m
+CONFIG_RTL8187=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_RTL8187_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+
+CONFIG_AT76C50X_USB=m
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_29
+CONFIG_CARL9170=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_CARL9170_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+CONFIG_CARL9170_DEBUGFS=y
+CONFIG_CARL9170_WPC=y
+endif #CONFIG_COMPAT_KERNEL_2_6_29
+
+# This activates a threading fix for usb urb.
+# this is mainline commit: b3e670443b7fb8a2d29831b62b44a039c283e351
+# This fix will be included in some stable releases.
+CONFIG_COMPAT_USB_URB_THREAD_FIX=y
+
+CONFIG_ATH9K_HTC=m
+CONFIG_ATH9K_HTC_DEBUGFS=y
+
+# RT2500USB does not require firmware
+CONFIG_RT2500USB=m
+ifdef CONFIG_CRC_CCITT
+CONFIG_RT2800USB=m
+CONFIG_RT2800USB_RT33XX=y
+CONFIG_RT2800USB_RT35XX=y
+# CONFIG_RT2800USB_RT53XX=y
+CONFIG_RT2800USB_UNKNOWN=y
+endif #CONFIG_CRC_CCITT
+CONFIG_RT2X00_LIB_USB=m
+NEED_RT2X00=y
+# RT73USB requires firmware
+ifdef CONFIG_CRC_ITU_T
+CONFIG_RT73USB=m
+endif #CONFIG_CRC_ITU_T
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_THINFIRM_USB=n
+CONFIG_LIBERTAS_USB=n
+NEED_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_THINFIRM_USB=m
+CONFIG_LIBERTAS_USB=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+CONFIG_ORINOCO_USB=m
+
+CONFIG_BT_HCIBTUSB=m
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_ATH3K=m
+
+CONFIG_RTL8192CU=m
+
+endif #CONFIG_USB end of USB driver list
+
+ifdef CONFIG_SPI_MASTER
+ifndef CONFIG_COMPAT_KERNEL_2_6_25
+
+ifdef CONFIG_CRC7
+CONFIG_WL1251_SPI=m
+CONFIG_WL12XX_SPI=m
+endif #CONFIG_CRC7
+CONFIG_P54_SPI=m
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SPI=n
+NEED_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SPI=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+endif #CONFIG_COMPAT_KERNEL_2_6_25
+endif #CONFIG_SPI_MASTER end of SPI driver list
+
+ifdef CONFIG_MMC
+
+CONFIG_SSB_SDIOHOST=y
+CONFIG_B43_SDIO=y
+
+ifdef CONFIG_CRC7
+ifdef CONFIG_WL12XX_PLATFORM_DATA
+CONFIG_COMPAT_WL1251_SDIO=m
+endif #CONFIG_WL12XX_PLATFORM_DATA
+
+ifndef CONFIG_COMPAT_KERNEL_2_6_32
+ifdef CONFIG_WL12XX_PLATFORM_DATA
+CONFIG_COMPAT_WL12XX_SDIO=m
+endif #CONFIG_WL12XX_PLATFORM_DATA
+endif #CONFIG_COMPAT_KERNEL_2_6_32
+
+endif #CONFIG_CRC7
+
+CONFIG_MWIFIEX_SDIO=m
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SDIO=n
+NEED_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS_SDIO=m
+NEED_LIBERTAS=y
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+CONFIG_IWM=m
+# CONFIG_IWM_DEBUG=y
+
+CONFIG_BT_HCIBTSDIO=m
+CONFIG_BT_MRVL_SDIO=m
+
+ifdef CONFIG_COMPAT_STAGING
+ifdef CONFIG_WIRELESS_EXT
+# CONFIG_ATH6KL_DISABLE_TARGET_DBGLOGS is not set
+# CONFIG_ATH6KL_ENABLE_COEXISTENCE is not set
+# CONFIG_ATH6KL_ENABLE_HOST_DEBUG is not set
+CONFIG_ATH6KL_ENABLE_TARGET_DEBUG_PRINTS=y
+# CONFIG_ATH6KL_HCI_BRIDGE is not set
+# CONFIG_ATH6KL_HTC_RAW_INTERFACE is not set
+# CONFIG_ATH6KL_SKIP_ABI_VERSION_CHECK is not set
+CONFIG_ATH6KL_VIRTUAL_SCATTER_GATHER=y
+CONFIG_ATH6K_LEGACY=m
+
+CONFIG_BRCMFMAC=m
+
+endif #CONFIG_WIRELESS_EXT
+endif #CONFIG_COMPAT_STAGING
+
+
+endif #CONFIG_MMC
+
+CONFIG_RTLWIFI=m
+CONFIG_RTL8192C_COMMON=m
+
+# Common rt2x00 requirements
+ifeq ($(NEED_RT2X00),y)
+CONFIG_RT2X00=y
+CONFIG_RT2X00_LIB=m
+CONFIG_RT2800_LIB=m
+CONFIG_RT2X00_LIB_FIRMWARE=y
+CONFIG_RT2X00_LIB_CRYPTO=y
+# CONFIG_RT2X00_LIB_SOC=y
+ifdef CONFIG_COMPAT_KERNEL_2_6_25
+CONFIG_RT2X00_LIB_LEDS=n
+else #CONFIG_COMPAT_KERNEL_2_6_25
+ifdef CONFIG_LEDS_CLASS
+CONFIG_RT2X00_LIB_LEDS=y
+endif #CONFIG_LEDS_CLASS
+endif #CONFIG_COMPAT_KERNEL_2_6_25
+# CONFIG_RT2X00_DEBUG=y
+# CONFIG_RT2X00_LIB_DEBUGFS
+endif
+
+# p54
+CONFIG_P54_COMMON=m
+ifdef CONFIG_MAC80211_LEDS
+CONFIG_P54_LEDS=y
+endif #CONFIG_MAC80211_LEDS
+
+# Atheros
+CONFIG_ATH_COMMON=m
+CONFIG_ATH_DEBUG=y
+
+ifdef CONFIG_COMPAT_STAGING
+CONFIG_BRCMUTIL=m
+# CONFIG_BRCMDBG=y
+endif #CONFIG_COMPAT_STAGING
+
+ifdef CONFIG_CRC7
+CONFIG_WL1251=m
+CONFIG_WL12XX=m
+CONFIG_WL12XX_HT=y
+endif #CONFIG_CRC7
+
+CONFIG_MWIFIEX=m
+
+ifdef CONFIG_COMPAT_KERNEL_2_6_27
+CONFIG_LIBERTAS=n
+else #CONFIG_COMPAT_KERNEL_2_6_27
+ifeq ($(NEED_LIBERTAS),y)
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_MESH=y
+# CONFIG_LIBERTAS_DEBUG=y
+endif
+endif #CONFIG_COMPAT_KERNEL_2_6_27
+
+# We need the backported rfkill module on kernel < 2.6.31.
+# In more recent kernel versions use the in kernel rfkill module.
+ifdef CONFIG_COMPAT_KERNEL_2_6_31
+CONFIG_RFKILL_BACKPORT=m
+ifdef CONFIG_LEDS_TRIGGERS
+CONFIG_RFKILL_BACKPORT_LEDS=y
+endif #CONFIG_LEDS_TRIGGERS
+CONFIG_RFKILL_BACKPORT_INPUT=y
+endif #CONFIG_COMPAT_KERNEL_2_6_31
+
diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
new file mode 100644
index 0000000..c1172da
--- /dev/null
+++ b/drivers/bcma/Kconfig
@@ -0,0 +1,57 @@
+config BCMA_POSSIBLE
+	bool
+	depends on HAS_IOMEM && HAS_DMA
+	default y
+
+menu "Broadcom specific AMBA"
+	depends on BCMA_POSSIBLE
+
+config BCMA
+	tristate "BCMA support"
+	depends on BCMA_POSSIBLE
+	help
+	  Bus driver for Broadcom specific Advanced Microcontroller Bus
+	  Architecture.
+
+# Support for Block-I/O. SELECT this from the driver that needs it.
+config BCMA_BLOCKIO
+	bool
+	depends on BCMA
+
+config BCMA_HOST_PCI_POSSIBLE
+	bool
+	depends on BCMA && PCI = y
+	default y
+
+config BCMA_HOST_PCI
+	bool "Support for BCMA on PCI-host bus"
+	depends on BCMA_HOST_PCI_POSSIBLE
+
+config BCMA_DRIVER_PCI_HOSTMODE
+	bool "Driver for PCI core working in hostmode"
+	depends on BCMA && MIPS
+	help
+	  PCI core hostmode operation (external PCI bus).
+
+config BCMA_HOST_SOC
+	bool
+	depends on BCMA_DRIVER_MIPS
+
+config BCMA_DRIVER_MIPS
+	bool "BCMA Broadcom MIPS core driver"
+	depends on BCMA && MIPS
+	help
+	  Driver for the Broadcom MIPS core attached to Broadcom specific
+	  Advanced Microcontroller Bus.
+
+	  If unsure, say N
+
+config BCMA_DEBUG
+	bool "BCMA debugging"
+	depends on BCMA
+	help
+	  This turns on additional debugging messages.
+
+	  If unsure, say N
+
+endmenu
diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile
new file mode 100644
index 0000000..82de24e
--- /dev/null
+++ b/drivers/bcma/Makefile
@@ -0,0 +1,10 @@
+bcma-y					+= main.o scan.o core.o sprom.o
+bcma-y					+= driver_chipcommon.o driver_chipcommon_pmu.o
+bcma-y					+= driver_pci.o
+bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE)	+= driver_pci_host.o
+bcma-$(CONFIG_BCMA_DRIVER_MIPS)		+= driver_mips.o
+bcma-$(CONFIG_BCMA_HOST_PCI)		+= host_pci.o
+bcma-$(CONFIG_BCMA_HOST_SOC)		+= host_soc.o
+obj-$(CONFIG_BCMA)			+= bcma.o
+
+ccflags-$(CONFIG_BCMA_DEBUG)		:= -DDEBUG
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
new file mode 100644
index 0000000..30a3085
--- /dev/null
+++ b/drivers/bcma/bcma_private.h
@@ -0,0 +1,51 @@
+#ifndef LINUX_BCMA_PRIVATE_H_
+#define LINUX_BCMA_PRIVATE_H_
+
+#ifndef pr_fmt
+#define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
+#endif
+
+#include <linux/bcma/bcma.h>
+#include <linux/delay.h>
+
+#define BCMA_CORE_SIZE		0x1000
+
+struct bcma_bus;
+
+/* main.c */
+int bcma_bus_register(struct bcma_bus *bus);
+void bcma_bus_unregister(struct bcma_bus *bus);
+int __init bcma_bus_early_register(struct bcma_bus *bus,
+				   struct bcma_device *core_cc,
+				   struct bcma_device *core_mips);
+
+/* scan.c */
+int bcma_bus_scan(struct bcma_bus *bus);
+int __init bcma_bus_scan_early(struct bcma_bus *bus,
+			       struct bcma_device_id *match,
+			       struct bcma_device *core);
+void bcma_init_bus(struct bcma_bus *bus);
+
+/* sprom.c */
+int bcma_sprom_get(struct bcma_bus *bus);
+
+/* driver_chipcommon.c */
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
+
+/* driver_chipcommon_pmu.c */
+u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc);
+u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc);
+
+#ifdef CONFIG_BCMA_HOST_PCI
+/* host_pci.c */
+extern int __init bcma_host_pci_init(void);
+extern void __exit bcma_host_pci_exit(void);
+#endif /* CONFIG_BCMA_HOST_PCI */
+
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
+#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
+
+#endif
diff --git a/drivers/bcma/core.c b/drivers/bcma/core.c
new file mode 100644
index 0000000..893f6e0
--- /dev/null
+++ b/drivers/bcma/core.c
@@ -0,0 +1,127 @@
+/*
+ * Broadcom specific AMBA
+ * Core ops
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/export.h>
+#include <linux/bcma/bcma.h>
+
+bool bcma_core_is_enabled(struct bcma_device *core)
+{
+	if ((bcma_aread32(core, BCMA_IOCTL) & (BCMA_IOCTL_CLK | BCMA_IOCTL_FGC))
+	    != BCMA_IOCTL_CLK)
+		return false;
+	if (bcma_aread32(core, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET)
+		return false;
+	return true;
+}
+EXPORT_SYMBOL_GPL(bcma_core_is_enabled);
+
+void bcma_core_disable(struct bcma_device *core, u32 flags)
+{
+	if (bcma_aread32(core, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET)
+		return;
+
+	bcma_awrite32(core, BCMA_IOCTL, flags);
+	bcma_aread32(core, BCMA_IOCTL);
+	udelay(10);
+
+	bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
+	udelay(1);
+}
+EXPORT_SYMBOL_GPL(bcma_core_disable);
+
+int bcma_core_enable(struct bcma_device *core, u32 flags)
+{
+	bcma_core_disable(core, flags);
+
+	bcma_awrite32(core, BCMA_IOCTL, (BCMA_IOCTL_CLK | BCMA_IOCTL_FGC | flags));
+	bcma_aread32(core, BCMA_IOCTL);
+
+	bcma_awrite32(core, BCMA_RESET_CTL, 0);
+	udelay(1);
+
+	bcma_awrite32(core, BCMA_IOCTL, (BCMA_IOCTL_CLK | flags));
+	bcma_aread32(core, BCMA_IOCTL);
+	udelay(1);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(bcma_core_enable);
+
+void bcma_core_set_clockmode(struct bcma_device *core,
+			     enum bcma_clkmode clkmode)
+{
+	u16 i;
+
+	WARN_ON(core->id.id != BCMA_CORE_CHIPCOMMON &&
+		core->id.id != BCMA_CORE_PCIE &&
+		core->id.id != BCMA_CORE_80211);
+
+	switch (clkmode) {
+	case BCMA_CLKMODE_FAST:
+		bcma_set32(core, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT);
+		udelay(64);
+		for (i = 0; i < 1500; i++) {
+			if (bcma_read32(core, BCMA_CLKCTLST) &
+			    BCMA_CLKCTLST_HAVEHT) {
+				i = 0;
+				break;
+			}
+			udelay(10);
+		}
+		if (i)
+			pr_err("HT force timeout\n");
+		break;
+	case BCMA_CLKMODE_DYNAMIC:
+		pr_warn("Dynamic clockmode not supported yet!\n");
+		break;
+	}
+}
+EXPORT_SYMBOL_GPL(bcma_core_set_clockmode);
+
+void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, bool on)
+{
+	u16 i;
+
+	WARN_ON(req & ~BCMA_CLKCTLST_EXTRESREQ);
+	WARN_ON(status & ~BCMA_CLKCTLST_EXTRESST);
+
+	if (on) {
+		bcma_set32(core, BCMA_CLKCTLST, req);
+		for (i = 0; i < 10000; i++) {
+			if ((bcma_read32(core, BCMA_CLKCTLST) & status) ==
+			    status) {
+				i = 0;
+				break;
+			}
+			udelay(10);
+		}
+		if (i)
+			pr_err("PLL enable timeout\n");
+	} else {
+		pr_warn("Disabling PLL not supported yet!\n");
+	}
+}
+EXPORT_SYMBOL_GPL(bcma_core_pll_ctl);
+
+u32 bcma_core_dma_translation(struct bcma_device *core)
+{
+	switch (core->bus->hosttype) {
+	case BCMA_HOSTTYPE_SOC:
+		return 0;
+	case BCMA_HOSTTYPE_PCI:
+		if (bcma_aread32(core, BCMA_IOST) & BCMA_IOST_DMA64)
+			return BCMA_DMA_TRANSLATION_DMA64_CMT;
+		else
+			return BCMA_DMA_TRANSLATION_DMA32_CMT;
+	default:
+		pr_err("DMA translation unknown for host %d\n",
+		       core->bus->hosttype);
+	}
+	return BCMA_DMA_TRANSLATION_NONE;
+}
+EXPORT_SYMBOL(bcma_core_dma_translation);
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
new file mode 100644
index 0000000..e9f1b3f
--- /dev/null
+++ b/drivers/bcma/driver_chipcommon.c
@@ -0,0 +1,157 @@
+/*
+ * Broadcom specific AMBA
+ * ChipCommon core driver
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/export.h>
+#include <linux/bcma/bcma.h>
+
+static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset,
+					 u32 mask, u32 value)
+{
+	value &= mask;
+	value |= bcma_cc_read32(cc, offset) & ~mask;
+	bcma_cc_write32(cc, offset, value);
+
+	return value;
+}
+
+void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
+{
+	u32 leddc_on = 10;
+	u32 leddc_off = 90;
+
+	if (cc->setup_done)
+		return;
+
+	if (cc->core->id.rev >= 11)
+		cc->status = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
+	cc->capabilities = bcma_cc_read32(cc, BCMA_CC_CAP);
+	if (cc->core->id.rev >= 35)
+		cc->capabilities_ext = bcma_cc_read32(cc, BCMA_CC_CAP_EXT);
+
+	if (cc->core->id.rev >= 20) {
+		bcma_cc_write32(cc, BCMA_CC_GPIOPULLUP, 0);
+		bcma_cc_write32(cc, BCMA_CC_GPIOPULLDOWN, 0);
+	}
+
+	if (cc->capabilities & BCMA_CC_CAP_PMU)
+		bcma_pmu_init(cc);
+	if (cc->capabilities & BCMA_CC_CAP_PCTL)
+		pr_err("Power control not implemented!\n");
+
+	if (cc->core->id.rev >= 16) {
+		if (cc->core->bus->sprom.leddc_on_time &&
+		    cc->core->bus->sprom.leddc_off_time) {
+			leddc_on = cc->core->bus->sprom.leddc_on_time;
+			leddc_off = cc->core->bus->sprom.leddc_off_time;
+		}
+		bcma_cc_write32(cc, BCMA_CC_GPIOTIMER,
+			((leddc_on << BCMA_CC_GPIOTIMER_ONTIME_SHIFT) |
+			 (leddc_off << BCMA_CC_GPIOTIMER_OFFTIME_SHIFT)));
+	}
+
+	cc->setup_done = true;
+}
+
+/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
+void bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
+{
+	/* instant NMI */
+	bcma_cc_write32(cc, BCMA_CC_WATCHDOG, ticks);
+}
+
+void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+	bcma_cc_write32_masked(cc, BCMA_CC_IRQMASK, mask, value);
+}
+
+u32 bcma_chipco_irq_status(struct bcma_drv_cc *cc, u32 mask)
+{
+	return bcma_cc_read32(cc, BCMA_CC_IRQSTAT) & mask;
+}
+
+u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask)
+{
+	return bcma_cc_read32(cc, BCMA_CC_GPIOIN) & mask;
+}
+
+u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+	return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUT, mask, value);
+}
+
+u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+	return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUTEN, mask, value);
+}
+
+u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+	return bcma_cc_write32_masked(cc, BCMA_CC_GPIOCTL, mask, value);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_gpio_control);
+
+u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+	return bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value);
+}
+
+u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+	return bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value);
+}
+
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
+{
+	unsigned int irq;
+	u32 baud_base;
+	u32 i;
+	unsigned int ccrev = cc->core->id.rev;
+	struct bcma_serial_port *ports = cc->serial_ports;
+
+	if (ccrev >= 11 && ccrev != 15) {
+		/* Fixed ALP clock */
+		baud_base = bcma_pmu_alp_clock(cc);
+		if (ccrev >= 21) {
+			/* Turn off UART clock before switching clocksource. */
+			bcma_cc_write32(cc, BCMA_CC_CORECTL,
+				       bcma_cc_read32(cc, BCMA_CC_CORECTL)
+				       & ~BCMA_CC_CORECTL_UARTCLKEN);
+		}
+		/* Set the override bit so we don't divide it */
+		bcma_cc_write32(cc, BCMA_CC_CORECTL,
+			       bcma_cc_read32(cc, BCMA_CC_CORECTL)
+			       | BCMA_CC_CORECTL_UARTCLK0);
+		if (ccrev >= 21) {
+			/* Re-enable the UART clock. */
+			bcma_cc_write32(cc, BCMA_CC_CORECTL,
+				       bcma_cc_read32(cc, BCMA_CC_CORECTL)
+				       | BCMA_CC_CORECTL_UARTCLKEN);
+		}
+	} else {
+		pr_err("serial not supported on this device ccrev: 0x%x\n",
+		       ccrev);
+		return;
+	}
+
+	irq = bcma_core_mips_irq(cc->core);
+
+	/* Determine the registers of the UARTs */
+	cc->nr_serial_ports = (cc->capabilities & BCMA_CC_CAP_NRUART);
+	for (i = 0; i < cc->nr_serial_ports; i++) {
+		ports[i].regs = cc->core->io_addr + BCMA_CC_UART0_DATA +
+				(i * 256);
+		ports[i].irq = irq;
+		ports[i].baud_base = baud_base;
+		ports[i].reg_shift = 0;
+	}
+}
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c
new file mode 100644
index 0000000..800163c
--- /dev/null
+++ b/drivers/bcma/driver_chipcommon_pmu.c
@@ -0,0 +1,310 @@
+/*
+ * Broadcom specific AMBA
+ * ChipCommon Power Management Unit driver
+ *
+ * Copyright 2009, Michael Buesch <m@bues.ch>
+ * Copyright 2007, Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/export.h>
+#include <linux/bcma/bcma.h>
+
+static u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset)
+{
+	bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+	bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+	return bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA);
+}
+
+void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value)
+{
+	bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+	bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+	bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_pll_write);
+
+void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
+			     u32 set)
+{
+	bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+	bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+	bcma_cc_maskset32(cc, BCMA_CC_PLLCTL_DATA, mask, set);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_pll_maskset);
+
+void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc,
+				 u32 offset, u32 mask, u32 set)
+{
+	bcma_cc_write32(cc, BCMA_CC_CHIPCTL_ADDR, offset);
+	bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR);
+	bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL_DATA, mask, set);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_chipctl_maskset);
+
+void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
+				u32 set)
+{
+	bcma_cc_write32(cc, BCMA_CC_REGCTL_ADDR, offset);
+	bcma_cc_read32(cc, BCMA_CC_REGCTL_ADDR);
+	bcma_cc_maskset32(cc, BCMA_CC_REGCTL_DATA, mask, set);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset);
+
+static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	switch (bus->chipinfo.id) {
+	case 0x4313:
+	case 0x4331:
+	case 43224:
+	case 43225:
+		break;
+	default:
+		pr_err("PLL init unknown for device 0x%04X\n",
+			bus->chipinfo.id);
+	}
+}
+
+static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+	u32 min_msk = 0, max_msk = 0;
+
+	switch (bus->chipinfo.id) {
+	case 0x4313:
+		min_msk = 0x200D;
+		max_msk = 0xFFFF;
+		break;
+	case 43224:
+	case 43225:
+		break;
+	default:
+		pr_err("PMU resource config unknown for device 0x%04X\n",
+			bus->chipinfo.id);
+	}
+
+	/* Set the resource masks. */
+	if (min_msk)
+		bcma_cc_write32(cc, BCMA_CC_PMU_MINRES_MSK, min_msk);
+	if (max_msk)
+		bcma_cc_write32(cc, BCMA_CC_PMU_MAXRES_MSK, max_msk);
+}
+
+void bcma_pmu_swreg_init(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	switch (bus->chipinfo.id) {
+	case 0x4313:
+	case 0x4331:
+	case 43224:
+	case 43225:
+		break;
+	default:
+		pr_err("PMU switch/regulators init unknown for device "
+			"0x%04X\n", bus->chipinfo.id);
+	}
+}
+
+/* Disable to allow reading SPROM. Don't know the adventages of enabling it. */
+void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable)
+{
+	struct bcma_bus *bus = cc->core->bus;
+	u32 val;
+
+	val = bcma_cc_read32(cc, BCMA_CC_CHIPCTL);
+	if (enable) {
+		val |= BCMA_CHIPCTL_4331_EXTPA_EN;
+		if (bus->chipinfo.pkg == 9 || bus->chipinfo.pkg == 11)
+			val |= BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5;
+	} else {
+		val &= ~BCMA_CHIPCTL_4331_EXTPA_EN;
+		val &= ~BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5;
+	}
+	bcma_cc_write32(cc, BCMA_CC_CHIPCTL, val);
+}
+
+void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	switch (bus->chipinfo.id) {
+	case 0x4313:
+		bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x7);
+		break;
+	case 0x4331:
+		/* BCM4331 workaround is SPROM-related, we put it in sprom.c */
+		break;
+	case 43224:
+		if (bus->chipinfo.rev == 0) {
+			pr_err("Workarounds for 43224 rev 0 not fully "
+				"implemented\n");
+			bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x00F000F0);
+		} else {
+			bcma_chipco_chipctl_maskset(cc, 0, ~0, 0xF0);
+		}
+		break;
+	case 43225:
+		break;
+	default:
+		pr_err("Workarounds unknown for device 0x%04X\n",
+			bus->chipinfo.id);
+	}
+}
+
+void bcma_pmu_init(struct bcma_drv_cc *cc)
+{
+	u32 pmucap;
+
+	pmucap = bcma_cc_read32(cc, BCMA_CC_PMU_CAP);
+	cc->pmu.rev = (pmucap & BCMA_CC_PMU_CAP_REVISION);
+
+	pr_debug("Found rev %u PMU (capabilities 0x%08X)\n", cc->pmu.rev,
+		 pmucap);
+
+	if (cc->pmu.rev == 1)
+		bcma_cc_mask32(cc, BCMA_CC_PMU_CTL,
+			      ~BCMA_CC_PMU_CTL_NOILPONW);
+	else
+		bcma_cc_set32(cc, BCMA_CC_PMU_CTL,
+			     BCMA_CC_PMU_CTL_NOILPONW);
+
+	if (cc->core->id.id == 0x4329 && cc->core->id.rev == 2)
+		pr_err("Fix for 4329b0 bad LPOM state not implemented!\n");
+
+	bcma_pmu_pll_init(cc);
+	bcma_pmu_resources_init(cc);
+	bcma_pmu_swreg_init(cc);
+	bcma_pmu_workarounds(cc);
+}
+
+u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	switch (bus->chipinfo.id) {
+	case 0x4716:
+	case 0x4748:
+	case 47162:
+	case 0x4313:
+	case 0x5357:
+	case 0x4749:
+	case 53572:
+		/* always 20Mhz */
+		return 20000 * 1000;
+	case 0x5356:
+	case 0x5300:
+		/* always 25Mhz */
+		return 25000 * 1000;
+	default:
+		pr_warn("No ALP clock specified for %04X device, "
+			"pmu rev. %d, using default %d Hz\n",
+			bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_ALP_CLOCK);
+	}
+	return BCMA_CC_PMU_ALP_CLOCK;
+}
+
+/* Find the output of the "m" pll divider given pll controls that start with
+ * pllreg "pll0" i.e. 12 for main 6 for phy, 0 for misc.
+ */
+static u32 bcma_pmu_clock(struct bcma_drv_cc *cc, u32 pll0, u32 m)
+{
+	u32 tmp, div, ndiv, p1, p2, fc;
+	struct bcma_bus *bus = cc->core->bus;
+
+	BUG_ON((pll0 & 3) || (pll0 > BCMA_CC_PMU4716_MAINPLL_PLL0));
+
+	BUG_ON(!m || m > 4);
+
+	if (bus->chipinfo.id == 0x5357 || bus->chipinfo.id == 0x4749) {
+		/* Detect failure in clock setting */
+		tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
+		if (tmp & 0x40000)
+			return 133 * 1000000;
+	}
+
+	tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_P1P2_OFF);
+	p1 = (tmp & BCMA_CC_PPL_P1_MASK) >> BCMA_CC_PPL_P1_SHIFT;
+	p2 = (tmp & BCMA_CC_PPL_P2_MASK) >> BCMA_CC_PPL_P2_SHIFT;
+
+	tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_M14_OFF);
+	div = (tmp >> ((m - 1) * BCMA_CC_PPL_MDIV_WIDTH)) &
+		BCMA_CC_PPL_MDIV_MASK;
+
+	tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_NM5_OFF);
+	ndiv = (tmp & BCMA_CC_PPL_NDIV_MASK) >> BCMA_CC_PPL_NDIV_SHIFT;
+
+	/* Do calculation in Mhz */
+	fc = bcma_pmu_alp_clock(cc) / 1000000;
+	fc = (p1 * ndiv * fc) / p2;
+
+	/* Return clock in Hertz */
+	return (fc / div) * 1000000;
+}
+
+/* query bus clock frequency for PMU-enabled chipcommon */
+u32 bcma_pmu_get_clockcontrol(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	switch (bus->chipinfo.id) {
+	case 0x4716:
+	case 0x4748:
+	case 47162:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU4716_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 0x5356:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU5356_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 0x5357:
+	case 0x4749:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU5357_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 0x5300:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU4706_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 53572:
+		return 75000000;
+	default:
+		pr_warn("No backplane clock specified for %04X device, "
+			"pmu rev. %d, using default %d Hz\n",
+			bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_HT_CLOCK);
+	}
+	return BCMA_CC_PMU_HT_CLOCK;
+}
+
+/* query cpu clock frequency for PMU-enabled chipcommon */
+u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	if (bus->chipinfo.id == 53572)
+		return 300000000;
+
+	if (cc->pmu.rev >= 5) {
+		u32 pll;
+		switch (bus->chipinfo.id) {
+		case 0x5356:
+			pll = BCMA_CC_PMU5356_MAINPLL_PLL0;
+			break;
+		case 0x5357:
+		case 0x4749:
+			pll = BCMA_CC_PMU5357_MAINPLL_PLL0;
+			break;
+		default:
+			pll = BCMA_CC_PMU4716_MAINPLL_PLL0;
+			break;
+		}
+
+		/* TODO: if (bus->chipinfo.id == 0x5300)
+		  return si_4706_pmu_clock(sih, osh, cc, PMU4706_MAINPLL_PLL0, PMU5_MAINPLL_CPU); */
+		return bcma_pmu_clock(cc, pll, BCMA_CC_PMU5_MAINPLL_CPU);
+	}
+
+	return bcma_pmu_get_clockcontrol(cc);
+}
diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c
new file mode 100644
index 0000000..c3e9dff
--- /dev/null
+++ b/drivers/bcma/driver_mips.c
@@ -0,0 +1,256 @@
+/*
+ * Broadcom specific AMBA
+ * Broadcom MIPS32 74K core driver
+ *
+ * Copyright 2009, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ * Copyright 2010, Bernhard Loos <bernhardloos@googlemail.com>
+ * Copyright 2011, Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+
+#include <linux/bcma/bcma.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+
+/* The 47162a0 hangs when reading MIPS DMP registers registers */
+static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev)
+{
+	return dev->bus->chipinfo.id == 47162 && dev->bus->chipinfo.rev == 0 &&
+	       dev->id.id == BCMA_CORE_MIPS_74K;
+}
+
+/* The 5357b0 hangs when reading USB20H DMP registers */
+static inline bool bcma_core_mips_bcm5357b0_quirk(struct bcma_device *dev)
+{
+	return (dev->bus->chipinfo.id == 0x5357 ||
+		dev->bus->chipinfo.id == 0x4749) &&
+	       dev->bus->chipinfo.pkg == 11 &&
+	       dev->id.id == BCMA_CORE_USB20_HOST;
+}
+
+static inline u32 mips_read32(struct bcma_drv_mips *mcore,
+			      u16 offset)
+{
+	return bcma_read32(mcore->core, offset);
+}
+
+static inline void mips_write32(struct bcma_drv_mips *mcore,
+				u16 offset,
+				u32 value)
+{
+	bcma_write32(mcore->core, offset, value);
+}
+
+static const u32 ipsflag_irq_mask[] = {
+	0,
+	BCMA_MIPS_IPSFLAG_IRQ1,
+	BCMA_MIPS_IPSFLAG_IRQ2,
+	BCMA_MIPS_IPSFLAG_IRQ3,
+	BCMA_MIPS_IPSFLAG_IRQ4,
+};
+
+static const u32 ipsflag_irq_shift[] = {
+	0,
+	BCMA_MIPS_IPSFLAG_IRQ1_SHIFT,
+	BCMA_MIPS_IPSFLAG_IRQ2_SHIFT,
+	BCMA_MIPS_IPSFLAG_IRQ3_SHIFT,
+	BCMA_MIPS_IPSFLAG_IRQ4_SHIFT,
+};
+
+static u32 bcma_core_mips_irqflag(struct bcma_device *dev)
+{
+	u32 flag;
+
+	if (bcma_core_mips_bcm47162a0_quirk(dev))
+		return dev->core_index;
+	if (bcma_core_mips_bcm5357b0_quirk(dev))
+		return dev->core_index;
+	flag = bcma_aread32(dev, BCMA_MIPS_OOBSELOUTA30);
+
+	return flag & 0x1F;
+}
+
+/* Get the MIPS IRQ assignment for a specified device.
+ * If unassigned, 0 is returned.
+ */
+unsigned int bcma_core_mips_irq(struct bcma_device *dev)
+{
+	struct bcma_device *mdev = dev->bus->drv_mips.core;
+	u32 irqflag;
+	unsigned int irq;
+
+	irqflag = bcma_core_mips_irqflag(dev);
+
+	for (irq = 1; irq <= 4; irq++)
+		if (bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq)) &
+		    (1 << irqflag))
+			return irq;
+
+	return 0;
+}
+EXPORT_SYMBOL(bcma_core_mips_irq);
+
+static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
+{
+	unsigned int oldirq = bcma_core_mips_irq(dev);
+	struct bcma_bus *bus = dev->bus;
+	struct bcma_device *mdev = bus->drv_mips.core;
+	u32 irqflag;
+
+	irqflag = bcma_core_mips_irqflag(dev);
+	BUG_ON(oldirq == 6);
+
+	dev->irq = irq + 2;
+
+	/* clear the old irq */
+	if (oldirq == 0)
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0),
+			    bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) &
+			    ~(1 << irqflag));
+	else
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq), 0);
+
+	/* assign the new one */
+	if (irq == 0) {
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0),
+			    bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) |
+			    (1 << irqflag));
+	} else {
+		u32 oldirqflag = bcma_read32(mdev,
+					     BCMA_MIPS_MIPS74K_INTMASK(irq));
+		if (oldirqflag) {
+			struct bcma_device *core;
+
+			/* backplane irq line is in use, find out who uses
+			 * it and set user to irq 0
+			 */
+			list_for_each_entry_reverse(core, &bus->cores, list) {
+				if ((1 << bcma_core_mips_irqflag(core)) ==
+				    oldirqflag) {
+					bcma_core_mips_set_irq(core, 0);
+					break;
+				}
+			}
+		}
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq),
+			     1 << irqflag);
+	}
+
+	pr_info("set_irq: core 0x%04x, irq %d => %d\n",
+		dev->id.id, oldirq + 2, irq + 2);
+}
+
+static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq)
+{
+	int i;
+	static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"};
+	printk(KERN_INFO KBUILD_MODNAME ": core 0x%04x, irq :", dev->id.id);
+	for (i = 0; i <= 6; i++)
+		printk(" %s%s", irq_name[i], i == irq ? "*" : " ");
+	printk("\n");
+}
+
+static void bcma_core_mips_dump_irq(struct bcma_bus *bus)
+{
+	struct bcma_device *core;
+
+	list_for_each_entry_reverse(core, &bus->cores, list) {
+		bcma_core_mips_print_irq(core, bcma_core_mips_irq(core));
+	}
+}
+
+u32 bcma_cpu_clock(struct bcma_drv_mips *mcore)
+{
+	struct bcma_bus *bus = mcore->core->bus;
+
+	if (bus->drv_cc.capabilities & BCMA_CC_CAP_PMU)
+		return bcma_pmu_get_clockcpu(&bus->drv_cc);
+
+	pr_err("No PMU available, need this to get the cpu clock\n");
+	return 0;
+}
+EXPORT_SYMBOL(bcma_cpu_clock);
+
+static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
+{
+	struct bcma_bus *bus = mcore->core->bus;
+
+	switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) {
+	case BCMA_CC_FLASHT_STSER:
+	case BCMA_CC_FLASHT_ATSER:
+		pr_err("Serial flash not supported.\n");
+		break;
+	case BCMA_CC_FLASHT_PARA:
+		pr_info("found parallel flash.\n");
+		bus->drv_cc.pflash.window = 0x1c000000;
+		bus->drv_cc.pflash.window_size = 0x02000000;
+
+		if ((bcma_read32(bus->drv_cc.core, BCMA_CC_FLASH_CFG) &
+		     BCMA_CC_FLASH_CFG_DS) == 0)
+			bus->drv_cc.pflash.buswidth = 1;
+		else
+			bus->drv_cc.pflash.buswidth = 2;
+		break;
+	default:
+		pr_err("flash not supported.\n");
+	}
+}
+
+void bcma_core_mips_init(struct bcma_drv_mips *mcore)
+{
+	struct bcma_bus *bus;
+	struct bcma_device *core;
+	bus = mcore->core->bus;
+
+	pr_info("Initializing MIPS core...\n");
+
+	if (!mcore->setup_done)
+		mcore->assigned_irqs = 1;
+
+	/* Assign IRQs to all cores on the bus */
+	list_for_each_entry_reverse(core, &bus->cores, list) {
+		int mips_irq;
+		if (core->irq)
+			continue;
+
+		mips_irq = bcma_core_mips_irq(core);
+		if (mips_irq > 4)
+			core->irq = 0;
+		else
+			core->irq = mips_irq + 2;
+		if (core->irq > 5)
+			continue;
+		switch (core->id.id) {
+		case BCMA_CORE_PCI:
+		case BCMA_CORE_PCIE:
+		case BCMA_CORE_ETHERNET:
+		case BCMA_CORE_ETHERNET_GBIT:
+		case BCMA_CORE_MAC_GBIT:
+		case BCMA_CORE_80211:
+		case BCMA_CORE_USB20_HOST:
+			/* These devices get their own IRQ line if available,
+			 * the rest goes on IRQ0
+			 */
+			if (mcore->assigned_irqs <= 4)
+				bcma_core_mips_set_irq(core,
+						       mcore->assigned_irqs++);
+			break;
+		}
+	}
+	pr_info("IRQ reconfiguration done\n");
+	bcma_core_mips_dump_irq(bus);
+
+	if (mcore->setup_done)
+		return;
+
+	bcma_chipco_serial_init(&bus->drv_cc);
+	bcma_core_mips_flash_detect(mcore);
+	mcore->setup_done = true;
+}
diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c
new file mode 100644
index 0000000..4fde625
--- /dev/null
+++ b/drivers/bcma/driver_pci.c
@@ -0,0 +1,238 @@
+/*
+ * Broadcom specific AMBA
+ * PCI Core
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/export.h>
+#include <linux/bcma/bcma.h>
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+static u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address)
+{
+	pcicore_write32(pc, 0x130, address);
+	pcicore_read32(pc, 0x130);
+	return pcicore_read32(pc, 0x134);
+}
+
+#if 0
+static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data)
+{
+	pcicore_write32(pc, 0x130, address);
+	pcicore_read32(pc, 0x130);
+	pcicore_write32(pc, 0x134, data);
+}
+#endif
+
+static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy)
+{
+	const u16 mdio_control = 0x128;
+	const u16 mdio_data = 0x12C;
+	u32 v;
+	int i;
+
+	v = (1 << 30); /* Start of Transaction */
+	v |= (1 << 28); /* Write Transaction */
+	v |= (1 << 17); /* Turnaround */
+	v |= (0x1F << 18);
+	v |= (phy << 4);
+	pcicore_write32(pc, mdio_data, v);
+
+	udelay(10);
+	for (i = 0; i < 200; i++) {
+		v = pcicore_read32(pc, mdio_control);
+		if (v & 0x100 /* Trans complete */)
+			break;
+		msleep(1);
+	}
+}
+
+static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address)
+{
+	const u16 mdio_control = 0x128;
+	const u16 mdio_data = 0x12C;
+	int max_retries = 10;
+	u16 ret = 0;
+	u32 v;
+	int i;
+
+	v = 0x80; /* Enable Preamble Sequence */
+	v |= 0x2; /* MDIO Clock Divisor */
+	pcicore_write32(pc, mdio_control, v);
+
+	if (pc->core->id.rev >= 10) {
+		max_retries = 200;
+		bcma_pcie_mdio_set_phy(pc, device);
+	}
+
+	v = (1 << 30); /* Start of Transaction */
+	v |= (1 << 29); /* Read Transaction */
+	v |= (1 << 17); /* Turnaround */
+	if (pc->core->id.rev < 10)
+		v |= (u32)device << 22;
+	v |= (u32)address << 18;
+	pcicore_write32(pc, mdio_data, v);
+	/* Wait for the device to complete the transaction */
+	udelay(10);
+	for (i = 0; i < max_retries; i++) {
+		v = pcicore_read32(pc, mdio_control);
+		if (v & 0x100 /* Trans complete */) {
+			udelay(10);
+			ret = pcicore_read32(pc, mdio_data);
+			break;
+		}
+		msleep(1);
+	}
+	pcicore_write32(pc, mdio_control, 0);
+	return ret;
+}
+
+static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device,
+				u8 address, u16 data)
+{
+	const u16 mdio_control = 0x128;
+	const u16 mdio_data = 0x12C;
+	int max_retries = 10;
+	u32 v;
+	int i;
+
+	v = 0x80; /* Enable Preamble Sequence */
+	v |= 0x2; /* MDIO Clock Divisor */
+	pcicore_write32(pc, mdio_control, v);
+
+	if (pc->core->id.rev >= 10) {
+		max_retries = 200;
+		bcma_pcie_mdio_set_phy(pc, device);
+	}
+
+	v = (1 << 30); /* Start of Transaction */
+	v |= (1 << 28); /* Write Transaction */
+	v |= (1 << 17); /* Turnaround */
+	if (pc->core->id.rev < 10)
+		v |= (u32)device << 22;
+	v |= (u32)address << 18;
+	v |= data;
+	pcicore_write32(pc, mdio_data, v);
+	/* Wait for the device to complete the transaction */
+	udelay(10);
+	for (i = 0; i < max_retries; i++) {
+		v = pcicore_read32(pc, mdio_control);
+		if (v & 0x100 /* Trans complete */)
+			break;
+		msleep(1);
+	}
+	pcicore_write32(pc, mdio_control, 0);
+}
+
+/**************************************************
+ * Workarounds.
+ **************************************************/
+
+static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc)
+{
+	return (bcma_pcie_read(pc, 0x204) & 0x10) ? 0xC0 : 0x80;
+}
+
+static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
+{
+	const u8 serdes_pll_device = 0x1D;
+	const u8 serdes_rx_device = 0x1F;
+	u16 tmp;
+
+	bcma_pcie_mdio_write(pc, serdes_rx_device, 1 /* Control */,
+			      bcma_pcicore_polarity_workaround(pc));
+	tmp = bcma_pcie_mdio_read(pc, serdes_pll_device, 1 /* Control */);
+	if (tmp & 0x4000)
+		bcma_pcie_mdio_write(pc, serdes_pll_device, 1, tmp & ~0x4000);
+}
+
+/**************************************************
+ * Init.
+ **************************************************/
+
+static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
+{
+	bcma_pcicore_serdes_workaround(pc);
+}
+
+static bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
+{
+	struct bcma_bus *bus = pc->core->bus;
+	u16 chipid_top;
+
+	chipid_top = (bus->chipinfo.id & 0xFF00);
+	if (chipid_top != 0x4700 &&
+	    chipid_top != 0x5300)
+		return false;
+
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+	if (bus->sprom.boardflags_lo & SSB_BFL_NOPCI)
+		return false;
+#endif /* CONFIG_SSB_DRIVER_PCICORE */
+
+#if 0
+	/* TODO: on BCMA we use address from EROM instead of magic formula */
+	u32 tmp;
+	return !mips_busprobe32(tmp, (bus->mmio +
+		(pc->core->core_index * BCMA_CORE_SIZE)));
+#endif
+
+	return true;
+}
+
+void bcma_core_pci_init(struct bcma_drv_pci *pc)
+{
+	if (pc->setup_done)
+		return;
+
+	if (bcma_core_pci_is_in_hostmode(pc)) {
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+		bcma_core_pci_hostmode_init(pc);
+#else
+		pr_err("Driver compiled without support for hostmode PCI\n");
+#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
+	} else {
+		bcma_core_pci_clientmode_init(pc);
+	}
+
+	pc->setup_done = true;
+}
+
+int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
+			  bool enable)
+{
+	struct pci_dev *pdev = pc->core->bus->host_pci;
+	u32 coremask, tmp;
+	int err = 0;
+
+	if (core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
+		/* This bcma device is not on a PCI host-bus. So the IRQs are
+		 * not routed through the PCI core.
+		 * So we must not enable routing through the PCI core. */
+		goto out;
+	}
+
+	err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
+	if (err)
+		goto out;
+
+	coremask = BIT(core->core_index) << 8;
+	if (enable)
+		tmp |= coremask;
+	else
+		tmp &= ~coremask;
+
+	err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
+
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c
new file mode 100644
index 0000000..eb332b7
--- /dev/null
+++ b/drivers/bcma/driver_pci_host.c
@@ -0,0 +1,14 @@
+/*
+ * Broadcom specific AMBA
+ * PCI Core in hostmode
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/bcma/bcma.h>
+
+void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
+{
+	pr_err("No support for PCI core in hostmode yet\n");
+}
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
new file mode 100644
index 0000000..1b51d8b
--- /dev/null
+++ b/drivers/bcma/host_pci.c
@@ -0,0 +1,252 @@
+/*
+ * Broadcom specific AMBA
+ * PCI Host
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/slab.h>
+#include <linux/bcma/bcma.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+
+static void bcma_host_pci_switch_core(struct bcma_device *core)
+{
+	pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN,
+			       core->addr);
+	pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN2,
+			       core->wrap);
+	core->bus->mapped_core = core;
+	pr_debug("Switched to core: 0x%X\n", core->id.id);
+}
+
+static u8 bcma_host_pci_read8(struct bcma_device *core, u16 offset)
+{
+	if (core->bus->mapped_core != core)
+		bcma_host_pci_switch_core(core);
+	return ioread8(core->bus->mmio + offset);
+}
+
+static u16 bcma_host_pci_read16(struct bcma_device *core, u16 offset)
+{
+	if (core->bus->mapped_core != core)
+		bcma_host_pci_switch_core(core);
+	return ioread16(core->bus->mmio + offset);
+}
+
+static u32 bcma_host_pci_read32(struct bcma_device *core, u16 offset)
+{
+	if (core->bus->mapped_core != core)
+		bcma_host_pci_switch_core(core);
+	return ioread32(core->bus->mmio + offset);
+}
+
+static void bcma_host_pci_write8(struct bcma_device *core, u16 offset,
+				 u8 value)
+{
+	if (core->bus->mapped_core != core)
+		bcma_host_pci_switch_core(core);
+	iowrite8(value, core->bus->mmio + offset);
+}
+
+static void bcma_host_pci_write16(struct bcma_device *core, u16 offset,
+				 u16 value)
+{
+	if (core->bus->mapped_core != core)
+		bcma_host_pci_switch_core(core);
+	iowrite16(value, core->bus->mmio + offset);
+}
+
+static void bcma_host_pci_write32(struct bcma_device *core, u16 offset,
+				 u32 value)
+{
+	if (core->bus->mapped_core != core)
+		bcma_host_pci_switch_core(core);
+	iowrite32(value, core->bus->mmio + offset);
+}
+
+#ifdef CONFIG_BCMA_BLOCKIO
+void bcma_host_pci_block_read(struct bcma_device *core, void *buffer,
+			      size_t count, u16 offset, u8 reg_width)
+{
+	void __iomem *addr = core->bus->mmio + offset;
+	if (core->bus->mapped_core != core)
+		bcma_host_pci_switch_core(core);
+	switch (reg_width) {
+	case sizeof(u8):
+		ioread8_rep(addr, buffer, count);
+		break;
+	case sizeof(u16):
+		WARN_ON(count & 1);
+		ioread16_rep(addr, buffer, count >> 1);
+		break;
+	case sizeof(u32):
+		WARN_ON(count & 3);
+		ioread32_rep(addr, buffer, count >> 2);
+		break;
+	default:
+		WARN_ON(1);
+	}
+}
+
+void bcma_host_pci_block_write(struct bcma_device *core, const void *buffer,
+			       size_t count, u16 offset, u8 reg_width)
+{
+	void __iomem *addr = core->bus->mmio + offset;
+	if (core->bus->mapped_core != core)
+		bcma_host_pci_switch_core(core);
+	switch (reg_width) {
+	case sizeof(u8):
+		iowrite8_rep(addr, buffer, count);
+		break;
+	case sizeof(u16):
+		WARN_ON(count & 1);
+		iowrite16_rep(addr, buffer, count >> 1);
+		break;
+	case sizeof(u32):
+		WARN_ON(count & 3);
+		iowrite32_rep(addr, buffer, count >> 2);
+		break;
+	default:
+		WARN_ON(1);
+	}
+}
+#endif
+
+static u32 bcma_host_pci_aread32(struct bcma_device *core, u16 offset)
+{
+	if (core->bus->mapped_core != core)
+		bcma_host_pci_switch_core(core);
+	return ioread32(core->bus->mmio + (1 * BCMA_CORE_SIZE) + offset);
+}
+
+static void bcma_host_pci_awrite32(struct bcma_device *core, u16 offset,
+				  u32 value)
+{
+	if (core->bus->mapped_core != core)
+		bcma_host_pci_switch_core(core);
+	iowrite32(value, core->bus->mmio + (1 * BCMA_CORE_SIZE) + offset);
+}
+
+const struct bcma_host_ops bcma_host_pci_ops = {
+	.read8		= bcma_host_pci_read8,
+	.read16		= bcma_host_pci_read16,
+	.read32		= bcma_host_pci_read32,
+	.write8		= bcma_host_pci_write8,
+	.write16	= bcma_host_pci_write16,
+	.write32	= bcma_host_pci_write32,
+#ifdef CONFIG_BCMA_BLOCKIO
+	.block_read	= bcma_host_pci_block_read,
+	.block_write	= bcma_host_pci_block_write,
+#endif
+	.aread32	= bcma_host_pci_aread32,
+	.awrite32	= bcma_host_pci_awrite32,
+};
+
+static int bcma_host_pci_probe(struct pci_dev *dev,
+			     const struct pci_device_id *id)
+{
+	struct bcma_bus *bus;
+	int err = -ENOMEM;
+	const char *name;
+	u32 val;
+
+	/* Alloc */
+	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+	if (!bus)
+		goto out;
+
+	/* Basic PCI configuration */
+	err = pci_enable_device(dev);
+	if (err)
+		goto err_kfree_bus;
+
+	name = dev_name(&dev->dev);
+	if (dev->driver && dev->driver->name)
+		name = dev->driver->name;
+	err = pci_request_regions(dev, name);
+	if (err)
+		goto err_pci_disable;
+	pci_set_master(dev);
+
+	/* Disable the RETRY_TIMEOUT register (0x41) to keep
+	 * PCI Tx retries from interfering with C3 CPU state */
+	pci_read_config_dword(dev, 0x40, &val);
+	if ((val & 0x0000ff00) != 0)
+		pci_write_config_dword(dev, 0x40, val & 0xffff00ff);
+
+	/* SSB needed additional powering up, do we have any AMBA PCI cards? */
+	if (!pci_is_pcie(dev))
+		pr_err("PCI card detected, report problems.\n");
+
+	/* Map MMIO */
+	err = -ENOMEM;
+	bus->mmio = pci_iomap(dev, 0, ~0UL);
+	if (!bus->mmio)
+		goto err_pci_release_regions;
+
+	/* Host specific */
+	bus->host_pci = dev;
+	bus->hosttype = BCMA_HOSTTYPE_PCI;
+	bus->ops = &bcma_host_pci_ops;
+
+	/* Register */
+	err = bcma_bus_register(bus);
+	if (err)
+		goto err_pci_unmap_mmio;
+
+	pci_set_drvdata(dev, bus);
+
+out:
+	return err;
+
+err_pci_unmap_mmio:
+	pci_iounmap(dev, bus->mmio);
+err_pci_release_regions:
+	pci_release_regions(dev);
+err_pci_disable:
+	pci_disable_device(dev);
+err_kfree_bus:
+	kfree(bus);
+	return err;
+}
+
+static void bcma_host_pci_remove(struct pci_dev *dev)
+{
+	struct bcma_bus *bus = pci_get_drvdata(dev);
+
+	bcma_bus_unregister(bus);
+	pci_iounmap(dev, bus->mmio);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+	kfree(bus);
+	pci_set_drvdata(dev, NULL);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
+	{ 0, },
+};
+MODULE_DEVICE_TABLE(pci, bcma_pci_bridge_tbl);
+
+static struct pci_driver bcma_pci_bridge_driver = {
+	.name = "bcma-pci-bridge",
+	.id_table = bcma_pci_bridge_tbl,
+	.probe = bcma_host_pci_probe,
+	.remove = bcma_host_pci_remove,
+};
+
+int __init bcma_host_pci_init(void)
+{
+	return pci_register_driver(&bcma_pci_bridge_driver);
+}
+
+void __exit bcma_host_pci_exit(void)
+{
+	pci_unregister_driver(&bcma_pci_bridge_driver);
+}
diff --git a/drivers/bcma/host_soc.c b/drivers/bcma/host_soc.c
new file mode 100644
index 0000000..3c381fb
--- /dev/null
+++ b/drivers/bcma/host_soc.c
@@ -0,0 +1,183 @@
+/*
+ * Broadcom specific AMBA
+ * System on Chip (SoC) Host
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include "scan.h"
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_soc.h>
+
+static u8 bcma_host_soc_read8(struct bcma_device *core, u16 offset)
+{
+	return readb(core->io_addr + offset);
+}
+
+static u16 bcma_host_soc_read16(struct bcma_device *core, u16 offset)
+{
+	return readw(core->io_addr + offset);
+}
+
+static u32 bcma_host_soc_read32(struct bcma_device *core, u16 offset)
+{
+	return readl(core->io_addr + offset);
+}
+
+static void bcma_host_soc_write8(struct bcma_device *core, u16 offset,
+				 u8 value)
+{
+	writeb(value, core->io_addr + offset);
+}
+
+static void bcma_host_soc_write16(struct bcma_device *core, u16 offset,
+				 u16 value)
+{
+	writew(value, core->io_addr + offset);
+}
+
+static void bcma_host_soc_write32(struct bcma_device *core, u16 offset,
+				 u32 value)
+{
+	writel(value, core->io_addr + offset);
+}
+
+#ifdef CONFIG_BCMA_BLOCKIO
+static void bcma_host_soc_block_read(struct bcma_device *core, void *buffer,
+				     size_t count, u16 offset, u8 reg_width)
+{
+	void __iomem *addr = core->io_addr + offset;
+
+	switch (reg_width) {
+	case sizeof(u8): {
+		u8 *buf = buffer;
+
+		while (count) {
+			*buf = __raw_readb(addr);
+			buf++;
+			count--;
+		}
+		break;
+	}
+	case sizeof(u16): {
+		__le16 *buf = buffer;
+
+		WARN_ON(count & 1);
+		while (count) {
+			*buf = (__force __le16)__raw_readw(addr);
+			buf++;
+			count -= 2;
+		}
+		break;
+	}
+	case sizeof(u32): {
+		__le32 *buf = buffer;
+
+		WARN_ON(count & 3);
+		while (count) {
+			*buf = (__force __le32)__raw_readl(addr);
+			buf++;
+			count -= 4;
+		}
+		break;
+	}
+	default:
+		WARN_ON(1);
+	}
+}
+
+static void bcma_host_soc_block_write(struct bcma_device *core,
+				      const void *buffer,
+				      size_t count, u16 offset, u8 reg_width)
+{
+	void __iomem *addr = core->io_addr + offset;
+
+	switch (reg_width) {
+	case sizeof(u8): {
+		const u8 *buf = buffer;
+
+		while (count) {
+			__raw_writeb(*buf, addr);
+			buf++;
+			count--;
+		}
+		break;
+	}
+	case sizeof(u16): {
+		const __le16 *buf = buffer;
+
+		WARN_ON(count & 1);
+		while (count) {
+			__raw_writew((__force u16)(*buf), addr);
+			buf++;
+			count -= 2;
+		}
+		break;
+	}
+	case sizeof(u32): {
+		const __le32 *buf = buffer;
+
+		WARN_ON(count & 3);
+		while (count) {
+			__raw_writel((__force u32)(*buf), addr);
+			buf++;
+			count -= 4;
+		}
+		break;
+	}
+	default:
+		WARN_ON(1);
+	}
+}
+#endif /* CONFIG_BCMA_BLOCKIO */
+
+static u32 bcma_host_soc_aread32(struct bcma_device *core, u16 offset)
+{
+	return readl(core->io_wrap + offset);
+}
+
+static void bcma_host_soc_awrite32(struct bcma_device *core, u16 offset,
+				  u32 value)
+{
+	writel(value, core->io_wrap + offset);
+}
+
+const struct bcma_host_ops bcma_host_soc_ops = {
+	.read8		= bcma_host_soc_read8,
+	.read16		= bcma_host_soc_read16,
+	.read32		= bcma_host_soc_read32,
+	.write8		= bcma_host_soc_write8,
+	.write16	= bcma_host_soc_write16,
+	.write32	= bcma_host_soc_write32,
+#ifdef CONFIG_BCMA_BLOCKIO
+	.block_read	= bcma_host_soc_block_read,
+	.block_write	= bcma_host_soc_block_write,
+#endif
+	.aread32	= bcma_host_soc_aread32,
+	.awrite32	= bcma_host_soc_awrite32,
+};
+
+int __init bcma_host_soc_register(struct bcma_soc *soc)
+{
+	struct bcma_bus *bus = &soc->bus;
+	int err;
+
+	/* iomap only first core. We have to read some register on this core
+	 * to scan the bus.
+	 */
+	bus->mmio = ioremap_nocache(BCMA_ADDR_BASE, BCMA_CORE_SIZE * 1);
+	if (!bus->mmio)
+		return -ENOMEM;
+
+	/* Host specific */
+	bus->hosttype = BCMA_HOSTTYPE_SOC;
+	bus->ops = &bcma_host_soc_ops;
+
+	/* Register */
+	err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips);
+	if (err)
+		iounmap(bus->mmio);
+
+	return err;
+}
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
new file mode 100644
index 0000000..70c84b9
--- /dev/null
+++ b/drivers/bcma/main.c
@@ -0,0 +1,338 @@
+/*
+ * Broadcom specific AMBA
+ * Bus subsystem
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/module.h>
+#include <linux/bcma/bcma.h>
+#include <linux/slab.h>
+
+MODULE_DESCRIPTION("Broadcom's specific AMBA driver");
+MODULE_LICENSE("GPL");
+
+static int bcma_bus_match(struct device *dev, struct device_driver *drv);
+static int bcma_device_probe(struct device *dev);
+static int bcma_device_remove(struct device *dev);
+static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env);
+
+static ssize_t manuf_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+	return sprintf(buf, "0x%03X\n", core->id.manuf);
+}
+static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+	return sprintf(buf, "0x%03X\n", core->id.id);
+}
+static ssize_t rev_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+	return sprintf(buf, "0x%02X\n", core->id.rev);
+}
+static ssize_t class_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+	return sprintf(buf, "0x%X\n", core->id.class);
+}
+static struct device_attribute bcma_device_attrs[] = {
+	__ATTR_RO(manuf),
+	__ATTR_RO(id),
+	__ATTR_RO(rev),
+	__ATTR_RO(class),
+	__ATTR_NULL,
+};
+
+static struct bus_type bcma_bus_type = {
+	.name		= "bcma",
+	.match		= bcma_bus_match,
+	.probe		= bcma_device_probe,
+	.remove		= bcma_device_remove,
+	.uevent		= bcma_device_uevent,
+	.dev_attrs	= bcma_device_attrs,
+};
+
+static struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid)
+{
+	struct bcma_device *core;
+
+	list_for_each_entry(core, &bus->cores, list) {
+		if (core->id.id == coreid)
+			return core;
+	}
+	return NULL;
+}
+
+static void bcma_release_core_dev(struct device *dev)
+{
+	struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+	if (core->io_addr)
+		iounmap(core->io_addr);
+	if (core->io_wrap)
+		iounmap(core->io_wrap);
+	kfree(core);
+}
+
+static int bcma_register_cores(struct bcma_bus *bus)
+{
+	struct bcma_device *core;
+	int err, dev_id = 0;
+
+	list_for_each_entry(core, &bus->cores, list) {
+		/* We support that cores ourself */
+		switch (core->id.id) {
+		case BCMA_CORE_CHIPCOMMON:
+		case BCMA_CORE_PCI:
+		case BCMA_CORE_PCIE:
+		case BCMA_CORE_MIPS_74K:
+			continue;
+		}
+
+		core->dev.release = bcma_release_core_dev;
+		core->dev.bus = &bcma_bus_type;
+		dev_set_name(&core->dev, "bcma%d:%d", 0/*bus->num*/, dev_id);
+
+		switch (bus->hosttype) {
+		case BCMA_HOSTTYPE_PCI:
+			core->dev.parent = &bus->host_pci->dev;
+			core->dma_dev = &bus->host_pci->dev;
+			core->irq = bus->host_pci->irq;
+			break;
+		case BCMA_HOSTTYPE_SOC:
+			core->dev.dma_mask = &core->dev.coherent_dma_mask;
+			core->dma_dev = &core->dev;
+			break;
+		case BCMA_HOSTTYPE_SDIO:
+			break;
+		}
+
+		err = device_register(&core->dev);
+		if (err) {
+			pr_err("Could not register dev for core 0x%03X\n",
+			       core->id.id);
+			continue;
+		}
+		core->dev_registered = true;
+		dev_id++;
+	}
+
+	return 0;
+}
+
+static void bcma_unregister_cores(struct bcma_bus *bus)
+{
+	struct bcma_device *core;
+
+	list_for_each_entry(core, &bus->cores, list) {
+		if (core->dev_registered)
+			device_unregister(&core->dev);
+	}
+}
+
+int bcma_bus_register(struct bcma_bus *bus)
+{
+	int err;
+	struct bcma_device *core;
+
+	/* Scan for devices (cores) */
+	err = bcma_bus_scan(bus);
+	if (err) {
+		pr_err("Failed to scan: %d\n", err);
+		return -1;
+	}
+
+	/* Init CC core */
+	core = bcma_find_core(bus, BCMA_CORE_CHIPCOMMON);
+	if (core) {
+		bus->drv_cc.core = core;
+		bcma_core_chipcommon_init(&bus->drv_cc);
+	}
+
+	/* Init MIPS core */
+	core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
+	if (core) {
+		bus->drv_mips.core = core;
+		bcma_core_mips_init(&bus->drv_mips);
+	}
+
+	/* Init PCIE core */
+	core = bcma_find_core(bus, BCMA_CORE_PCIE);
+	if (core) {
+		bus->drv_pci.core = core;
+		bcma_core_pci_init(&bus->drv_pci);
+	}
+
+	/* Try to get SPROM */
+	err = bcma_sprom_get(bus);
+	if (err == -ENOENT) {
+		pr_err("No SPROM available\n");
+	} else if (err) {
+		pr_err("Failed to get SPROM: %d\n", err);
+		return -ENOENT;
+	}
+
+	/* Register found cores */
+	bcma_register_cores(bus);
+
+	pr_info("Bus registered\n");
+
+	return 0;
+}
+
+void bcma_bus_unregister(struct bcma_bus *bus)
+{
+	bcma_unregister_cores(bus);
+}
+
+int __init bcma_bus_early_register(struct bcma_bus *bus,
+				   struct bcma_device *core_cc,
+				   struct bcma_device *core_mips)
+{
+	int err;
+	struct bcma_device *core;
+	struct bcma_device_id match;
+
+	bcma_init_bus(bus);
+
+	match.manuf = BCMA_MANUF_BCM;
+	match.id = BCMA_CORE_CHIPCOMMON;
+	match.class = BCMA_CL_SIM;
+	match.rev = BCMA_ANY_REV;
+
+	/* Scan for chip common core */
+	err = bcma_bus_scan_early(bus, &match, core_cc);
+	if (err) {
+		pr_err("Failed to scan for common core: %d\n", err);
+		return -1;
+	}
+
+	match.manuf = BCMA_MANUF_MIPS;
+	match.id = BCMA_CORE_MIPS_74K;
+	match.class = BCMA_CL_SIM;
+	match.rev = BCMA_ANY_REV;
+
+	/* Scan for mips core */
+	err = bcma_bus_scan_early(bus, &match, core_mips);
+	if (err) {
+		pr_err("Failed to scan for mips core: %d\n", err);
+		return -1;
+	}
+
+	/* Init CC core */
+	core = bcma_find_core(bus, BCMA_CORE_CHIPCOMMON);
+	if (core) {
+		bus->drv_cc.core = core;
+		bcma_core_chipcommon_init(&bus->drv_cc);
+	}
+
+	/* Init MIPS core */
+	core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
+	if (core) {
+		bus->drv_mips.core = core;
+		bcma_core_mips_init(&bus->drv_mips);
+	}
+
+	pr_info("Early bus registered\n");
+
+	return 0;
+}
+
+int __bcma_driver_register(struct bcma_driver *drv, struct module *owner)
+{
+	drv->drv.name = drv->name;
+	drv->drv.bus = &bcma_bus_type;
+	drv->drv.owner = owner;
+
+	return driver_register(&drv->drv);
+}
+EXPORT_SYMBOL_GPL(__bcma_driver_register);
+
+void bcma_driver_unregister(struct bcma_driver *drv)
+{
+	driver_unregister(&drv->drv);
+}
+EXPORT_SYMBOL_GPL(bcma_driver_unregister);
+
+static int bcma_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+	struct bcma_driver *adrv = container_of(drv, struct bcma_driver, drv);
+	const struct bcma_device_id *cid = &core->id;
+	const struct bcma_device_id *did;
+
+	for (did = adrv->id_table; did->manuf || did->id || did->rev; did++) {
+	    if ((did->manuf == cid->manuf || did->manuf == BCMA_ANY_MANUF) &&
+		(did->id == cid->id || did->id == BCMA_ANY_ID) &&
+		(did->rev == cid->rev || did->rev == BCMA_ANY_REV) &&
+		(did->class == cid->class || did->class == BCMA_ANY_CLASS))
+			return 1;
+	}
+	return 0;
+}
+
+static int bcma_device_probe(struct device *dev)
+{
+	struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+	struct bcma_driver *adrv = container_of(dev->driver, struct bcma_driver,
+					       drv);
+	int err = 0;
+
+	if (adrv->probe)
+		err = adrv->probe(core);
+
+	return err;
+}
+
+static int bcma_device_remove(struct device *dev)
+{
+	struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+	struct bcma_driver *adrv = container_of(dev->driver, struct bcma_driver,
+					       drv);
+
+	if (adrv->remove)
+		adrv->remove(core);
+
+	return 0;
+}
+
+static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+
+	return add_uevent_var(env,
+			      "MODALIAS=bcma:m%04Xid%04Xrev%02Xcl%02X",
+			      core->id.manuf, core->id.id,
+			      core->id.rev, core->id.class);
+}
+
+static int __init bcma_modinit(void)
+{
+	int err;
+
+	err = bus_register(&bcma_bus_type);
+	if (err)
+		return err;
+
+#ifdef CONFIG_BCMA_HOST_PCI
+	err = bcma_host_pci_init();
+	if (err) {
+		pr_err("PCI host initialization failed\n");
+		err = 0;
+	}
+#endif
+
+	return err;
+}
+fs_initcall(bcma_modinit);
+
+static void __exit bcma_modexit(void)
+{
+#ifdef CONFIG_BCMA_HOST_PCI
+	bcma_host_pci_exit();
+#endif
+	bus_unregister(&bcma_bus_type);
+}
+module_exit(bcma_modexit)
diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c
new file mode 100644
index 0000000..cad9948
--- /dev/null
+++ b/drivers/bcma/scan.c
@@ -0,0 +1,486 @@
+/*
+ * Broadcom specific AMBA
+ * Bus scanning
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "scan.h"
+#include "bcma_private.h"
+
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+struct bcma_device_id_name {
+	u16 id;
+	const char *name;
+};
+struct bcma_device_id_name bcma_device_names[] = {
+	{ BCMA_CORE_OOB_ROUTER, "OOB Router" },
+	{ BCMA_CORE_INVALID, "Invalid" },
+	{ BCMA_CORE_CHIPCOMMON, "ChipCommon" },
+	{ BCMA_CORE_ILINE20, "ILine 20" },
+	{ BCMA_CORE_SRAM, "SRAM" },
+	{ BCMA_CORE_SDRAM, "SDRAM" },
+	{ BCMA_CORE_PCI, "PCI" },
+	{ BCMA_CORE_MIPS, "MIPS" },
+	{ BCMA_CORE_ETHERNET, "Fast Ethernet" },
+	{ BCMA_CORE_V90, "V90" },
+	{ BCMA_CORE_USB11_HOSTDEV, "USB 1.1 Hostdev" },
+	{ BCMA_CORE_ADSL, "ADSL" },
+	{ BCMA_CORE_ILINE100, "ILine 100" },
+	{ BCMA_CORE_IPSEC, "IPSEC" },
+	{ BCMA_CORE_UTOPIA, "UTOPIA" },
+	{ BCMA_CORE_PCMCIA, "PCMCIA" },
+	{ BCMA_CORE_INTERNAL_MEM, "Internal Memory" },
+	{ BCMA_CORE_MEMC_SDRAM, "MEMC SDRAM" },
+	{ BCMA_CORE_OFDM, "OFDM" },
+	{ BCMA_CORE_EXTIF, "EXTIF" },
+	{ BCMA_CORE_80211, "IEEE 802.11" },
+	{ BCMA_CORE_PHY_A, "PHY A" },
+	{ BCMA_CORE_PHY_B, "PHY B" },
+	{ BCMA_CORE_PHY_G, "PHY G" },
+	{ BCMA_CORE_MIPS_3302, "MIPS 3302" },
+	{ BCMA_CORE_USB11_HOST, "USB 1.1 Host" },
+	{ BCMA_CORE_USB11_DEV, "USB 1.1 Device" },
+	{ BCMA_CORE_USB20_HOST, "USB 2.0 Host" },
+	{ BCMA_CORE_USB20_DEV, "USB 2.0 Device" },
+	{ BCMA_CORE_SDIO_HOST, "SDIO Host" },
+	{ BCMA_CORE_ROBOSWITCH, "Roboswitch" },
+	{ BCMA_CORE_PARA_ATA, "PATA" },
+	{ BCMA_CORE_SATA_XORDMA, "SATA XOR-DMA" },
+	{ BCMA_CORE_ETHERNET_GBIT, "GBit Ethernet" },
+	{ BCMA_CORE_PCIE, "PCIe" },
+	{ BCMA_CORE_PHY_N, "PHY N" },
+	{ BCMA_CORE_SRAM_CTL, "SRAM Controller" },
+	{ BCMA_CORE_MINI_MACPHY, "Mini MACPHY" },
+	{ BCMA_CORE_ARM_1176, "ARM 1176" },
+	{ BCMA_CORE_ARM_7TDMI, "ARM 7TDMI" },
+	{ BCMA_CORE_PHY_LP, "PHY LP" },
+	{ BCMA_CORE_PMU, "PMU" },
+	{ BCMA_CORE_PHY_SSN, "PHY SSN" },
+	{ BCMA_CORE_SDIO_DEV, "SDIO Device" },
+	{ BCMA_CORE_ARM_CM3, "ARM CM3" },
+	{ BCMA_CORE_PHY_HT, "PHY HT" },
+	{ BCMA_CORE_MIPS_74K, "MIPS 74K" },
+	{ BCMA_CORE_MAC_GBIT, "GBit MAC" },
+	{ BCMA_CORE_DDR12_MEM_CTL, "DDR1/DDR2 Memory Controller" },
+	{ BCMA_CORE_PCIE_RC, "PCIe Root Complex" },
+	{ BCMA_CORE_OCP_OCP_BRIDGE, "OCP to OCP Bridge" },
+	{ BCMA_CORE_SHARED_COMMON, "Common Shared" },
+	{ BCMA_CORE_OCP_AHB_BRIDGE, "OCP to AHB Bridge" },
+	{ BCMA_CORE_SPI_HOST, "SPI Host" },
+	{ BCMA_CORE_I2S, "I2S" },
+	{ BCMA_CORE_SDR_DDR1_MEM_CTL, "SDR/DDR1 Memory Controller" },
+	{ BCMA_CORE_SHIM, "SHIM" },
+	{ BCMA_CORE_DEFAULT, "Default" },
+};
+const char *bcma_device_name(struct bcma_device_id *id)
+{
+	int i;
+
+	if (id->manuf == BCMA_MANUF_BCM) {
+		for (i = 0; i < ARRAY_SIZE(bcma_device_names); i++) {
+			if (bcma_device_names[i].id == id->id)
+				return bcma_device_names[i].name;
+		}
+	}
+	return "UNKNOWN";
+}
+
+static u32 bcma_scan_read32(struct bcma_bus *bus, u8 current_coreidx,
+		       u16 offset)
+{
+	return readl(bus->mmio + offset);
+}
+
+static void bcma_scan_switch_core(struct bcma_bus *bus, u32 addr)
+{
+	if (bus->hosttype == BCMA_HOSTTYPE_PCI)
+		pci_write_config_dword(bus->host_pci, BCMA_PCI_BAR0_WIN,
+				       addr);
+}
+
+static u32 bcma_erom_get_ent(struct bcma_bus *bus, u32 **eromptr)
+{
+	u32 ent = readl(*eromptr);
+	(*eromptr)++;
+	return ent;
+}
+
+static void bcma_erom_push_ent(u32 **eromptr)
+{
+	(*eromptr)--;
+}
+
+static s32 bcma_erom_get_ci(struct bcma_bus *bus, u32 **eromptr)
+{
+	u32 ent = bcma_erom_get_ent(bus, eromptr);
+	if (!(ent & SCAN_ER_VALID))
+		return -ENOENT;
+	if ((ent & SCAN_ER_TAG) != SCAN_ER_TAG_CI)
+		return -ENOENT;
+	return ent;
+}
+
+static bool bcma_erom_is_end(struct bcma_bus *bus, u32 **eromptr)
+{
+	u32 ent = bcma_erom_get_ent(bus, eromptr);
+	bcma_erom_push_ent(eromptr);
+	return (ent == (SCAN_ER_TAG_END | SCAN_ER_VALID));
+}
+
+static bool bcma_erom_is_bridge(struct bcma_bus *bus, u32 **eromptr)
+{
+	u32 ent = bcma_erom_get_ent(bus, eromptr);
+	bcma_erom_push_ent(eromptr);
+	return (((ent & SCAN_ER_VALID)) &&
+		((ent & SCAN_ER_TAGX) == SCAN_ER_TAG_ADDR) &&
+		((ent & SCAN_ADDR_TYPE) == SCAN_ADDR_TYPE_BRIDGE));
+}
+
+static void bcma_erom_skip_component(struct bcma_bus *bus, u32 **eromptr)
+{
+	u32 ent;
+	while (1) {
+		ent = bcma_erom_get_ent(bus, eromptr);
+		if ((ent & SCAN_ER_VALID) &&
+		    ((ent & SCAN_ER_TAG) == SCAN_ER_TAG_CI))
+			break;
+		if (ent == (SCAN_ER_TAG_END | SCAN_ER_VALID))
+			break;
+	}
+	bcma_erom_push_ent(eromptr);
+}
+
+static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 **eromptr)
+{
+	u32 ent = bcma_erom_get_ent(bus, eromptr);
+	if (!(ent & SCAN_ER_VALID))
+		return -ENOENT;
+	if ((ent & SCAN_ER_TAG) != SCAN_ER_TAG_MP)
+		return -ENOENT;
+	return ent;
+}
+
+static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 **eromptr,
+				  u32 type, u8 port)
+{
+	u32 addrl, addrh, sizel, sizeh = 0;
+	u32 size;
+
+	u32 ent = bcma_erom_get_ent(bus, eromptr);
+	if ((!(ent & SCAN_ER_VALID)) ||
+	    ((ent & SCAN_ER_TAGX) != SCAN_ER_TAG_ADDR) ||
+	    ((ent & SCAN_ADDR_TYPE) != type) ||
+	    (((ent & SCAN_ADDR_PORT) >> SCAN_ADDR_PORT_SHIFT) != port)) {
+		bcma_erom_push_ent(eromptr);
+		return -EINVAL;
+	}
+
+	addrl = ent & SCAN_ADDR_ADDR;
+	if (ent & SCAN_ADDR_AG32)
+		addrh = bcma_erom_get_ent(bus, eromptr);
+	else
+		addrh = 0;
+
+	if ((ent & SCAN_ADDR_SZ) == SCAN_ADDR_SZ_SZD) {
+		size = bcma_erom_get_ent(bus, eromptr);
+		sizel = size & SCAN_SIZE_SZ;
+		if (size & SCAN_SIZE_SG32)
+			sizeh = bcma_erom_get_ent(bus, eromptr);
+	} else
+		sizel = SCAN_ADDR_SZ_BASE <<
+				((ent & SCAN_ADDR_SZ) >> SCAN_ADDR_SZ_SHIFT);
+
+	return addrl;
+}
+
+static struct bcma_device *bcma_find_core_by_index(struct bcma_bus *bus,
+						   u16 index)
+{
+	struct bcma_device *core;
+
+	list_for_each_entry(core, &bus->cores, list) {
+		if (core->core_index == index)
+			return core;
+	}
+	return NULL;
+}
+
+static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
+			      struct bcma_device_id *match, int core_num,
+			      struct bcma_device *core)
+{
+	s32 tmp;
+	u8 i, j;
+	s32 cia, cib;
+	u8 ports[2], wrappers[2];
+
+	/* get CIs */
+	cia = bcma_erom_get_ci(bus, eromptr);
+	if (cia < 0) {
+		bcma_erom_push_ent(eromptr);
+		if (bcma_erom_is_end(bus, eromptr))
+			return -ESPIPE;
+		return -EILSEQ;
+	}
+	cib = bcma_erom_get_ci(bus, eromptr);
+	if (cib < 0)
+		return -EILSEQ;
+
+	/* parse CIs */
+	core->id.class = (cia & SCAN_CIA_CLASS) >> SCAN_CIA_CLASS_SHIFT;
+	core->id.id = (cia & SCAN_CIA_ID) >> SCAN_CIA_ID_SHIFT;
+	core->id.manuf = (cia & SCAN_CIA_MANUF) >> SCAN_CIA_MANUF_SHIFT;
+	ports[0] = (cib & SCAN_CIB_NMP) >> SCAN_CIB_NMP_SHIFT;
+	ports[1] = (cib & SCAN_CIB_NSP) >> SCAN_CIB_NSP_SHIFT;
+	wrappers[0] = (cib & SCAN_CIB_NMW) >> SCAN_CIB_NMW_SHIFT;
+	wrappers[1] = (cib & SCAN_CIB_NSW) >> SCAN_CIB_NSW_SHIFT;
+	core->id.rev = (cib & SCAN_CIB_REV) >> SCAN_CIB_REV_SHIFT;
+
+	if (((core->id.manuf == BCMA_MANUF_ARM) &&
+	     (core->id.id == 0xFFF)) ||
+	    (ports[1] == 0)) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENXIO;
+	}
+
+	/* check if component is a core at all */
+	if (wrappers[0] + wrappers[1] == 0) {
+		/* we could save addrl of the router
+		if (cid == BCMA_CORE_OOB_ROUTER)
+		 */
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENXIO;
+	}
+
+	if (bcma_erom_is_bridge(bus, eromptr)) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENXIO;
+	}
+
+	if (bcma_find_core_by_index(bus, core_num)) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENODEV;
+	}
+
+	if (match && ((match->manuf != BCMA_ANY_MANUF &&
+	      match->manuf != core->id.manuf) ||
+	     (match->id != BCMA_ANY_ID && match->id != core->id.id) ||
+	     (match->rev != BCMA_ANY_REV && match->rev != core->id.rev) ||
+	     (match->class != BCMA_ANY_CLASS && match->class != core->id.class)
+	    )) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENODEV;
+	}
+
+	/* get & parse master ports */
+	for (i = 0; i < ports[0]; i++) {
+		s32 mst_port_d = bcma_erom_get_mst_port(bus, eromptr);
+		if (mst_port_d < 0)
+			return -EILSEQ;
+	}
+
+	/* get & parse slave ports */
+	for (i = 0; i < ports[1]; i++) {
+		for (j = 0; ; j++) {
+			tmp = bcma_erom_get_addr_desc(bus, eromptr,
+				SCAN_ADDR_TYPE_SLAVE, i);
+			if (tmp < 0) {
+				/* no more entries for port _i_ */
+				/* pr_debug("erom: slave port %d "
+				 * "has %d descriptors\n", i, j); */
+				break;
+			} else {
+				if (i == 0 && j == 0)
+					core->addr = tmp;
+			}
+		}
+	}
+
+	/* get & parse master wrappers */
+	for (i = 0; i < wrappers[0]; i++) {
+		for (j = 0; ; j++) {
+			tmp = bcma_erom_get_addr_desc(bus, eromptr,
+				SCAN_ADDR_TYPE_MWRAP, i);
+			if (tmp < 0) {
+				/* no more entries for port _i_ */
+				/* pr_debug("erom: master wrapper %d "
+				 * "has %d descriptors\n", i, j); */
+				break;
+			} else {
+				if (i == 0 && j == 0)
+					core->wrap = tmp;
+			}
+		}
+	}
+
+	/* get & parse slave wrappers */
+	for (i = 0; i < wrappers[1]; i++) {
+		u8 hack = (ports[1] == 1) ? 0 : 1;
+		for (j = 0; ; j++) {
+			tmp = bcma_erom_get_addr_desc(bus, eromptr,
+				SCAN_ADDR_TYPE_SWRAP, i + hack);
+			if (tmp < 0) {
+				/* no more entries for port _i_ */
+				/* pr_debug("erom: master wrapper %d "
+				 * has %d descriptors\n", i, j); */
+				break;
+			} else {
+				if (wrappers[0] == 0 && !i && !j)
+					core->wrap = tmp;
+			}
+		}
+	}
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+		core->io_addr = ioremap_nocache(core->addr, BCMA_CORE_SIZE);
+		if (!core->io_addr)
+			return -ENOMEM;
+		core->io_wrap = ioremap_nocache(core->wrap, BCMA_CORE_SIZE);
+		if (!core->io_wrap) {
+			iounmap(core->io_addr);
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+void bcma_init_bus(struct bcma_bus *bus)
+{
+	s32 tmp;
+
+	if (bus->init_done)
+		return;
+
+	INIT_LIST_HEAD(&bus->cores);
+	bus->nr_cores = 0;
+
+	bcma_scan_switch_core(bus, BCMA_ADDR_BASE);
+
+	tmp = bcma_scan_read32(bus, 0, BCMA_CC_ID);
+	bus->chipinfo.id = (tmp & BCMA_CC_ID_ID) >> BCMA_CC_ID_ID_SHIFT;
+	bus->chipinfo.rev = (tmp & BCMA_CC_ID_REV) >> BCMA_CC_ID_REV_SHIFT;
+	bus->chipinfo.pkg = (tmp & BCMA_CC_ID_PKG) >> BCMA_CC_ID_PKG_SHIFT;
+	bus->init_done = true;
+}
+
+int bcma_bus_scan(struct bcma_bus *bus)
+{
+	u32 erombase;
+	u32 __iomem *eromptr, *eromend;
+
+	int err, core_num = 0;
+
+	bcma_init_bus(bus);
+
+	erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+		eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
+		if (!eromptr)
+			return -ENOMEM;
+	} else {
+		eromptr = bus->mmio;
+	}
+
+	eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
+
+	bcma_scan_switch_core(bus, erombase);
+
+	while (eromptr < eromend) {
+		struct bcma_device *core = kzalloc(sizeof(*core), GFP_KERNEL);
+		if (!core)
+			return -ENOMEM;
+		INIT_LIST_HEAD(&core->list);
+		core->bus = bus;
+
+		err = bcma_get_next_core(bus, &eromptr, NULL, core_num, core);
+		if (err == -ENODEV) {
+			core_num++;
+			continue;
+		} else if (err == -ENXIO)
+			continue;
+		else if (err == -ESPIPE)
+			break;
+		else if (err < 0)
+			return err;
+
+		core->core_index = core_num++;
+		bus->nr_cores++;
+
+		pr_info("Core %d found: %s "
+			"(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
+			core->core_index, bcma_device_name(&core->id),
+			core->id.manuf, core->id.id, core->id.rev,
+			core->id.class);
+
+		list_add(&core->list, &bus->cores);
+	}
+
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+		iounmap(eromptr);
+
+	return 0;
+}
+
+int __init bcma_bus_scan_early(struct bcma_bus *bus,
+			       struct bcma_device_id *match,
+			       struct bcma_device *core)
+{
+	u32 erombase;
+	u32 __iomem *eromptr, *eromend;
+
+	int err = -ENODEV;
+	int core_num = 0;
+
+	erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+		eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
+		if (!eromptr)
+			return -ENOMEM;
+	} else {
+		eromptr = bus->mmio;
+	}
+
+	eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
+
+	bcma_scan_switch_core(bus, erombase);
+
+	while (eromptr < eromend) {
+		memset(core, 0, sizeof(*core));
+		INIT_LIST_HEAD(&core->list);
+		core->bus = bus;
+
+		err = bcma_get_next_core(bus, &eromptr, match, core_num, core);
+		if (err == -ENODEV) {
+			core_num++;
+			continue;
+		} else if (err == -ENXIO)
+			continue;
+		else if (err == -ESPIPE)
+			break;
+		else if (err < 0)
+			return err;
+
+		core->core_index = core_num++;
+		bus->nr_cores++;
+		pr_info("Core %d found: %s "
+			"(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
+			core->core_index, bcma_device_name(&core->id),
+			core->id.manuf, core->id.id, core->id.rev,
+			core->id.class);
+
+		list_add(&core->list, &bus->cores);
+		err = 0;
+		break;
+	}
+
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+		iounmap(eromptr);
+
+	return err;
+}
diff --git a/drivers/bcma/scan.h b/drivers/bcma/scan.h
new file mode 100644
index 0000000..113e6a6
--- /dev/null
+++ b/drivers/bcma/scan.h
@@ -0,0 +1,56 @@
+#ifndef BCMA_SCAN_H_
+#define BCMA_SCAN_H_
+
+#define BCMA_ADDR_BASE		0x18000000
+#define BCMA_WRAP_BASE		0x18100000
+
+#define SCAN_ER_VALID		0x00000001
+#define SCAN_ER_TAGX		0x00000006 /* we have to ignore 0x8 bit when checking tag for SCAN_ER_TAG_ADDR */
+#define SCAN_ER_TAG		0x0000000E
+#define  SCAN_ER_TAG_CI		0x00000000
+#define  SCAN_ER_TAG_MP		0x00000002
+#define  SCAN_ER_TAG_ADDR	0x00000004
+#define  SCAN_ER_TAG_END	0x0000000E
+#define SCAN_ER_BAD		0xFFFFFFFF
+
+#define SCAN_CIA_CLASS		0x000000F0
+#define SCAN_CIA_CLASS_SHIFT	4
+#define SCAN_CIA_ID		0x000FFF00
+#define SCAN_CIA_ID_SHIFT	8
+#define SCAN_CIA_MANUF		0xFFF00000
+#define SCAN_CIA_MANUF_SHIFT	20
+
+#define SCAN_CIB_NMP		0x000001F0
+#define SCAN_CIB_NMP_SHIFT	4
+#define SCAN_CIB_NSP		0x00003E00
+#define SCAN_CIB_NSP_SHIFT	9
+#define SCAN_CIB_NMW		0x0007C000
+#define SCAN_CIB_NMW_SHIFT	14
+#define SCAN_CIB_NSW		0x00F80000
+#define SCAN_CIB_NSW_SHIFT	17
+#define SCAN_CIB_REV		0xFF000000
+#define SCAN_CIB_REV_SHIFT	24
+
+#define SCAN_ADDR_AG32		0x00000008
+#define SCAN_ADDR_SZ		0x00000030
+#define SCAN_ADDR_SZ_SHIFT	4
+#define  SCAN_ADDR_SZ_4K	0x00000000
+#define  SCAN_ADDR_SZ_8K	0x00000010
+#define  SCAN_ADDR_SZ_16K	0x00000020
+#define  SCAN_ADDR_SZ_SZD	0x00000030
+#define SCAN_ADDR_TYPE		0x000000C0
+#define  SCAN_ADDR_TYPE_SLAVE	0x00000000
+#define  SCAN_ADDR_TYPE_BRIDGE	0x00000040
+#define  SCAN_ADDR_TYPE_SWRAP	0x00000080
+#define  SCAN_ADDR_TYPE_MWRAP	0x000000C0
+#define SCAN_ADDR_PORT		0x00000F00
+#define SCAN_ADDR_PORT_SHIFT	8
+#define SCAN_ADDR_ADDR		0xFFFFF000
+
+#define SCAN_ADDR_SZ_BASE	0x00001000	/* 4KB */
+
+#define SCAN_SIZE_SZ_ALIGN	0x00000FFF
+#define SCAN_SIZE_SZ		0xFFFFF000
+#define SCAN_SIZE_SG32		0x00000008
+
+#endif /* BCMA_SCAN_H_ */
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
new file mode 100644
index 0000000..d729239
--- /dev/null
+++ b/drivers/bcma/sprom.c
@@ -0,0 +1,186 @@
+/*
+ * Broadcom specific AMBA
+ * SPROM reading
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+#define SPOFF(offset)	((offset) / sizeof(u16))
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
+{
+	int i;
+	for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
+		sprom[i] = bcma_read16(bus->drv_cc.core,
+				       offset + (i * 2));
+}
+
+/**************************************************
+ * Validation.
+ **************************************************/
+
+static inline u8 bcma_crc8(u8 crc, u8 data)
+{
+	/* Polynomial:   x^8 + x^7 + x^6 + x^4 + x^2 + 1   */
+	static const u8 t[] = {
+		0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
+		0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
+		0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
+		0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
+		0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
+		0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
+		0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
+		0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
+		0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
+		0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
+		0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
+		0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
+		0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
+		0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
+		0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
+		0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
+		0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
+		0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
+		0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
+		0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
+		0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
+		0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
+		0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
+		0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
+		0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
+		0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
+		0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
+		0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
+		0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
+		0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
+		0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
+		0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
+	};
+	return t[crc ^ data];
+}
+
+static u8 bcma_sprom_crc(const u16 *sprom)
+{
+	int word;
+	u8 crc = 0xFF;
+
+	for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) {
+		crc = bcma_crc8(crc, sprom[word] & 0x00FF);
+		crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
+	}
+	crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
+	crc ^= 0xFF;
+
+	return crc;
+}
+
+static int bcma_sprom_check_crc(const u16 *sprom)
+{
+	u8 crc;
+	u8 expected_crc;
+	u16 tmp;
+
+	crc = bcma_sprom_crc(sprom);
+	tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC;
+	expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
+	if (crc != expected_crc)
+		return -EPROTO;
+
+	return 0;
+}
+
+static int bcma_sprom_valid(const u16 *sprom)
+{
+	u16 revision;
+	int err;
+
+	err = bcma_sprom_check_crc(sprom);
+	if (err)
+		return err;
+
+	revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV;
+	if (revision != 8 && revision != 9) {
+		pr_err("Unsupported SPROM revision: %d\n", revision);
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+/**************************************************
+ * SPROM extraction.
+ **************************************************/
+
+static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
+{
+	u16 v;
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
+		*(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
+	}
+
+	bus->sprom.board_rev = sprom[SPOFF(SSB_SPROM8_BOARDREV)];
+
+	bus->sprom.boardflags_lo = sprom[SPOFF(SSB_SPROM8_BFLLO)];
+	bus->sprom.boardflags_hi = sprom[SPOFF(SSB_SPROM8_BFLHI)];
+	bus->sprom.boardflags2_lo = sprom[SPOFF(SSB_SPROM8_BFL2LO)];
+	bus->sprom.boardflags2_hi = sprom[SPOFF(SSB_SPROM8_BFL2HI)];
+
+	bus->sprom.country_code = sprom[SPOFF(SSB_SPROM8_CCODE)];
+}
+
+int bcma_sprom_get(struct bcma_bus *bus)
+{
+	u16 offset;
+	u16 *sprom;
+	int err = 0;
+
+	if (!bus->drv_cc.core)
+		return -EOPNOTSUPP;
+
+	if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
+		return -ENOENT;
+
+	sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
+			GFP_KERNEL);
+	if (!sprom)
+		return -ENOMEM;
+
+	if (bus->chipinfo.id == 0x4331)
+		bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
+
+	/* Most cards have SPROM moved by additional offset 0x30 (48 dwords).
+	 * According to brcm80211 this applies to cards with PCIe rev >= 6
+	 * TODO: understand this condition and use it */
+	offset = (bus->chipinfo.id == 0x4331) ? BCMA_CC_SPROM :
+		BCMA_CC_SPROM_PCIE6;
+	bcma_sprom_read(bus, offset, sprom);
+
+	if (bus->chipinfo.id == 0x4331)
+		bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
+
+	err = bcma_sprom_valid(sprom);
+	if (err)
+		goto out;
+
+	bcma_sprom_extract_r8(bus, sprom);
+
+out:
+	kfree(sprom);
+	return err;
+}
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
new file mode 100644
index 0000000..f4460f4
--- /dev/null
+++ b/drivers/bluetooth/Makefile
@@ -0,0 +1,31 @@
+#
+# Makefile for the Linux Bluetooth HCI device drivers.
+#
+
+obj-$(CONFIG_BT_HCIVHCI)	+= hci_vhci.o
+obj-$(CONFIG_BT_HCIUART)	+= hci_uart.o
+obj-$(CONFIG_BT_HCIBCM203X)	+= bcm203x.o
+obj-$(CONFIG_BT_HCIBPA10X)	+= bpa10x.o
+obj-$(CONFIG_BT_HCIBFUSB)	+= bfusb.o
+obj-$(CONFIG_BT_HCIDTL1)	+= dtl1_cs.o
+obj-$(CONFIG_BT_HCIBT3C)	+= bt3c_cs.o
+obj-$(CONFIG_BT_HCIBLUECARD)	+= bluecard_cs.o
+obj-$(CONFIG_BT_HCIBTUART)	+= btuart_cs.o
+
+obj-$(CONFIG_BT_HCIBTUSB)	+= btusb.o
+obj-$(CONFIG_BT_HCIBTSDIO)	+= btsdio.o
+
+obj-$(CONFIG_BT_ATH3K)		+= ath3k.o
+obj-$(CONFIG_BT_MRVL)		+= btmrvl.o
+obj-$(CONFIG_BT_MRVL_SDIO)	+= btmrvl_sdio.o
+obj-$(CONFIG_BT_WILINK)		+= btwilink.o
+
+btmrvl-y			:= btmrvl_main.o
+btmrvl-$(CONFIG_DEBUG_FS)	+= btmrvl_debugfs.o
+
+hci_uart-y				:= hci_ldisc.o
+hci_uart-$(CONFIG_BT_HCIUART_H4)	+= hci_h4.o
+hci_uart-$(CONFIG_BT_HCIUART_BCSP)	+= hci_bcsp.o
+hci_uart-$(CONFIG_BT_HCIUART_LL)	+= hci_ll.o
+hci_uart-$(CONFIG_BT_HCIUART_ATH3K)	+= hci_ath.o
+hci_uart-objs				:= $(hci_uart-y)
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
new file mode 100644
index 0000000..1622772
--- /dev/null
+++ b/drivers/bluetooth/ath3k.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/usb.h>
+#include <net/bluetooth/bluetooth.h>
+
+#define VERSION "1.0"
+#define ATH3K_FIRMWARE	"ath3k-1.fw"
+
+#define ATH3K_DNLOAD				0x01
+#define ATH3K_GETSTATE				0x05
+#define ATH3K_SET_NORMAL_MODE			0x07
+#define ATH3K_GETVERSION			0x09
+#define USB_REG_SWITCH_VID_PID			0x0a
+
+#define ATH3K_MODE_MASK				0x3F
+#define ATH3K_NORMAL_MODE			0x0E
+
+#define ATH3K_PATCH_UPDATE			0x80
+#define ATH3K_SYSCFG_UPDATE			0x40
+
+#define ATH3K_XTAL_FREQ_26M			0x00
+#define ATH3K_XTAL_FREQ_40M			0x01
+#define ATH3K_XTAL_FREQ_19P2			0x02
+#define ATH3K_NAME_LEN				0xFF
+
+struct ath3k_version {
+	unsigned int	rom_version;
+	unsigned int	build_version;
+	unsigned int	ram_version;
+	unsigned char	ref_clock;
+	unsigned char	reserved[0x07];
+};
+
+static struct usb_device_id ath3k_table[] = {
+	/* Atheros AR3011 */
+	{ USB_DEVICE(0x0CF3, 0x3000) },
+
+	/* Atheros AR3011 with sflash firmware*/
+	{ USB_DEVICE(0x0CF3, 0x3002) },
+	{ USB_DEVICE(0x13d3, 0x3304) },
+	{ USB_DEVICE(0x0930, 0x0215) },
+
+	/* Atheros AR9285 Malbec with sflash firmware */
+	{ USB_DEVICE(0x03F0, 0x311D) },
+
+	/* Atheros AR3012 with sflash firmware*/
+	{ USB_DEVICE(0x0CF3, 0x3004) },
+
+	/* Atheros AR5BBU12 with sflash firmware */
+	{ USB_DEVICE(0x0489, 0xE02C) },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ath3k_table);
+
+#define BTUSB_ATH3012		0x80
+/* This table is to load patch and sysconfig files
+ * for AR3012 */
+static struct usb_device_id ath3k_blist_tbl[] = {
+
+	/* Atheros AR3012 with sflash firmware*/
+	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
+
+	{ }	/* Terminating entry */
+};
+
+#define USB_REQ_DFU_DNLOAD	1
+#define BULK_SIZE		4096
+#define FW_HDR_SIZE		20
+
+static int ath3k_load_firmware(struct usb_device *udev,
+				const struct firmware *firmware)
+{
+	u8 *send_buf;
+	int err, pipe, len, size, sent = 0;
+	int count = firmware->size;
+
+	BT_DBG("udev %p", udev);
+
+	pipe = usb_sndctrlpipe(udev, 0);
+
+	send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
+	if (!send_buf) {
+		BT_ERR("Can't allocate memory chunk for firmware");
+		return -ENOMEM;
+	}
+
+	memcpy(send_buf, firmware->data, 20);
+	if ((err = usb_control_msg(udev, pipe,
+				USB_REQ_DFU_DNLOAD,
+				USB_TYPE_VENDOR, 0, 0,
+				send_buf, 20, USB_CTRL_SET_TIMEOUT)) < 0) {
+		BT_ERR("Can't change to loading configuration err");
+		goto error;
+	}
+	sent += 20;
+	count -= 20;
+
+	while (count) {
+		size = min_t(uint, count, BULK_SIZE);
+		pipe = usb_sndbulkpipe(udev, 0x02);
+		memcpy(send_buf, firmware->data + sent, size);
+
+		err = usb_bulk_msg(udev, pipe, send_buf, size,
+					&len, 3000);
+
+		if (err || (len != size)) {
+			BT_ERR("Error in firmware loading err = %d,"
+				"len = %d, size = %d", err, len, size);
+			goto error;
+		}
+
+		sent  += size;
+		count -= size;
+	}
+
+error:
+	kfree(send_buf);
+	return err;
+}
+
+static int ath3k_get_state(struct usb_device *udev, unsigned char *state)
+{
+	int pipe = 0;
+
+	pipe = usb_rcvctrlpipe(udev, 0);
+	return usb_control_msg(udev, pipe, ATH3K_GETSTATE,
+			USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
+			state, 0x01, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_get_version(struct usb_device *udev,
+			struct ath3k_version *version)
+{
+	int pipe = 0;
+
+	pipe = usb_rcvctrlpipe(udev, 0);
+	return usb_control_msg(udev, pipe, ATH3K_GETVERSION,
+			USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, version,
+			sizeof(struct ath3k_version),
+			USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_load_fwfile(struct usb_device *udev,
+		const struct firmware *firmware)
+{
+	u8 *send_buf;
+	int err, pipe, len, size, count, sent = 0;
+	int ret;
+
+	count = firmware->size;
+
+	send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
+	if (!send_buf) {
+		BT_ERR("Can't allocate memory chunk for firmware");
+		return -ENOMEM;
+	}
+
+	size = min_t(uint, count, FW_HDR_SIZE);
+	memcpy(send_buf, firmware->data, size);
+
+	pipe = usb_sndctrlpipe(udev, 0);
+	ret = usb_control_msg(udev, pipe, ATH3K_DNLOAD,
+			USB_TYPE_VENDOR, 0, 0, send_buf,
+			size, USB_CTRL_SET_TIMEOUT);
+	if (ret < 0) {
+		BT_ERR("Can't change to loading configuration err");
+		kfree(send_buf);
+		return ret;
+	}
+
+	sent += size;
+	count -= size;
+
+	while (count) {
+		size = min_t(uint, count, BULK_SIZE);
+		pipe = usb_sndbulkpipe(udev, 0x02);
+
+		memcpy(send_buf, firmware->data + sent, size);
+
+		err = usb_bulk_msg(udev, pipe, send_buf, size,
+					&len, 3000);
+		if (err || (len != size)) {
+			BT_ERR("Error in firmware loading err = %d,"
+				"len = %d, size = %d", err, len, size);
+			kfree(send_buf);
+			return err;
+		}
+		sent  += size;
+		count -= size;
+	}
+
+	kfree(send_buf);
+	return 0;
+}
+
+static int ath3k_switch_pid(struct usb_device *udev)
+{
+	int pipe = 0;
+
+	pipe = usb_sndctrlpipe(udev, 0);
+	return usb_control_msg(udev, pipe, USB_REG_SWITCH_VID_PID,
+			USB_TYPE_VENDOR, 0, 0,
+			NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_set_normal_mode(struct usb_device *udev)
+{
+	unsigned char fw_state;
+	int pipe = 0, ret;
+
+	ret = ath3k_get_state(udev, &fw_state);
+	if (ret < 0) {
+		BT_ERR("Can't get state to change to normal mode err");
+		return ret;
+	}
+
+	if ((fw_state & ATH3K_MODE_MASK) == ATH3K_NORMAL_MODE) {
+		BT_DBG("firmware was already in normal mode");
+		return 0;
+	}
+
+	pipe = usb_sndctrlpipe(udev, 0);
+	return usb_control_msg(udev, pipe, ATH3K_SET_NORMAL_MODE,
+			USB_TYPE_VENDOR, 0, 0,
+			NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_load_patch(struct usb_device *udev)
+{
+	unsigned char fw_state;
+	char filename[ATH3K_NAME_LEN] = {0};
+	const struct firmware *firmware;
+	struct ath3k_version fw_version, pt_version;
+	int ret;
+
+	ret = ath3k_get_state(udev, &fw_state);
+	if (ret < 0) {
+		BT_ERR("Can't get state to change to load ram patch err");
+		return ret;
+	}
+
+	if (fw_state & ATH3K_PATCH_UPDATE) {
+		BT_DBG("Patch was already downloaded");
+		return 0;
+	}
+
+	ret = ath3k_get_version(udev, &fw_version);
+	if (ret < 0) {
+		BT_ERR("Can't get version to change to load ram patch err");
+		return ret;
+	}
+
+	snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu",
+		fw_version.rom_version);
+
+	ret = request_firmware(&firmware, filename, &udev->dev);
+	if (ret < 0) {
+		BT_ERR("Patch file not found %s", filename);
+		return ret;
+	}
+
+	pt_version.rom_version = *(int *)(firmware->data + firmware->size - 8);
+	pt_version.build_version = *(int *)
+		(firmware->data + firmware->size - 4);
+
+	if ((pt_version.rom_version != fw_version.rom_version) ||
+		(pt_version.build_version <= fw_version.build_version)) {
+		BT_ERR("Patch file version did not match with firmware");
+		release_firmware(firmware);
+		return -EINVAL;
+	}
+
+	ret = ath3k_load_fwfile(udev, firmware);
+	release_firmware(firmware);
+
+	return ret;
+}
+
+static int ath3k_load_syscfg(struct usb_device *udev)
+{
+	unsigned char fw_state;
+	char filename[ATH3K_NAME_LEN] = {0};
+	const struct firmware *firmware;
+	struct ath3k_version fw_version;
+	int clk_value, ret;
+
+	ret = ath3k_get_state(udev, &fw_state);
+	if (ret < 0) {
+		BT_ERR("Can't get state to change to load configration err");
+		return -EBUSY;
+	}
+
+	ret = ath3k_get_version(udev, &fw_version);
+	if (ret < 0) {
+		BT_ERR("Can't get version to change to load ram patch err");
+		return ret;
+	}
+
+	switch (fw_version.ref_clock) {
+
+	case ATH3K_XTAL_FREQ_26M:
+		clk_value = 26;
+		break;
+	case ATH3K_XTAL_FREQ_40M:
+		clk_value = 40;
+		break;
+	case ATH3K_XTAL_FREQ_19P2:
+		clk_value = 19;
+		break;
+	default:
+		clk_value = 0;
+		break;
+	}
+
+	snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s",
+		fw_version.rom_version, clk_value, ".dfu");
+
+	ret = request_firmware(&firmware, filename, &udev->dev);
+	if (ret < 0) {
+		BT_ERR("Configuration file not found %s", filename);
+		return ret;
+	}
+
+	ret = ath3k_load_fwfile(udev, firmware);
+	release_firmware(firmware);
+
+	return ret;
+}
+
+static int ath3k_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	const struct firmware *firmware;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int ret;
+
+	BT_DBG("intf %p id %p", intf, id);
+
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+		return -ENODEV;
+
+	/* match device ID in ath3k blacklist table */
+	if (!id->driver_info) {
+		const struct usb_device_id *match;
+		match = usb_match_id(intf, ath3k_blist_tbl);
+		if (match)
+			id = match;
+	}
+
+	/* load patch and sysconfig files for AR3012 */
+	if (id->driver_info & BTUSB_ATH3012) {
+
+		/* New firmware with patch and sysconfig files already loaded */
+		if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x0001)
+			return -ENODEV;
+
+		ret = ath3k_load_patch(udev);
+		if (ret < 0) {
+			BT_ERR("Loading patch file failed");
+			return ret;
+		}
+		ret = ath3k_load_syscfg(udev);
+		if (ret < 0) {
+			BT_ERR("Loading sysconfig file failed");
+			return ret;
+		}
+		ret = ath3k_set_normal_mode(udev);
+		if (ret < 0) {
+			BT_ERR("Set normal mode failed");
+			return ret;
+		}
+		ath3k_switch_pid(udev);
+		return 0;
+	}
+
+	ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev);
+	if (ret < 0) {
+		if (ret == -ENOENT)
+			BT_ERR("Firmware file \"%s\" not found",
+							ATH3K_FIRMWARE);
+		else
+			BT_ERR("Firmware file \"%s\" request failed (err=%d)",
+							ATH3K_FIRMWARE, ret);
+		return ret;
+	}
+
+	ret = ath3k_load_firmware(udev, firmware);
+	release_firmware(firmware);
+
+	return ret;
+}
+
+static void ath3k_disconnect(struct usb_interface *intf)
+{
+	BT_DBG("ath3k_disconnect intf %p", intf);
+}
+
+static struct usb_driver ath3k_driver = {
+	.name		= "ath3k",
+	.probe		= ath3k_probe,
+	.disconnect	= ath3k_disconnect,
+	.id_table	= ath3k_table,
+};
+
+static int __init ath3k_init(void)
+{
+	BT_INFO("Atheros AR30xx firmware driver ver %s", VERSION);
+	return usb_register(&ath3k_driver);
+}
+
+static void __exit ath3k_exit(void)
+{
+	usb_deregister(&ath3k_driver);
+}
+
+module_init(ath3k_init);
+module_exit(ath3k_exit);
+
+MODULE_AUTHOR("Atheros Communications");
+MODULE_DESCRIPTION("Atheros AR30xx firmware driver");
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(ATH3K_FIRMWARE);
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
new file mode 100644
index 0000000..54952ab
--- /dev/null
+++ b/drivers/bluetooth/bcm203x.c
@@ -0,0 +1,310 @@
+/*
+ *
+ *  Broadcom Blutonium firmware driver
+ *
+ *  Copyright (C) 2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2003  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+
+#define VERSION "1.2"
+
+static const struct usb_device_id bcm203x_table[] = {
+	/* Broadcom Blutonium (BCM2033) */
+	{ USB_DEVICE(0x0a5c, 0x2033) },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bcm203x_table);
+
+#define BCM203X_ERROR		0
+#define BCM203X_RESET		1
+#define BCM203X_LOAD_MINIDRV	2
+#define BCM203X_SELECT_MEMORY	3
+#define BCM203X_CHECK_MEMORY	4
+#define BCM203X_LOAD_FIRMWARE	5
+#define BCM203X_CHECK_FIRMWARE	6
+
+#define BCM203X_IN_EP		0x81
+#define BCM203X_OUT_EP		0x02
+
+struct bcm203x_data {
+	struct usb_device	*udev;
+
+	unsigned long		state;
+
+	struct work_struct	work;
+	atomic_t		shutdown;
+
+	struct urb		*urb;
+	unsigned char		*buffer;
+
+	unsigned char		*fw_data;
+	unsigned int		fw_size;
+	unsigned int		fw_sent;
+};
+
+static void bcm203x_complete(struct urb *urb)
+{
+	struct bcm203x_data *data = urb->context;
+	struct usb_device *udev = urb->dev;
+	int len;
+
+	BT_DBG("udev %p urb %p", udev, urb);
+
+	if (urb->status) {
+		BT_ERR("URB failed with status %d", urb->status);
+		data->state = BCM203X_ERROR;
+		return;
+	}
+
+	switch (data->state) {
+	case BCM203X_LOAD_MINIDRV:
+		memcpy(data->buffer, "#", 1);
+
+		usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+				data->buffer, 1, bcm203x_complete, data);
+
+		data->state = BCM203X_SELECT_MEMORY;
+
+		/* use workqueue to have a small delay */
+		schedule_work(&data->work);
+		break;
+
+	case BCM203X_SELECT_MEMORY:
+		usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
+				data->buffer, 32, bcm203x_complete, data, 1);
+
+		data->state = BCM203X_CHECK_MEMORY;
+
+		if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+			BT_ERR("Can't submit URB");
+		break;
+
+	case BCM203X_CHECK_MEMORY:
+		if (data->buffer[0] != '#') {
+			BT_ERR("Memory select failed");
+			data->state = BCM203X_ERROR;
+			break;
+		}
+
+		data->state = BCM203X_LOAD_FIRMWARE;
+
+	case BCM203X_LOAD_FIRMWARE:
+		if (data->fw_sent == data->fw_size) {
+			usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
+				data->buffer, 32, bcm203x_complete, data, 1);
+
+			data->state = BCM203X_CHECK_FIRMWARE;
+		} else {
+			len = min_t(uint, data->fw_size - data->fw_sent, 4096);
+
+			usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+				data->fw_data + data->fw_sent, len, bcm203x_complete, data);
+
+			data->fw_sent += len;
+		}
+
+		if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+			BT_ERR("Can't submit URB");
+		break;
+
+	case BCM203X_CHECK_FIRMWARE:
+		if (data->buffer[0] != '.') {
+			BT_ERR("Firmware loading failed");
+			data->state = BCM203X_ERROR;
+			break;
+		}
+
+		data->state = BCM203X_RESET;
+		break;
+	}
+}
+
+static void bcm203x_work(struct work_struct *work)
+{
+	struct bcm203x_data *data =
+		container_of(work, struct bcm203x_data, work);
+
+	if (atomic_read(&data->shutdown))
+		return;
+
+	if (usb_submit_urb(data->urb, GFP_KERNEL) < 0)
+		BT_ERR("Can't submit URB");
+}
+
+static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	const struct firmware *firmware;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct bcm203x_data *data;
+	int size;
+
+	BT_DBG("intf %p id %p", intf, id);
+
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+		return -ENODEV;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		BT_ERR("Can't allocate memory for data structure");
+		return -ENOMEM;
+	}
+
+	data->udev  = udev;
+	data->state = BCM203X_LOAD_MINIDRV;
+
+	data->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!data->urb) {
+		BT_ERR("Can't allocate URB");
+		kfree(data);
+		return -ENOMEM;
+	}
+
+	if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
+		BT_ERR("Mini driver request failed");
+		usb_free_urb(data->urb);
+		kfree(data);
+		return -EIO;
+	}
+
+	BT_DBG("minidrv data %p size %zu", firmware->data, firmware->size);
+
+	size = max_t(uint, firmware->size, 4096);
+
+	data->buffer = kmalloc(size, GFP_KERNEL);
+	if (!data->buffer) {
+		BT_ERR("Can't allocate memory for mini driver");
+		release_firmware(firmware);
+		usb_free_urb(data->urb);
+		kfree(data);
+		return -ENOMEM;
+	}
+
+	memcpy(data->buffer, firmware->data, firmware->size);
+
+	usb_fill_bulk_urb(data->urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+			data->buffer, firmware->size, bcm203x_complete, data);
+
+	release_firmware(firmware);
+
+	if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) {
+		BT_ERR("Firmware request failed");
+		usb_free_urb(data->urb);
+		kfree(data->buffer);
+		kfree(data);
+		return -EIO;
+	}
+
+	BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
+
+	data->fw_data = kmemdup(firmware->data, firmware->size, GFP_KERNEL);
+	if (!data->fw_data) {
+		BT_ERR("Can't allocate memory for firmware image");
+		release_firmware(firmware);
+		usb_free_urb(data->urb);
+		kfree(data->buffer);
+		kfree(data);
+		return -ENOMEM;
+	}
+
+	data->fw_size = firmware->size;
+	data->fw_sent = 0;
+
+	release_firmware(firmware);
+
+	INIT_WORK(&data->work, bcm203x_work);
+
+	usb_set_intfdata(intf, data);
+
+	/* use workqueue to have a small delay */
+	schedule_work(&data->work);
+
+	return 0;
+}
+
+static void bcm203x_disconnect(struct usb_interface *intf)
+{
+	struct bcm203x_data *data = usb_get_intfdata(intf);
+
+	BT_DBG("intf %p", intf);
+
+	atomic_inc(&data->shutdown);
+	cancel_work_sync(&data->work);
+
+	usb_kill_urb(data->urb);
+
+	usb_set_intfdata(intf, NULL);
+
+	usb_free_urb(data->urb);
+	kfree(data->fw_data);
+	kfree(data->buffer);
+	kfree(data);
+}
+
+static struct usb_driver bcm203x_driver = {
+	.name		= "bcm203x",
+	.probe		= bcm203x_probe,
+	.disconnect	= bcm203x_disconnect,
+	.id_table	= bcm203x_table,
+};
+
+static int __init bcm203x_init(void)
+{
+	int err;
+
+	BT_INFO("Broadcom Blutonium firmware driver ver %s", VERSION);
+
+	err = usb_register(&bcm203x_driver);
+	if (err < 0)
+		BT_ERR("Failed to register USB driver");
+
+	return err;
+}
+
+static void __exit bcm203x_exit(void)
+{
+	usb_deregister(&bcm203x_driver);
+}
+
+module_init(bcm203x_init);
+module_exit(bcm203x_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("BCM2033-MD.hex");
+MODULE_FIRMWARE("BCM2033-FW.bin");
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
new file mode 100644
index 0000000..a936763
--- /dev/null
+++ b/drivers/bluetooth/bfusb.c
@@ -0,0 +1,790 @@
+/*
+ *
+ *  AVM BlueFRITZ! USB driver
+ *
+ *  Copyright (C) 2003-2006  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "1.2"
+
+static struct usb_driver bfusb_driver;
+
+static struct usb_device_id bfusb_table[] = {
+	/* AVM BlueFRITZ! USB */
+	{ USB_DEVICE(0x057c, 0x2200) },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bfusb_table);
+
+#define BFUSB_MAX_BLOCK_SIZE	256
+
+#define BFUSB_BLOCK_TIMEOUT	3000
+
+#define BFUSB_TX_PROCESS	1
+#define BFUSB_TX_WAKEUP		2
+
+#define BFUSB_MAX_BULK_TX	2
+#define BFUSB_MAX_BULK_RX	2
+
+struct bfusb_data {
+	struct hci_dev		*hdev;
+
+	unsigned long		state;
+
+	struct usb_device	*udev;
+
+	unsigned int		bulk_in_ep;
+	unsigned int		bulk_out_ep;
+	unsigned int		bulk_pkt_size;
+
+	rwlock_t		lock;
+
+	struct sk_buff_head	transmit_q;
+
+	struct sk_buff		*reassembly;
+
+	atomic_t		pending_tx;
+	struct sk_buff_head	pending_q;
+	struct sk_buff_head	completed_q;
+};
+
+struct bfusb_data_scb {
+	struct urb *urb;
+};
+
+static void bfusb_tx_complete(struct urb *urb);
+static void bfusb_rx_complete(struct urb *urb);
+
+static struct urb *bfusb_get_completed(struct bfusb_data *data)
+{
+	struct sk_buff *skb;
+	struct urb *urb = NULL;
+
+	BT_DBG("bfusb %p", data);
+
+	skb = skb_dequeue(&data->completed_q);
+	if (skb) {
+		urb = ((struct bfusb_data_scb *) skb->cb)->urb;
+		kfree_skb(skb);
+	}
+
+	return urb;
+}
+
+static void bfusb_unlink_urbs(struct bfusb_data *data)
+{
+	struct sk_buff *skb;
+	struct urb *urb;
+
+	BT_DBG("bfusb %p", data);
+
+	while ((skb = skb_dequeue(&data->pending_q))) {
+		urb = ((struct bfusb_data_scb *) skb->cb)->urb;
+		usb_kill_urb(urb);
+		skb_queue_tail(&data->completed_q, skb);
+	}
+
+	while ((urb = bfusb_get_completed(data)))
+		usb_free_urb(urb);
+}
+
+static int bfusb_send_bulk(struct bfusb_data *data, struct sk_buff *skb)
+{
+	struct bfusb_data_scb *scb = (void *) skb->cb;
+	struct urb *urb = bfusb_get_completed(data);
+	int err, pipe;
+
+	BT_DBG("bfusb %p skb %p len %d", data, skb, skb->len);
+
+	if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+		return -ENOMEM;
+
+	pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
+
+	usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len,
+			bfusb_tx_complete, skb);
+
+	scb->urb = urb;
+
+	skb_queue_tail(&data->pending_q, skb);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		BT_ERR("%s bulk tx submit failed urb %p err %d", 
+					data->hdev->name, urb, err);
+		skb_unlink(skb, &data->pending_q);
+		usb_free_urb(urb);
+	} else
+		atomic_inc(&data->pending_tx);
+
+	return err;
+}
+
+static void bfusb_tx_wakeup(struct bfusb_data *data)
+{
+	struct sk_buff *skb;
+
+	BT_DBG("bfusb %p", data);
+
+	if (test_and_set_bit(BFUSB_TX_PROCESS, &data->state)) {
+		set_bit(BFUSB_TX_WAKEUP, &data->state);
+		return;
+	}
+
+	do {
+		clear_bit(BFUSB_TX_WAKEUP, &data->state);
+
+		while ((atomic_read(&data->pending_tx) < BFUSB_MAX_BULK_TX) &&
+				(skb = skb_dequeue(&data->transmit_q))) {
+			if (bfusb_send_bulk(data, skb) < 0) {
+				skb_queue_head(&data->transmit_q, skb);
+				break;
+			}
+		}
+
+	} while (test_bit(BFUSB_TX_WAKEUP, &data->state));
+
+	clear_bit(BFUSB_TX_PROCESS, &data->state);
+}
+
+static void bfusb_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = (struct sk_buff *) urb->context;
+	struct bfusb_data *data = (struct bfusb_data *) skb->dev;
+
+	BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
+
+	atomic_dec(&data->pending_tx);
+
+	if (!test_bit(HCI_RUNNING, &data->hdev->flags))
+		return;
+
+	if (!urb->status)
+		data->hdev->stat.byte_tx += skb->len;
+	else
+		data->hdev->stat.err_tx++;
+
+	read_lock(&data->lock);
+
+	skb_unlink(skb, &data->pending_q);
+	skb_queue_tail(&data->completed_q, skb);
+
+	bfusb_tx_wakeup(data);
+
+	read_unlock(&data->lock);
+}
+
+
+static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb)
+{
+	struct bfusb_data_scb *scb;
+	struct sk_buff *skb;
+	int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
+
+	BT_DBG("bfusb %p urb %p", data, urb);
+
+	if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+		return -ENOMEM;
+
+	skb = bt_skb_alloc(size, GFP_ATOMIC);
+	if (!skb) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	skb->dev = (void *) data;
+
+	scb = (struct bfusb_data_scb *) skb->cb;
+	scb->urb = urb;
+
+	pipe = usb_rcvbulkpipe(data->udev, data->bulk_in_ep);
+
+	usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, size,
+			bfusb_rx_complete, skb);
+
+	skb_queue_tail(&data->pending_q, skb);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		BT_ERR("%s bulk rx submit failed urb %p err %d",
+					data->hdev->name, urb, err);
+		skb_unlink(skb, &data->pending_q);
+		kfree_skb(skb);
+		usb_free_urb(urb);
+	}
+
+	return err;
+}
+
+static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned char *buf, int len)
+{
+	BT_DBG("bfusb %p hdr 0x%02x data %p len %d", data, hdr, buf, len);
+
+	if (hdr & 0x10) {
+		BT_ERR("%s error in block", data->hdev->name);
+		kfree_skb(data->reassembly);
+		data->reassembly = NULL;
+		return -EIO;
+	}
+
+	if (hdr & 0x04) {
+		struct sk_buff *skb;
+		unsigned char pkt_type;
+		int pkt_len = 0;
+
+		if (data->reassembly) {
+			BT_ERR("%s unexpected start block", data->hdev->name);
+			kfree_skb(data->reassembly);
+			data->reassembly = NULL;
+		}
+
+		if (len < 1) {
+			BT_ERR("%s no packet type found", data->hdev->name);
+			return -EPROTO;
+		}
+
+		pkt_type = *buf++; len--;
+
+		switch (pkt_type) {
+		case HCI_EVENT_PKT:
+			if (len >= HCI_EVENT_HDR_SIZE) {
+				struct hci_event_hdr *hdr = (struct hci_event_hdr *) buf;
+				pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
+			} else {
+				BT_ERR("%s event block is too short", data->hdev->name);
+				return -EILSEQ;
+			}
+			break;
+
+		case HCI_ACLDATA_PKT:
+			if (len >= HCI_ACL_HDR_SIZE) {
+				struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) buf;
+				pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
+			} else {
+				BT_ERR("%s data block is too short", data->hdev->name);
+				return -EILSEQ;
+			}
+			break;
+
+		case HCI_SCODATA_PKT:
+			if (len >= HCI_SCO_HDR_SIZE) {
+				struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) buf;
+				pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
+			} else {
+				BT_ERR("%s audio block is too short", data->hdev->name);
+				return -EILSEQ;
+			}
+			break;
+		}
+
+		skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
+		if (!skb) {
+			BT_ERR("%s no memory for the packet", data->hdev->name);
+			return -ENOMEM;
+		}
+
+		skb->dev = (void *) data->hdev;
+		bt_cb(skb)->pkt_type = pkt_type;
+
+		data->reassembly = skb;
+	} else {
+		if (!data->reassembly) {
+			BT_ERR("%s unexpected continuation block", data->hdev->name);
+			return -EIO;
+		}
+	}
+
+	if (len > 0)
+		memcpy(skb_put(data->reassembly, len), buf, len);
+
+	if (hdr & 0x08) {
+		hci_recv_frame(data->reassembly);
+		data->reassembly = NULL;
+	}
+
+	return 0;
+}
+
+static void bfusb_rx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = (struct sk_buff *) urb->context;
+	struct bfusb_data *data = (struct bfusb_data *) skb->dev;
+	unsigned char *buf = urb->transfer_buffer;
+	int count = urb->actual_length;
+	int err, hdr, len;
+
+	BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
+
+	read_lock(&data->lock);
+
+	if (!test_bit(HCI_RUNNING, &data->hdev->flags))
+		goto unlock;
+
+	if (urb->status || !count)
+		goto resubmit;
+
+	data->hdev->stat.byte_rx += count;
+
+	skb_put(skb, count);
+
+	while (count) {
+		hdr = buf[0] | (buf[1] << 8);
+
+		if (hdr & 0x4000) {
+			len = 0;
+			count -= 2;
+			buf   += 2;
+		} else {
+			len = (buf[2] == 0) ? 256 : buf[2];
+			count -= 3;
+			buf   += 3;
+		}
+
+		if (count < len) {
+			BT_ERR("%s block extends over URB buffer ranges",
+					data->hdev->name);
+		}
+
+		if ((hdr & 0xe1) == 0xc1)
+			bfusb_recv_block(data, hdr, buf, len);
+
+		count -= len;
+		buf   += len;
+	}
+
+	skb_unlink(skb, &data->pending_q);
+	kfree_skb(skb);
+
+	bfusb_rx_submit(data, urb);
+
+	read_unlock(&data->lock);
+
+	return;
+
+resubmit:
+	urb->dev = data->udev;
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		BT_ERR("%s bulk resubmit failed urb %p err %d",
+					data->hdev->name, urb, err);
+	}
+
+unlock:
+	read_unlock(&data->lock);
+}
+
+static int bfusb_open(struct hci_dev *hdev)
+{
+	struct bfusb_data *data = hdev->driver_data;
+	unsigned long flags;
+	int i, err;
+
+	BT_DBG("hdev %p bfusb %p", hdev, data);
+
+	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	write_lock_irqsave(&data->lock, flags);
+
+	err = bfusb_rx_submit(data, NULL);
+	if (!err) {
+		for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
+			bfusb_rx_submit(data, NULL);
+	} else {
+		clear_bit(HCI_RUNNING, &hdev->flags);
+	}
+
+	write_unlock_irqrestore(&data->lock, flags);
+
+	return err;
+}
+
+static int bfusb_flush(struct hci_dev *hdev)
+{
+	struct bfusb_data *data = hdev->driver_data;
+
+	BT_DBG("hdev %p bfusb %p", hdev, data);
+
+	skb_queue_purge(&data->transmit_q);
+
+	return 0;
+}
+
+static int bfusb_close(struct hci_dev *hdev)
+{
+	struct bfusb_data *data = hdev->driver_data;
+	unsigned long flags;
+
+	BT_DBG("hdev %p bfusb %p", hdev, data);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	write_lock_irqsave(&data->lock, flags);
+	write_unlock_irqrestore(&data->lock, flags);
+
+	bfusb_unlink_urbs(data);
+	bfusb_flush(hdev);
+
+	return 0;
+}
+
+static int bfusb_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct bfusb_data *data;
+	struct sk_buff *nskb;
+	unsigned char buf[3];
+	int sent = 0, size, count;
+
+	BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	data = hdev->driver_data;
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+	count = skb->len;
+
+	/* Max HCI frame size seems to be 1511 + 1 */
+	nskb = bt_skb_alloc(count + 32, GFP_ATOMIC);
+	if (!nskb) {
+		BT_ERR("Can't allocate memory for new packet");
+		return -ENOMEM;
+	}
+
+	nskb->dev = (void *) data;
+
+	while (count) {
+		size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
+
+		buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
+		buf[1] = 0x00;
+		buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
+
+		memcpy(skb_put(nskb, 3), buf, 3);
+		skb_copy_from_linear_data_offset(skb, sent, skb_put(nskb, size), size);
+
+		sent  += size;
+		count -= size;
+	}
+
+	/* Don't send frame with multiple size of bulk max packet */
+	if ((nskb->len % data->bulk_pkt_size) == 0) {
+		buf[0] = 0xdd;
+		buf[1] = 0x00;
+		memcpy(skb_put(nskb, 2), buf, 2);
+	}
+
+	read_lock(&data->lock);
+
+	skb_queue_tail(&data->transmit_q, nskb);
+	bfusb_tx_wakeup(data);
+
+	read_unlock(&data->lock);
+
+	kfree_skb(skb);
+
+	return 0;
+}
+
+static void bfusb_destruct(struct hci_dev *hdev)
+{
+	struct bfusb_data *data = hdev->driver_data;
+
+	BT_DBG("hdev %p bfusb %p", hdev, data);
+
+	kfree(data);
+}
+
+static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+static int bfusb_load_firmware(struct bfusb_data *data,
+			       const unsigned char *firmware, int count)
+{
+	unsigned char *buf;
+	int err, pipe, len, size, sent = 0;
+
+	BT_DBG("bfusb %p udev %p", data, data->udev);
+
+	BT_INFO("BlueFRITZ! USB loading firmware");
+
+	buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_KERNEL);
+	if (!buf) {
+		BT_ERR("Can't allocate memory chunk for firmware");
+		return -ENOMEM;
+	}
+
+	pipe = usb_sndctrlpipe(data->udev, 0);
+
+	if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+				0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) {
+		BT_ERR("Can't change to loading configuration");
+		kfree(buf);
+		return -EBUSY;
+	}
+
+	data->udev->toggle[0] = data->udev->toggle[1] = 0;
+
+	pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
+
+	while (count) {
+		size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
+
+		memcpy(buf, firmware + sent, size);
+
+		err = usb_bulk_msg(data->udev, pipe, buf, size,
+					&len, BFUSB_BLOCK_TIMEOUT);
+
+		if (err || (len != size)) {
+			BT_ERR("Error in firmware loading");
+			goto error;
+		}
+
+		sent  += size;
+		count -= size;
+	}
+
+	err = usb_bulk_msg(data->udev, pipe, NULL, 0,
+					&len, BFUSB_BLOCK_TIMEOUT);
+	if (err < 0) {
+		BT_ERR("Error in null packet request");
+		goto error;
+	}
+
+	pipe = usb_sndctrlpipe(data->udev, 0);
+
+	err = usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+				0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+	if (err < 0) {
+		BT_ERR("Can't change to running configuration");
+		goto error;
+	}
+
+	data->udev->toggle[0] = data->udev->toggle[1] = 0;
+
+	BT_INFO("BlueFRITZ! USB device ready");
+
+	kfree(buf);
+	return 0;
+
+error:
+	kfree(buf);
+
+	pipe = usb_sndctrlpipe(data->udev, 0);
+
+	usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+				0, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+	return err;
+}
+
+static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	const struct firmware *firmware;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_host_endpoint *bulk_out_ep;
+	struct usb_host_endpoint *bulk_in_ep;
+	struct hci_dev *hdev;
+	struct bfusb_data *data;
+
+	BT_DBG("intf %p id %p", intf, id);
+
+	/* Check number of endpoints */
+	if (intf->cur_altsetting->desc.bNumEndpoints < 2)
+		return -EIO;
+
+	bulk_out_ep = &intf->cur_altsetting->endpoint[0];
+	bulk_in_ep  = &intf->cur_altsetting->endpoint[1];
+
+	if (!bulk_out_ep || !bulk_in_ep) {
+		BT_ERR("Bulk endpoints not found");
+		goto done;
+	}
+
+	/* Initialize control structure and load firmware */
+	data = kzalloc(sizeof(struct bfusb_data), GFP_KERNEL);
+	if (!data) {
+		BT_ERR("Can't allocate memory for control structure");
+		goto done;
+	}
+
+	data->udev = udev;
+	data->bulk_in_ep    = bulk_in_ep->desc.bEndpointAddress;
+	data->bulk_out_ep   = bulk_out_ep->desc.bEndpointAddress;
+	data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize);
+
+	rwlock_init(&data->lock);
+
+	data->reassembly = NULL;
+
+	skb_queue_head_init(&data->transmit_q);
+	skb_queue_head_init(&data->pending_q);
+	skb_queue_head_init(&data->completed_q);
+
+	if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) {
+		BT_ERR("Firmware request failed");
+		goto error;
+	}
+
+	BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
+
+	if (bfusb_load_firmware(data, firmware->data, firmware->size) < 0) {
+		BT_ERR("Firmware loading failed");
+		goto release;
+	}
+
+	release_firmware(firmware);
+
+	/* Initialize and register HCI device */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can't allocate HCI device");
+		goto error;
+	}
+
+	data->hdev = hdev;
+
+	hdev->bus = HCI_USB;
+	hdev->driver_data = data;
+	SET_HCIDEV_DEV(hdev, &intf->dev);
+
+	hdev->open     = bfusb_open;
+	hdev->close    = bfusb_close;
+	hdev->flush    = bfusb_flush;
+	hdev->send     = bfusb_send_frame;
+	hdev->destruct = bfusb_destruct;
+	hdev->ioctl    = bfusb_ioctl;
+
+	hdev->owner = THIS_MODULE;
+
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device");
+		hci_free_dev(hdev);
+		goto error;
+	}
+
+	usb_set_intfdata(intf, data);
+
+	return 0;
+
+release:
+	release_firmware(firmware);
+
+error:
+	kfree(data);
+
+done:
+	return -EIO;
+}
+
+static void bfusb_disconnect(struct usb_interface *intf)
+{
+	struct bfusb_data *data = usb_get_intfdata(intf);
+	struct hci_dev *hdev = data->hdev;
+
+	BT_DBG("intf %p", intf);
+
+	if (!hdev)
+		return;
+
+	usb_set_intfdata(intf, NULL);
+
+	bfusb_close(hdev);
+
+	hci_unregister_dev(hdev);
+	hci_free_dev(hdev);
+}
+
+static struct usb_driver bfusb_driver = {
+	.name		= "bfusb",
+	.probe		= bfusb_probe,
+	.disconnect	= bfusb_disconnect,
+	.id_table	= bfusb_table,
+};
+
+static int __init bfusb_init(void)
+{
+	int err;
+
+	BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
+
+	err = usb_register(&bfusb_driver);
+	if (err < 0)
+		BT_ERR("Failed to register BlueFRITZ! USB driver");
+
+	return err;
+}
+
+static void __exit bfusb_exit(void)
+{
+	usb_deregister(&bfusb_driver);
+}
+
+module_init(bfusb_init);
+module_exit(bfusb_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("bfubase.frm");
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
new file mode 100644
index 0000000..f5a857f
--- /dev/null
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -0,0 +1,1034 @@
+/*
+ *
+ *  Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
+ *
+ *  Copyright (C) 2001-2002  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/wait.h>
+
+#include <linux/skbuff.h>
+#include <linux/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bluecard_info_t {
+	struct pcmcia_device *p_dev;
+
+	struct hci_dev *hdev;
+
+	spinlock_t lock;		/* For serializing operations */
+	struct timer_list timer;	/* For LED control */
+
+	struct sk_buff_head txq;
+	unsigned long tx_state;
+
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+
+	unsigned char ctrl_reg;
+	unsigned long hw_state;		/* Status of the hardware and LED control */
+} bluecard_info_t;
+
+
+static int bluecard_config(struct pcmcia_device *link);
+static void bluecard_release(struct pcmcia_device *link);
+
+static void bluecard_detach(struct pcmcia_device *p_dev);
+
+
+/* Default baud rate: 57600, 115200, 230400 or 460800 */
+#define DEFAULT_BAUD_RATE  230400
+
+
+/* Hardware states */
+#define CARD_READY             1
+#define CARD_HAS_PCCARD_ID     4
+#define CARD_HAS_POWER_LED     5
+#define CARD_HAS_ACTIVITY_LED  6
+
+/* Transmit states  */
+#define XMIT_SENDING         1
+#define XMIT_WAKEUP          2
+#define XMIT_BUFFER_NUMBER   5	/* unset = buffer one, set = buffer two */
+#define XMIT_BUF_ONE_READY   6
+#define XMIT_BUF_TWO_READY   7
+#define XMIT_SENDING_READY   8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE   0
+#define RECV_WAIT_EVENT_HEADER  1
+#define RECV_WAIT_ACL_HEADER    2
+#define RECV_WAIT_SCO_HEADER    3
+#define RECV_WAIT_DATA          4
+
+/* Special packet types */
+#define PKT_BAUD_RATE_57600   0x80
+#define PKT_BAUD_RATE_115200  0x81
+#define PKT_BAUD_RATE_230400  0x82
+#define PKT_BAUD_RATE_460800  0x83
+
+
+/* These are the register offsets */
+#define REG_COMMAND     0x20
+#define REG_INTERRUPT   0x21
+#define REG_CONTROL     0x22
+#define REG_RX_CONTROL  0x24
+#define REG_CARD_RESET  0x30
+#define REG_LED_CTRL    0x30
+
+/* REG_COMMAND */
+#define REG_COMMAND_TX_BUF_ONE  0x01
+#define REG_COMMAND_TX_BUF_TWO  0x02
+#define REG_COMMAND_RX_BUF_ONE  0x04
+#define REG_COMMAND_RX_BUF_TWO  0x08
+#define REG_COMMAND_RX_WIN_ONE  0x00
+#define REG_COMMAND_RX_WIN_TWO  0x10
+
+/* REG_CONTROL */
+#define REG_CONTROL_BAUD_RATE_57600   0x00
+#define REG_CONTROL_BAUD_RATE_115200  0x01
+#define REG_CONTROL_BAUD_RATE_230400  0x02
+#define REG_CONTROL_BAUD_RATE_460800  0x03
+#define REG_CONTROL_RTS               0x04
+#define REG_CONTROL_BT_ON             0x08
+#define REG_CONTROL_BT_RESET          0x10
+#define REG_CONTROL_BT_RES_PU         0x20
+#define REG_CONTROL_INTERRUPT         0x40
+#define REG_CONTROL_CARD_RESET        0x80
+
+/* REG_RX_CONTROL */
+#define RTS_LEVEL_SHIFT_BITS  0x02
+
+
+
+/* ======================== LED handling routines ======================== */
+
+
+static void bluecard_activity_led_timeout(u_long arg)
+{
+	bluecard_info_t *info = (bluecard_info_t *)arg;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+	unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+
+
+	if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+		return;
+
+	if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+		/* Disable activity LED */
+		outb(0x08 | 0x20, iobase + 0x30);
+	} else {
+		/* Disable power LED */
+		outb(0x00, iobase + 0x30);
+	}
+}
+
+
+static void bluecard_enable_activity_led(bluecard_info_t *info)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+	unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+
+	if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+		return;
+
+	if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+		/* Enable activity LED */
+		outb(0x10 | 0x40, iobase + 0x30);
+
+		/* Stop the LED after HZ/4 */
+		mod_timer(&(info->timer), jiffies + HZ / 4);
+	} else {
+		/* Enable power LED */
+		outb(0x08 | 0x20, iobase + 0x30);
+
+		/* Stop the LED after HZ/2 */
+		mod_timer(&(info->timer), jiffies + HZ / 2);
+	}
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len)
+{
+	int i, actual;
+
+	actual = (len > 15) ? 15 : len;
+
+	outb_p(actual, iobase + offset);
+
+	for (i = 0; i < actual; i++)
+		outb_p(buf[i], iobase + offset + i + 1);
+
+	return actual;
+}
+
+
+static void bluecard_write_wakeup(bluecard_info_t *info)
+{
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+	if (!test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+		return;
+
+	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+		return;
+	}
+
+	do {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+		register unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+		register unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+		register unsigned int offset;
+		register unsigned char command;
+		register unsigned long ready_bit;
+		register struct sk_buff *skb;
+		register int len;
+
+		clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+		if (!pcmcia_dev_present(info->p_dev))
+			return;
+
+		if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
+			if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state)))
+				break;
+			offset = 0x10;
+			command = REG_COMMAND_TX_BUF_TWO;
+			ready_bit = XMIT_BUF_TWO_READY;
+		} else {
+			if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state)))
+				break;
+			offset = 0x00;
+			command = REG_COMMAND_TX_BUF_ONE;
+			ready_bit = XMIT_BUF_ONE_READY;
+		}
+
+		if (!(skb = skb_dequeue(&(info->txq))))
+			break;
+
+		if (bt_cb(skb)->pkt_type & 0x80) {
+			/* Disable RTS */
+			info->ctrl_reg |= REG_CONTROL_RTS;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+		}
+
+		/* Activate LED */
+		bluecard_enable_activity_led(info);
+
+		/* Send frame */
+		len = bluecard_write(iobase, offset, skb->data, skb->len);
+
+		/* Tell the FPGA to send the data */
+		outb_p(command, iobase + REG_COMMAND);
+
+		/* Mark the buffer as dirty */
+		clear_bit(ready_bit, &(info->tx_state));
+
+		if (bt_cb(skb)->pkt_type & 0x80) {
+			DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
+			DEFINE_WAIT(wait);
+
+			unsigned char baud_reg;
+
+			switch (bt_cb(skb)->pkt_type) {
+			case PKT_BAUD_RATE_460800:
+				baud_reg = REG_CONTROL_BAUD_RATE_460800;
+				break;
+			case PKT_BAUD_RATE_230400:
+				baud_reg = REG_CONTROL_BAUD_RATE_230400;
+				break;
+			case PKT_BAUD_RATE_115200:
+				baud_reg = REG_CONTROL_BAUD_RATE_115200;
+				break;
+			case PKT_BAUD_RATE_57600:
+				/* Fall through... */
+			default:
+				baud_reg = REG_CONTROL_BAUD_RATE_57600;
+				break;
+			}
+
+			/* Wait until the command reaches the baseband */
+			prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
+			schedule_timeout(HZ/10);
+			finish_wait(&wq, &wait);
+
+			/* Set baud on baseband */
+			info->ctrl_reg &= ~0x03;
+			info->ctrl_reg |= baud_reg;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+			/* Enable RTS */
+			info->ctrl_reg &= ~REG_CONTROL_RTS;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+			/* Wait before the next HCI packet can be send */
+			prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
+			schedule_timeout(HZ);
+			finish_wait(&wq, &wait);
+		}
+
+		if (len == skb->len) {
+			kfree_skb(skb);
+		} else {
+			skb_pull(skb, len);
+			skb_queue_head(&(info->txq), skb);
+		}
+
+		info->hdev->stat.byte_tx += len;
+
+		/* Change buffer */
+		change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state));
+
+	} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+	clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size)
+{
+	int i, n, len;
+
+	outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND);
+
+	len = inb(iobase + offset);
+	n = 0;
+	i = 1;
+
+	while (n < len) {
+
+		if (i == 16) {
+			outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND);
+			i = 0;
+		}
+
+		buf[n] = inb(iobase + offset + i);
+
+		n++;
+		i++;
+
+	}
+
+	return len;
+}
+
+
+static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
+{
+	unsigned int iobase;
+	unsigned char buf[31];
+	int i, len;
+
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	iobase = info->p_dev->resource[0]->start;
+#else
+	iobase = info->p_dev->io.BasePort1;
+#endif
+
+	if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+		bluecard_enable_activity_led(info);
+
+	len = bluecard_read(iobase, offset, buf, sizeof(buf));
+
+	for (i = 0; i < len; i++) {
+
+		/* Allocate packet */
+		if (info->rx_skb == NULL) {
+			info->rx_state = RECV_WAIT_PACKET_TYPE;
+			info->rx_count = 0;
+			if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+				BT_ERR("Can't allocate mem for new packet");
+				return;
+			}
+		}
+
+		if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+			info->rx_skb->dev = (void *) info->hdev;
+			bt_cb(info->rx_skb)->pkt_type = buf[i];
+
+			switch (bt_cb(info->rx_skb)->pkt_type) {
+
+			case 0x00:
+				/* init packet */
+				if (offset != 0x00) {
+					set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+					set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+					set_bit(XMIT_SENDING_READY, &(info->tx_state));
+					bluecard_write_wakeup(info);
+				}
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			case HCI_EVENT_PKT:
+				info->rx_state = RECV_WAIT_EVENT_HEADER;
+				info->rx_count = HCI_EVENT_HDR_SIZE;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				info->rx_state = RECV_WAIT_ACL_HEADER;
+				info->rx_count = HCI_ACL_HDR_SIZE;
+				break;
+
+			case HCI_SCODATA_PKT:
+				info->rx_state = RECV_WAIT_SCO_HEADER;
+				info->rx_count = HCI_SCO_HDR_SIZE;
+				break;
+
+			default:
+				/* unknown packet */
+				BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+				info->hdev->stat.err_rx++;
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			}
+
+		} else {
+
+			*skb_put(info->rx_skb, 1) = buf[i];
+			info->rx_count--;
+
+			if (info->rx_count == 0) {
+
+				int dlen;
+				struct hci_event_hdr *eh;
+				struct hci_acl_hdr *ah;
+				struct hci_sco_hdr *sh;
+
+				switch (info->rx_state) {
+
+				case RECV_WAIT_EVENT_HEADER:
+					eh = hci_event_hdr(info->rx_skb);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = eh->plen;
+					break;
+
+				case RECV_WAIT_ACL_HEADER:
+					ah = hci_acl_hdr(info->rx_skb);
+					dlen = __le16_to_cpu(ah->dlen);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = dlen;
+					break;
+
+				case RECV_WAIT_SCO_HEADER:
+					sh = hci_sco_hdr(info->rx_skb);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = sh->dlen;
+					break;
+
+				case RECV_WAIT_DATA:
+					hci_recv_frame(info->rx_skb);
+					info->rx_skb = NULL;
+					break;
+
+				}
+
+			}
+
+		}
+
+
+	}
+
+	info->hdev->stat.byte_rx += len;
+}
+
+
+static irqreturn_t bluecard_interrupt(int irq, void *dev_inst)
+{
+	bluecard_info_t *info = dev_inst;
+	unsigned int iobase;
+	unsigned char reg;
+
+	if (!info || !info->hdev)
+		/* our irq handler is shared */
+		return IRQ_NONE;
+
+	if (!test_bit(CARD_READY, &(info->hw_state)))
+		return IRQ_HANDLED;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	iobase = info->p_dev->resource[0]->start;
+#else
+	iobase = info->p_dev->io.BasePort1;
+#endif
+
+	spin_lock(&(info->lock));
+
+	/* Disable interrupt */
+	info->ctrl_reg &= ~REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	reg = inb(iobase + REG_INTERRUPT);
+
+	if ((reg != 0x00) && (reg != 0xff)) {
+
+		if (reg & 0x04) {
+			bluecard_receive(info, 0x00);
+			outb(0x04, iobase + REG_INTERRUPT);
+			outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+		}
+
+		if (reg & 0x08) {
+			bluecard_receive(info, 0x10);
+			outb(0x08, iobase + REG_INTERRUPT);
+			outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+		}
+
+		if (reg & 0x01) {
+			set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+			outb(0x01, iobase + REG_INTERRUPT);
+			bluecard_write_wakeup(info);
+		}
+
+		if (reg & 0x02) {
+			set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+			outb(0x02, iobase + REG_INTERRUPT);
+			bluecard_write_wakeup(info);
+		}
+
+	}
+
+	/* Enable interrupt */
+	info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	spin_unlock(&(info->lock));
+
+	return IRQ_HANDLED;
+}
+
+
+
+/* ======================== Device specific HCI commands ======================== */
+
+
+static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
+{
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+	struct sk_buff *skb;
+
+	/* Ericsson baud rate command */
+	unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
+
+	if (!(skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+		BT_ERR("Can't allocate mem for new packet");
+		return -1;
+	}
+
+	switch (baud) {
+	case 460800:
+		cmd[4] = 0x00;
+		bt_cb(skb)->pkt_type = PKT_BAUD_RATE_460800;
+		break;
+	case 230400:
+		cmd[4] = 0x01;
+		bt_cb(skb)->pkt_type = PKT_BAUD_RATE_230400;
+		break;
+	case 115200:
+		cmd[4] = 0x02;
+		bt_cb(skb)->pkt_type = PKT_BAUD_RATE_115200;
+		break;
+	case 57600:
+		/* Fall through... */
+	default:
+		cmd[4] = 0x03;
+		bt_cb(skb)->pkt_type = PKT_BAUD_RATE_57600;
+		break;
+	}
+
+	memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
+
+	skb_queue_tail(&(info->txq), skb);
+
+	bluecard_write_wakeup(info);
+
+	return 0;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bluecard_hci_flush(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	return 0;
+}
+
+
+static int bluecard_hci_open(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+	unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+
+	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+		bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
+
+	if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
+		/* Enable LED */
+		outb(0x08 | 0x20, iobase + 0x30);
+	}
+
+	return 0;
+}
+
+
+static int bluecard_hci_close(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+	unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+
+	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	bluecard_hci_flush(hdev);
+
+	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
+		/* Disable LED */
+		outb(0x00, iobase + 0x30);
+	}
+
+	return 0;
+}
+
+
+static int bluecard_hci_send_frame(struct sk_buff *skb)
+{
+	bluecard_info_t *info;
+	struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	info = (bluecard_info_t *)(hdev->driver_data);
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+	skb_queue_tail(&(info->txq), skb);
+
+	bluecard_write_wakeup(info);
+
+	return 0;
+}
+
+
+static void bluecard_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int bluecard_open(bluecard_info_t *info)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+	unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+	struct hci_dev *hdev;
+	unsigned char id;
+
+	spin_lock_init(&(info->lock));
+
+	init_timer(&(info->timer));
+	info->timer.function = &bluecard_activity_led_timeout;
+	info->timer.data = (u_long)info;
+
+	skb_queue_head_init(&(info->txq));
+
+	info->rx_state = RECV_WAIT_PACKET_TYPE;
+	info->rx_count = 0;
+	info->rx_skb = NULL;
+
+	/* Initialize HCI device */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can't allocate HCI device");
+		return -ENOMEM;
+	}
+
+	info->hdev = hdev;
+
+	hdev->bus = HCI_PCCARD;
+	hdev->driver_data = info;
+	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+	hdev->open     = bluecard_hci_open;
+	hdev->close    = bluecard_hci_close;
+	hdev->flush    = bluecard_hci_flush;
+	hdev->send     = bluecard_hci_send_frame;
+	hdev->destruct = bluecard_hci_destruct;
+	hdev->ioctl    = bluecard_hci_ioctl;
+
+	hdev->owner = THIS_MODULE;
+
+	id = inb(iobase + 0x30);
+
+	if ((id & 0x0f) == 0x02)
+		set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state));
+
+	if (id & 0x10)
+		set_bit(CARD_HAS_POWER_LED, &(info->hw_state));
+
+	if (id & 0x20)
+		set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state));
+
+	/* Reset card */
+	info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Turn FPGA off */
+	outb(0x80, iobase + 0x30);
+
+	/* Wait some time */
+	msleep(10);
+
+	/* Turn FPGA on */
+	outb(0x00, iobase + 0x30);
+
+	/* Activate card */
+	info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Enable interrupt */
+	outb(0xff, iobase + REG_INTERRUPT);
+	info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	if ((id & 0x0f) == 0x03) {
+		/* Disable RTS */
+		info->ctrl_reg |= REG_CONTROL_RTS;
+		outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+		/* Set baud rate */
+		info->ctrl_reg |= 0x03;
+		outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+		/* Enable RTS */
+		info->ctrl_reg &= ~REG_CONTROL_RTS;
+		outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+		set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+		set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+		set_bit(XMIT_SENDING_READY, &(info->tx_state));
+	}
+
+	/* Start the RX buffers */
+	outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+	outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+
+	/* Signal that the hardware is ready */
+	set_bit(CARD_READY, &(info->hw_state));
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	/* Control the point at which RTS is enabled */
+	outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
+
+	/* Timeout before it is safe to send the first HCI packet */
+	msleep(1250);
+
+	/* Register HCI device */
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device");
+		info->hdev = NULL;
+		hci_free_dev(hdev);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+static int bluecard_close(bluecard_info_t *info)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+	unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+	struct hci_dev *hdev = info->hdev;
+
+	if (!hdev)
+		return -ENODEV;
+
+	bluecard_hci_close(hdev);
+
+	clear_bit(CARD_READY, &(info->hw_state));
+
+	/* Reset card */
+	info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Turn FPGA off */
+	outb(0x80, iobase + 0x30);
+
+	hci_unregister_dev(hdev);
+	hci_free_dev(hdev);
+
+	return 0;
+}
+
+static int bluecard_probe(struct pcmcia_device *link)
+{
+	bluecard_info_t *info;
+
+	/* Create new info device */
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->p_dev = link;
+	link->priv = info;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
+	link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+
+	link->irq.Handler = bluecard_interrupt;
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+	link->config_flags |= CONF_ENABLE_IRQ;
+#else
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+#endif
+
+	return bluecard_config(link);
+}
+
+
+static void bluecard_detach(struct pcmcia_device *link)
+{
+	bluecard_info_t *info = link->priv;
+
+	bluecard_release(link);
+	kfree(info);
+}
+
+
+static int bluecard_config(struct pcmcia_device *link)
+{
+	bluecard_info_t *info = link->priv;
+	int i, n;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+	link->config_index = 0x20;
+#else
+	link->conf.ConfigIndex = 0x20;
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+	link->resource[0]->end = 64;
+	link->io_lines = 6;
+#else
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+	link->io.NumPorts1 = 64;
+	link->io.IOAddrLines = 6;
+#endif
+
+	for (n = 0; n < 0x400; n += 0x40) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+		link->resource[0]->start = n ^ 0x300;
+		i = pcmcia_request_io(link);
+#else
+		link->io.BasePort1 = n ^ 0x300;
+		i = pcmcia_request_io(link, &link->io);
+#endif
+		if (i == 0)
+			break;
+	}
+
+	if (i != 0)
+		goto failed;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
+	i = pcmcia_request_irq(link, bluecard_interrupt);
+	if (i != 0)
+		goto failed;
+#else
+	i = pcmcia_request_irq(link, &link->irq);
+	if (i != 0)
+		link->irq.AssignedIRQ = 0;
+#endif
+
+	i = pcmcia_enable_device(link);
+	if (i != 0)
+		goto failed;
+
+	if (bluecard_open(info) != 0)
+		goto failed;
+
+	return 0;
+
+failed:
+	bluecard_release(link);
+	return -ENODEV;
+}
+
+
+static void bluecard_release(struct pcmcia_device *link)
+{
+	bluecard_info_t *info = link->priv;
+
+	bluecard_close(info);
+
+	del_timer(&(info->timer));
+
+	pcmcia_disable_device(link);
+}
+
+static const struct pcmcia_device_id bluecard_ids[] = {
+	PCMCIA_DEVICE_PROD_ID12("BlueCard", "LSE041", 0xbaf16fbf, 0x657cc15e),
+	PCMCIA_DEVICE_PROD_ID12("BTCFCARD", "LSE139", 0xe3987764, 0x2524b59c),
+	PCMCIA_DEVICE_PROD_ID12("WSS", "LSE039", 0x0a0736ec, 0x24e6dfab),
+	PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, bluecard_ids);
+
+static struct pcmcia_driver bluecard_driver = {
+	.owner		= THIS_MODULE,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37))
+	.name		= "bluecard_cs",
+#else
+	.drv		= {
+		.name	= "bluecard_cs",
+	},
+#endif
+	.probe		= bluecard_probe,
+	.remove		= bluecard_detach,
+	.id_table	= bluecard_ids,
+};
+
+static int __init init_bluecard_cs(void)
+{
+	return pcmcia_register_driver(&bluecard_driver);
+}
+
+
+static void __exit exit_bluecard_cs(void)
+{
+	pcmcia_unregister_driver(&bluecard_driver);
+}
+
+module_init(init_bluecard_cs);
+module_exit(exit_bluecard_cs);
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
new file mode 100644
index 0000000..751b338
--- /dev/null
+++ b/drivers/bluetooth/bpa10x.c
@@ -0,0 +1,542 @@
+/*
+ *
+ *  Digianswer Bluetooth USB driver
+ *
+ *  Copyright (C) 2004-2007  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "0.10"
+
+static struct usb_device_id bpa10x_table[] = {
+	/* Tektronix BPA 100/105 (Digianswer) */
+	{ USB_DEVICE(0x08fd, 0x0002) },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bpa10x_table);
+
+struct bpa10x_data {
+	struct hci_dev    *hdev;
+	struct usb_device *udev;
+
+	struct usb_anchor tx_anchor;
+	struct usb_anchor rx_anchor;
+
+	struct sk_buff *rx_skb[2];
+};
+
+#define HCI_VENDOR_HDR_SIZE 5
+
+struct hci_vendor_hdr {
+	__u8    type;
+	__le16  snum;
+	__le16  dlen;
+} __packed;
+
+static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
+{
+	struct bpa10x_data *data = hdev->driver_data;
+
+	BT_DBG("%s queue %d buffer %p count %d", hdev->name,
+							queue, buf, count);
+
+	if (queue < 0 || queue > 1)
+		return -EILSEQ;
+
+	hdev->stat.byte_rx += count;
+
+	while (count) {
+		struct sk_buff *skb = data->rx_skb[queue];
+		struct { __u8 type; int expect; } *scb;
+		int type, len = 0;
+
+		if (!skb) {
+			/* Start of the frame */
+
+			type = *((__u8 *) buf);
+			count--; buf++;
+
+			switch (type) {
+			case HCI_EVENT_PKT:
+				if (count >= HCI_EVENT_HDR_SIZE) {
+					struct hci_event_hdr *h = buf;
+					len = HCI_EVENT_HDR_SIZE + h->plen;
+				} else
+					return -EILSEQ;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				if (count >= HCI_ACL_HDR_SIZE) {
+					struct hci_acl_hdr *h = buf;
+					len = HCI_ACL_HDR_SIZE +
+							__le16_to_cpu(h->dlen);
+				} else
+					return -EILSEQ;
+				break;
+
+			case HCI_SCODATA_PKT:
+				if (count >= HCI_SCO_HDR_SIZE) {
+					struct hci_sco_hdr *h = buf;
+					len = HCI_SCO_HDR_SIZE + h->dlen;
+				} else
+					return -EILSEQ;
+				break;
+
+			case HCI_VENDOR_PKT:
+				if (count >= HCI_VENDOR_HDR_SIZE) {
+					struct hci_vendor_hdr *h = buf;
+					len = HCI_VENDOR_HDR_SIZE +
+							__le16_to_cpu(h->dlen);
+				} else
+					return -EILSEQ;
+				break;
+			}
+
+			skb = bt_skb_alloc(len, GFP_ATOMIC);
+			if (!skb) {
+				BT_ERR("%s no memory for packet", hdev->name);
+				return -ENOMEM;
+			}
+
+			skb->dev = (void *) hdev;
+
+			data->rx_skb[queue] = skb;
+
+			scb = (void *) skb->cb;
+			scb->type = type;
+			scb->expect = len;
+		} else {
+			/* Continuation */
+
+			scb = (void *) skb->cb;
+			len = scb->expect;
+		}
+
+		len = min(len, count);
+
+		memcpy(skb_put(skb, len), buf, len);
+
+		scb->expect -= len;
+
+		if (scb->expect == 0) {
+			/* Complete frame */
+
+			data->rx_skb[queue] = NULL;
+
+			bt_cb(skb)->pkt_type = scb->type;
+			hci_recv_frame(skb);
+		}
+
+		count -= len; buf += len;
+	}
+
+	return 0;
+}
+
+static void bpa10x_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = urb->context;
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+	BT_DBG("%s urb %p status %d count %d", hdev->name,
+					urb, urb->status, urb->actual_length);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		goto done;
+
+	if (!urb->status)
+		hdev->stat.byte_tx += urb->transfer_buffer_length;
+	else
+		hdev->stat.err_tx++;
+
+done:
+	kfree(urb->setup_packet);
+
+	kfree_skb(skb);
+}
+
+static void bpa10x_rx_complete(struct urb *urb)
+{
+	struct hci_dev *hdev = urb->context;
+	struct bpa10x_data *data = hdev->driver_data;
+	int err;
+
+	BT_DBG("%s urb %p status %d count %d", hdev->name,
+					urb, urb->status, urb->actual_length);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return;
+
+	if (urb->status == 0) {
+		if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe),
+						urb->transfer_buffer,
+						urb->actual_length) < 0) {
+			BT_ERR("%s corrupted event packet", hdev->name);
+			hdev->stat.err_rx++;
+		}
+	}
+
+	usb_anchor_urb(urb, &data->rx_anchor);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err < 0) {
+		BT_ERR("%s urb %p failed to resubmit (%d)",
+						hdev->name, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+}
+
+static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hdev->driver_data;
+	struct urb *urb;
+	unsigned char *buf;
+	unsigned int pipe;
+	int err, size = 16;
+
+	BT_DBG("%s", hdev->name);
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return -ENOMEM;
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	pipe = usb_rcvintpipe(data->udev, 0x81);
+
+	usb_fill_int_urb(urb, data->udev, pipe, buf, size,
+						bpa10x_rx_complete, hdev, 1);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_anchor_urb(urb, &data->rx_anchor);
+
+	err = usb_submit_urb(urb, GFP_KERNEL);
+	if (err < 0) {
+		BT_ERR("%s urb %p submission failed (%d)",
+						hdev->name, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+
+	usb_free_urb(urb);
+
+	return err;
+}
+
+static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hdev->driver_data;
+	struct urb *urb;
+	unsigned char *buf;
+	unsigned int pipe;
+	int err, size = 64;
+
+	BT_DBG("%s", hdev->name);
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return -ENOMEM;
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	pipe = usb_rcvbulkpipe(data->udev, 0x82);
+
+	usb_fill_bulk_urb(urb, data->udev, pipe,
+					buf, size, bpa10x_rx_complete, hdev);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_anchor_urb(urb, &data->rx_anchor);
+
+	err = usb_submit_urb(urb, GFP_KERNEL);
+	if (err < 0) {
+		BT_ERR("%s urb %p submission failed (%d)",
+						hdev->name, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+
+	usb_free_urb(urb);
+
+	return err;
+}
+
+static int bpa10x_open(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hdev->driver_data;
+	int err;
+
+	BT_DBG("%s", hdev->name);
+
+	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	err = bpa10x_submit_intr_urb(hdev);
+	if (err < 0)
+		goto error;
+
+	err = bpa10x_submit_bulk_urb(hdev);
+	if (err < 0)
+		goto error;
+
+	return 0;
+
+error:
+	usb_kill_anchored_urbs(&data->rx_anchor);
+
+	clear_bit(HCI_RUNNING, &hdev->flags);
+
+	return err;
+}
+
+static int bpa10x_close(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hdev->driver_data;
+
+	BT_DBG("%s", hdev->name);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	usb_kill_anchored_urbs(&data->rx_anchor);
+
+	return 0;
+}
+
+static int bpa10x_flush(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hdev->driver_data;
+
+	BT_DBG("%s", hdev->name);
+
+	usb_kill_anchored_urbs(&data->tx_anchor);
+
+	return 0;
+}
+
+static int bpa10x_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct bpa10x_data *data = hdev->driver_data;
+	struct usb_ctrlrequest *dr;
+	struct urb *urb;
+	unsigned int pipe;
+	int err;
+
+	BT_DBG("%s", hdev->name);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb)
+		return -ENOMEM;
+
+	/* Prepend skb with frame type */
+	*skb_push(skb, 1) = bt_cb(skb)->pkt_type;
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
+		if (!dr) {
+			usb_free_urb(urb);
+			return -ENOMEM;
+		}
+
+		dr->bRequestType = USB_TYPE_VENDOR;
+		dr->bRequest     = 0;
+		dr->wIndex       = 0;
+		dr->wValue       = 0;
+		dr->wLength      = __cpu_to_le16(skb->len);
+
+		pipe = usb_sndctrlpipe(data->udev, 0x00);
+
+		usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
+				skb->data, skb->len, bpa10x_tx_complete, skb);
+
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		pipe = usb_sndbulkpipe(data->udev, 0x02);
+
+		usb_fill_bulk_urb(urb, data->udev, pipe,
+				skb->data, skb->len, bpa10x_tx_complete, skb);
+
+		hdev->stat.acl_tx++;
+		break;
+
+	case HCI_SCODATA_PKT:
+		pipe = usb_sndbulkpipe(data->udev, 0x02);
+
+		usb_fill_bulk_urb(urb, data->udev, pipe,
+				skb->data, skb->len, bpa10x_tx_complete, skb);
+
+		hdev->stat.sco_tx++;
+		break;
+
+	default:
+		usb_free_urb(urb);
+		return -EILSEQ;
+	}
+
+	usb_anchor_urb(urb, &data->tx_anchor);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err < 0) {
+		BT_ERR("%s urb %p submission failed", hdev->name, urb);
+		kfree(urb->setup_packet);
+		usb_unanchor_urb(urb);
+	}
+
+	usb_free_urb(urb);
+
+	return 0;
+}
+
+static void bpa10x_destruct(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hdev->driver_data;
+
+	BT_DBG("%s", hdev->name);
+
+	kfree_skb(data->rx_skb[0]);
+	kfree_skb(data->rx_skb[1]);
+	kfree(data);
+}
+
+static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct bpa10x_data *data;
+	struct hci_dev *hdev;
+	int err;
+
+	BT_DBG("intf %p id %p", intf, id);
+
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+		return -ENODEV;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->udev = interface_to_usbdev(intf);
+
+	init_usb_anchor(&data->tx_anchor);
+	init_usb_anchor(&data->rx_anchor);
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		kfree(data);
+		return -ENOMEM;
+	}
+
+	hdev->bus = HCI_USB;
+	hdev->driver_data = data;
+
+	data->hdev = hdev;
+
+	SET_HCIDEV_DEV(hdev, &intf->dev);
+
+	hdev->open     = bpa10x_open;
+	hdev->close    = bpa10x_close;
+	hdev->flush    = bpa10x_flush;
+	hdev->send     = bpa10x_send_frame;
+	hdev->destruct = bpa10x_destruct;
+
+	hdev->owner = THIS_MODULE;
+
+	set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+
+	err = hci_register_dev(hdev);
+	if (err < 0) {
+		hci_free_dev(hdev);
+		kfree(data);
+		return err;
+	}
+
+	usb_set_intfdata(intf, data);
+
+	return 0;
+}
+
+static void bpa10x_disconnect(struct usb_interface *intf)
+{
+	struct bpa10x_data *data = usb_get_intfdata(intf);
+
+	BT_DBG("intf %p", intf);
+
+	if (!data)
+		return;
+
+	usb_set_intfdata(intf, NULL);
+
+	hci_unregister_dev(data->hdev);
+
+	hci_free_dev(data->hdev);
+}
+
+static struct usb_driver bpa10x_driver = {
+	.name		= "bpa10x",
+	.probe		= bpa10x_probe,
+	.disconnect	= bpa10x_disconnect,
+	.id_table	= bpa10x_table,
+};
+
+static int __init bpa10x_init(void)
+{
+	BT_INFO("Digianswer Bluetooth USB driver ver %s", VERSION);
+
+	return usb_register(&bpa10x_driver);
+}
+
+static void __exit bpa10x_exit(void)
+{
+	usb_deregister(&bpa10x_driver);
+}
+
+module_init(bpa10x_init);
+module_exit(bpa10x_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Digianswer Bluetooth USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
new file mode 100644
index 0000000..27f4f4f
--- /dev/null
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -0,0 +1,893 @@
+/*
+ *
+ *  Driver for the 3Com Bluetooth PCMCIA card
+ *
+ *  Copyright (C) 2001-2002  Marcel Holtmann <marcel@holtmann.org>
+ *                           Jose Orlando Pereira <jop@di.uminho.pt>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/bitops.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("BT3CPCC.bin");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bt3c_info_t {
+	struct pcmcia_device *p_dev;
+
+	struct hci_dev *hdev;
+
+	spinlock_t lock;		/* For serializing operations */
+
+	struct sk_buff_head txq;
+	unsigned long tx_state;
+
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+} bt3c_info_t;
+
+
+static int bt3c_config(struct pcmcia_device *link);
+static void bt3c_release(struct pcmcia_device *link);
+
+static void bt3c_detach(struct pcmcia_device *p_dev);
+
+
+/* Transmit states  */
+#define XMIT_SENDING  1
+#define XMIT_WAKEUP   2
+#define XMIT_WAITING  8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE   0
+#define RECV_WAIT_EVENT_HEADER  1
+#define RECV_WAIT_ACL_HEADER    2
+#define RECV_WAIT_SCO_HEADER    3
+#define RECV_WAIT_DATA          4
+
+
+
+/* ======================== Special I/O functions ======================== */
+
+
+#define DATA_L   0
+#define DATA_H   1
+#define ADDR_L   2
+#define ADDR_H   3
+#define CONTROL  4
+
+
+static inline void bt3c_address(unsigned int iobase, unsigned short addr)
+{
+	outb(addr & 0xff, iobase + ADDR_L);
+	outb((addr >> 8) & 0xff, iobase + ADDR_H);
+}
+
+
+static inline void bt3c_put(unsigned int iobase, unsigned short value)
+{
+	outb(value & 0xff, iobase + DATA_L);
+	outb((value >> 8) & 0xff, iobase + DATA_H);
+}
+
+
+static inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
+{
+	bt3c_address(iobase, addr);
+	bt3c_put(iobase, value);
+}
+
+
+static inline unsigned short bt3c_get(unsigned int iobase)
+{
+	unsigned short value = inb(iobase + DATA_L);
+
+	value |= inb(iobase + DATA_H) << 8;
+
+	return value;
+}
+
+
+static inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr)
+{
+	bt3c_address(iobase, addr);
+
+	return bt3c_get(iobase);
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+	int actual = 0;
+
+	bt3c_address(iobase, 0x7080);
+
+	/* Fill FIFO with current frame */
+	while (actual < len) {
+		/* Transmit next byte */
+		bt3c_put(iobase, buf[actual]);
+		actual++;
+	}
+
+	bt3c_io_write(iobase, 0x7005, actual);
+
+	return actual;
+}
+
+
+static void bt3c_write_wakeup(bt3c_info_t *info)
+{
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
+		return;
+
+	do {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+		register unsigned int iobase = info->p_dev->resource[0]->start;
+#else
+		register unsigned int iobase = info->p_dev->io.BasePort1;
+#endif
+		register struct sk_buff *skb;
+		register int len;
+
+		if (!pcmcia_dev_present(info->p_dev))
+			break;
+
+
+		if (!(skb = skb_dequeue(&(info->txq)))) {
+			clear_bit(XMIT_SENDING, &(info->tx_state));
+			break;
+		}
+
+		/* Send frame */
+		len = bt3c_write(iobase, 256, skb->data, skb->len);
+
+		if (len != skb->len) {
+			BT_ERR("Very strange");
+		}
+
+		kfree_skb(skb);
+
+		info->hdev->stat.byte_tx += len;
+
+	} while (0);
+}
+
+
+static void bt3c_receive(bt3c_info_t *info)
+{
+	unsigned int iobase;
+	int size = 0, avail;
+
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	iobase = info->p_dev->resource[0]->start;
+#else
+	iobase = info->p_dev->io.BasePort1;
+#endif
+
+	avail = bt3c_read(iobase, 0x7006);
+	//printk("bt3c_cs: receiving %d bytes\n", avail);
+
+	bt3c_address(iobase, 0x7480);
+	while (size < avail) {
+		size++;
+		info->hdev->stat.byte_rx++;
+
+		/* Allocate packet */
+		if (info->rx_skb == NULL) {
+			info->rx_state = RECV_WAIT_PACKET_TYPE;
+			info->rx_count = 0;
+			if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+				BT_ERR("Can't allocate mem for new packet");
+				return;
+			}
+		}
+
+
+		if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+			info->rx_skb->dev = (void *) info->hdev;
+			bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);
+			inb(iobase + DATA_H);
+			//printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type);
+
+			switch (bt_cb(info->rx_skb)->pkt_type) {
+
+			case HCI_EVENT_PKT:
+				info->rx_state = RECV_WAIT_EVENT_HEADER;
+				info->rx_count = HCI_EVENT_HDR_SIZE;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				info->rx_state = RECV_WAIT_ACL_HEADER;
+				info->rx_count = HCI_ACL_HDR_SIZE;
+				break;
+
+			case HCI_SCODATA_PKT:
+				info->rx_state = RECV_WAIT_SCO_HEADER;
+				info->rx_count = HCI_SCO_HDR_SIZE;
+				break;
+
+			default:
+				/* Unknown packet */
+				BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+				info->hdev->stat.err_rx++;
+				clear_bit(HCI_RUNNING, &(info->hdev->flags));
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			}
+
+		} else {
+
+			__u8 x = inb(iobase + DATA_L);
+
+			*skb_put(info->rx_skb, 1) = x;
+			inb(iobase + DATA_H);
+			info->rx_count--;
+
+			if (info->rx_count == 0) {
+
+				int dlen;
+				struct hci_event_hdr *eh;
+				struct hci_acl_hdr *ah;
+				struct hci_sco_hdr *sh;
+
+				switch (info->rx_state) {
+
+				case RECV_WAIT_EVENT_HEADER:
+					eh = hci_event_hdr(info->rx_skb);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = eh->plen;
+					break;
+
+				case RECV_WAIT_ACL_HEADER:
+					ah = hci_acl_hdr(info->rx_skb);
+					dlen = __le16_to_cpu(ah->dlen);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = dlen;
+					break;
+
+				case RECV_WAIT_SCO_HEADER:
+					sh = hci_sco_hdr(info->rx_skb);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = sh->dlen;
+					break;
+
+				case RECV_WAIT_DATA:
+					hci_recv_frame(info->rx_skb);
+					info->rx_skb = NULL;
+					break;
+
+				}
+
+			}
+
+		}
+
+	}
+
+	bt3c_io_write(iobase, 0x7006, 0x0000);
+}
+
+
+static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
+{
+	bt3c_info_t *info = dev_inst;
+	unsigned int iobase;
+	int iir;
+	irqreturn_t r = IRQ_NONE;
+
+	if (!info || !info->hdev)
+		/* our irq handler is shared */
+		return IRQ_NONE;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	iobase = info->p_dev->resource[0]->start;
+#else
+	iobase = info->p_dev->io.BasePort1;
+#endif
+
+	spin_lock(&(info->lock));
+
+	iir = inb(iobase + CONTROL);
+	if (iir & 0x80) {
+		int stat = bt3c_read(iobase, 0x7001);
+
+		if ((stat & 0xff) == 0x7f) {
+			BT_ERR("Very strange (stat=0x%04x)", stat);
+		} else if ((stat & 0xff) != 0xff) {
+			if (stat & 0x0020) {
+				int status = bt3c_read(iobase, 0x7002) & 0x10;
+				BT_INFO("%s: Antenna %s", info->hdev->name,
+							status ? "out" : "in");
+			}
+			if (stat & 0x0001)
+				bt3c_receive(info);
+			if (stat & 0x0002) {
+				//BT_ERR("Ack (stat=0x%04x)", stat);
+				clear_bit(XMIT_SENDING, &(info->tx_state));
+				bt3c_write_wakeup(info);
+			}
+
+			bt3c_io_write(iobase, 0x7001, 0x0000);
+
+			outb(iir, iobase + CONTROL);
+		}
+		r = IRQ_HANDLED;
+	}
+
+	spin_unlock(&(info->lock));
+
+	return r;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bt3c_hci_flush(struct hci_dev *hdev)
+{
+	bt3c_info_t *info = (bt3c_info_t *)(hdev->driver_data);
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	return 0;
+}
+
+
+static int bt3c_hci_open(struct hci_dev *hdev)
+{
+	set_bit(HCI_RUNNING, &(hdev->flags));
+
+	return 0;
+}
+
+
+static int bt3c_hci_close(struct hci_dev *hdev)
+{
+	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	bt3c_hci_flush(hdev);
+
+	return 0;
+}
+
+
+static int bt3c_hci_send_frame(struct sk_buff *skb)
+{
+	bt3c_info_t *info;
+	struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+	unsigned long flags;
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	info = (bt3c_info_t *) (hdev->driver_data);
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+	skb_queue_tail(&(info->txq), skb);
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	bt3c_write_wakeup(info);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+
+	return 0;
+}
+
+
+static void bt3c_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int bt3c_load_firmware(bt3c_info_t *info, const unsigned char *firmware,
+			      int count)
+{
+	char *ptr = (char *) firmware;
+	char b[9];
+	unsigned int iobase, size, addr, fcs, tmp;
+	int i, err = 0;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36))
+	iobase = info->p_dev->resource[0]->start;
+#else
+	iobase = info->p_dev->io.BasePort1;
+#endif
+
+	/* Reset */
+	bt3c_io_write(iobase, 0x8040, 0x0404);
+	bt3c_io_write(iobase, 0x8040, 0x0400);
+
+	udelay(1);
+
+	bt3c_io_write(iobase, 0x8040, 0x0404);
+
+	udelay(17);
+
+	/* Load */
+	while (count) {
+		if (ptr[0] != 'S') {
+			BT_ERR("Bad address in firmware");
+			err = -EFAULT;
+			goto error;
+		}
+
+		memset(b, 0, sizeof(b));
+		memcpy(b, ptr + 2, 2);
+		size = simple_strtoul(b, NULL, 16);
+
+		memset(b, 0, sizeof(b));
+		memcpy(b, ptr + 4, 8);
+		addr = simple_strtoul(b, NULL, 16);
+
+		memset(b, 0, sizeof(b));
+		memcpy(b, ptr + (size * 2) + 2, 2);
+		fcs = simple_strtoul(b, NULL, 16);
+
+		memset(b, 0, sizeof(b));
+		for (tmp = 0, i = 0; i < size; i++) {
+			memcpy(b, ptr + (i * 2) + 2, 2);
+			tmp += simple_strtol(b, NULL, 16);
+		}
+
+		if (((tmp + fcs) & 0xff) != 0xff) {
+			BT_ERR("Checksum error in firmware");
+			err = -EILSEQ;
+			goto error;
+		}
+
+		if (ptr[1] == '3') {
+			bt3c_address(iobase, addr);
+
+			memset(b, 0, sizeof(b));
+			for (i = 0; i < (size - 4) / 2; i++) {
+				memcpy(b, ptr + (i * 4) + 12, 4);
+				tmp = simple_strtoul(b, NULL, 16);
+				bt3c_put(iobase, tmp);
+			}
+		}
+
+		ptr   += (size * 2) + 6;
+		count -= (size * 2) + 6;
+	}
+
+	udelay(17);
+
+	/* Boot */
+	bt3c_address(iobase, 0x3000);
+	outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
+
+error:
+	udelay(17);
+
+	/* Clear */
+	bt3c_io_write(iobase, 0x7006, 0x0000);
+	bt3c_io_write(iobase, 0x7005, 0x0000);
+	bt3c_io_write(iobase, 0x7001, 0x0000);
+
+	return err;
+}
+
+
+static int bt3c_open(bt3c_info_t *info)
+{
+	const struct firmware *firmware;
+	struct hci_dev *hdev;
+	int err;
+
+	spin_lock_init(&(info->lock));
+
+	skb_queue_head_init(&(info->txq));
+
+	info->rx_state = RECV_WAIT_PACKET_TYPE;
+	info->rx_count = 0;
+	info->rx_skb = NULL;
+
+	/* Initialize HCI device */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can't allocate HCI device");
+		return -ENOMEM;
+	}
+
+	info->hdev = hdev;
+
+	hdev->bus = HCI_PCCARD;
+	hdev->driver_data = info;
+	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+	hdev->open     = bt3c_hci_open;
+	hdev->close    = bt3c_hci_close;
+	hdev->flush    = bt3c_hci_flush;
+	hdev->send     = bt3c_hci_send_frame;
+	hdev->destruct = bt3c_hci_destruct;
+	hdev->ioctl    = bt3c_hci_ioctl;
+
+	hdev->owner = THIS_MODULE;
+
+	/* Load firmware */
+	err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev);
+	if (err < 0) {
+		BT_ERR("Firmware request failed");
+		goto error;
+	}
+
+	err = bt3c_load_firmware(info, firmware->data, firmware->size);
+
+	release_firmware(firmware);