diff options
Diffstat (limited to 'drivers/net/wireless/orinoco/main.c')
-rw-r--r-- | drivers/net/wireless/orinoco/main.c | 169 |
1 files changed, 106 insertions, 63 deletions
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c index 884a777..97e954e 100644 --- a/drivers/net/wireless/orinoco/main.c +++ b/drivers/net/wireless/orinoco/main.c @@ -340,18 +340,109 @@ EXPORT_SYMBOL(orinoco_change_mtu); /* Tx path */ /********************************************************************/ +/* Add encapsulation and MIC to the existing SKB. + * The main xmit routine will then send the whole lot to the card. + * Need 8 bytes headroom + * Need 8 bytes tailroom + * + * With encapsulated ethernet II frame + * -------- + * 803.3 header (14 bytes) + * dst[6] + * -------- src[6] + * 803.3 header (14 bytes) len[2] + * dst[6] 803.2 header (8 bytes) + * src[6] encaps[6] + * len[2] <- leave alone -> len[2] + * -------- -------- <-- 0 + * Payload Payload + * ... ... + * + * -------- -------- + * MIC (8 bytes) + * -------- + * + * returns 0 on success, -ENOMEM on error. + */ +int orinoco_process_xmit_skb(struct sk_buff *skb, + struct net_device *dev, + struct orinoco_private *priv, + int *tx_control, + u8 *mic_buf) +{ + struct orinoco_tkip_key *key; + struct ethhdr *eh; + int do_mic; + + key = (struct orinoco_tkip_key *) priv->keys[priv->tx_key].key; + + do_mic = ((priv->encode_alg == ORINOCO_ALG_TKIP) && + (key != NULL)); + + if (do_mic) + *tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) | + HERMES_TXCTRL_MIC; + + eh = (struct ethhdr *)skb->data; + + /* Encapsulate Ethernet-II frames */ + if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */ + struct header_struct { + struct ethhdr eth; /* 802.3 header */ + u8 encap[6]; /* 802.2 header */ + } __attribute__ ((packed)) hdr; + int len = skb->len + sizeof(encaps_hdr) - (2 * ETH_ALEN); + + if (skb_headroom(skb) < ENCAPS_OVERHEAD) { + if (net_ratelimit()) + printk(KERN_ERR + "%s: Not enough headroom for 802.2 headers %d\n", + dev->name, skb_headroom(skb)); + return -ENOMEM; + } + + /* Fill in new header */ + memcpy(&hdr.eth, eh, 2 * ETH_ALEN); + hdr.eth.h_proto = htons(len); + memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr)); + + /* Make room for the new header, and copy it in */ + eh = (struct ethhdr *) skb_push(skb, ENCAPS_OVERHEAD); + memcpy(eh, &hdr, sizeof(hdr)); + } + + /* Calculate Michael MIC */ + if (do_mic) { + size_t len = skb->len - ETH_HLEN; + u8 *mic = &mic_buf[0]; + + /* Have to write to an even address, so copy the spare + * byte across */ + if (skb->len % 2) { + *mic = skb->data[skb->len - 1]; + mic++; + } + + orinoco_mic(priv->tx_tfm_mic, key->tx_mic, + eh->h_dest, eh->h_source, 0 /* priority */, + skb->data + ETH_HLEN, + len, mic); + } + + return 0; +} +EXPORT_SYMBOL(orinoco_process_xmit_skb); + static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; - struct orinoco_tkip_key *key; hermes_t *hw = &priv->hw; int err = 0; u16 txfid = priv->txfid; - struct ethhdr *eh; int tx_control; unsigned long flags; - int do_mic; + u8 mic_buf[MICHAEL_MIC_LEN+1]; if (!netif_running(dev)) { printk(KERN_ERR "%s: Tx on stopped device!\n", @@ -383,16 +474,12 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) if (skb->len < ETH_HLEN) goto drop; - key = (struct orinoco_tkip_key *) priv->keys[priv->tx_key].key; - - do_mic = ((priv->encode_alg == ORINOCO_ALG_TKIP) && - (key != NULL)); - tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; - if (do_mic) - tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) | - HERMES_TXCTRL_MIC; + err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control, + &mic_buf[0]); + if (err) + goto drop; if (priv->has_alt_txcntl) { /* WPA enabled firmwares have tx_cntl at the end of @@ -435,34 +522,6 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); } - eh = (struct ethhdr *)skb->data; - - /* Encapsulate Ethernet-II frames */ - if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */ - struct header_struct { - struct ethhdr eth; /* 802.3 header */ - u8 encap[6]; /* 802.2 header */ - } __attribute__ ((packed)) hdr; - - /* Strip destination and source from the data */ - skb_pull(skb, 2 * ETH_ALEN); - - /* And move them to a separate header */ - memcpy(&hdr.eth, eh, 2 * ETH_ALEN); - hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len); - memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr)); - - /* Insert the SNAP header */ - if (skb_headroom(skb) < sizeof(hdr)) { - printk(KERN_ERR - "%s: Not enough headroom for 802.2 headers %d\n", - dev->name, skb_headroom(skb)); - goto drop; - } - eh = (struct ethhdr *) skb_push(skb, sizeof(hdr)); - memcpy(eh, &hdr, sizeof(hdr)); - } - err = hw->ops->bap_pwrite(hw, USER_BAP, skb->data, skb->len, txfid, HERMES_802_3_OFFSET); if (err) { @@ -471,32 +530,16 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) goto busy; } - /* Calculate Michael MIC */ - if (do_mic) { - u8 mic_buf[MICHAEL_MIC_LEN + 1]; - u8 *mic; - size_t offset; - size_t len; + if (tx_control & HERMES_TXCTRL_MIC) { + size_t offset = HERMES_802_3_OFFSET + skb->len; + size_t len = MICHAEL_MIC_LEN; - if (skb->len % 2) { - /* MIC start is on an odd boundary */ - mic_buf[0] = skb->data[skb->len - 1]; - mic = &mic_buf[1]; - offset = skb->len - 1; - len = MICHAEL_MIC_LEN + 1; - } else { - mic = &mic_buf[0]; - offset = skb->len; - len = MICHAEL_MIC_LEN; + if (offset % 2) { + offset--; + len++; } - - orinoco_mic(priv->tx_tfm_mic, key->tx_mic, - eh->h_dest, eh->h_source, 0 /* priority */, - skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic); - - /* Write the MIC */ err = hw->ops->bap_pwrite(hw, USER_BAP, &mic_buf[0], len, - txfid, HERMES_802_3_OFFSET + offset); + txfid, offset); if (err) { printk(KERN_ERR "%s: Error %d writing MIC to BAP\n", dev->name, err); @@ -2234,7 +2277,7 @@ int orinoco_if_add(struct orinoco_private *priv, /* we use the default eth_mac_addr for setting the MAC addr */ /* Reserve space in skb for the SNAP header */ - dev->hard_header_len += ENCAPS_OVERHEAD; + dev->needed_headroom = ENCAPS_OVERHEAD; netif_carrier_off(dev); |