Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 98c4cae1 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jeff Garzik
Browse files

[PATCH] orinoco: monitor mode support

Patch from Pavel Roskin
parent 8f2abf44
Loading
Loading
Loading
Loading
+179 −29
Original line number Original line Diff line number Diff line
@@ -499,6 +499,10 @@ static int ignore_disconnect; /* = 0 */
module_param(ignore_disconnect, int, 0644);
module_param(ignore_disconnect, int, 0644);
MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer");
MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer");


static int force_monitor; /* = 0 */
module_param(force_monitor, int, 0644);
MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions");

/********************************************************************/
/********************************************************************/
/* Compile time configuration and compatibility stuff               */
/* Compile time configuration and compatibility stuff               */
/********************************************************************/
/********************************************************************/
@@ -670,6 +674,10 @@ static inline void set_port_type(struct orinoco_private *priv)
			priv->createibss = 1;
			priv->createibss = 1;
		}
		}
		break;
		break;
	case IW_MODE_MONITOR:
		priv->port_type = 3;
		priv->createibss = 0;
		break;
	default:
	default:
		printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
		printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
		       priv->ndev->name);
		       priv->ndev->name);
@@ -856,7 +864,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
		return 1;
		return 1;
	}
	}


	if (! netif_carrier_ok(dev)) {
	if (! netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) {
		/* Oops, the firmware hasn't established a connection,
		/* Oops, the firmware hasn't established a connection,
                   silently drop the packet (this seems to be the
                   silently drop the packet (this seems to be the
                   safest approach). */
                   safest approach). */
@@ -1118,6 +1126,117 @@ static void orinoco_stat_gather(struct net_device *dev,
	}
	}
}
}


/*
 * orinoco_rx_monitor - handle received monitor frames.
 *
 * Arguments:
 *	dev		network device
 *	rxfid		received FID
 *	desc		rx descriptor of the frame
 *
 * Call context: interrupt
 */
static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
			       struct hermes_rx_descriptor *desc)
{
	u32 hdrlen = 30;	/* return full header by default */
	u32 datalen = 0;
	u16 fc;
	int err;
	int len;
	struct sk_buff *skb;
	struct orinoco_private *priv = netdev_priv(dev);
	struct net_device_stats *stats = &priv->stats;
	hermes_t *hw = &priv->hw;

	len = le16_to_cpu(desc->data_len);

	/* Determine the size of the header and the data */
	fc = le16_to_cpu(desc->frame_ctl);
	switch (fc & IEEE80211_FCTL_FTYPE) {
	case IEEE80211_FTYPE_DATA:
		if ((fc & IEEE80211_FCTL_TODS)
		    && (fc & IEEE80211_FCTL_FROMDS))
			hdrlen = 30;
		else
			hdrlen = 24;
		datalen = len;
		break;
	case IEEE80211_FTYPE_MGMT:
		hdrlen = 24;
		datalen = len;
		break;
	case IEEE80211_FTYPE_CTL:
		switch (fc & IEEE80211_FCTL_STYPE) {
		case IEEE80211_STYPE_PSPOLL:
		case IEEE80211_STYPE_RTS:
		case IEEE80211_STYPE_CFEND:
		case IEEE80211_STYPE_CFENDACK:
			hdrlen = 16;
			break;
		case IEEE80211_STYPE_CTS:
		case IEEE80211_STYPE_ACK:
			hdrlen = 10;
			break;
		}
		break;
	default:
		/* Unknown frame type */
		break;
	}

	/* sanity check the length */
	if (datalen > IEEE80211_DATA_LEN + 12) {
		printk(KERN_DEBUG "%s: oversized monitor frame, "
		       "data length = %d\n", dev->name, datalen);
		err = -EIO;
		stats->rx_length_errors++;
		goto update_stats;
	}

	skb = dev_alloc_skb(hdrlen + datalen);
	if (!skb) {
		printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n",
		       dev->name);
		err = -ENOMEM;
		goto drop;
	}

	/* Copy the 802.11 header to the skb */
	memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen);
	skb->mac.raw = skb->data;

	/* If any, copy the data from the card to the skb */
	if (datalen > 0) {
		err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
				       ALIGN(datalen, 2), rxfid,
				       HERMES_802_2_OFFSET);
		if (err) {
			printk(KERN_ERR "%s: error %d reading monitor frame\n",
			       dev->name, err);
			goto drop;
		}
	}

	skb->dev = dev;
	skb->ip_summed = CHECKSUM_NONE;
	skb->pkt_type = PACKET_OTHERHOST;
	skb->protocol = __constant_htons(ETH_P_802_2);
	
	dev->last_rx = jiffies;
	stats->rx_packets++;
	stats->rx_bytes += skb->len;

	netif_rx(skb);
	return;

 drop:
	dev_kfree_skb_irq(skb);
 update_stats:
	stats->rx_errors++;
	stats->rx_dropped++;
}

static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
{
{
	struct orinoco_private *priv = netdev_priv(dev);
	struct orinoco_private *priv = netdev_priv(dev);
@@ -1137,24 +1256,29 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
	if (err) {
	if (err) {
		printk(KERN_ERR "%s: error %d reading Rx descriptor. "
		printk(KERN_ERR "%s: error %d reading Rx descriptor. "
		       "Frame dropped.\n", dev->name, err);
		       "Frame dropped.\n", dev->name, err);
		stats->rx_errors++;
		goto update_stats;
		goto drop;
	}
	}


	status = le16_to_cpu(desc.status);
	status = le16_to_cpu(desc.status);


	if (status & HERMES_RXSTAT_ERR) {
	if (status & HERMES_RXSTAT_BADCRC) {
		if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
		DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n",
			wstats->discard.code++;
			DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
		      dev->name);
		      dev->name);
		} else {
		stats->rx_crc_errors++;
		stats->rx_crc_errors++;
			DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name);
		goto update_stats;
	}
	}


		stats->rx_errors++;
	/* Handle frames in monitor mode */
		goto drop;
	if (priv->iw_mode == IW_MODE_MONITOR) {
		orinoco_rx_monitor(dev, rxfid, &desc);
		return;
	}

	if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
		DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
		      dev->name);
		wstats->discard.code++;
		goto update_stats;
	}
	}


	length = le16_to_cpu(desc.data_len);
	length = le16_to_cpu(desc.data_len);
@@ -1165,15 +1289,13 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
		/* At least on Symbol firmware with PCF we get quite a
		/* At least on Symbol firmware with PCF we get quite a
                   lot of these legitimately - Poll frames with no
                   lot of these legitimately - Poll frames with no
                   data. */
                   data. */
		stats->rx_dropped++;
		return;
		goto drop;
	}
	}
	if (length > IEEE802_11_DATA_LEN) {
	if (length > IEEE802_11_DATA_LEN) {
		printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
		printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
		       dev->name, length);
		       dev->name, length);
		stats->rx_length_errors++;
		stats->rx_length_errors++;
		stats->rx_errors++;
		goto update_stats;
		goto drop;
	}
	}


	/* We need space for the packet data itself, plus an ethernet
	/* We need space for the packet data itself, plus an ethernet
@@ -1185,7 +1307,7 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
	if (!skb) {
	if (!skb) {
		printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
		printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
		       dev->name);
		       dev->name);
		goto drop;
		goto update_stats;
	}
	}


	/* We'll prepend the header, so reserve space for it.  The worst
	/* We'll prepend the header, so reserve space for it.  The worst
@@ -1199,7 +1321,6 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
	if (err) {
	if (err) {
		printk(KERN_ERR "%s: error %d reading frame. "
		printk(KERN_ERR "%s: error %d reading frame. "
		       "Frame dropped.\n", dev->name, err);
		       "Frame dropped.\n", dev->name, err);
		stats->rx_errors++;
		goto drop;
		goto drop;
	}
	}


@@ -1245,11 +1366,10 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
	return;
	return;


 drop:	
 drop:	
	stats->rx_dropped++;

	if (skb)
	dev_kfree_skb_irq(skb);
	dev_kfree_skb_irq(skb);
	return;
 update_stats:
	stats->rx_errors++;
	stats->rx_dropped++;
}
}


/********************************************************************/
/********************************************************************/
@@ -2065,6 +2185,20 @@ static int __orinoco_program_rids(struct net_device *dev)
		}
		}
	}
	}


	if (priv->iw_mode == IW_MODE_MONITOR) {
		/* Enable monitor mode */
		dev->type = ARPHRD_IEEE80211;
		err = hermes_docmd_wait(hw, HERMES_CMD_TEST | 
					    HERMES_TEST_MONITOR, 0, NULL);
	} else {
		/* Disable monitor mode */
		dev->type = ARPHRD_ETHER;
		err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
					    HERMES_TEST_STOP, 0, NULL);
	}
	if (err)
		return err;

	/* Set promiscuity / multicast*/
	/* Set promiscuity / multicast*/
	priv->promiscuous = 0;
	priv->promiscuous = 0;
	priv->mc_count = 0;
	priv->mc_count = 0;
@@ -2413,6 +2547,7 @@ static int determine_firmware(struct net_device *dev)
		priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
		priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
		priv->ibss_port = 1;
		priv->ibss_port = 1;
		priv->has_hostscan = (firmver >= 0x8000a);
		priv->has_hostscan = (firmver >= 0x8000a);
		priv->broken_monitor = (firmver >= 0x80000);


		/* Tested with Agere firmware :
		/* Tested with Agere firmware :
		 *	1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
		 *	1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
@@ -2980,6 +3115,15 @@ static int orinoco_ioctl_setmode(struct net_device *dev,
	case IW_MODE_INFRA:
	case IW_MODE_INFRA:
		break;
		break;


	case IW_MODE_MONITOR:
		if (priv->broken_monitor && !force_monitor) {
			printk(KERN_WARNING "%s: Monitor mode support is "
			       "buggy in this firmware, not enabling\n",
			       dev->name);
			err = -EOPNOTSUPP;
		}
		break;

	default:
	default:
		err = -EOPNOTSUPP;
		err = -EOPNOTSUPP;
		break;
		break;
@@ -3355,11 +3499,9 @@ static int orinoco_ioctl_setfreq(struct net_device *dev,
	unsigned long flags;
	unsigned long flags;
	int err = -EINPROGRESS;		/* Call commit handler */
	int err = -EINPROGRESS;		/* Call commit handler */


	/* We can only use this in Ad-Hoc demo mode to set the operating
	/* In infrastructure mode the AP sets the channel */
	 * frequency, or in IBSS mode to set the frequency where the IBSS
	if (priv->iw_mode == IW_MODE_INFRA)
	 * will be created - Jean II */
		return -EBUSY;
	if (priv->iw_mode != IW_MODE_ADHOC)
		return -EOPNOTSUPP;


	if ( (frq->e == 0) && (frq->m <= 1000) ) {
	if ( (frq->e == 0) && (frq->m <= 1000) ) {
		/* Setting by channel number */
		/* Setting by channel number */
@@ -3383,7 +3525,15 @@ static int orinoco_ioctl_setfreq(struct net_device *dev,


	if (orinoco_lock(priv, &flags) != 0)
	if (orinoco_lock(priv, &flags) != 0)
		return -EBUSY;
		return -EBUSY;

	priv->channel = chan;
	priv->channel = chan;
	if (priv->iw_mode == IW_MODE_MONITOR) {
		/* Fast channel change - no commit if successful */
		hermes_t *hw = &priv->hw;
		err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
					    HERMES_TEST_SET_CHANNEL,
					chan, NULL);
	}
	orinoco_unlock(priv, &flags);
	orinoco_unlock(priv, &flags);


	return err;
	return err;
+1 −0
Original line number Original line Diff line number Diff line
@@ -94,6 +94,7 @@ struct orinoco_private {
	unsigned int has_sensitivity:1;
	unsigned int has_sensitivity:1;
	unsigned int has_hostscan:1;
	unsigned int has_hostscan:1;
	unsigned int broken_disableport:1;
	unsigned int broken_disableport:1;
	unsigned int broken_monitor:1;


	/* Configuration paramaters */
	/* Configuration paramaters */
	u32 iw_mode;
	u32 iw_mode;