1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ntpfw.c - Firmware helper functions for Neofidelity codecs 4 * 5 * Copyright (c) 2024, SaluteDevices. All Rights Reserved. 6 */ 7 8 #include <linux/i2c.h> 9 #include <linux/firmware.h> 10 #include <linux/module.h> 11 12 #include "ntpfw.h" 13 14 struct ntpfw_chunk { 15 __be16 length; 16 u8 step; 17 u8 data[]; 18 } __packed; 19 20 struct ntpfw_header { 21 __be32 magic; 22 } __packed; 23 24 static bool ntpfw_verify(struct device *dev, const u8 *buf, size_t buf_size, u32 magic) 25 { 26 const struct ntpfw_header *header = (struct ntpfw_header *)buf; 27 u32 buf_magic; 28 29 if (buf_size <= sizeof(*header)) { 30 dev_err(dev, "Failed to load firmware: image too small\n"); 31 return false; 32 } 33 34 buf_magic = be32_to_cpu(header->magic); 35 if (buf_magic != magic) { 36 dev_err(dev, "Failed to load firmware: invalid magic 0x%x:\n", buf_magic); 37 return false; 38 } 39 40 return true; 41 } 42 43 static bool ntpfw_verify_chunk(struct device *dev, const struct ntpfw_chunk *chunk, size_t buf_size) 44 { 45 size_t chunk_size; 46 47 if (buf_size <= sizeof(*chunk)) { 48 dev_err(dev, "Failed to load firmware: chunk size too big\n"); 49 return false; 50 } 51 52 if (chunk->step != 2 && chunk->step != 5) { 53 dev_err(dev, "Failed to load firmware: invalid chunk step: %d\n", chunk->step); 54 return false; 55 } 56 57 chunk_size = be16_to_cpu(chunk->length); 58 if (chunk_size > buf_size) { 59 dev_err(dev, "Failed to load firmware: invalid chunk length\n"); 60 return false; 61 } 62 63 if (chunk_size % chunk->step) { 64 dev_err(dev, "Failed to load firmware: chunk length and step mismatch\n"); 65 return false; 66 } 67 68 return true; 69 } 70 71 static int ntpfw_send_chunk(struct i2c_client *i2c, const struct ntpfw_chunk *chunk) 72 { 73 int ret; 74 size_t i; 75 size_t length = be16_to_cpu(chunk->length); 76 77 for (i = 0; i < length; i += chunk->step) { 78 ret = i2c_master_send(i2c, &chunk->data[i], chunk->step); 79 if (ret != chunk->step) { 80 dev_err(&i2c->dev, "I2C send failed: %d\n", ret); 81 return ret < 0 ? ret : -EIO; 82 } 83 } 84 85 return 0; 86 } 87 88 int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) 89 { 90 struct device *dev = &i2c->dev; 91 const struct ntpfw_chunk *chunk; 92 const struct firmware *fw; 93 const u8 *data; 94 size_t leftover; 95 int ret; 96 97 ret = request_firmware(&fw, name, dev); 98 if (ret) { 99 dev_warn(dev, "request_firmware '%s' failed with %d\n", 100 name, ret); 101 return ret; 102 } 103 104 if (!ntpfw_verify(dev, fw->data, fw->size, magic)) { 105 ret = -EINVAL; 106 goto done; 107 } 108 109 data = fw->data + sizeof(struct ntpfw_header); 110 leftover = fw->size - sizeof(struct ntpfw_header); 111 112 while (leftover) { 113 chunk = (struct ntpfw_chunk *)data; 114 115 if (!ntpfw_verify_chunk(dev, chunk, leftover)) { 116 ret = -EINVAL; 117 goto done; 118 } 119 120 ret = ntpfw_send_chunk(i2c, chunk); 121 if (ret) 122 goto done; 123 124 data += be16_to_cpu(chunk->length) + sizeof(*chunk); 125 leftover -= be16_to_cpu(chunk->length) + sizeof(*chunk); 126 } 127 128 done: 129 release_firmware(fw); 130 131 return ret; 132 } 133 EXPORT_SYMBOL_GPL(ntpfw_load); 134 135 MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>"); 136 MODULE_DESCRIPTION("Helper for loading Neofidelity amplifiers firmware"); 137 MODULE_LICENSE("GPL"); 138