1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * NCI based driver for Samsung S3FWRN5 NFC chip
4 *
5 * Copyright (C) 2015 Samsung Electrnoics
6 * Robert Baldyga <r.baldyga@samsung.com>
7 */
8
9#include <linux/completion.h>
10#include <linux/firmware.h>
11#include <crypto/hash.h>
12#include <crypto/sha1.h>
13
14#include "s3fwrn5.h"
15#include "firmware.h"
16
17struct s3fwrn5_fw_version {
18	__u8 major;
19	__u8 build1;
20	__u8 build2;
21	__u8 target;
22};
23
24static int s3fwrn5_fw_send_msg(struct s3fwrn5_fw_info *fw_info,
25	struct sk_buff *msg, struct sk_buff **rsp)
26{
27	struct s3fwrn5_info *info =
28		container_of(fw_info, struct s3fwrn5_info, fw_info);
29	long ret;
30
31	reinit_completion(&fw_info->completion);
32
33	ret = s3fwrn5_write(info, msg);
34	if (ret < 0)
35		return ret;
36
37	ret = wait_for_completion_interruptible_timeout(
38		&fw_info->completion, msecs_to_jiffies(1000));
39	if (ret < 0)
40		return ret;
41	else if (ret == 0)
42		return -ENXIO;
43
44	if (!fw_info->rsp)
45		return -EINVAL;
46
47	*rsp = fw_info->rsp;
48	fw_info->rsp = NULL;
49
50	return 0;
51}
52
53static int s3fwrn5_fw_prep_msg(struct s3fwrn5_fw_info *fw_info,
54	struct sk_buff **msg, u8 type, u8 code, const void *data, u16 len)
55{
56	struct s3fwrn5_fw_header hdr;
57	struct sk_buff *skb;
58
59	hdr.type = type | fw_info->parity;
60	fw_info->parity ^= 0x80;
61	hdr.code = code;
62	hdr.len = len;
63
64	skb = alloc_skb(S3FWRN5_FW_HDR_SIZE + len, GFP_KERNEL);
65	if (!skb)
66		return -ENOMEM;
67
68	skb_put_data(skb, &hdr, S3FWRN5_FW_HDR_SIZE);
69	if (len)
70		skb_put_data(skb, data, len);
71
72	*msg = skb;
73
74	return 0;
75}
76
77static int s3fwrn5_fw_get_bootinfo(struct s3fwrn5_fw_info *fw_info,
78	struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo)
79{
80	struct sk_buff *msg, *rsp = NULL;
81	struct s3fwrn5_fw_header *hdr;
82	int ret;
83
84	/* Send GET_BOOTINFO command */
85
86	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
87		S3FWRN5_FW_CMD_GET_BOOTINFO, NULL, 0);
88	if (ret < 0)
89		return ret;
90
91	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
92	kfree_skb(msg);
93	if (ret < 0)
94		return ret;
95
96	hdr = (struct s3fwrn5_fw_header *) rsp->data;
97	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
98		ret = -EINVAL;
99		goto out;
100	}
101
102	memcpy(bootinfo, rsp->data + S3FWRN5_FW_HDR_SIZE, 10);
103
104out:
105	kfree_skb(rsp);
106	return ret;
107}
108
109static int s3fwrn5_fw_enter_update_mode(struct s3fwrn5_fw_info *fw_info,
110	const void *hash_data, u16 hash_size,
111	const void *sig_data, u16 sig_size)
112{
113	struct s3fwrn5_fw_cmd_enter_updatemode args;
114	struct sk_buff *msg, *rsp = NULL;
115	struct s3fwrn5_fw_header *hdr;
116	int ret;
117
118	/* Send ENTER_UPDATE_MODE command */
119
120	args.hashcode_size = hash_size;
121	args.signature_size = sig_size;
122
123	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
124		S3FWRN5_FW_CMD_ENTER_UPDATE_MODE, &args, sizeof(args));
125	if (ret < 0)
126		return ret;
127
128	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
129	kfree_skb(msg);
130	if (ret < 0)
131		return ret;
132
133	hdr = (struct s3fwrn5_fw_header *) rsp->data;
134	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
135		ret = -EPROTO;
136		goto out;
137	}
138
139	kfree_skb(rsp);
140
141	/* Send hashcode data */
142
143	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0,
144		hash_data, hash_size);
145	if (ret < 0)
146		return ret;
147
148	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
149	kfree_skb(msg);
150	if (ret < 0)
151		return ret;
152
153	hdr = (struct s3fwrn5_fw_header *) rsp->data;
154	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
155		ret = -EPROTO;
156		goto out;
157	}
158
159	kfree_skb(rsp);
160
161	/* Send signature data */
162
163	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0,
164		sig_data, sig_size);
165	if (ret < 0)
166		return ret;
167
168	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
169	kfree_skb(msg);
170	if (ret < 0)
171		return ret;
172
173	hdr = (struct s3fwrn5_fw_header *) rsp->data;
174	if (hdr->code != S3FWRN5_FW_RET_SUCCESS)
175		ret = -EPROTO;
176
177out:
178	kfree_skb(rsp);
179	return ret;
180}
181
182static int s3fwrn5_fw_update_sector(struct s3fwrn5_fw_info *fw_info,
183	u32 base_addr, const void *data)
184{
185	struct s3fwrn5_fw_cmd_update_sector args;
186	struct sk_buff *msg, *rsp = NULL;
187	struct s3fwrn5_fw_header *hdr;
188	int ret, i;
189
190	/* Send UPDATE_SECTOR command */
191
192	args.base_address = base_addr;
193
194	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
195		S3FWRN5_FW_CMD_UPDATE_SECTOR, &args, sizeof(args));
196	if (ret < 0)
197		return ret;
198
199	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
200	kfree_skb(msg);
201	if (ret < 0)
202		return ret;
203
204	hdr = (struct s3fwrn5_fw_header *) rsp->data;
205	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
206		ret = -EPROTO;
207		goto err;
208	}
209
210	kfree_skb(rsp);
211
212	/* Send data split into 256-byte packets */
213
214	for (i = 0; i < 16; ++i) {
215		ret = s3fwrn5_fw_prep_msg(fw_info, &msg,
216			S3FWRN5_FW_MSG_DATA, 0, data+256*i, 256);
217		if (ret < 0)
218			break;
219
220		ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
221		kfree_skb(msg);
222		if (ret < 0)
223			break;
224
225		hdr = (struct s3fwrn5_fw_header *) rsp->data;
226		if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
227			ret = -EPROTO;
228			goto err;
229		}
230
231		kfree_skb(rsp);
232	}
233
234	return ret;
235
236err:
237	kfree_skb(rsp);
238	return ret;
239}
240
241static int s3fwrn5_fw_complete_update_mode(struct s3fwrn5_fw_info *fw_info)
242{
243	struct sk_buff *msg, *rsp = NULL;
244	struct s3fwrn5_fw_header *hdr;
245	int ret;
246
247	/* Send COMPLETE_UPDATE_MODE command */
248
249	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
250		S3FWRN5_FW_CMD_COMPLETE_UPDATE_MODE, NULL, 0);
251	if (ret < 0)
252		return ret;
253
254	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
255	kfree_skb(msg);
256	if (ret < 0)
257		return ret;
258
259	hdr = (struct s3fwrn5_fw_header *) rsp->data;
260	if (hdr->code != S3FWRN5_FW_RET_SUCCESS)
261		ret = -EPROTO;
262
263	kfree_skb(rsp);
264
265	return ret;
266}
267
268/*
269 * Firmware header structure:
270 *
271 * 0x00 - 0x0B : Date and time string (w/o NUL termination)
272 * 0x10 - 0x13 : Firmware version
273 * 0x14 - 0x17 : Signature address
274 * 0x18 - 0x1B : Signature size
275 * 0x1C - 0x1F : Firmware image address
276 * 0x20 - 0x23 : Firmware sectors count
277 * 0x24 - 0x27 : Custom signature address
278 * 0x28 - 0x2B : Custom signature size
279 */
280
281#define S3FWRN5_FW_IMAGE_HEADER_SIZE 44
282
283int s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info *fw_info)
284{
285	struct s3fwrn5_fw_image *fw = &fw_info->fw;
286	u32 sig_off;
287	u32 image_off;
288	u32 custom_sig_off;
289	int ret;
290
291	ret = request_firmware(&fw->fw, fw_info->fw_name,
292		&fw_info->ndev->nfc_dev->dev);
293	if (ret < 0)
294		return ret;
295
296	if (fw->fw->size < S3FWRN5_FW_IMAGE_HEADER_SIZE) {
297		release_firmware(fw->fw);
298		return -EINVAL;
299	}
300
301	memcpy(fw->date, fw->fw->data + 0x00, 12);
302	fw->date[12] = '\0';
303
304	memcpy(&fw->version, fw->fw->data + 0x10, 4);
305
306	memcpy(&sig_off, fw->fw->data + 0x14, 4);
307	fw->sig = fw->fw->data + sig_off;
308	memcpy(&fw->sig_size, fw->fw->data + 0x18, 4);
309
310	memcpy(&image_off, fw->fw->data + 0x1C, 4);
311	fw->image = fw->fw->data + image_off;
312	memcpy(&fw->image_sectors, fw->fw->data + 0x20, 4);
313
314	memcpy(&custom_sig_off, fw->fw->data + 0x24, 4);
315	fw->custom_sig = fw->fw->data + custom_sig_off;
316	memcpy(&fw->custom_sig_size, fw->fw->data + 0x28, 4);
317
318	return 0;
319}
320
321static void s3fwrn5_fw_release_firmware(struct s3fwrn5_fw_info *fw_info)
322{
323	release_firmware(fw_info->fw.fw);
324}
325
326static int s3fwrn5_fw_get_base_addr(
327	struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo, u32 *base_addr)
328{
329	int i;
330	static const struct {
331		u8 version[4];
332		u32 base_addr;
333	} match[] = {
334		{{0x05, 0x00, 0x00, 0x00}, 0x00005000},
335		{{0x05, 0x00, 0x00, 0x01}, 0x00003000},
336		{{0x05, 0x00, 0x00, 0x02}, 0x00003000},
337		{{0x05, 0x00, 0x00, 0x03}, 0x00003000},
338		{{0x05, 0x00, 0x00, 0x05}, 0x00003000}
339	};
340
341	for (i = 0; i < ARRAY_SIZE(match); ++i)
342		if (bootinfo->hw_version[0] == match[i].version[0] &&
343			bootinfo->hw_version[1] == match[i].version[1] &&
344			bootinfo->hw_version[3] == match[i].version[3]) {
345			*base_addr = match[i].base_addr;
346			return 0;
347		}
348
349	return -EINVAL;
350}
351
352static inline bool
353s3fwrn5_fw_is_custom(const struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo)
354{
355	return !!bootinfo->hw_version[2];
356}
357
358int s3fwrn5_fw_setup(struct s3fwrn5_fw_info *fw_info)
359{
360	struct device *dev = &fw_info->ndev->nfc_dev->dev;
361	struct s3fwrn5_fw_cmd_get_bootinfo_rsp bootinfo;
362	int ret;
363
364	/* Get bootloader info */
365
366	ret = s3fwrn5_fw_get_bootinfo(fw_info, &bootinfo);
367	if (ret < 0) {
368		dev_err(dev, "Failed to get bootinfo, ret=%02x\n", ret);
369		goto err;
370	}
371
372	/* Match hardware version to obtain firmware base address */
373
374	ret = s3fwrn5_fw_get_base_addr(&bootinfo, &fw_info->base_addr);
375	if (ret < 0) {
376		dev_err(dev, "Unknown hardware version\n");
377		goto err;
378	}
379
380	fw_info->sector_size = bootinfo.sector_size;
381
382	fw_info->sig_size = s3fwrn5_fw_is_custom(&bootinfo) ?
383		fw_info->fw.custom_sig_size : fw_info->fw.sig_size;
384	fw_info->sig = s3fwrn5_fw_is_custom(&bootinfo) ?
385		fw_info->fw.custom_sig : fw_info->fw.sig;
386
387	return 0;
388
389err:
390	s3fwrn5_fw_release_firmware(fw_info);
391	return ret;
392}
393
394bool s3fwrn5_fw_check_version(const struct s3fwrn5_fw_info *fw_info, u32 version)
395{
396	struct s3fwrn5_fw_version *new = (void *) &fw_info->fw.version;
397	struct s3fwrn5_fw_version *old = (void *) &version;
398
399	if (new->major > old->major)
400		return true;
401	if (new->build1 > old->build1)
402		return true;
403	if (new->build2 > old->build2)
404		return true;
405
406	return false;
407}
408
409int s3fwrn5_fw_download(struct s3fwrn5_fw_info *fw_info)
410{
411	struct device *dev = &fw_info->ndev->nfc_dev->dev;
412	struct s3fwrn5_fw_image *fw = &fw_info->fw;
413	u8 hash_data[SHA1_DIGEST_SIZE];
414	struct crypto_shash *tfm;
415	u32 image_size, off;
416	int ret;
417
418	image_size = fw_info->sector_size * fw->image_sectors;
419
420	/* Compute SHA of firmware data */
421
422	tfm = crypto_alloc_shash("sha1", 0, 0);
423	if (IS_ERR(tfm)) {
424		dev_err(dev, "Cannot allocate shash (code=%pe)\n", tfm);
425		return PTR_ERR(tfm);
426	}
427
428	ret = crypto_shash_tfm_digest(tfm, fw->image, image_size, hash_data);
429
430	crypto_free_shash(tfm);
431	if (ret) {
432		dev_err(dev, "Cannot compute hash (code=%d)\n", ret);
433		return ret;
434	}
435
436	/* Firmware update process */
437
438	dev_info(dev, "Firmware update: %s\n", fw_info->fw_name);
439
440	ret = s3fwrn5_fw_enter_update_mode(fw_info, hash_data,
441		SHA1_DIGEST_SIZE, fw_info->sig, fw_info->sig_size);
442	if (ret < 0) {
443		dev_err(dev, "Unable to enter update mode\n");
444		return ret;
445	}
446
447	for (off = 0; off < image_size; off += fw_info->sector_size) {
448		ret = s3fwrn5_fw_update_sector(fw_info,
449			fw_info->base_addr + off, fw->image + off);
450		if (ret < 0) {
451			dev_err(dev, "Firmware update error (code=%d)\n", ret);
452			return ret;
453		}
454	}
455
456	ret = s3fwrn5_fw_complete_update_mode(fw_info);
457	if (ret < 0) {
458		dev_err(dev, "Unable to complete update mode\n");
459		return ret;
460	}
461
462	dev_info(dev, "Firmware update: success\n");
463
464	return ret;
465}
466
467void s3fwrn5_fw_init(struct s3fwrn5_fw_info *fw_info, const char *fw_name)
468{
469	fw_info->parity = 0x00;
470	fw_info->rsp = NULL;
471	fw_info->fw.fw = NULL;
472	strcpy(fw_info->fw_name, fw_name);
473	init_completion(&fw_info->completion);
474}
475
476void s3fwrn5_fw_cleanup(struct s3fwrn5_fw_info *fw_info)
477{
478	s3fwrn5_fw_release_firmware(fw_info);
479}
480
481int s3fwrn5_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
482{
483	struct s3fwrn5_info *info = nci_get_drvdata(ndev);
484	struct s3fwrn5_fw_info *fw_info = &info->fw_info;
485
486	if (WARN_ON(fw_info->rsp)) {
487		kfree_skb(skb);
488		return -EINVAL;
489	}
490
491	fw_info->rsp = skb;
492
493	complete(&fw_info->completion);
494
495	return 0;
496}
497