orinoco: refactor xmit path
... so orinoco_usb can share some common functionality. Handle 802.2 encapsulation and MIC calculation in that function. The 802.3 header is prepended to the SKB. The calculated MIC is written to a specified buffer. Also modify the transmit control word that will be passed onto the hardware to specify whether the MIC is present, and the key used. Signed-off-by: David Kilroy <kilroyd@googlemail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
3ef83d745b
commit
bac6fafd4d
@ -339,18 +339,109 @@ EXPORT_SYMBOL(orinoco_change_mtu);
|
|||||||
/* Tx path */
|
/* 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)
|
static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct orinoco_private *priv = ndev_priv(dev);
|
struct orinoco_private *priv = ndev_priv(dev);
|
||||||
struct net_device_stats *stats = &priv->stats;
|
struct net_device_stats *stats = &priv->stats;
|
||||||
struct orinoco_tkip_key *key;
|
|
||||||
hermes_t *hw = &priv->hw;
|
hermes_t *hw = &priv->hw;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
u16 txfid = priv->txfid;
|
u16 txfid = priv->txfid;
|
||||||
struct ethhdr *eh;
|
|
||||||
int tx_control;
|
int tx_control;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int do_mic;
|
u8 mic_buf[MICHAEL_MIC_LEN+1];
|
||||||
|
|
||||||
if (!netif_running(dev)) {
|
if (!netif_running(dev)) {
|
||||||
printk(KERN_ERR "%s: Tx on stopped device!\n",
|
printk(KERN_ERR "%s: Tx on stopped device!\n",
|
||||||
@ -382,16 +473,12 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
if (skb->len < ETH_HLEN)
|
if (skb->len < ETH_HLEN)
|
||||||
goto drop;
|
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;
|
tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
|
||||||
|
|
||||||
if (do_mic)
|
err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control,
|
||||||
tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) |
|
&mic_buf[0]);
|
||||||
HERMES_TXCTRL_MIC;
|
if (err)
|
||||||
|
goto drop;
|
||||||
|
|
||||||
if (priv->has_alt_txcntl) {
|
if (priv->has_alt_txcntl) {
|
||||||
/* WPA enabled firmwares have tx_cntl at the end of
|
/* WPA enabled firmwares have tx_cntl at the end of
|
||||||
@ -434,34 +521,6 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
|
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,
|
err = hw->ops->bap_pwrite(hw, USER_BAP, skb->data, skb->len,
|
||||||
txfid, HERMES_802_3_OFFSET);
|
txfid, HERMES_802_3_OFFSET);
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -470,32 +529,16 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
goto busy;
|
goto busy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate Michael MIC */
|
if (tx_control & HERMES_TXCTRL_MIC) {
|
||||||
if (do_mic) {
|
size_t offset = HERMES_802_3_OFFSET + skb->len;
|
||||||
u8 mic_buf[MICHAEL_MIC_LEN + 1];
|
size_t len = MICHAEL_MIC_LEN;
|
||||||
u8 *mic;
|
|
||||||
size_t offset;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
if (skb->len % 2) {
|
if (offset % 2) {
|
||||||
/* MIC start is on an odd boundary */
|
offset--;
|
||||||
mic_buf[0] = skb->data[skb->len - 1];
|
len++;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
err = hw->ops->bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
|
||||||
txfid, HERMES_802_3_OFFSET + offset);
|
txfid, offset);
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
|
printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
|
||||||
dev->name, err);
|
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 */
|
/* we use the default eth_mac_addr for setting the MAC addr */
|
||||||
|
|
||||||
/* Reserve space in skb for the SNAP header */
|
/* Reserve space in skb for the SNAP header */
|
||||||
dev->hard_header_len += ENCAPS_OVERHEAD;
|
dev->needed_headroom = ENCAPS_OVERHEAD;
|
||||||
|
|
||||||
netif_carrier_off(dev);
|
netif_carrier_off(dev);
|
||||||
|
|
||||||
|
@ -200,6 +200,12 @@ extern irqreturn_t orinoco_interrupt(int irq, void *dev_id);
|
|||||||
extern void __orinoco_ev_info(struct net_device *dev, hermes_t *hw);
|
extern void __orinoco_ev_info(struct net_device *dev, hermes_t *hw);
|
||||||
extern void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw);
|
extern void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw);
|
||||||
|
|
||||||
|
int orinoco_process_xmit_skb(struct sk_buff *skb,
|
||||||
|
struct net_device *dev,
|
||||||
|
struct orinoco_private *priv,
|
||||||
|
int *tx_control,
|
||||||
|
u8 *mic);
|
||||||
|
|
||||||
/* Common ndo functions exported for reuse by orinoco_usb */
|
/* Common ndo functions exported for reuse by orinoco_usb */
|
||||||
int orinoco_open(struct net_device *dev);
|
int orinoco_open(struct net_device *dev);
|
||||||
int orinoco_stop(struct net_device *dev);
|
int orinoco_stop(struct net_device *dev);
|
||||||
|
@ -67,6 +67,7 @@
|
|||||||
#include <linux/wireless.h>
|
#include <linux/wireless.h>
|
||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
|
|
||||||
|
#include "mic.h"
|
||||||
#include "orinoco.h"
|
#include "orinoco.h"
|
||||||
|
|
||||||
#ifndef URB_ASYNC_UNLINK
|
#ifndef URB_ASYNC_UNLINK
|
||||||
@ -1198,11 +1199,9 @@ static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
struct orinoco_private *priv = ndev_priv(dev);
|
struct orinoco_private *priv = ndev_priv(dev);
|
||||||
struct net_device_stats *stats = &priv->stats;
|
struct net_device_stats *stats = &priv->stats;
|
||||||
struct ezusb_priv *upriv = priv->card;
|
struct ezusb_priv *upriv = priv->card;
|
||||||
|
u8 mic[MICHAEL_MIC_LEN+1];
|
||||||
int err = 0;
|
int err = 0;
|
||||||
char *p;
|
int tx_control;
|
||||||
struct ethhdr *eh;
|
|
||||||
int len, data_len, data_off;
|
|
||||||
__le16 tx_control;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct request_context *ctx;
|
struct request_context *ctx;
|
||||||
u8 *buf;
|
u8 *buf;
|
||||||
@ -1222,7 +1221,7 @@ static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
|
|
||||||
if (orinoco_lock(priv, &flags) != 0) {
|
if (orinoco_lock(priv, &flags) != 0) {
|
||||||
printk(KERN_ERR
|
printk(KERN_ERR
|
||||||
"%s: orinoco_xmit() called while hw_unavailable\n",
|
"%s: ezusb_xmit() called while hw_unavailable\n",
|
||||||
dev->name);
|
dev->name);
|
||||||
return NETDEV_TX_BUSY;
|
return NETDEV_TX_BUSY;
|
||||||
}
|
}
|
||||||
@ -1232,53 +1231,46 @@ static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
/* 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). */
|
||||||
stats->tx_errors++;
|
goto drop;
|
||||||
orinoco_unlock(priv, &flags);
|
|
||||||
dev_kfree_skb(skb);
|
|
||||||
return NETDEV_TX_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check packet length */
|
||||||
|
if (skb->len < ETH_HLEN)
|
||||||
|
goto drop;
|
||||||
|
|
||||||
ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_TX, 0);
|
ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_TX, 0);
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
goto fail;
|
goto busy;
|
||||||
|
|
||||||
memset(ctx->buf, 0, BULK_BUF_SIZE);
|
memset(ctx->buf, 0, BULK_BUF_SIZE);
|
||||||
buf = ctx->buf->data;
|
buf = ctx->buf->data;
|
||||||
|
|
||||||
/* Length of the packet body */
|
tx_control = 0;
|
||||||
/* FIXME: what if the skb is smaller than this? */
|
|
||||||
len = max_t(int, skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN);
|
|
||||||
|
|
||||||
eh = (struct ethhdr *) skb->data;
|
err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control,
|
||||||
|
&mic[0]);
|
||||||
|
if (err)
|
||||||
|
goto drop;
|
||||||
|
|
||||||
tx_control = cpu_to_le16(0);
|
{
|
||||||
memcpy(buf, &tx_control, sizeof(tx_control));
|
__le16 *tx_cntl = (__le16 *)buf;
|
||||||
buf += sizeof(tx_control);
|
*tx_cntl = cpu_to_le16(tx_control);
|
||||||
/* Encapsulate Ethernet-II frames */
|
buf += sizeof(*tx_cntl);
|
||||||
if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
|
|
||||||
struct header_struct *hdr = (void *) buf;
|
|
||||||
buf += sizeof(*hdr);
|
|
||||||
data_len = len;
|
|
||||||
data_off = sizeof(tx_control) + sizeof(*hdr);
|
|
||||||
p = skb->data + ETH_HLEN;
|
|
||||||
|
|
||||||
/* 802.3 header */
|
|
||||||
memcpy(hdr->dest, eh->h_dest, ETH_ALEN);
|
|
||||||
memcpy(hdr->src, eh->h_source, ETH_ALEN);
|
|
||||||
hdr->len = htons(data_len + ENCAPS_OVERHEAD);
|
|
||||||
|
|
||||||
/* 802.2 header */
|
|
||||||
memcpy(&hdr->dsap, &encaps_hdr, sizeof(encaps_hdr));
|
|
||||||
|
|
||||||
hdr->ethertype = eh->h_proto;
|
|
||||||
} else { /* IEEE 802.3 frame */
|
|
||||||
data_len = len + ETH_HLEN;
|
|
||||||
data_off = sizeof(tx_control);
|
|
||||||
p = skb->data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(buf, p, data_len);
|
memcpy(buf, skb->data, skb->len);
|
||||||
buf += data_len;
|
buf += skb->len;
|
||||||
|
|
||||||
|
if (tx_control & HERMES_TXCTRL_MIC) {
|
||||||
|
u8 *m = mic;
|
||||||
|
/* Mic has been offset so it can be copied to an even
|
||||||
|
* address. We're copying eveything anyway, so we
|
||||||
|
* don't need to copy that first byte. */
|
||||||
|
if (skb->len % 2)
|
||||||
|
m++;
|
||||||
|
memcpy(buf, m, MICHAEL_MIC_LEN);
|
||||||
|
buf += MICHAEL_MIC_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
/* Finally, we actually initiate the send */
|
/* Finally, we actually initiate the send */
|
||||||
netif_stop_queue(dev);
|
netif_stop_queue(dev);
|
||||||
@ -1294,20 +1286,23 @@ static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||||||
if (net_ratelimit())
|
if (net_ratelimit())
|
||||||
printk(KERN_ERR "%s: Error %d transmitting packet\n",
|
printk(KERN_ERR "%s: Error %d transmitting packet\n",
|
||||||
dev->name, err);
|
dev->name, err);
|
||||||
stats->tx_errors++;
|
goto busy;
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->trans_start = jiffies;
|
dev->trans_start = jiffies;
|
||||||
stats->tx_bytes += data_off + data_len;
|
stats->tx_bytes += skb->len;
|
||||||
|
goto ok;
|
||||||
|
|
||||||
|
drop:
|
||||||
|
stats->tx_errors++;
|
||||||
|
stats->tx_dropped++;
|
||||||
|
|
||||||
|
ok:
|
||||||
orinoco_unlock(priv, &flags);
|
orinoco_unlock(priv, &flags);
|
||||||
|
|
||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
|
|
||||||
return NETDEV_TX_OK;
|
return NETDEV_TX_OK;
|
||||||
|
|
||||||
fail:
|
busy:
|
||||||
orinoco_unlock(priv, &flags);
|
orinoco_unlock(priv, &flags);
|
||||||
return NETDEV_TX_BUSY;
|
return NETDEV_TX_BUSY;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user