carl9170: export HW random number generator
authorChristian Lamparter <chunkeey@googlemail.com>
Mon, 15 Aug 2011 18:09:54 +0000 (20:09 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 24 Aug 2011 18:41:42 +0000 (14:41 -0400)
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>
drivers/net/wireless/ath/carl9170/Kconfig
drivers/net/wireless/ath/carl9170/carl9170.h
drivers/net/wireless/ath/carl9170/main.c

index 2d1b821b440d6ebda30969fd240c07fbee3ce12b..267d5dcf82dc61b5e992a524338965a29f979c56 100644 (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.
index 74350d63f6865eef0991c5d36b190f288fa0eca2..6cfbb419e2f6d1efe8ddf7e2d551928efcd95f9d 100644 (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 {
index 85cb1bdebaaaa11c3d372ddcfe95440f916a6cd3..782b8f3ae58f3ac408c43a6deea388a1cc962340 100644 (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);