linux/drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
   4 *
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of the GNU General Public License version
   7 * 2 as published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 *
  15 */
  16#include <linux/i2c.h>
  17#include <linux/firmware.h>
  18#include <linux/device.h>
  19#include <linux/export.h>
  20#include "../include/linux/libmsrlisthelper.h"
  21#include <linux/module.h>
  22#include <linux/slab.h>
  23
  24/* Tagged binary data container structure definitions. */
  25struct tbd_header {
  26        u32 tag;          /*!< Tag identifier, also checks endianness */
  27        u32 size;         /*!< Container size including this header */
  28        u32 version;      /*!< Version, format 0xYYMMDDVV */
  29        u32 revision;     /*!< Revision, format 0xYYMMDDVV */
  30        u32 config_bits;  /*!< Configuration flag bits set */
  31        u32 checksum;     /*!< Global checksum, header included */
  32} __packed;
  33
  34struct tbd_record_header {
  35        u32 size;        /*!< Size of record including header */
  36        u8 format_id;    /*!< tbd_format_t enumeration values used */
  37        u8 packing_key;  /*!< Packing method; 0 = no packing */
  38        u16 class_id;    /*!< tbd_class_t enumeration values used */
  39} __packed;
  40
  41struct tbd_data_record_header {
  42        u16 next_offset;
  43        u16 flags;
  44        u16 data_offset;
  45        u16 data_size;
  46} __packed;
  47
  48#define TBD_CLASS_DRV_ID 2
  49
  50static int set_msr_configuration(struct i2c_client *client, uint8_t *bufptr,
  51                                 unsigned int size)
  52{
  53        /*
  54         * The configuration data contains any number of sequences where
  55         * the first byte (that is, uint8_t) that marks the number of bytes
  56         * in the sequence to follow, is indeed followed by the indicated
  57         * number of bytes of actual data to be written to sensor.
  58         * By convention, the first two bytes of actual data should be
  59         * understood as an address in the sensor address space (hibyte
  60         * followed by lobyte) where the remaining data in the sequence
  61         * will be written.
  62         */
  63
  64        u8 *ptr = bufptr;
  65
  66        while (ptr < bufptr + size) {
  67                struct i2c_msg msg = {
  68                        .addr = client->addr,
  69                        .flags = 0,
  70                };
  71                int ret;
  72
  73                /* How many bytes */
  74                msg.len = *ptr++;
  75                /* Where the bytes are located */
  76                msg.buf = ptr;
  77                ptr += msg.len;
  78
  79                if (ptr > bufptr + size)
  80                        /* Accessing data beyond bounds is not tolerated */
  81                        return -EINVAL;
  82
  83                ret = i2c_transfer(client->adapter, &msg, 1);
  84                if (ret < 0) {
  85                        dev_err(&client->dev, "i2c write error: %d", ret);
  86                        return ret;
  87                }
  88        }
  89        return 0;
  90}
  91
  92static int parse_and_apply(struct i2c_client *client, uint8_t *buffer,
  93                           unsigned int size)
  94{
  95        u8 *endptr8 = buffer + size;
  96        struct tbd_data_record_header *header =
  97            (struct tbd_data_record_header *)buffer;
  98
  99        /* There may be any number of datasets present */
 100        unsigned int dataset = 0;
 101
 102        do {
 103                /* In below, four variables are read from buffer */
 104                if ((uint8_t *)header + sizeof(*header) > endptr8)
 105                        return -EINVAL;
 106
 107                /* All data should be located within given buffer */
 108                if ((uint8_t *)header + header->data_offset +
 109                    header->data_size > endptr8)
 110                        return -EINVAL;
 111
 112                /* We have a new valid dataset */
 113                dataset++;
 114                /* See whether there is MSR data */
 115                /* If yes, update the reg info */
 116                if (header->data_size && (header->flags & 1)) {
 117                        int ret;
 118
 119                        dev_info(&client->dev,
 120                                 "New MSR data for sensor driver (dataset %02d) size:%d\n",
 121                                 dataset, header->data_size);
 122                        ret = set_msr_configuration(client,
 123                                                    buffer + header->data_offset,
 124                                                    header->data_size);
 125                        if (ret)
 126                                return ret;
 127                }
 128                header = (struct tbd_data_record_header *)(buffer +
 129                         header->next_offset);
 130        } while (header->next_offset);
 131
 132        return 0;
 133}
 134
 135int apply_msr_data(struct i2c_client *client, const struct firmware *fw)
 136{
 137        struct tbd_header *header;
 138        struct tbd_record_header *record;
 139
 140        if (!fw) {
 141                dev_warn(&client->dev, "Drv data is not loaded.\n");
 142                return -EINVAL;
 143        }
 144
 145        if (sizeof(*header) > fw->size)
 146                return -EINVAL;
 147
 148        header = (struct tbd_header *)fw->data;
 149        /* Check that we have drvb block. */
 150        if (memcmp(&header->tag, "DRVB", 4))
 151                return -EINVAL;
 152
 153        /* Check the size */
 154        if (header->size != fw->size)
 155                return -EINVAL;
 156
 157        if (sizeof(*header) + sizeof(*record) > fw->size)
 158                return -EINVAL;
 159
 160        record = (struct tbd_record_header *)(header + 1);
 161        /* Check that class id mathes tbd's drv id. */
 162        if (record->class_id != TBD_CLASS_DRV_ID)
 163                return -EINVAL;
 164
 165        /* Size 0 shall not be treated as an error */
 166        if (!record->size)
 167                return 0;
 168
 169        return parse_and_apply(client, (uint8_t *)(record + 1), record->size);
 170}
 171EXPORT_SYMBOL_GPL(apply_msr_data);
 172
 173int load_msr_list(struct i2c_client *client, char *name,
 174                  const struct firmware **fw)
 175{
 176        int ret = request_firmware(fw, name, &client->dev);
 177
 178        if (ret) {
 179                dev_err(&client->dev,
 180                        "Error %d while requesting firmware %s\n",
 181                        ret, name);
 182                return ret;
 183        }
 184        dev_info(&client->dev, "Received %lu bytes drv data\n",
 185                 (unsigned long)(*fw)->size);
 186
 187        return 0;
 188}
 189EXPORT_SYMBOL_GPL(load_msr_list);
 190
 191void release_msr_list(struct i2c_client *client, const struct firmware *fw)
 192{
 193        release_firmware(fw);
 194}
 195EXPORT_SYMBOL_GPL(release_msr_list);
 196
 197static int init_msrlisthelper(void)
 198{
 199        return 0;
 200}
 201
 202static void exit_msrlisthelper(void)
 203{
 204}
 205
 206module_init(init_msrlisthelper);
 207module_exit(exit_msrlisthelper);
 208
 209MODULE_AUTHOR("Jukka Kaartinen <jukka.o.kaartinen@intel.com>");
 210MODULE_LICENSE("GPL");
 211