Update for v366
diff --git a/.compat_autoconf_3.3-OSR-2012-10-11-15-g2bd3ebf b/.compat_autoconf_3.3-OSR-2012-10-11-15-g2bd3ebf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.compat_autoconf_3.3-OSR-2012-10-11-15-g2bd3ebf
diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c
index cc67ec3..de39a8e 100644
--- a/drivers/net/wireless/ath/ath6kl/htc.c
+++ b/drivers/net/wireless/ath/ath6kl/htc.c
@@ -26,6 +26,8 @@
 /* threshold to re-enable Tx bundling for an AC*/
 #define TX_RESUME_BUNDLE_THRESHOLD	1500
 
+static void ath6kl_htc_tx_from_queue(struct htc_target *, struct htc_endpoint *);
+
 /* Functions for Tx credit handling */
 static void ath6kl_credit_deposit(struct ath6kl_htc_credit_info *cred_info,
 				  struct htc_endpoint_credit_dist *ep_dist,
@@ -402,18 +404,24 @@
 			       struct htc_endpoint *endpoint,
 			       struct htc_packet *packet)
 {
+	int packets_in_flight;
+
 	packet->completion = NULL;
 	packet->buf += HTC_HDR_LENGTH;
 
+	spin_lock_bh(&target->tx_lock);
+
+	// We're done working.
+	packets_in_flight = --endpoint->tx_proc_cnt;
+
 	if (!packet->status)
-		return;
+		goto done;
 
 	ath6kl_err("req failed (status:%d, ep:%d, len:%d creds:%d)\n",
 		   packet->status, packet->endpoint, packet->act_len,
 		   packet->info.tx.cred_used);
 
 	/* on failure to submit, reclaim credits for this packet */
-	spin_lock_bh(&target->tx_lock);
 	endpoint->cred_dist.cred_to_dist +=
 				packet->info.tx.cred_used;
 	endpoint->cred_dist.txq_depth = get_queue_depth(&endpoint->txq);
@@ -425,7 +433,12 @@
 				 &target->cred_dist_list,
 				 HTC_CREDIT_DIST_SEND_COMPLETE);
 
+done:
 	spin_unlock_bh(&target->tx_lock);
+
+	if (packets_in_flight == 0) {
+		ath6kl_htc_tx_from_queue(target, endpoint);
+	}
 }
 
 static void htc_tx_complete(struct htc_endpoint *endpoint,
@@ -630,6 +643,9 @@
 		packet->context = target;
 		endpoint->ep_st.tx_issued += 1;
 
+		// Queue up packets while we work.
+		endpoint->tx_proc_cnt++;
+
 		/* save send flags */
 		packet->info.tx.flags = flags;
 		packet->info.tx.seqno = endpoint->seqno;
@@ -693,10 +709,14 @@
 
 		cred_pad = htc_get_credit_padding(target->tgt_cred_sz,
 						  &len, endpoint);
-		if (cred_pad < 0 || rem_scat < len) {
+		if (unlikely(cred_pad < 0)) {
+			// We encountered a packet that could not be bundled.
 			status = -ENOSPC;
 			break;
 		}
+		if (unlikely(rem_scat < len))
+			// We've used up the space in this bundle.
+			break;
 
 		rem_scat -= len;
 		/* now remove it from the queue */
@@ -755,7 +775,7 @@
 	u32 txb_mask;
 	u8 ac = WMM_NUM_AC;
 
-	if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) ||
+	if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) &&
 		(WMI_CONTROL_SVC != endpoint->svc_id))
 		ac = target->dev->ar->ep2ac_map[endpoint->eid];
 
@@ -826,6 +846,8 @@
 			   scat_req->len, scat_req->scat_entries);
 		ath6kl_hif_submit_scat_req(target->dev, scat_req, false);
 
+		// We get -ENOSPC in ath6kl_htc_tx_setup_scat_list because we
+		// encountered a packet that could not be bundled.
 		if (status)
 			break;
 	}
@@ -846,16 +868,16 @@
 	int bundle_sent;
 	int n_pkts_bundle;
 	u8 ac = WMM_NUM_AC;
+	int status;
 
 	spin_lock_bh(&target->tx_lock);
 
-	endpoint->tx_proc_cnt++;
-	if (endpoint->tx_proc_cnt > 1) {
-		endpoint->tx_proc_cnt--;
+	if (endpoint->tx_proc_cnt > 0) {
 		spin_unlock_bh(&target->tx_lock);
 		ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx busy\n");
 		return;
 	}
+	endpoint->tx_proc_cnt++;
 
 	/*
 	 * drain the endpoint TX queue for transmission as long
@@ -863,7 +885,7 @@
 	 */
 	INIT_LIST_HEAD(&txq);
 
-	if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) ||
+	if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) &&
 		(WMI_CONTROL_SVC != endpoint->svc_id))
 		ac = target->dev->ar->ep2ac_map[endpoint->eid];
 
@@ -882,32 +904,33 @@
 		bundle_sent = 0;
 		n_pkts_bundle = 0;
 
-		while (true) {
-			/* try to send a bundle on each pass */
-			if ((target->tx_bndl_mask) &&
-			    (get_queue_depth(&txq) >=
-			    HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
+		while (!(list_empty(&txq))) {
+			// Try to send everything in bundles.
+			if (likely((target->tx_bndl_mask & (1 << ac)) &&
+					(get_queue_depth(&txq) >= HTC_MIN_HTC_MSGS_TO_BUNDLE))) {
 				int temp1 = 0, temp2 = 0;
-
-				/* check if bundling is enabled for an AC */
-				if (target->tx_bndl_mask & (1 << ac)) {
-					ath6kl_htc_tx_bundle(endpoint, &txq,
-							     &temp1, &temp2);
-					bundle_sent += temp1;
-					n_pkts_bundle += temp2;
-				}
+				ath6kl_htc_tx_bundle(endpoint, &txq,
+							 &temp1, &temp2);
+				bundle_sent += temp1;
+				n_pkts_bundle += temp2;
 			}
-
-			if (list_empty(&txq))
+			if (likely(list_empty(&txq)))
 				break;
 
+			// We encountered a packet that cannot be sent in bundle.
+			// Give it an exception to pass.
 			packet = list_first_entry(&txq, struct htc_packet,
 						  list);
 			list_del(&packet->list);
 
 			ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags,
 					       0, packet->info.tx.seqno);
-			ath6kl_htc_tx_issue(target, packet);
+			status = ath6kl_htc_tx_issue(target, packet);
+
+			if (status) {
+				packet->status = status;
+				packet->completion(packet->context, packet);
+			}
 		}
 
 		spin_lock_bh(&target->tx_lock);
@@ -934,15 +957,19 @@
 			if (ac < WMM_NUM_AC)
 				target->ac_tx_count[ac] = 0;
 		}
+
+		if (endpoint->tx_proc_cnt > 1)
+			break;
 	}
 
-	endpoint->tx_proc_cnt = 0;
+	endpoint->tx_proc_cnt--;
 	spin_unlock_bh(&target->tx_lock);
 }
 
 static bool ath6kl_htc_tx_try(struct htc_target *target,
 			      struct htc_endpoint *endpoint,
-			      struct htc_packet *tx_pkt)
+			      struct htc_packet *tx_pkt,
+			      int push)
 {
 	struct htc_ep_callbacks ep_cb;
 	int txq_depth;
@@ -975,7 +1002,8 @@
 	list_add_tail(&tx_pkt->list, &endpoint->txq);
 	spin_unlock_bh(&target->tx_lock);
 
-	ath6kl_htc_tx_from_queue(target, endpoint);
+	if (push)
+		ath6kl_htc_tx_from_queue(target, endpoint);
 
 	return true;
 }
@@ -1098,7 +1126,7 @@
 	}
 }
 
-int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet)
+int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet, int push)
 {
 	struct htc_endpoint *endpoint;
 	struct list_head queue;
@@ -1114,7 +1142,7 @@
 
 	endpoint = &target->endpoint[packet->endpoint];
 
-	if (!ath6kl_htc_tx_try(target, endpoint, packet)) {
+	if (!ath6kl_htc_tx_try(target, endpoint, packet, push)) {
 		packet->status = (target->htc_flags & HTC_OP_STATE_STOPPING) ?
 				 -ECANCELED : -ENOSPC;
 		INIT_LIST_HEAD(&queue);
diff --git a/drivers/net/wireless/ath/ath6kl/htc.h b/drivers/net/wireless/ath/ath6kl/htc.h
index 4fe5d17..e8acb5a 100644
--- a/drivers/net/wireless/ath/ath6kl/htc.h
+++ b/drivers/net/wireless/ath/ath6kl/htc.h
@@ -569,7 +569,7 @@
 int ath6kl_htc_conn_service(struct htc_target *target,
 			    struct htc_service_connect_req *req,
 			    struct htc_service_connect_resp *resp);
-int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet);
+int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet, int push);
 void ath6kl_htc_stop(struct htc_target *target);
 void ath6kl_htc_cleanup(struct htc_target *target);
 void ath6kl_htc_flush_txep(struct htc_target *target,
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
index 21cc7a9..1695de3 100644
--- a/drivers/net/wireless/ath/ath6kl/main.c
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -1395,6 +1395,13 @@
 				sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH
 				+ WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES;
 
+	// We wish to receive oversized TCP skbs, so we could process them into
+	// multiple packets and bundle-send them.
+	dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+	dev->features |= NETIF_F_TSO;
+	// TODO(davidgao): do ipv6 header and TCP pseudoheader as well.
+	// dev->features |= NETIF_F_TSO6;
+
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39))
 	dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
 #endif
diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c
index dbfe461..931ff00 100644
--- a/drivers/net/wireless/ath/ath6kl/sdio.c
+++ b/drivers/net/wireless/ath/ath6kl/sdio.c
@@ -551,7 +551,7 @@
 
 	bus_req = ath6kl_sdio_alloc_busreq(ar_sdio);
 
-	if (!bus_req)
+	if (WARN_ON_ONCE(!bus_req))
 		return -ENOMEM;
 
 	bus_req->address = address;
diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c
index 0b4b703..c50a992 100644
--- a/drivers/net/wireless/ath/ath6kl/txrx.c
+++ b/drivers/net/wireless/ath/ath6kl/txrx.c
@@ -15,6 +15,11 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <net/xfrm.h>
+
 #include "core.h"
 #include "debug.h"
 
@@ -354,7 +359,7 @@
 	 * This interface is asynchronous, if there is an error, cleanup
 	 * will happen in the TX completion callback.
 	 */
-	ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt);
+	ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt, /*push=*/1);
 
 	return 0;
 
@@ -363,6 +368,185 @@
 	return status;
 }
 
+// Copied from net/core/skbuff.c at 2.6.38
+static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
+{
+	new->tstamp		= old->tstamp;
+	new->dev		= old->dev;
+	new->transport_header	= old->transport_header;
+	new->network_header	= old->network_header;
+	new->mac_header		= old->mac_header;
+	skb_dst_copy(new, old);
+	new->rxhash		= old->rxhash;
+#ifdef CONFIG_XFRM
+	new->sp			= secpath_get(old->sp);
+#endif
+	memcpy(new->cb, old->cb, sizeof(old->cb));
+	new->csum		= old->csum;
+	new->local_df		= old->local_df;
+	new->pkt_type		= old->pkt_type;
+	new->ip_summed		= old->ip_summed;
+	skb_copy_queue_mapping(new, old);
+	new->priority		= old->priority;
+	new->deliver_no_wcard	= old->deliver_no_wcard;
+#if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
+	new->ipvs_property	= old->ipvs_property;
+#endif
+	new->protocol		= old->protocol;
+	new->mark		= old->mark;
+	new->skb_iif		= old->skb_iif;
+	__nf_copy(new, old);
+#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
+    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+	new->nf_trace		= old->nf_trace;
+#endif
+#ifdef CONFIG_NET_SCHED
+	new->tc_index		= old->tc_index;
+#ifdef CONFIG_NET_CLS_ACT
+	new->tc_verd		= old->tc_verd;
+#endif
+#endif
+	new->vlan_tci		= old->vlan_tci;
+
+	skb_copy_secmark(new, old);
+}
+
+// Adapted from net/core/skbuff.c at 2.6.38
+static void copy_skb_header_for_segment(struct sk_buff *new, const struct sk_buff *old)
+{
+#ifndef NET_SKBUFF_DATA_USES_OFFSET
+	/*
+	 *	Shift between the two data areas in bytes
+	 */
+	unsigned long offset = new->data - old->data;
+#endif
+
+	__copy_skb_header(new, old);
+
+#ifndef NET_SKBUFF_DATA_USES_OFFSET
+	/* {transport,network,mac}_header are relative to skb->head */
+	new->transport_header += offset;
+	new->network_header   += offset;
+	if (skb_mac_header_was_set(new))
+		new->mac_header	      += offset;
+#endif
+	skb_shinfo(new)->gso_size = 0;
+	skb_shinfo(new)->gso_segs = 1;
+	skb_shinfo(new)->gso_type = 0;
+}
+
+static struct sk_buff *preprocess_pskb(struct sk_buff *skb)
+{
+	struct skb_shared_info *shinfo = skb_shinfo(skb);
+	struct sk_buff *head = NULL, *nskb;
+	struct tcphdr *th;
+	struct iphdr *iph;
+	struct ipv6hdr *ipv6h;
+	int offset;
+	u32 seq, len_diff, partial_csum;
+	size_t header_size, body_size;
+
+	if (shinfo->gso_size == 0) {
+		// Kernel tells us to send packet as-is, without segmentation.
+		if (unlikely(skb_linearize(skb))) {
+			ath6kl_err("skb_linearize failed\n");
+			return NULL;
+		} else {
+			return skb;
+		}
+	}
+
+	switch (shinfo->gso_type) {
+		case SKB_GSO_TCPV4:
+		// case SKB_GSO_TCPV6:
+			// Find segmentation number
+			th = tcp_hdr(skb);
+			seq = ntohl(th->seq);
+			offset = header_size = (unsigned char *)th - skb->data + th->doff * 4;
+			break;
+		default:
+			// We don't know how to segment this yet.
+			ath6kl_err("Unknown GSO type %x\n", shinfo->gso_type);
+			return NULL;
+	}
+
+	if (skb->ip_summed != CHECKSUM_PARTIAL) BUG();
+
+	while (offset < skb->len) {
+		body_size = skb->len - offset;
+		if (body_size > shinfo->gso_size) body_size = shinfo->gso_size;
+		len_diff = skb->len - header_size - body_size; // Positive
+		nskb = alloc_skb(skb_headroom(skb) + header_size + body_size,  GFP_ATOMIC);
+		if (nskb == NULL) goto cleanup;
+		if (head == NULL) {
+			head = nskb;
+			nskb->next = nskb->prev = nskb;
+		}
+		else {
+			nskb->next = head;
+			nskb->prev = head->prev;
+			head->prev = nskb->prev->next = nskb;
+		}
+		skb_reserve(nskb, skb_headroom(skb));
+		skb_put(nskb, header_size + body_size);
+		// Copy the header
+		if (skb_copy_bits(skb, 0, nskb->data, header_size)) BUG();
+		// Copy the body
+		if (skb_copy_bits(skb, offset, nskb->data + header_size, body_size)) BUG();
+		// Copy the struct itself
+		copy_skb_header_for_segment(nskb, skb);
+		switch (shinfo->gso_type) {
+			case SKB_GSO_TCPV4:
+				// FIX IP length and checksum
+				iph = ip_hdr(nskb);
+				iph->tot_len = htons(ntohs(iph->tot_len) - len_diff);
+
+				partial_csum = (~ntohs(iph->check)) & 0xFFFF;
+				partial_csum -= len_diff;
+				partial_csum += (partial_csum >> 16);
+				iph->check = htons(~partial_csum);
+				// Fix segmentation number and partial checksum
+				th = tcp_hdr(nskb);
+				th->seq = htonl(seq);
+
+				// TCP partial checksum is not complemented
+				partial_csum = ntohs(th->check);
+				partial_csum -= len_diff;
+				partial_csum += (partial_csum >> 16);
+				th->check = htons(partial_csum);
+				break;
+			/*case SKB_GSO_TCPV6:
+				// FIX IP length
+				ipv6h = ipv6_hdr(nskb);
+				ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) - len_diff);
+				// IPv6 header has no checksum.
+				// Fix segmentation number and partial checksum
+				th = tcp_hdr(nskb);
+				th->seq = htonl(seq);
+				// Length in pseudoheader is 4-bytes.
+				th->check = htons(ntohs(th->check) + ~len_diff);
+				break;*/
+		}
+
+		offset += body_size;
+		seq += body_size;
+	}
+	head->prev->next = NULL;
+	head->prev = NULL;
+
+	return head;
+
+cleanup:
+	ath6kl_err("preprocess_pskb fails\n");
+	while (head) {
+		struct sk_buff *tmp = head;
+		head = head->next;
+		tmp->prev = tmp->next = NULL;
+		dev_kfree_skb(tmp);
+	}
+	return NULL;
+}
+
 int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
 {
 	struct ath6kl *ar = ath6kl_priv(dev);
@@ -374,10 +558,6 @@
 	u8 ac = 99 ; /* initialize to unmapped ac */
 	bool chk_adhoc_ps_mapping = false;
 	int ret;
-	struct wmi_tx_meta_v2 meta_v2;
-	void *meta;
-	u8 csum_start = 0, csum_dest = 0, csum = skb->ip_summed;
-	u8 meta_ver = 0;
 	u32 flags = 0;
 
 	ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
@@ -404,72 +584,100 @@
 			return 0;
 	}
 
+	{
+		// If we get a pskb, we want to apply segmentation and linearize it
+		// as requested by the kernel.
+		struct sk_buff *nskb = preprocess_pskb(skb);
+		if (nskb == NULL) goto fail_tx;
+		if (nskb != skb) {
+			dev_kfree_skb(skb);
+			skb = nskb;
+		}
+	}
+
 	if (test_bit(WMI_ENABLED, &ar->flag)) {
-		if ((dev->features & NETIF_F_IP_CSUM) &&
-				(csum == CHECKSUM_PARTIAL)) {
-			csum_start = skb->csum_start -
-					(skb_network_header(skb) - skb->head) +
-					sizeof(struct ath6kl_llc_snap_hdr);
-			csum_dest = skb->csum_offset + csum_start;
-		}
-
-		if (skb_headroom(skb) < dev->needed_headroom) {
-			struct sk_buff *tmp_skb = skb;
-
-			skb = skb_realloc_headroom(skb, dev->needed_headroom);
-			kfree_skb(tmp_skb);
-			if (skb == NULL) {
-				vif->net_stats.tx_dropped++;
-				return 0;
+		struct sk_buff *iter;
+		for (iter = skb; iter != NULL; iter = iter->next) {
+			// We do not wish to use the HW for checksum offloading because of
+			// its firmware dependency.
+			if (iter->ip_summed == CHECKSUM_PARTIAL) {
+				skb_checksum_help(iter);
 			}
-		}
 
-		if (ath6kl_wmi_dix_2_dot3(ar->wmi, skb)) {
-			ath6kl_err("ath6kl_wmi_dix_2_dot3 failed\n");
-			goto fail_tx;
-		}
+			if (skb_headroom(iter) < dev->needed_headroom) {
+				ath6kl_info("skb headroom too small\n");
+				struct sk_buff *tmp_skb = skb_realloc_headroom(iter, dev->needed_headroom);
+				if (tmp_skb == NULL) goto fail_tx;
+				tmp_skb->next = iter->next;
+				tmp_skb->prev = iter->prev;
+				if (tmp_skb->next) tmp_skb->next->prev = tmp_skb;
+				if (tmp_skb->prev)
+					tmp_skb->prev->next = tmp_skb;
+				else
+					// Replacing the first item.
+					skb = tmp_skb;
+				kfree_skb(iter);
+				iter = tmp_skb;
+			}
 
-		if ((dev->features & NETIF_F_IP_CSUM) &&
-				(csum == CHECKSUM_PARTIAL)) {
-			meta_v2.csum_start = csum_start;
-			meta_v2.csum_dest = csum_dest;
-
-			/* instruct target to calculate checksum */
-			meta_v2.csum_flags = WMI_META_V2_FLAG_CSUM_OFFLOAD;
-			meta_ver = WMI_META_VERSION_2;
-			meta = &meta_v2;
-		} else {
-			meta_ver = 0;
-			meta = NULL;
-		}
-
-		ret = ath6kl_wmi_data_hdr_add(ar->wmi, skb,
-				DATA_MSGTYPE, flags, 0,
-				meta_ver,
-				meta, vif->fw_vif_idx);
-
-		if (ret) {
-			ath6kl_warn("failed to add wmi data header:%d\n"
-				, ret);
-			goto fail_tx;
-		}
-
-		if ((vif->nw_type == ADHOC_NETWORK) &&
-		     ar->ibss_ps_enable && test_bit(CONNECTED, &vif->flags))
-			chk_adhoc_ps_mapping = true;
-		else {
-			/* get the stream mapping */
-			ret = ath6kl_wmi_implicit_create_pstream(ar->wmi,
-				    vif->fw_vif_idx, skb,
-				    0, test_bit(WMM_ENABLED, &vif->flags), &ac);
-			if (ret)
+			if (ath6kl_wmi_dix_2_dot3(ar->wmi, iter)) {
+				ath6kl_err("ath6kl_wmi_dix_2_dot3 failed\n");
 				goto fail_tx;
+			}
+
+			ret = ath6kl_wmi_data_hdr_add(ar->wmi, iter,
+					DATA_MSGTYPE, flags, 0,
+					0, NULL, vif->fw_vif_idx);
+
+			if (ret) {
+				ath6kl_warn("failed to add wmi data header:%d\n"
+					, ret);
+				goto fail_tx;
+			}
+
+			if ((vif->nw_type == ADHOC_NETWORK) &&
+				 ar->ibss_ps_enable && test_bit(CONNECTED, &vif->flags))
+				chk_adhoc_ps_mapping = true;
+			else {
+				/* get the stream mapping */
+				ret = ath6kl_wmi_implicit_create_pstream(ar->wmi,
+						vif->fw_vif_idx, iter,
+						0, test_bit(WMM_ENABLED, &vif->flags), &ac);
+				if (ret)
+					goto fail_tx;
+			}
+
+			if (!IS_ALIGNED((unsigned long) iter->data - HTC_HDR_LENGTH, 4) &&
+				skb_cloned(iter)) {
+				/*
+				 * We will touch (move the buffer data to align it. Since the
+				 * skb buffer is cloned and not only the header is changed, we
+				 * have to copy it to allow the changes. Since we are copying
+				 * the data here, we may as well align it by reserving suitable
+				 * headroom to avoid the memmove in ath6kl_htc_tx_buf_align().
+				 */
+				struct sk_buff *nskb;
+
+				nskb = skb_copy_expand(iter, HTC_HDR_LENGTH, 0, GFP_ATOMIC);
+				if (nskb == NULL) goto fail_tx;
+				nskb->next = iter->next;
+				nskb->prev = iter->prev;
+				if (nskb->next != NULL) nskb->next->prev = nskb;
+				if (nskb->prev != NULL)
+					nskb->prev->next = nskb;
+				else
+					// Replacing the first item.
+					skb = nskb;
+				kfree_skb(iter);
+				iter = nskb;
+			}
 		}
 	} else
 		goto fail_tx;
 
 	spin_lock_bh(&ar->lock);
 
+	// This one just asks for ethernet address. no need to loop.
 	if (chk_adhoc_ps_mapping)
 		eid = ath6kl_ibss_map_epid(skb, dev, &map_no);
 	else
@@ -481,56 +689,54 @@
 		goto fail_tx;
 	}
 
-	/* allocate resource for this packet */
-	cookie = ath6kl_alloc_cookie(ar, eid == ar->ctrl_ep);
+	while (skb != NULL) {
+		struct sk_buff *tmp_skb = skb;
+		/* allocate resource for this packet */
+		cookie = ath6kl_alloc_cookie(ar, eid == ar->ctrl_ep);
 
-	if (!cookie) {
-		spin_unlock_bh(&ar->lock);
-		goto fail_tx;
-	}
-
-	/* update counts while the lock is held */
-	ar->tx_pending[eid]++;
-	ar->total_tx_data_pend++;
-
-	spin_unlock_bh(&ar->lock);
-
-	if (!IS_ALIGNED((unsigned long) skb->data - HTC_HDR_LENGTH, 4) &&
-	    skb_cloned(skb)) {
-		/*
-		 * We will touch (move the buffer data to align it. Since the
-		 * skb buffer is cloned and not only the header is changed, we
-		 * have to copy it to allow the changes. Since we are copying
-		 * the data here, we may as well align it by reserving suitable
-		 * headroom to avoid the memmove in ath6kl_htc_tx_buf_align().
-		 */
-		struct sk_buff *nskb;
-
-		nskb = skb_copy_expand(skb, HTC_HDR_LENGTH, 0, GFP_ATOMIC);
-		if (nskb == NULL)
+		if (!cookie) {
+			spin_unlock_bh(&ar->lock);
 			goto fail_tx;
-		kfree_skb(skb);
-		skb = nskb;
+		}
+
+		/* update counts while the lock is held */
+		ar->tx_pending[eid]++;
+		ar->total_tx_data_pend++;
+
+		spin_unlock_bh(&ar->lock);
+
+		// Unlink one skb.
+		skb = skb->next;
+		//tmp_skb->next = NULL;
+		//if (skb != NULL) skb->prev = NULL;
+
+		cookie->skb = tmp_skb;
+		cookie->map_no = map_no;
+		set_htc_pkt_info(&cookie->htc_pkt, cookie, tmp_skb->data, tmp_skb->len,
+				 eid, htc_tag);
+
+		ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ",
+				tmp_skb->data, tmp_skb->len);
+
+		/*
+		 * HTC interface is asynchronous, if this fails, cleanup will
+		 * happen in the ath6kl_tx_complete callback.
+		 */
+		ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt, skb == NULL);
+
+		spin_lock_bh(&ar->lock);
 	}
-
-	cookie->skb = skb;
-	cookie->map_no = map_no;
-	set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
-			 eid, htc_tag);
-
-	ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ",
-			skb->data, skb->len);
-
-	/*
-	 * HTC interface is asynchronous, if this fails, cleanup will
-	 * happen in the ath6kl_tx_complete callback.
-	 */
-	ath6kl_htc_tx(ar->htc_target, &cookie->htc_pkt);
+	spin_unlock_bh(&ar->lock);
 
 	return 0;
 
 fail_tx:
-	dev_kfree_skb(skb);
+	ath6kl_err("fail_tx\n");
+	while (skb) {
+		struct sk_buff *tmp = skb;
+		skb = skb->next;
+		dev_kfree_skb(tmp);
+	}
 
 	vif->net_stats.tx_dropped++;
 	vif->net_stats.tx_aborted_errors++;
@@ -1349,7 +1555,7 @@
 		   __func__, ar, ept, skb, packet->buf,
 		   packet->act_len, status);
 
-	if (status || !(skb->data + HTC_HDR_LENGTH)) {
+	if (status || packet->act_len < HTC_HDR_LENGTH) {
 		dev_kfree_skb(skb);
 		return;
 	}
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index 49cb0af..427fb4f 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -4144,7 +4144,10 @@
 		break;
 	case WMI_REGDOMAIN_EVENTID:
 		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n");
+#if 0
+		/* remove country code setting based on 11d message */
 		ath6kl_wmi_regdomain_event(wmi, datap, len);
+#endif
 		break;
 	case WMI_PSTREAM_TIMEOUT_EVENTID:
 		ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n");
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index f02e0bc..05a7caa 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -508,10 +508,13 @@
 	 * - country_ie + 2, the start of the country ie data, and
 	 * - and country_ie[1] which is the IE length
 	 */
+#if 0
+	/* remove country code setting based on connected AP's country IE */
 	regulatory_hint_11d(wdev->wiphy,
 			    bss->channel->band,
 			    country_ie + 2,
 			    country_ie[1]);
+#endif
 }
 
 void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,