# For kernels 2.6.24.x only. Applying ieee80211 patch is required too. Some distros may require the 2.6.25.x version of the patch
# Features: sets monitor mode properly, discards invalid frames, ...
diff -pur linux-2.6.24.4-zd1211rw/drivers/net/wireless/zd1211rw/zd_mac.c linux-2.6.24.4-sud/drivers/net/wireless/zd1211rw/zd_mac.c
--- linux-2.6.24.4-zd1211rw/drivers/net/wireless/zd1211rw/zd_mac.c	2008-01-24 23:58:37.000000000 +0100
+++ linux-2.6.24.4-sud/drivers/net/wireless/zd1211rw/zd_mac.c	2008-04-12 21:21:59.000000000 +0200
@@ -163,8 +163,17 @@ void zd_mac_clear(struct zd_mac *mac)
 static int set_rx_filter(struct zd_mac *mac)
 {
 	struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
-	u32 filter = (ieee->iw_mode == IW_MODE_MONITOR) ? ~0 : STA_RX_FILTER;
-	return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter);
+	struct zd_ioreq32 ioreqs[] = {
+		{ CR_RX_FILTER, STA_RX_FILTER },
+		{ CR_SNIFFER_ON, 0U },
+	};
+
+	if (ieee->iw_mode == IW_MODE_MONITOR) {
+		ioreqs[0].value = 0xffffffff;
+		ioreqs[1].value = 0x1;
+	}
+
+	return zd_iowrite32a(&mac->chip, ioreqs, ARRAY_SIZE(ioreqs));
 }
 
 static int set_sniffer(struct zd_mac *mac)
@@ -236,7 +245,13 @@ int zd_mac_open(struct net_device *netde
 		goto disable_rx;
 
 	housekeeping_enable(mac);
-	ieee80211softmac_start(netdev);
+        netif_carrier_on(netdev);
+        ieee80211softmac_start(netdev);
+        if(!netif_queue_stopped(netdev))
+                netif_start_queue(netdev);
+        else
+                netif_wake_queue(netdev);
+
 	return 0;
 disable_rx:
 	zd_chip_disable_rx(chip);
@@ -768,6 +783,8 @@ static void cs_set_modulation(struct zd_
 	                      struct ieee80211_hdr_4addr *hdr)
 {
 	struct ieee80211softmac_device *softmac = ieee80211_priv(mac->netdev);
+	struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
+	struct ieee80211softmac_txrates *txrates = &softmac->txrates;
 	u16 ftype = WLAN_FC_GET_TYPE(le16_to_cpu(hdr->frame_ctl));
 	u8 rate;
 	int is_mgt = (ftype == IEEE80211_FTYPE_MGMT) != 0;
@@ -788,6 +805,7 @@ static void cs_set_control(struct zd_mac
 	                   struct ieee80211_hdr_4addr *header)
 {
 	struct ieee80211softmac_device *softmac = ieee80211_priv(mac->netdev);
+	struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
 	unsigned int tx_length = le16_to_cpu(cs->tx_length);
 	u16 fctl = le16_to_cpu(header->frame_ctl);
 	u16 ftype = WLAN_FC_GET_TYPE(fctl);
@@ -801,13 +819,21 @@ static void cs_set_control(struct zd_mac
 
 	cs->control = 0;
 
-	/* First fragment */
-	if (WLAN_GET_SEQ_FRAG(le16_to_cpu(header->seq_ctl)) == 0)
+	if(ieee->iw_mode == IW_MODE_MONITOR)
+	{
 		cs->control |= ZD_CS_NEED_RANDOM_BACKOFF;
-
-	/* Multicast */
-	if (is_multicast_ether_addr(header->addr1))
 		cs->control |= ZD_CS_MULTICAST;
+	}
+	else
+	{
+		/* First fragment */
+		if (WLAN_GET_SEQ_FRAG(le16_to_cpu(header->seq_ctl)) == 0)
+			cs->control |= ZD_CS_NEED_RANDOM_BACKOFF;
+
+		/* Multicast */
+		if (is_multicast_ether_addr(header->addr1))
+			cs->control |= ZD_CS_MULTICAST;
+	}
 
 	/* PS-POLL */
 	if (ftype == IEEE80211_FTYPE_CTL && stype == IEEE80211_STYPE_PSPOLL)
@@ -834,6 +860,7 @@ static int fill_ctrlset(struct zd_mac *m
 	                struct ieee80211_txb *txb,
 			int frag_num)
 {
+        struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
 	int r;
 	struct sk_buff *skb = txb->fragments[frag_num];
 	struct ieee80211_hdr_4addr *hdr =
@@ -857,7 +884,10 @@ static int fill_ctrlset(struct zd_mac *m
 
 	cs->tx_length = cpu_to_le16(frag_len);
 
-	cs_set_control(mac, cs, hdr);
+/*        if(ieee->iw_mode == IW_MODE_MONITOR)
+                cs->control = ZD_CS_MULTICAST;
+        else*/
+                cs_set_control(mac, cs, hdr);
 
 	packet_length = frag_len + sizeof(struct zd_ctrlset) + 10;
 	ZD_ASSERT(packet_length <= 0xffff);
@@ -913,7 +943,11 @@ static int zd_mac_tx(struct zd_mac *mac,
 			ieee->stats.tx_dropped++;
 			return r;
 		}
-		r = zd_usb_tx(&mac->chip.usb, skb->data, skb->len);
+
+                if(ieee->iw_mode == IW_MODE_MONITOR)
+                        r = zd_usb_tx_inject(&mac->chip.usb, skb->data, skb->len);
+                else
+                        r = zd_usb_tx(&mac->chip.usb, skb->data, skb->len);
 		if (r) {
 			ieee->stats.tx_dropped++;
 			return r;
@@ -933,6 +967,8 @@ struct zd_rt_hdr {
 	u8  rt_rate;
 	u16 rt_channel;
 	u16 rt_chbitmask;
+        u8  rt_antsignal;
+        u8  rt_antnoise;
 } __attribute__((packed));
 
 static void fill_rt_header(void *buffer, struct zd_mac *mac,
@@ -946,7 +982,9 @@ static void fill_rt_header(void *buffer,
 	hdr->rt_hdr.it_len = cpu_to_le16(sizeof(struct zd_rt_hdr));
 	hdr->rt_hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
 		                 (1 << IEEE80211_RADIOTAP_CHANNEL) |
-				 (1 << IEEE80211_RADIOTAP_RATE));
+                                 (1 << IEEE80211_RADIOTAP_RATE) |
+                                 (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
+                                 (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE));
 
 	hdr->rt_flags = 0;
 	if (status->decryption_type & (ZD_RX_WEP64|ZD_RX_WEP128|ZD_RX_WEP256))
@@ -960,6 +998,9 @@ static void fill_rt_header(void *buffer,
 	hdr->rt_chbitmask = cpu_to_le16(IEEE80211_CHAN_2GHZ |
 		((status->frame_status & ZD_RX_FRAME_MODULATION_MASK) ==
 		ZD_RX_OFDM ? IEEE80211_CHAN_OFDM : IEEE80211_CHAN_CCK));
+
+        hdr->rt_antsignal = status->signal_strength;
+        hdr->rt_antnoise = stats->noise;
 }
 
 /* Returns 1 if the data packet is for us and 0 otherwise. */
@@ -1067,7 +1108,8 @@ static int fill_rx_stats(struct ieee8021
 
 	*pstatus = status = (struct rx_status *)
 		(buffer + (length - sizeof(struct rx_status)));
-	if (status->frame_status & ZD_RX_ERROR) {
+	if ((status->frame_status & ZD_RX_ERROR) ||
+		(status->frame_status & ~0x21)) {
 		struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
 		ieee->stats.rx_errors++;
 		if (status->frame_status & ZD_RX_TIMEOUT_ERROR)
diff -pur linux-2.6.24.4-zd1211rw/drivers/net/wireless/zd1211rw/zd_usb.c linux-2.6.24.4-sud/drivers/net/wireless/zd1211rw/zd_usb.c
--- linux-2.6.24.4-zd1211rw/drivers/net/wireless/zd1211rw/zd_usb.c	2008-01-24 23:58:37.000000000 +0100
+++ linux-2.6.24.4-sud/drivers/net/wireless/zd1211rw/zd_usb.c	2008-04-12 19:48:06.000000000 +0200
@@ -814,6 +814,46 @@ out:
 	return r;
 }
 
+/* Puts the frame on the USB endpoint. It doesn't wait for
+ * completion. The frame must contain the control set.
+ */
+int zd_usb_tx_inject(struct zd_usb *usb, const u8 *frame, unsigned int length)
+{
+       int r;
+       struct usb_device *udev = zd_usb_to_usbdev(usb);
+       struct urb *urb;
+       void *buffer;
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               r = -ENOMEM;
+               goto out;
+       }
+
+       buffer = usb_buffer_alloc(zd_usb_to_usbdev(usb), length, GFP_ATOMIC,
+                                 &urb->transfer_dma);
+       if (!buffer) {
+               r = -ENOMEM;
+               goto error_free_urb;
+       }
+       memcpy(buffer, frame, length);
+
+       usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+                         buffer, length, tx_urb_complete, NULL);
+
+       r = usb_submit_urb(urb, GFP_ATOMIC);
+       if (r)
+               goto error;
+       return 0;
+error:
+       usb_buffer_free(zd_usb_to_usbdev(usb), length, buffer,
+                       urb->transfer_dma);
+error_free_urb:
+       usb_free_urb(urb);
+out:
+       return r;
+}
+
 static inline void init_usb_interrupt(struct zd_usb *usb)
 {
 	struct zd_usb_interrupt *intr = &usb->intr;
diff -pur linux-2.6.24.4-zd1211rw/drivers/net/wireless/zd1211rw/zd_usb.h linux-2.6.24.4-sud/drivers/net/wireless/zd1211rw/zd_usb.h
--- linux-2.6.24.4-zd1211rw/drivers/net/wireless/zd1211rw/zd_usb.h	2008-01-24 23:58:37.000000000 +0100
+++ linux-2.6.24.4-sud/drivers/net/wireless/zd1211rw/zd_usb.h	2008-04-12 19:48:06.000000000 +0200
@@ -222,6 +222,7 @@ int zd_usb_enable_rx(struct zd_usb *usb)
 void zd_usb_disable_rx(struct zd_usb *usb);
 
 int zd_usb_tx(struct zd_usb *usb, const u8 *frame, unsigned int length);
+int zd_usb_tx_inject(struct zd_usb *usb, const u8 *frame, unsigned int length);
 
 int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
 	         const zd_addr_t *addresses, unsigned int count);
