GNU Linux-libre 4.14.313-gnu1
[releases.git] / drivers / staging / media / atomisp / i2c / libmsrlisthelper.c
1 /*
2  * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License version
6  * 2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16  * 02110-1301, USA.
17  *
18  */
19 #include <linux/i2c.h>
20 #include <linux/firmware.h>
21 #include <linux/device.h>
22 #include <linux/export.h>
23 #include "../include/linux/libmsrlisthelper.h"
24 #include <linux/module.h>
25 #include <linux/slab.h>
26
27 /* Tagged binary data container structure definitions. */
28 struct tbd_header {
29         uint32_t tag;          /*!< Tag identifier, also checks endianness */
30         uint32_t size;         /*!< Container size including this header */
31         uint32_t version;      /*!< Version, format 0xYYMMDDVV */
32         uint32_t revision;     /*!< Revision, format 0xYYMMDDVV */
33         uint32_t config_bits;  /*!< Configuration flag bits set */
34         uint32_t checksum;     /*!< Global checksum, header included */
35 } __packed;
36
37 struct tbd_record_header {
38         uint32_t size;        /*!< Size of record including header */
39         uint8_t format_id;    /*!< tbd_format_t enumeration values used */
40         uint8_t packing_key;  /*!< Packing method; 0 = no packing */
41         uint16_t class_id;    /*!< tbd_class_t enumeration values used */
42 } __packed;
43
44 struct tbd_data_record_header {
45         uint16_t next_offset;
46         uint16_t flags;
47         uint16_t data_offset;
48         uint16_t data_size;
49 } __packed;
50
51 #define TBD_CLASS_DRV_ID 2
52
53 static int set_msr_configuration(struct i2c_client *client, uint8_t *bufptr,
54                 unsigned int size)
55 {
56         /* The configuration data contains any number of sequences where
57          * the first byte (that is, uint8_t) that marks the number of bytes
58          * in the sequence to follow, is indeed followed by the indicated
59          * number of bytes of actual data to be written to sensor.
60          * By convention, the first two bytes of actual data should be
61          * understood as an address in the sensor address space (hibyte
62          * followed by lobyte) where the remaining data in the sequence
63          * will be written. */
64
65         uint8_t *ptr = bufptr;
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
92 static int parse_and_apply(struct i2c_client *client, uint8_t *buffer,
93                 unsigned int size)
94 {
95         uint8_t *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
135 int 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 }
171 EXPORT_SYMBOL_GPL(apply_msr_data);
172
173 int 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         if (ret) {
178                 dev_err(&client->dev,
179                         "Error %d while requesting firmware %s\n",
180                         ret, name);
181                 return ret;
182         }
183         dev_info(&client->dev, "Received %lu bytes drv data\n",
184                         (unsigned long)(*fw)->size);
185
186         return 0;
187 }
188 EXPORT_SYMBOL_GPL(load_msr_list);
189
190 void release_msr_list(struct i2c_client *client, const struct firmware *fw)
191 {
192         release_firmware(fw);
193 }
194 EXPORT_SYMBOL_GPL(release_msr_list);
195
196 static int init_msrlisthelper(void)
197 {
198         return 0;
199 }
200
201 static void exit_msrlisthelper(void)
202 {
203 }
204
205 module_init(init_msrlisthelper);
206 module_exit(exit_msrlisthelper);
207
208 MODULE_AUTHOR("Jukka Kaartinen <jukka.o.kaartinen@intel.com>");
209 MODULE_LICENSE("GPL");