linux/drivers/media/common/cypress_firmware.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*  cypress_firmware.c is part of the DVB USB library.
   3 *
   4 * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
   5 * see dvb-usb-init.c for copyright information.
   6 *
   7 * This file contains functions for downloading the firmware to Cypress FX 1
   8 * and 2 based devices.
   9 *
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/slab.h>
  14#include <linux/usb.h>
  15#include <linux/firmware.h>
  16#include "cypress_firmware.h"
  17
  18struct usb_cypress_controller {
  19        u8 id;
  20        const char *name;       /* name of the usb controller */
  21        u16 cs_reg;             /* needs to be restarted,
  22                                 * when the firmware has been downloaded */
  23};
  24
  25static const struct usb_cypress_controller cypress[] = {
  26        { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cs_reg = 0x7f92 },
  27        { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cs_reg = 0x7f92 },
  28        { .id = CYPRESS_FX2,    .name = "Cypress FX2",    .cs_reg = 0xe600 },
  29};
  30
  31/*
  32 * load a firmware packet to the device
  33 */
  34static int usb_cypress_writemem(struct usb_device *udev, u16 addr, u8 *data,
  35                u8 len)
  36{
  37        return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
  38                        0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000);
  39}
  40
  41static int cypress_get_hexline(const struct firmware *fw,
  42                                struct hexline *hx, int *pos)
  43{
  44        u8 *b = (u8 *) &fw->data[*pos];
  45        int data_offs = 4;
  46
  47        if (*pos >= fw->size)
  48                return 0;
  49
  50        memset(hx, 0, sizeof(struct hexline));
  51        hx->len = b[0];
  52
  53        if ((*pos + hx->len + 4) >= fw->size)
  54                return -EINVAL;
  55
  56        hx->addr = b[1] | (b[2] << 8);
  57        hx->type = b[3];
  58
  59        if (hx->type == 0x04) {
  60                /* b[4] and b[5] are the Extended linear address record data
  61                 * field */
  62                hx->addr |= (b[4] << 24) | (b[5] << 16);
  63        }
  64
  65        memcpy(hx->data, &b[data_offs], hx->len);
  66        hx->chk = b[hx->len + data_offs];
  67        *pos += hx->len + 5;
  68
  69        return *pos;
  70}
  71
  72int cypress_load_firmware(struct usb_device *udev,
  73                const struct firmware *fw, int type)
  74{
  75        struct hexline *hx;
  76        int ret, pos = 0;
  77
  78        hx = kmalloc(sizeof(*hx), GFP_KERNEL);
  79        if (!hx)
  80                return -ENOMEM;
  81
  82        /* stop the CPU */
  83        hx->data[0] = 1;
  84        ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
  85        if (ret != 1) {
  86                dev_err(&udev->dev, "%s: CPU stop failed=%d\n",
  87                                KBUILD_MODNAME, ret);
  88                ret = -EIO;
  89                goto err_kfree;
  90        }
  91
  92        /* write firmware to memory */
  93        for (;;) {
  94                ret = cypress_get_hexline(fw, hx, &pos);
  95                if (ret < 0)
  96                        goto err_kfree;
  97                else if (ret == 0)
  98                        break;
  99
 100                ret = usb_cypress_writemem(udev, hx->addr, hx->data, hx->len);
 101                if (ret < 0) {
 102                        goto err_kfree;
 103                } else if (ret != hx->len) {
 104                        dev_err(&udev->dev,
 105                                        "%s: error while transferring firmware (transferred size=%d, block size=%d)\n",
 106                                        KBUILD_MODNAME, ret, hx->len);
 107                        ret = -EIO;
 108                        goto err_kfree;
 109                }
 110        }
 111
 112        /* start the CPU */
 113        hx->data[0] = 0;
 114        ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
 115        if (ret != 1) {
 116                dev_err(&udev->dev, "%s: CPU start failed=%d\n",
 117                                KBUILD_MODNAME, ret);
 118                ret = -EIO;
 119                goto err_kfree;
 120        }
 121
 122        ret = 0;
 123err_kfree:
 124        kfree(hx);
 125        return ret;
 126}
 127EXPORT_SYMBOL(cypress_load_firmware);
 128
 129MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
 130MODULE_DESCRIPTION("Cypress firmware download");
 131MODULE_LICENSE("GPL");
 132