linux/drivers/uwb/i1480/dfu/dfu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Intel Wireless UWB Link 1480
   4 * Main driver
   5 *
   6 * Copyright (C) 2005-2006 Intel Corporation
   7 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
   8 *
   9 * Common code for firmware upload used by the USB and PCI version;
  10 * i1480_fw_upload() takes a device descriptor and uses the function
  11 * pointers it provides to upload firmware and prepare the PHY.
  12 *
  13 * As well, provides common functions used by the rest of the code.
  14 */
  15#include "i1480-dfu.h"
  16#include <linux/errno.h>
  17#include <linux/delay.h>
  18#include <linux/pci.h>
  19#include <linux/device.h>
  20#include <linux/uwb.h>
  21#include <linux/random.h>
  22#include <linux/export.h>
  23
  24/*
  25 * i1480_rceb_check - Check RCEB for expected field values
  26 * @i1480: pointer to device for which RCEB is being checked
  27 * @rceb: RCEB being checked
  28 * @cmd: which command the RCEB is related to
  29 * @context: expected context
  30 * @expected_type: expected event type
  31 * @expected_event: expected event
  32 *
  33 * If @cmd is NULL, do not print error messages, but still return an error
  34 * code.
  35 *
  36 * Return 0 if @rceb matches the expected values, -EINVAL otherwise.
  37 */
  38int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb,
  39                     const char *cmd, u8 context, u8 expected_type,
  40                     unsigned expected_event)
  41{
  42        int result = 0;
  43        struct device *dev = i1480->dev;
  44        if (rceb->bEventContext != context) {
  45                if (cmd)
  46                        dev_err(dev, "%s: unexpected context id 0x%02x "
  47                                "(expected 0x%02x)\n", cmd,
  48                                rceb->bEventContext, context);
  49                result = -EINVAL;
  50        }
  51        if (rceb->bEventType != expected_type) {
  52                if (cmd)
  53                        dev_err(dev, "%s: unexpected event type 0x%02x "
  54                                "(expected 0x%02x)\n", cmd,
  55                                rceb->bEventType, expected_type);
  56                result = -EINVAL;
  57        }
  58        if (le16_to_cpu(rceb->wEvent) != expected_event) {
  59                if (cmd)
  60                        dev_err(dev, "%s: unexpected event 0x%04x "
  61                                "(expected 0x%04x)\n", cmd,
  62                                le16_to_cpu(rceb->wEvent), expected_event);
  63                result = -EINVAL;
  64        }
  65        return result;
  66}
  67EXPORT_SYMBOL_GPL(i1480_rceb_check);
  68
  69
  70/*
  71 * Execute a Radio Control Command
  72 *
  73 * Command data has to be in i1480->cmd_buf.
  74 *
  75 * @returns size of the reply data filled in i1480->evt_buf or < 0 errno
  76 *          code on error.
  77 */
  78ssize_t i1480_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size,
  79                  size_t reply_size)
  80{
  81        ssize_t result;
  82        struct uwb_rceb *reply = i1480->evt_buf;
  83        struct uwb_rccb *cmd = i1480->cmd_buf;
  84        u16 expected_event = reply->wEvent;
  85        u8 expected_type = reply->bEventType;
  86        u8 context;
  87
  88        init_completion(&i1480->evt_complete);
  89        i1480->evt_result = -EINPROGRESS;
  90        do {
  91                get_random_bytes(&context, 1);
  92        } while (context == 0x00 || context == 0xff);
  93        cmd->bCommandContext = context;
  94        result = i1480->cmd(i1480, cmd_name, cmd_size);
  95        if (result < 0)
  96                goto error;
  97        /* wait for the callback to report a event was received */
  98        result = wait_for_completion_interruptible_timeout(
  99                &i1480->evt_complete, HZ);
 100        if (result == 0) {
 101                result = -ETIMEDOUT;
 102                goto error;
 103        }
 104        if (result < 0)
 105                goto error;
 106        result = i1480->evt_result;
 107        if (result < 0) {
 108                dev_err(i1480->dev, "%s: command reply reception failed: %zd\n",
 109                        cmd_name, result);
 110                goto error;
 111        }
 112        /*
 113         * Firmware versions >= 1.4.12224 for IOGear GUWA100U generate a
 114         * spurious notification after firmware is downloaded. So check whether
 115         * the receibed RCEB is such notification before assuming that the
 116         * command has failed.
 117         */
 118        if (i1480_rceb_check(i1480, i1480->evt_buf, NULL,
 119                             0, 0xfd, 0x0022) == 0) {
 120                /* Now wait for the actual RCEB for this command. */
 121                result = i1480->wait_init_done(i1480);
 122                if (result < 0)
 123                        goto error;
 124                result = i1480->evt_result;
 125        }
 126        if (result != reply_size) {
 127                dev_err(i1480->dev, "%s returned only %zu bytes, %zu expected\n",
 128                        cmd_name, result, reply_size);
 129                result = -EINVAL;
 130                goto error;
 131        }
 132        /* Verify we got the right event in response */
 133        result = i1480_rceb_check(i1480, i1480->evt_buf, cmd_name, context,
 134                                  expected_type, expected_event);
 135error:
 136        return result;
 137}
 138EXPORT_SYMBOL_GPL(i1480_cmd);
 139
 140
 141static
 142int i1480_print_state(struct i1480 *i1480)
 143{
 144        int result;
 145        u32 *buf = (u32 *) i1480->cmd_buf;
 146
 147        result = i1480->read(i1480, 0x80080000, 2 * sizeof(*buf));
 148        if (result < 0) {
 149                dev_err(i1480->dev, "cannot read U & L states: %d\n", result);
 150                goto error;
 151        }
 152        dev_info(i1480->dev, "state U 0x%08x, L 0x%08x\n", buf[0], buf[1]);
 153error:
 154        return result;
 155}
 156
 157
 158/*
 159 * PCI probe, firmware uploader
 160 *
 161 * _mac_fw_upload() will call rc_setup(), which needs an rc_release().
 162 */
 163int i1480_fw_upload(struct i1480 *i1480)
 164{
 165        int result;
 166
 167        result = i1480_pre_fw_upload(i1480);    /* PHY pre fw */
 168        if (result < 0 && result != -ENOENT) {
 169                i1480_print_state(i1480);
 170                goto error;
 171        }
 172        result = i1480_mac_fw_upload(i1480);    /* MAC fw */
 173        if (result < 0) {
 174                if (result == -ENOENT)
 175                        dev_err(i1480->dev, "Cannot locate MAC FW file '%s'\n",
 176                                i1480->mac_fw_name);
 177                else
 178                        i1480_print_state(i1480);
 179                goto error;
 180        }
 181        result = i1480_phy_fw_upload(i1480);    /* PHY fw */
 182        if (result < 0 && result != -ENOENT) {
 183                i1480_print_state(i1480);
 184                goto error_rc_release;
 185        }
 186        /*
 187         * FIXME: find some reliable way to check whether firmware is running
 188         * properly. Maybe use some standard request that has no side effects?
 189         */
 190        dev_info(i1480->dev, "firmware uploaded successfully\n");
 191error_rc_release:
 192        if (i1480->rc_release)
 193                i1480->rc_release(i1480);
 194        result = 0;
 195error:
 196        return result;
 197}
 198EXPORT_SYMBOL_GPL(i1480_fw_upload);
 199