2008-11-24 21:15:25

by Christian Lamparter

[permalink] [raw]
Subject: [RFT][RFC][PATCH] p54: utilize cryptographic accelerator

This patch allows p54 to utilize its WEP, TKIP and CCMP accelerator.

Signed-off-by: Christian Lamparter <[email protected]>
---
diff -Nurp a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c
--- a/drivers/net/wireless/p54/p54common.c 2008-11-24 21:05:42.590287518 +0100
+++ b/drivers/net/wireless/p54/p54common.c 2008-11-24 22:06:13.951266225 +0100
@@ -25,6 +25,9 @@
#include "p54.h"
#include "p54common.h"

+static int modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
MODULE_AUTHOR("Michael Wu <[email protected]>");
MODULE_DESCRIPTION("Softmac Prism54 common code");
MODULE_LICENSE("GPL");
@@ -185,6 +188,8 @@ int p54_parse_firmware(struct ieee80211_
priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500;
priv->headroom = desc->headroom;
priv->tailroom = desc->tailroom;
+ priv->privacy_caps = desc->privacy_caps;
+ priv->rx_keycache_size = desc->rx_keycache_size;
if (le32_to_cpu(bootrec->len) == 11)
priv->rx_mtu = le16_to_cpu(desc->rx_mtu);
else
@@ -228,6 +233,16 @@ int p54_parse_firmware(struct ieee80211_
dev->queues = 4;
}

+ if (!modparam_nohwcrypt)
+ printk(KERN_INFO "%s: cryptographic accelerator "
+ " WEP:%s, TKIP:%s, CCMP:%s\n",
+ wiphy_name(dev->wiphy),
+ (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" :
+ "no", (priv->privacy_caps & (BR_DESC_PRIV_CAP_TKIP |
+ BR_DESC_PRIV_CAP_MICHAEL)) ? "YES" : "no",
+ (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP) ?
+ "YES" : "no");
+
return 0;
}
EXPORT_SYMBOL_GPL(p54_parse_firmware);
@@ -527,6 +542,11 @@ static int p54_rx_data(struct ieee80211_
return 0;
}

+ if (hdr->decrypt_status == P54_DECRYPT_OK)
+ rx_status.flag |= RX_FLAG_DECRYPTED;
+ if (hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL)
+ rx_status.flag |= RX_FLAG_MMIC_ERROR;
+
rx_status.signal = p54_rssi_to_dbm(dev, hdr->rssi);
rx_status.noise = priv->noise;
/* XX correct? */
@@ -843,6 +863,8 @@ static int p54_assign_address(struct iee

spin_lock_irqsave(&priv->tx_queue.lock, flags);
left = skb_queue_len(&priv->tx_queue);
+ if (left > 30)
+ return -ENOMEM;
while (left--) {
u32 hole_size;
info = IEEE80211_SKB_CB(entry);
@@ -1082,6 +1104,20 @@ static int p54_tx_fill(struct ieee80211_
return ret;
}

+static u8 p54_convert_algo(enum ieee80211_key_alg alg)
+{
+ switch (alg) {
+ case ALG_WEP:
+ return P54_CRYPTO_WEP;
+ case ALG_TKIP:
+ return P54_CRYPTO_TKIPMICHAEL;
+ case ALG_CCMP:
+ return P54_CRYPTO_AESCCMP;
+ default:
+ return 0;
+ }
+}
+
static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1092,7 +1128,7 @@ static int p54_tx(struct ieee80211_hw *d
size_t padding, len, tim_len = 0;
int i, j, ridx, ret;
u16 hdr_flags = 0, aid = 0;
- u8 rate, queue;
+ u8 rate, queue, crypt_offset = 0;
u8 cts_rate = 0x20;
u8 rc_flags;
u8 calculated_tries[4];
@@ -1116,12 +1152,25 @@ static int p54_tx(struct ieee80211_hw *d
padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3;
len = skb->len;

+ if (info->control.hw_key) {
+ crypt_offset = ieee80211_get_hdrlen_from_skb(skb);
+ if (info->control.hw_key->alg == ALG_TKIP) {
+ u8 *iv = (u8 *)(skb->data + crypt_offset);
+ /*
+ * The firmware excepts that the IV has to have
+ * this special format
+ */
+ iv[1] = iv[0];
+ iv[0] = iv[2];
+ iv[2] = 0;
+ }
+ }
+
txhdr = (struct p54_tx_data *) skb_push(skb, sizeof(*txhdr) + padding);
hdr = (struct p54_hdr *) skb_push(skb, sizeof(*hdr));

if (padding)
hdr_flags |= P54_HDR_FLAG_DATA_ALIGN;
- hdr->len = cpu_to_le16(len);
hdr->type = cpu_to_le16(aid);
hdr->rts_tries = info->control.rates[0].count;

@@ -1196,10 +1245,27 @@ static int p54_tx(struct ieee80211_hw *d
/* TODO: enable bursting */
hdr->flags = cpu_to_le16(hdr_flags);
hdr->tries = ridx;
- txhdr->crypt_offset = 0;
txhdr->rts_rate_idx = 0;
- txhdr->key_type = 0;
- txhdr->key_len = 0;
+ if (info->control.hw_key) {
+ crypt_offset += info->control.hw_key->iv_len;
+ txhdr->key_type = p54_convert_algo(info->control.hw_key->alg);
+ txhdr->key_len = min((u8)16, info->control.hw_key->keylen);
+ memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len);
+ if (info->control.hw_key->alg == ALG_TKIP) {
+ if (unlikely(skb_tailroom(skb) < 12))
+ goto err;
+ /* reserve space for the MIC key */
+ len += 8;
+ memcpy(skb_put(skb, 8), &(info->control.hw_key->key
+ [NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]), 8);
+ }
+ /* reserve some space for ICV */
+ len += info->control.hw_key->icv_len;
+ } else {
+ txhdr->key_type = 0;
+ txhdr->key_len = 0;
+ }
+ txhdr->crypt_offset = crypt_offset;
txhdr->hw_queue = queue;
if (current_queue)
txhdr->backlog = current_queue->len;
@@ -1213,17 +1279,21 @@ static int p54_tx(struct ieee80211_hw *d
if (padding)
txhdr->align[0] = padding;

+ hdr->len = cpu_to_le16(len);
/* modifies skb->cb and with it info, so must be last! */
- if (unlikely(p54_assign_address(dev, skb, hdr, skb->len + tim_len))) {
- skb_pull(skb, sizeof(*hdr) + sizeof(*txhdr) + padding);
- if (current_queue) {
- current_queue->len--;
- current_queue->count--;
- }
- return NETDEV_TX_BUSY;
- }
+ if (unlikely(p54_assign_address(dev, skb, hdr, skb->len + tim_len)))
+ goto err;
priv->tx(dev, skb, 0);
return 0;
+
+ err:
+ skb_pull(skb, sizeof(*hdr) + sizeof(*txhdr) + padding);
+ if (current_queue) {
+ current_queue->len--;
+ current_queue->count--;
+ }
+ kfree_skb(skb);
+ return 0; NETDEV_TX_BUSY;
}

static int p54_setup_mac(struct ieee80211_hw *dev, u16 mode, const u8 *bssid)
@@ -1827,6 +1897,76 @@ static void p54_bss_info_changed(struct

}

+static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ const u8 *local_address, const u8 *address,
+ struct ieee80211_key_conf *key)
+{
+ struct p54_common *priv = dev->priv;
+ struct sk_buff *skb;
+ struct p54_keycache *rxkey;
+ u8 algo = 0;
+
+ if (modparam_nohwcrypt)
+ return -ENOSPC;
+
+ if (cmd == DISABLE_KEY)
+ algo = 0;
+ else {
+ switch (key->alg) {
+ case ALG_TKIP:
+ if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL |
+ BR_DESC_PRIV_CAP_TKIP)))
+ return -EOPNOTSUPP;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ algo = P54_CRYPTO_TKIPMICHAEL;
+ break;
+ case ALG_WEP:
+ if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP))
+ return -EOPNOTSUPP;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ algo = P54_CRYPTO_WEP;
+ break;
+ case ALG_CCMP:
+ if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP))
+ return -EOPNOTSUPP;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ algo = P54_CRYPTO_AESCCMP;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ mutex_lock(&priv->conf_mutex);
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey) +
+ sizeof(struct p54_hdr), P54_CONTROL_TYPE_RX_KEYCACHE,
+ GFP_ATOMIC);
+ if (!skb) {
+ mutex_unlock(&priv->conf_mutex);
+ return -ENOMEM;
+ }
+
+ rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey));
+ rxkey->entry = cpu_to_le16(key->keyidx);
+ rxkey->key_id = cpu_to_le16(key->keyidx);
+ rxkey->key_len = min((u8)24, key->keylen);
+ rxkey->key_type = algo;
+ if (address)
+ memcpy(rxkey->mac, address, ETH_ALEN);
+ else
+ memset(rxkey->mac, ~0, ETH_ALEN);
+ if (key->alg != ALG_TKIP)
+ memcpy(rxkey->key, key->key, rxkey->key_len);
+ else {
+ memcpy(rxkey->key, key->key, 16);
+ memcpy(&(rxkey->key[16]), &(key->key
+ [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8);
+ }
+ priv->tx(dev, skb, 1);
+ mutex_unlock(&priv->conf_mutex);
+ return 0;
+}
+
static const struct ieee80211_ops p54_ops = {
.tx = p54_tx,
.start = p54_start,
@@ -1834,6 +1974,7 @@ static const struct ieee80211_ops p54_op
.add_interface = p54_add_interface,
.remove_interface = p54_remove_interface,
.set_tim = p54_set_tim,
+ .set_key = p54_set_key,
.config = p54_config,
.config_interface = p54_config_interface,
.bss_info_changed = p54_bss_info_changed,
diff -Nurp a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h
--- a/drivers/net/wireless/p54/p54.h 2008-11-24 15:13:52.353757667 +0100
+++ b/drivers/net/wireless/p54/p54.h 2008-11-24 21:39:22.075824370 +0100
@@ -115,6 +115,8 @@ struct p54_common {
int noise;
void *eeprom;
struct completion eeprom_comp;
+ u8 privacy_caps;
+ u8 rx_keycache_size;
};

int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb);


2008-11-27 11:26:06

by Christian Lamparter

[permalink] [raw]
Subject: Re: [RFT][RFC][PATCH] p54: utilize cryptographic accelerator

On Thursday 27 November 2008 01:15:57 Larry Finger wrote:
> > diff -Nurp a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c
> > --- a/drivers/net/wireless/p54/p54common.c 2008-11-24 21:05:42.590287518 +0100
> > +++ b/drivers/net/wireless/p54/p54common.c 2008-11-24 22:06:13.951266225 +0100
> > @@ -843,6 +863,8 @@ static int p54_assign_address(struct iee
> >
> > spin_lock_irqsave(&priv->tx_queue.lock, flags);
> > left = skb_queue_len(&priv->tx_queue);
> > + if (left > 30)
> > + return -ENOMEM;
>
> The above returns with tx_queue.lock still set.
Oops, this piece should be in another patch.
So, ok it's gone now.

> --snip--
> > + }
> > + }
> > +
> > + mutex_lock(&priv->conf_mutex);
> > + skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey) +
> > + sizeof(struct p54_hdr), P54_CONTROL_TYPE_RX_KEYCACHE,
> > + GFP_ATOMIC);
> > + if (!skb) {
> > + mutex_unlock(&priv->conf_mutex);
> > + return -ENOMEM;
> > + }
> > +
> > + rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey));
> > + rxkey->entry = cpu_to_le16(key->keyidx);
> > + rxkey->key_id = cpu_to_le16(key->keyidx);
>
> Sparse objects to the above two statments because key->keyidx is of type s8, and
> both rxkey->entry and rxkey->key_id are of type u8. A conversion to le16 is not
> needed.
>
> A patch for both problems is below:
Thanks... I guess the final RFT/RFC will be ready on friday/saturday.
there are a two outstanding issues:
- do we need to swap CCMP's IV as well?
- does MIC failure & replay detection work?
(or did you test that, when you were working with TKIP?)

Regards,
Chr

2008-11-27 14:37:29

by Larry Finger

[permalink] [raw]
Subject: Re: [RFT][RFC][PATCH] p54: utilize cryptographic accelerator

Christian Lamparter wrote:
> Thanks... I guess the final RFT/RFC will be ready on friday/saturday.
> there are a two outstanding issues:
> - do we need to swap CCMP's IV as well?
> - does MIC failure & replay detection work?
> (or did you test that, when you were working with TKIP?)

No.

Larry

2008-11-27 00:15:58

by Larry Finger

[permalink] [raw]
Subject: Re: [RFT][RFC][PATCH] p54: utilize cryptographic accelerator

Christian Lamparter wrote:
> This patch allows p54 to utilize its WEP, TKIP and CCMP accelerator.
>
> Signed-off-by: Christian Lamparter <[email protected]>
> ---
> diff -Nurp a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c
> --- a/drivers/net/wireless/p54/p54common.c 2008-11-24 21:05:42.590287518 +0100
> +++ b/drivers/net/wireless/p54/p54common.c 2008-11-24 22:06:13.951266225 +0100
> @@ -25,6 +25,9 @@
> #include "p54.h"
> #include "p54common.h"
>
> +static int modparam_nohwcrypt;
> +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
> +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
> MODULE_AUTHOR("Michael Wu <[email protected]>");
--snip--
> +
> return 0;
> }
> EXPORT_SYMBOL_GPL(p54_parse_firmware);
> @@ -527,6 +542,11 @@ static int p54_rx_data(struct ieee80211_
> return 0;
> }
>
> + if (hdr->decrypt_status == P54_DECRYPT_OK)
> + rx_status.flag |= RX_FLAG_DECRYPTED;
> + if (hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL)
> + rx_status.flag |= RX_FLAG_MMIC_ERROR;
> +
> rx_status.signal = p54_rssi_to_dbm(dev, hdr->rssi);
> rx_status.noise = priv->noise;
> /* XX correct? */
> @@ -843,6 +863,8 @@ static int p54_assign_address(struct iee
>
> spin_lock_irqsave(&priv->tx_queue.lock, flags);
> left = skb_queue_len(&priv->tx_queue);
> + if (left > 30)
> + return -ENOMEM;

The above returns with tx_queue.lock still set.

> while (left--) {
> u32 hole_size;
> info = IEEE80211_SKB_CB(entry);
> @@ -1082,6 +1104,20 @@ static int p54_tx_fill(struct ieee80211_
> return ret;
> }
>
--snip--
> + }
> + }
> +
> + mutex_lock(&priv->conf_mutex);
> + skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey) +
> + sizeof(struct p54_hdr), P54_CONTROL_TYPE_RX_KEYCACHE,
> + GFP_ATOMIC);
> + if (!skb) {
> + mutex_unlock(&priv->conf_mutex);
> + return -ENOMEM;
> + }
> +
> + rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey));
> + rxkey->entry = cpu_to_le16(key->keyidx);
> + rxkey->key_id = cpu_to_le16(key->keyidx);

Sparse objects to the above two statments because key->keyidx is of type s8, and
both rxkey->entry and rxkey->key_id are of type u8. A conversion to le16 is not
needed.

A patch for both problems is below:

Larry

=========

Index: wireless-testing/drivers/net/wireless/p54/p54common.c
===================================================================
--- wireless-testing.orig/drivers/net/wireless/p54/p54common.c
+++ wireless-testing/drivers/net/wireless/p54/p54common.c
@@ -862,8 +862,10 @@ static int p54_assign_address(struct iee

spin_lock_irqsave(&priv->tx_queue.lock, flags);
left = skb_queue_len(&priv->tx_queue);
- if (left > 30)
+ if (left > 30) {
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
return -ENOMEM;
+ }
while (left--) {
u32 hole_size;
info = IEEE80211_SKB_CB(entry);
@@ -1944,8 +1946,8 @@ static int p54_set_key(struct ieee80211_
}

rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey));
- rxkey->entry = cpu_to_le16(key->keyidx);
- rxkey->key_id = cpu_to_le16(key->keyidx);
+ rxkey->entry = key->keyidx;
+ rxkey->key_id = key->keyidx;
rxkey->key_len = min((u8)24, key->keylen);
rxkey->key_type = algo;
if (address)

2008-11-25 03:52:19

by Larry Finger

[permalink] [raw]
Subject: Re: [RFT][RFC][PATCH] p54: utilize cryptographic accelerator

Christian Lamparter wrote:
> This patch allows p54 to utilize its WEP, TKIP and CCMP accelerator.
>
> Signed-off-by: Christian Lamparter <[email protected]>

OK for WPA-PSK/TKIP.

Tested-by: Larry Finger <[email protected]>