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: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Christian Lamparter 2011-08-15 20:09:54 +02:00 committed by John W. Linville
parent acf1771221
commit 00044f17af
3 changed files with 139 additions and 0 deletions

View File

@ -39,3 +39,17 @@ config CARL9170_WPC
bool
depends on CARL9170 && (INPUT = y || INPUT = CARL9170)
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.

View File

@ -43,6 +43,7 @@
#include <linux/firmware.h>
#include <linux/completion.h>
#include <linux/spinlock.h>
#include <linux/hw_random.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include <linux/usb.h>
@ -449,6 +450,17 @@ struct ar9170 {
unsigned int off_override;
bool state;
} 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 {

View File

@ -1468,6 +1468,109 @@ static int carl9170_register_wps_button(struct ar9170 *ar)
}
#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,
struct survey_info *survey)
{
@ -1878,6 +1981,12 @@ int carl9170_register(struct ar9170 *ar)
goto err_unreg;
#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",
wiphy_name(ar->hw->wiphy));
@ -1910,6 +2019,10 @@ void carl9170_unregister(struct ar9170 *ar)
}
#endif /* CONFIG_CARL9170_WPC */
#ifdef CONFIG_CARL9170_HWRNG
carl9170_unregister_hwrng(ar);
#endif /* CONFIG_CARL9170_HWRNG */
carl9170_cancel_worker(ar);
cancel_work_sync(&ar->restart_work);