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