PageRenderTime 76ms CodeModel.GetById 54ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/firmware/sigma.c

https://bitbucket.org/wisechild/galaxy-nexus
C | 153 lines | 103 code | 30 blank | 20 comment | 16 complexity | e35bb2bab4c63a593e98e8662c9b1f87 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  1/*
  2 * Load Analog Devices SigmaStudio firmware files
  3 *
  4 * Copyright 2009-2011 Analog Devices Inc.
  5 *
  6 * Licensed under the GPL-2 or later.
  7 */
  8
  9#include <linux/crc32.h>
 10#include <linux/delay.h>
 11#include <linux/firmware.h>
 12#include <linux/kernel.h>
 13#include <linux/i2c.h>
 14#include <linux/module.h>
 15#include <linux/sigma.h>
 16
 17static size_t sigma_action_size(struct sigma_action *sa)
 18{
 19	size_t payload = 0;
 20
 21	switch (sa->instr) {
 22	case SIGMA_ACTION_WRITEXBYTES:
 23	case SIGMA_ACTION_WRITESINGLE:
 24	case SIGMA_ACTION_WRITESAFELOAD:
 25		payload = sigma_action_len(sa);
 26		break;
 27	default:
 28		break;
 29	}
 30
 31	payload = ALIGN(payload, 2);
 32
 33	return payload + sizeof(struct sigma_action);
 34}
 35
 36/*
 37 * Returns a negative error value in case of an error, 0 if processing of
 38 * the firmware should be stopped after this action, 1 otherwise.
 39 */
 40static int
 41process_sigma_action(struct i2c_client *client, struct sigma_action *sa)
 42{
 43	size_t len = sigma_action_len(sa);
 44	int ret;
 45
 46	pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__,
 47		sa->instr, sa->addr, len);
 48
 49	switch (sa->instr) {
 50	case SIGMA_ACTION_WRITEXBYTES:
 51	case SIGMA_ACTION_WRITESINGLE:
 52	case SIGMA_ACTION_WRITESAFELOAD:
 53		ret = i2c_master_send(client, (void *)&sa->addr, len);
 54		if (ret < 0)
 55			return -EINVAL;
 56		break;
 57	case SIGMA_ACTION_DELAY:
 58		udelay(len);
 59		len = 0;
 60		break;
 61	case SIGMA_ACTION_END:
 62		return 0;
 63	default:
 64		return -EINVAL;
 65	}
 66
 67	return 1;
 68}
 69
 70static int
 71process_sigma_actions(struct i2c_client *client, struct sigma_firmware *ssfw)
 72{
 73	struct sigma_action *sa;
 74	size_t size;
 75	int ret;
 76
 77	while (ssfw->pos + sizeof(*sa) <= ssfw->fw->size) {
 78		sa = (struct sigma_action *)(ssfw->fw->data + ssfw->pos);
 79
 80		size = sigma_action_size(sa);
 81		ssfw->pos += size;
 82		if (ssfw->pos > ssfw->fw->size || size == 0)
 83			break;
 84
 85		ret = process_sigma_action(client, sa);
 86
 87		pr_debug("%s: action returned %i\n", __func__, ret);
 88
 89		if (ret <= 0)
 90			return ret;
 91	}
 92
 93	if (ssfw->pos != ssfw->fw->size)
 94		return -EINVAL;
 95
 96	return 0;
 97}
 98
 99int process_sigma_firmware(struct i2c_client *client, const char *name)
100{
101	int ret;
102	struct sigma_firmware_header *ssfw_head;
103	struct sigma_firmware ssfw;
104	const struct firmware *fw;
105	u32 crc;
106
107	pr_debug("%s: loading firmware %s\n", __func__, name);
108
109	/* first load the blob */
110	ret = request_firmware(&fw, name, &client->dev);
111	if (ret) {
112		pr_debug("%s: request_firmware() failed with %i\n", __func__, ret);
113		return ret;
114	}
115	ssfw.fw = fw;
116
117	/* then verify the header */
118	ret = -EINVAL;
119
120	/*
121	 * Reject too small or unreasonable large files. The upper limit has been
122	 * chosen a bit arbitrarily, but it should be enough for all practical
123	 * purposes and having the limit makes it easier to avoid integer
124	 * overflows later in the loading process.
125	 */
126	if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000)
127		goto done;
128
129	ssfw_head = (void *)fw->data;
130	if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic)))
131		goto done;
132
133	crc = crc32(0, fw->data + sizeof(*ssfw_head),
134			fw->size - sizeof(*ssfw_head));
135	pr_debug("%s: crc=%x\n", __func__, crc);
136	if (crc != le32_to_cpu(ssfw_head->crc))
137		goto done;
138
139	ssfw.pos = sizeof(*ssfw_head);
140
141	/* finally process all of the actions */
142	ret = process_sigma_actions(client, &ssfw);
143
144 done:
145	release_firmware(fw);
146
147	pr_debug("%s: loaded %s\n", __func__, name);
148
149	return ret;
150}
151EXPORT_SYMBOL(process_sigma_firmware);
152
153MODULE_LICENSE("GPL");