Commit 00044f17 authored by Christian Lamparter's avatar Christian Lamparter Committed by John W. Linville

carl9170: export HW random number generator

All AR9170 hardware have a 16-Bit random number generator.
The documentation claims the values are suitable for
"security keys".

The "throughput" is around 320Kibit/s. It's slow, but it
does work without introducing any special offload
firmware commands.
Signed-off-by: default avatarChristian Lamparter <chunkeey@googlemail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent acf17712
...@@ -39,3 +39,17 @@ config CARL9170_WPC ...@@ -39,3 +39,17 @@ config CARL9170_WPC
bool bool
depends on CARL9170 && (INPUT = y || INPUT = CARL9170) depends on CARL9170 && (INPUT = y || INPUT = CARL9170)
default y default y
config CARL9170_HWRNG
bool "Random number generator"
depends on CARL9170 && (HW_RANDOM = y || HW_RANDOM = CARL9170)
default n
help
Provides a hardware random number generator to the kernel.
SECURITY WARNING: It's relatively easy to eavesdrop all
generated random numbers from the transport stream with
usbmon [software] or special usb sniffer hardware.
Say N, unless your setup[i.e.: embedded system] has no
other rng source and you can afford to take the risk.
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/hw_random.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
#include <net/mac80211.h> #include <net/mac80211.h>
#include <linux/usb.h> #include <linux/usb.h>
...@@ -449,6 +450,17 @@ struct ar9170 { ...@@ -449,6 +450,17 @@ struct ar9170 {
unsigned int off_override; unsigned int off_override;
bool state; bool state;
} ps; } ps;
#ifdef CONFIG_CARL9170_HWRNG
# define CARL9170_HWRNG_CACHE_SIZE CARL9170_MAX_CMD_PAYLOAD_LEN
struct {
struct hwrng rng;
bool initialized;
char name[30 + 1];
u16 cache[CARL9170_HWRNG_CACHE_SIZE / sizeof(u16)];
unsigned int cache_idx;
} rng;
#endif /* CONFIG_CARL9170_HWRNG */
}; };
enum carl9170_ps_off_override_reasons { enum carl9170_ps_off_override_reasons {
......
...@@ -1468,6 +1468,109 @@ static int carl9170_register_wps_button(struct ar9170 *ar) ...@@ -1468,6 +1468,109 @@ static int carl9170_register_wps_button(struct ar9170 *ar)
} }
#endif /* CONFIG_CARL9170_WPC */ #endif /* CONFIG_CARL9170_WPC */
#ifdef CONFIG_CARL9170_HWRNG
static int carl9170_rng_get(struct ar9170 *ar)
{
#define RW (CARL9170_MAX_CMD_PAYLOAD_LEN / sizeof(u32))
#define RB (CARL9170_MAX_CMD_PAYLOAD_LEN)
static const __le32 rng_load[RW] = {
[0 ... (RW - 1)] = cpu_to_le32(AR9170_RAND_REG_NUM)};
u32 buf[RW];
unsigned int i, off = 0, transfer, count;
int err;
BUILD_BUG_ON(RB > CARL9170_MAX_CMD_PAYLOAD_LEN);
if (!IS_ACCEPTING_CMD(ar) || !ar->rng.initialized)
return -EAGAIN;
count = ARRAY_SIZE(ar->rng.cache);
while (count) {
err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG,
RB, (u8 *) rng_load,
RB, (u8 *) buf);
if (err)
return err;
transfer = min_t(unsigned int, count, RW);
for (i = 0; i < transfer; i++)
ar->rng.cache[off + i] = buf[i];
off += transfer;
count -= transfer;
}
ar->rng.cache_idx = 0;
#undef RW
#undef RB
return 0;
}
static int carl9170_rng_read(struct hwrng *rng, u32 *data)
{
struct ar9170 *ar = (struct ar9170 *)rng->priv;
int ret = -EIO;
mutex_lock(&ar->mutex);
if (ar->rng.cache_idx >= ARRAY_SIZE(ar->rng.cache)) {
ret = carl9170_rng_get(ar);
if (ret) {
mutex_unlock(&ar->mutex);
return ret;
}
}
*data = ar->rng.cache[ar->rng.cache_idx++];
mutex_unlock(&ar->mutex);
return sizeof(u16);
}
static void carl9170_unregister_hwrng(struct ar9170 *ar)
{
if (ar->rng.initialized) {
hwrng_unregister(&ar->rng.rng);
ar->rng.initialized = false;
}
}
static int carl9170_register_hwrng(struct ar9170 *ar)
{
int err;
snprintf(ar->rng.name, ARRAY_SIZE(ar->rng.name),
"%s_%s", KBUILD_MODNAME, wiphy_name(ar->hw->wiphy));
ar->rng.rng.name = ar->rng.name;
ar->rng.rng.data_read = carl9170_rng_read;
ar->rng.rng.priv = (unsigned long)ar;
if (WARN_ON(ar->rng.initialized))
return -EALREADY;
err = hwrng_register(&ar->rng.rng);
if (err) {
dev_err(&ar->udev->dev, "Failed to register the random "
"number generator (%d)\n", err);
return err;
}
ar->rng.initialized = true;
err = carl9170_rng_get(ar);
if (err) {
carl9170_unregister_hwrng(ar);
return err;
}
return 0;
}
#endif /* CONFIG_CARL9170_HWRNG */
static int carl9170_op_get_survey(struct ieee80211_hw *hw, int idx, static int carl9170_op_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey) struct survey_info *survey)
{ {
...@@ -1878,6 +1981,12 @@ int carl9170_register(struct ar9170 *ar) ...@@ -1878,6 +1981,12 @@ int carl9170_register(struct ar9170 *ar)
goto err_unreg; goto err_unreg;
#endif /* CONFIG_CARL9170_WPC */ #endif /* CONFIG_CARL9170_WPC */
#ifdef CONFIG_CARL9170_HWRNG
err = carl9170_register_hwrng(ar);
if (err)
goto err_unreg;
#endif /* CONFIG_CARL9170_HWRNG */
dev_info(&ar->udev->dev, "Atheros AR9170 is registered as '%s'\n", dev_info(&ar->udev->dev, "Atheros AR9170 is registered as '%s'\n",
wiphy_name(ar->hw->wiphy)); wiphy_name(ar->hw->wiphy));
...@@ -1910,6 +2019,10 @@ void carl9170_unregister(struct ar9170 *ar) ...@@ -1910,6 +2019,10 @@ void carl9170_unregister(struct ar9170 *ar)
} }
#endif /* CONFIG_CARL9170_WPC */ #endif /* CONFIG_CARL9170_WPC */
#ifdef CONFIG_CARL9170_HWRNG
carl9170_unregister_hwrng(ar);
#endif /* CONFIG_CARL9170_HWRNG */
carl9170_cancel_worker(ar); carl9170_cancel_worker(ar);
cancel_work_sync(&ar->restart_work); cancel_work_sync(&ar->restart_work);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment