diff options
author | Christian Lamparter <chunkeey@web.de> | 2008-11-29 16:34:37 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-12-05 09:35:43 -0500 |
commit | 25900ef0191af98bbb24d8088c6887af31c1ba27 (patch) | |
tree | ed054c65ad207c5e9cb12215964dcb200b7e4b84 /drivers/net | |
parent | c772a08ba7192fa5450f85ef53adcbc6e0c5e1c9 (diff) |
p54: utilize cryptographic accelerator
This patch allows p54 to utilize its WEP, TKIP and CCMP accelerator.
Tested-by: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: Christian Lamparter <chunkeey@web.de>
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/p54/p54.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/p54/p54common.c | 179 |
2 files changed, 168 insertions, 13 deletions
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h index 491ab96c7b6..ebe1ea1f565 100644 --- a/drivers/net/wireless/p54/p54.h +++ b/drivers/net/wireless/p54/p54.h | |||
@@ -115,6 +115,8 @@ struct p54_common { | |||
115 | int noise; | 115 | int noise; |
116 | void *eeprom; | 116 | void *eeprom; |
117 | struct completion eeprom_comp; | 117 | struct completion eeprom_comp; |
118 | u8 privacy_caps; | ||
119 | u8 rx_keycache_size; | ||
118 | }; | 120 | }; |
119 | 121 | ||
120 | int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb); | 122 | int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb); |
diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index de92b58d450..6f7e82b56cb 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c | |||
@@ -25,6 +25,9 @@ | |||
25 | #include "p54.h" | 25 | #include "p54.h" |
26 | #include "p54common.h" | 26 | #include "p54common.h" |
27 | 27 | ||
28 | static int modparam_nohwcrypt; | ||
29 | module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); | ||
30 | MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); | ||
28 | MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); | 31 | MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); |
29 | MODULE_DESCRIPTION("Softmac Prism54 common code"); | 32 | MODULE_DESCRIPTION("Softmac Prism54 common code"); |
30 | MODULE_LICENSE("GPL"); | 33 | MODULE_LICENSE("GPL"); |
@@ -185,6 +188,8 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) | |||
185 | priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500; | 188 | priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500; |
186 | priv->headroom = desc->headroom; | 189 | priv->headroom = desc->headroom; |
187 | priv->tailroom = desc->tailroom; | 190 | priv->tailroom = desc->tailroom; |
191 | priv->privacy_caps = desc->privacy_caps; | ||
192 | priv->rx_keycache_size = desc->rx_keycache_size; | ||
188 | if (le32_to_cpu(bootrec->len) == 11) | 193 | if (le32_to_cpu(bootrec->len) == 11) |
189 | priv->rx_mtu = le16_to_cpu(desc->rx_mtu); | 194 | priv->rx_mtu = le16_to_cpu(desc->rx_mtu); |
190 | else | 195 | else |
@@ -228,6 +233,16 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) | |||
228 | dev->queues = 4; | 233 | dev->queues = 4; |
229 | } | 234 | } |
230 | 235 | ||
236 | if (!modparam_nohwcrypt) | ||
237 | printk(KERN_INFO "%s: cryptographic accelerator " | ||
238 | "WEP:%s, TKIP:%s, CCMP:%s\n", | ||
239 | wiphy_name(dev->wiphy), | ||
240 | (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" : | ||
241 | "no", (priv->privacy_caps & (BR_DESC_PRIV_CAP_TKIP | | ||
242 | BR_DESC_PRIV_CAP_MICHAEL)) ? "YES" : "no", | ||
243 | (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP) ? | ||
244 | "YES" : "no"); | ||
245 | |||
231 | return 0; | 246 | return 0; |
232 | } | 247 | } |
233 | EXPORT_SYMBOL_GPL(p54_parse_firmware); | 248 | EXPORT_SYMBOL_GPL(p54_parse_firmware); |
@@ -526,6 +541,12 @@ static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
526 | return 0; | 541 | return 0; |
527 | } | 542 | } |
528 | 543 | ||
544 | if (hdr->decrypt_status == P54_DECRYPT_OK) | ||
545 | rx_status.flag |= RX_FLAG_DECRYPTED; | ||
546 | if ((hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL) || | ||
547 | (hdr->decrypt_status == P54_DECRYPT_FAIL_TKIP)) | ||
548 | rx_status.flag |= RX_FLAG_MMIC_ERROR; | ||
549 | |||
529 | rx_status.signal = p54_rssi_to_dbm(dev, hdr->rssi); | 550 | rx_status.signal = p54_rssi_to_dbm(dev, hdr->rssi); |
530 | rx_status.noise = priv->noise; | 551 | rx_status.noise = priv->noise; |
531 | /* XX correct? */ | 552 | /* XX correct? */ |
@@ -1107,6 +1128,20 @@ static int p54_tx_fill(struct ieee80211_hw *dev, struct sk_buff *skb, | |||
1107 | return ret; | 1128 | return ret; |
1108 | } | 1129 | } |
1109 | 1130 | ||
1131 | static u8 p54_convert_algo(enum ieee80211_key_alg alg) | ||
1132 | { | ||
1133 | switch (alg) { | ||
1134 | case ALG_WEP: | ||
1135 | return P54_CRYPTO_WEP; | ||
1136 | case ALG_TKIP: | ||
1137 | return P54_CRYPTO_TKIPMICHAEL; | ||
1138 | case ALG_CCMP: | ||
1139 | return P54_CRYPTO_AESCCMP; | ||
1140 | default: | ||
1141 | return 0; | ||
1142 | } | ||
1143 | } | ||
1144 | |||
1110 | static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb) | 1145 | static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb) |
1111 | { | 1146 | { |
1112 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 1147 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); |
@@ -1117,7 +1152,7 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
1117 | size_t padding, len, tim_len = 0; | 1152 | size_t padding, len, tim_len = 0; |
1118 | int i, j, ridx, ret; | 1153 | int i, j, ridx, ret; |
1119 | u16 hdr_flags = 0, aid = 0; | 1154 | u16 hdr_flags = 0, aid = 0; |
1120 | u8 rate, queue; | 1155 | u8 rate, queue, crypt_offset = 0; |
1121 | u8 cts_rate = 0x20; | 1156 | u8 cts_rate = 0x20; |
1122 | u8 rc_flags; | 1157 | u8 rc_flags; |
1123 | u8 calculated_tries[4]; | 1158 | u8 calculated_tries[4]; |
@@ -1137,12 +1172,25 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
1137 | padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3; | 1172 | padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3; |
1138 | len = skb->len; | 1173 | len = skb->len; |
1139 | 1174 | ||
1175 | if (info->control.hw_key) { | ||
1176 | crypt_offset = ieee80211_get_hdrlen_from_skb(skb); | ||
1177 | if (info->control.hw_key->alg == ALG_TKIP) { | ||
1178 | u8 *iv = (u8 *)(skb->data + crypt_offset); | ||
1179 | /* | ||
1180 | * The firmware excepts that the IV has to have | ||
1181 | * this special format | ||
1182 | */ | ||
1183 | iv[1] = iv[0]; | ||
1184 | iv[0] = iv[2]; | ||
1185 | iv[2] = 0; | ||
1186 | } | ||
1187 | } | ||
1188 | |||
1140 | txhdr = (struct p54_tx_data *) skb_push(skb, sizeof(*txhdr) + padding); | 1189 | txhdr = (struct p54_tx_data *) skb_push(skb, sizeof(*txhdr) + padding); |
1141 | hdr = (struct p54_hdr *) skb_push(skb, sizeof(*hdr)); | 1190 | hdr = (struct p54_hdr *) skb_push(skb, sizeof(*hdr)); |
1142 | 1191 | ||
1143 | if (padding) | 1192 | if (padding) |
1144 | hdr_flags |= P54_HDR_FLAG_DATA_ALIGN; | 1193 | hdr_flags |= P54_HDR_FLAG_DATA_ALIGN; |
1145 | hdr->len = cpu_to_le16(len); | ||
1146 | hdr->type = cpu_to_le16(aid); | 1194 | hdr->type = cpu_to_le16(aid); |
1147 | hdr->rts_tries = info->control.rates[0].count; | 1195 | hdr->rts_tries = info->control.rates[0].count; |
1148 | 1196 | ||
@@ -1217,10 +1265,27 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
1217 | /* TODO: enable bursting */ | 1265 | /* TODO: enable bursting */ |
1218 | hdr->flags = cpu_to_le16(hdr_flags); | 1266 | hdr->flags = cpu_to_le16(hdr_flags); |
1219 | hdr->tries = ridx; | 1267 | hdr->tries = ridx; |
1220 | txhdr->crypt_offset = 0; | ||
1221 | txhdr->rts_rate_idx = 0; | 1268 | txhdr->rts_rate_idx = 0; |
1222 | txhdr->key_type = 0; | 1269 | if (info->control.hw_key) { |
1223 | txhdr->key_len = 0; | 1270 | crypt_offset += info->control.hw_key->iv_len; |
1271 | txhdr->key_type = p54_convert_algo(info->control.hw_key->alg); | ||
1272 | txhdr->key_len = min((u8)16, info->control.hw_key->keylen); | ||
1273 | memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len); | ||
1274 | if (info->control.hw_key->alg == ALG_TKIP) { | ||
1275 | if (unlikely(skb_tailroom(skb) < 12)) | ||
1276 | goto err; | ||
1277 | /* reserve space for the MIC key */ | ||
1278 | len += 8; | ||
1279 | memcpy(skb_put(skb, 8), &(info->control.hw_key->key | ||
1280 | [NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]), 8); | ||
1281 | } | ||
1282 | /* reserve some space for ICV */ | ||
1283 | len += info->control.hw_key->icv_len; | ||
1284 | } else { | ||
1285 | txhdr->key_type = 0; | ||
1286 | txhdr->key_len = 0; | ||
1287 | } | ||
1288 | txhdr->crypt_offset = crypt_offset; | ||
1224 | txhdr->hw_queue = queue; | 1289 | txhdr->hw_queue = queue; |
1225 | if (current_queue) | 1290 | if (current_queue) |
1226 | txhdr->backlog = current_queue->len; | 1291 | txhdr->backlog = current_queue->len; |
@@ -1234,17 +1299,20 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
1234 | if (padding) | 1299 | if (padding) |
1235 | txhdr->align[0] = padding; | 1300 | txhdr->align[0] = padding; |
1236 | 1301 | ||
1302 | hdr->len = cpu_to_le16(len); | ||
1237 | /* modifies skb->cb and with it info, so must be last! */ | 1303 | /* modifies skb->cb and with it info, so must be last! */ |
1238 | if (unlikely(p54_assign_address(dev, skb, hdr, skb->len + tim_len))) { | 1304 | if (unlikely(p54_assign_address(dev, skb, hdr, skb->len + tim_len))) |
1239 | skb_pull(skb, sizeof(*hdr) + sizeof(*txhdr) + padding); | 1305 | goto err; |
1240 | if (current_queue) { | ||
1241 | current_queue->len--; | ||
1242 | current_queue->count--; | ||
1243 | } | ||
1244 | return NETDEV_TX_BUSY; | ||
1245 | } | ||
1246 | priv->tx(dev, skb, 0); | 1306 | priv->tx(dev, skb, 0); |
1247 | return 0; | 1307 | return 0; |
1308 | |||
1309 | err: | ||
1310 | skb_pull(skb, sizeof(*hdr) + sizeof(*txhdr) + padding); | ||
1311 | if (current_queue) { | ||
1312 | current_queue->len--; | ||
1313 | current_queue->count--; | ||
1314 | } | ||
1315 | return NETDEV_TX_BUSY; | ||
1248 | } | 1316 | } |
1249 | 1317 | ||
1250 | static int p54_setup_mac(struct ieee80211_hw *dev, u16 mode, const u8 *bssid) | 1318 | static int p54_setup_mac(struct ieee80211_hw *dev, u16 mode, const u8 *bssid) |
@@ -1848,6 +1916,90 @@ static void p54_bss_info_changed(struct ieee80211_hw *dev, | |||
1848 | 1916 | ||
1849 | } | 1917 | } |
1850 | 1918 | ||
1919 | static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, | ||
1920 | const u8 *local_address, const u8 *address, | ||
1921 | struct ieee80211_key_conf *key) | ||
1922 | { | ||
1923 | struct p54_common *priv = dev->priv; | ||
1924 | struct sk_buff *skb; | ||
1925 | struct p54_keycache *rxkey; | ||
1926 | u8 algo = 0; | ||
1927 | |||
1928 | if (modparam_nohwcrypt) | ||
1929 | return -EOPNOTSUPP; | ||
1930 | |||
1931 | if (cmd == DISABLE_KEY) | ||
1932 | algo = 0; | ||
1933 | else { | ||
1934 | switch (key->alg) { | ||
1935 | case ALG_TKIP: | ||
1936 | if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL | | ||
1937 | BR_DESC_PRIV_CAP_TKIP))) | ||
1938 | return -EOPNOTSUPP; | ||
1939 | key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | ||
1940 | algo = P54_CRYPTO_TKIPMICHAEL; | ||
1941 | break; | ||
1942 | case ALG_WEP: | ||
1943 | if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) | ||
1944 | return -EOPNOTSUPP; | ||
1945 | key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | ||
1946 | algo = P54_CRYPTO_WEP; | ||
1947 | break; | ||
1948 | case ALG_CCMP: | ||
1949 | if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) | ||
1950 | return -EOPNOTSUPP; | ||
1951 | key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | ||
1952 | algo = P54_CRYPTO_AESCCMP; | ||
1953 | break; | ||
1954 | default: | ||
1955 | return -EINVAL; | ||
1956 | } | ||
1957 | } | ||
1958 | |||
1959 | if (key->keyidx > priv->rx_keycache_size) { | ||
1960 | /* | ||
1961 | * The device supports the choosen algorithm, but the firmware | ||
1962 | * does not provide enough key slots to store all of them. | ||
1963 | * So, incoming frames have to be decoded by the mac80211 stack, | ||
1964 | * but we can still offload encryption for outgoing frames. | ||
1965 | */ | ||
1966 | |||
1967 | return 0; | ||
1968 | } | ||
1969 | |||
1970 | mutex_lock(&priv->conf_mutex); | ||
1971 | skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey) + | ||
1972 | sizeof(struct p54_hdr), P54_CONTROL_TYPE_RX_KEYCACHE, | ||
1973 | GFP_ATOMIC); | ||
1974 | if (!skb) { | ||
1975 | mutex_unlock(&priv->conf_mutex); | ||
1976 | return -ENOMEM; | ||
1977 | } | ||
1978 | |||
1979 | /* TODO: some devices have 4 more free slots for rx keys */ | ||
1980 | rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey)); | ||
1981 | rxkey->entry = key->keyidx; | ||
1982 | rxkey->key_id = key->keyidx; | ||
1983 | rxkey->key_type = algo; | ||
1984 | if (address) | ||
1985 | memcpy(rxkey->mac, address, ETH_ALEN); | ||
1986 | else | ||
1987 | memset(rxkey->mac, ~0, ETH_ALEN); | ||
1988 | if (key->alg != ALG_TKIP) { | ||
1989 | rxkey->key_len = min((u8)16, key->keylen); | ||
1990 | memcpy(rxkey->key, key->key, rxkey->key_len); | ||
1991 | } else { | ||
1992 | rxkey->key_len = 24; | ||
1993 | memcpy(rxkey->key, key->key, 16); | ||
1994 | memcpy(&(rxkey->key[16]), &(key->key | ||
1995 | [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8); | ||
1996 | } | ||
1997 | |||
1998 | priv->tx(dev, skb, 1); | ||
1999 | mutex_unlock(&priv->conf_mutex); | ||
2000 | return 0; | ||
2001 | } | ||
2002 | |||
1851 | static const struct ieee80211_ops p54_ops = { | 2003 | static const struct ieee80211_ops p54_ops = { |
1852 | .tx = p54_tx, | 2004 | .tx = p54_tx, |
1853 | .start = p54_start, | 2005 | .start = p54_start, |
@@ -1857,6 +2009,7 @@ static const struct ieee80211_ops p54_ops = { | |||
1857 | .set_tim = p54_set_tim, | 2009 | .set_tim = p54_set_tim, |
1858 | .sta_notify_ps = p54_sta_notify_ps, | 2010 | .sta_notify_ps = p54_sta_notify_ps, |
1859 | .sta_notify = p54_sta_notify, | 2011 | .sta_notify = p54_sta_notify, |
2012 | .set_key = p54_set_key, | ||
1860 | .config = p54_config, | 2013 | .config = p54_config, |
1861 | .config_interface = p54_config_interface, | 2014 | .config_interface = p54_config_interface, |
1862 | .bss_info_changed = p54_bss_info_changed, | 2015 | .bss_info_changed = p54_bss_info_changed, |