fw.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // SPDX-License-Identifier: BSD-3-Clause-Clear
  2. /*
  3. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  4. */
  5. #include "core.h"
  6. #include "debug.h"
  7. static int ath12k_fw_request_firmware_api_n(struct ath12k_base *ab,
  8. const char *name)
  9. {
  10. size_t magic_len, len, ie_len;
  11. int ie_id, i, index, bit, ret;
  12. struct ath12k_fw_ie *hdr;
  13. const u8 *data;
  14. __le32 *timestamp;
  15. ab->fw.fw = ath12k_core_firmware_request(ab, name);
  16. if (IS_ERR(ab->fw.fw)) {
  17. ret = PTR_ERR(ab->fw.fw);
  18. ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to load %s: %d\n", name, ret);
  19. ab->fw.fw = NULL;
  20. return ret;
  21. }
  22. data = ab->fw.fw->data;
  23. len = ab->fw.fw->size;
  24. /* magic also includes the null byte, check that as well */
  25. magic_len = strlen(ATH12K_FIRMWARE_MAGIC) + 1;
  26. if (len < magic_len) {
  27. ath12k_err(ab, "firmware image too small to contain magic: %zu\n",
  28. len);
  29. ret = -EINVAL;
  30. goto err;
  31. }
  32. if (memcmp(data, ATH12K_FIRMWARE_MAGIC, magic_len) != 0) {
  33. ath12k_err(ab, "Invalid firmware magic\n");
  34. ret = -EINVAL;
  35. goto err;
  36. }
  37. /* jump over the padding */
  38. magic_len = ALIGN(magic_len, 4);
  39. /* make sure there's space for padding */
  40. if (magic_len > len) {
  41. ath12k_err(ab, "No space for padding after magic\n");
  42. ret = -EINVAL;
  43. goto err;
  44. }
  45. len -= magic_len;
  46. data += magic_len;
  47. /* loop elements */
  48. while (len > sizeof(struct ath12k_fw_ie)) {
  49. hdr = (struct ath12k_fw_ie *)data;
  50. ie_id = le32_to_cpu(hdr->id);
  51. ie_len = le32_to_cpu(hdr->len);
  52. len -= sizeof(*hdr);
  53. data += sizeof(*hdr);
  54. if (len < ie_len) {
  55. ath12k_err(ab, "Invalid length for FW IE %d (%zu < %zu)\n",
  56. ie_id, len, ie_len);
  57. ret = -EINVAL;
  58. goto err;
  59. }
  60. switch (ie_id) {
  61. case ATH12K_FW_IE_TIMESTAMP:
  62. if (ie_len != sizeof(u32))
  63. break;
  64. timestamp = (__le32 *)data;
  65. ath12k_dbg(ab, ATH12K_DBG_BOOT, "found fw timestamp %d\n",
  66. le32_to_cpup(timestamp));
  67. break;
  68. case ATH12K_FW_IE_FEATURES:
  69. ath12k_dbg(ab, ATH12K_DBG_BOOT,
  70. "found firmware features ie (%zd B)\n",
  71. ie_len);
  72. for (i = 0; i < ATH12K_FW_FEATURE_COUNT; i++) {
  73. index = i / 8;
  74. bit = i % 8;
  75. if (index == ie_len)
  76. break;
  77. if (data[index] & (1 << bit))
  78. __set_bit(i, ab->fw.fw_features);
  79. }
  80. ab->fw.fw_features_valid = true;
  81. ath12k_dbg_dump(ab, ATH12K_DBG_BOOT, "features", "",
  82. ab->fw.fw_features,
  83. sizeof(ab->fw.fw_features));
  84. break;
  85. case ATH12K_FW_IE_AMSS_IMAGE:
  86. ath12k_dbg(ab, ATH12K_DBG_BOOT,
  87. "found fw image ie (%zd B)\n",
  88. ie_len);
  89. ab->fw.amss_data = data;
  90. ab->fw.amss_len = ie_len;
  91. break;
  92. case ATH12K_FW_IE_M3_IMAGE:
  93. ath12k_dbg(ab, ATH12K_DBG_BOOT,
  94. "found m3 image ie (%zd B)\n",
  95. ie_len);
  96. ab->fw.m3_data = data;
  97. ab->fw.m3_len = ie_len;
  98. break;
  99. case ATH12K_FW_IE_AUX_UC_IMAGE:
  100. ath12k_dbg(ab, ATH12K_DBG_BOOT,
  101. "found aux_uc image ie (%zd B)\n",
  102. ie_len);
  103. ab->fw.aux_uc_data = data;
  104. ab->fw.aux_uc_len = ie_len;
  105. break;
  106. case ATH12K_FW_IE_AMSS_DUALMAC_IMAGE:
  107. ath12k_dbg(ab, ATH12K_DBG_BOOT,
  108. "found dualmac fw image ie (%zd B)\n",
  109. ie_len);
  110. ab->fw.amss_dualmac_data = data;
  111. ab->fw.amss_dualmac_len = ie_len;
  112. break;
  113. default:
  114. ath12k_warn(ab, "Unknown FW IE: %u\n", ie_id);
  115. break;
  116. }
  117. /* jump over the padding */
  118. ie_len = ALIGN(ie_len, 4);
  119. /* make sure there's space for padding */
  120. if (ie_len > len)
  121. break;
  122. len -= ie_len;
  123. data += ie_len;
  124. }
  125. return 0;
  126. err:
  127. release_firmware(ab->fw.fw);
  128. ab->fw.fw = NULL;
  129. return ret;
  130. }
  131. void ath12k_fw_map(struct ath12k_base *ab)
  132. {
  133. int ret;
  134. ret = ath12k_fw_request_firmware_api_n(ab, ATH12K_FW_API2_FILE);
  135. if (ret == 0)
  136. ab->fw.api_version = 2;
  137. else
  138. ab->fw.api_version = 1;
  139. ath12k_dbg(ab, ATH12K_DBG_BOOT, "using fw api %d\n",
  140. ab->fw.api_version);
  141. }
  142. void ath12k_fw_unmap(struct ath12k_base *ab)
  143. {
  144. release_firmware(ab->fw.fw);
  145. memset(&ab->fw, 0, sizeof(ab->fw));
  146. }
  147. bool ath12k_fw_feature_supported(struct ath12k_base *ab, enum ath12k_fw_features feat)
  148. {
  149. return ab->fw.fw_features_valid && test_bit(feat, ab->fw.fw_features);
  150. }