blob: 675c6ce810c01dbf0f39ddc65b16edc9d0882771 [file] [log] [blame]
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -08001/*
2 * Intel Wireless WiMAX Connection 2400m
3 * Firmware uploader
4 *
5 *
6 * Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 * * Neither the name of Intel Corporation nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 *
35 * Intel Corporation <linux-wimax@intel.com>
36 * Yanir Lubetkin <yanirx.lubetkin@intel.com>
37 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
38 * - Initial implementation
39 *
40 *
41 * THE PROCEDURE
42 *
43 * (this is decribed for USB, but for SDIO is similar)
44 *
45 * The 2400m works in two modes: boot-mode or normal mode. In boot
46 * mode we can execute only a handful of commands targeted at
47 * uploading the firmware and launching it.
48 *
49 * The 2400m enters boot mode when it is first connected to the
50 * system, when it crashes and when you ask it to reboot. There are
51 * two submodes of the boot mode: signed and non-signed. Signed takes
52 * firmwares signed with a certain private key, non-signed takes any
53 * firmware. Normal hardware takes only signed firmware.
54 *
55 * Upon entrance to boot mode, the device sends a few zero length
56 * packets (ZLPs) on the notification endpoint, then a reboot barker
57 * (4 le32 words with value I2400M_{S,N}BOOT_BARKER). We ack it by
58 * sending the same barker on the bulk out endpoint. The device acks
59 * with a reboot ack barker (4 le32 words with value 0xfeedbabe) and
60 * then the device is fully rebooted. At this point we can upload the
61 * firmware.
62 *
63 * This process is accomplished by the i2400m_bootrom_init()
64 * function. All the device interaction happens through the
65 * i2400m_bm_cmd() [boot mode command]. Special return values will
66 * indicate if the device resets.
67 *
68 * After this, we read the MAC address and then (if needed)
69 * reinitialize the device. We need to read it ahead of time because
70 * in the future, we might not upload the firmware until userspace
71 * 'ifconfig up's the device.
72 *
73 * We can then upload the firmware file. The file is composed of a BCF
74 * header (basic data, keys and signatures) and a list of write
75 * commands and payloads. We first upload the header
76 * [i2400m_dnload_init()] and then pass the commands and payloads
77 * verbatim to the i2400m_bm_cmd() function
78 * [i2400m_dnload_bcf()]. Then we tell the device to jump to the new
79 * firmware [i2400m_dnload_finalize()].
80 *
81 * Once firmware is uploaded, we are good to go :)
82 *
83 * When we don't know in which mode we are, we first try by sending a
84 * warm reset request that will take us to boot-mode. If we time out
85 * waiting for a reboot barker, that means maybe we are already in
86 * boot mode, so we send a reboot barker.
87 *
88 * COMMAND EXECUTION
89 *
90 * This code (and process) is single threaded; for executing commands,
91 * we post a URB to the notification endpoint, post the command, wait
92 * for data on the notification buffer. We don't need to worry about
93 * others as we know we are the only ones in there.
94 *
95 * BACKEND IMPLEMENTATION
96 *
97 * This code is bus-generic; the bus-specific driver provides back end
98 * implementations to send a boot mode command to the device and to
99 * read an acknolwedgement from it (or an asynchronous notification)
100 * from it.
101 *
102 * ROADMAP
103 *
104 * i2400m_dev_bootstrap Called by __i2400m_dev_start()
105 * request_firmware
106 * i2400m_fw_check
107 * i2400m_fw_dnload
108 * release_firmware
109 *
110 * i2400m_fw_dnload
111 * i2400m_bootrom_init
112 * i2400m_bm_cmd
113 * i2400m->bus_reset
114 * i2400m_dnload_init
115 * i2400m_dnload_init_signed
116 * i2400m_dnload_init_nonsigned
117 * i2400m_download_chunk
118 * i2400m_bm_cmd
119 * i2400m_dnload_bcf
120 * i2400m_bm_cmd
121 * i2400m_dnload_finalize
122 * i2400m_bm_cmd
123 *
124 * i2400m_bm_cmd
125 * i2400m->bus_bm_cmd_send()
126 * i2400m->bus_bm_wait_for_ack
127 * __i2400m_bm_ack_verify
128 *
129 * i2400m_bm_cmd_prepare Used by bus-drivers to prep
130 * commands before sending
131 */
132#include <linux/firmware.h>
133#include <linux/sched.h>
134#include <linux/usb.h>
135#include "i2400m.h"
136
137
138#define D_SUBMODULE fw
139#include "debug-levels.h"
140
141
142static const __le32 i2400m_ACK_BARKER[4] = {
Harvey Harrisonee437772009-02-01 00:43:54 -0800143 cpu_to_le32(I2400M_ACK_BARKER),
144 cpu_to_le32(I2400M_ACK_BARKER),
145 cpu_to_le32(I2400M_ACK_BARKER),
146 cpu_to_le32(I2400M_ACK_BARKER)
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -0800147};
148
149
150/**
151 * Prepare a boot-mode command for delivery
152 *
153 * @cmd: pointer to bootrom header to prepare
154 *
155 * Computes checksum if so needed. After calling this function, DO NOT
156 * modify the command or header as the checksum won't work anymore.
157 *
158 * We do it from here because some times we cannot do it in the
159 * original context the command was sent (it is a const), so when we
160 * copy it to our staging buffer, we add the checksum there.
161 */
162void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *cmd)
163{
164 if (i2400m_brh_get_use_checksum(cmd)) {
165 int i;
166 u32 checksum = 0;
167 const u32 *checksum_ptr = (void *) cmd->payload;
168 for (i = 0; i < cmd->data_size / 4; i++)
169 checksum += cpu_to_le32(*checksum_ptr++);
170 checksum += cmd->command + cmd->target_addr + cmd->data_size;
171 cmd->block_checksum = cpu_to_le32(checksum);
172 }
173}
174EXPORT_SYMBOL_GPL(i2400m_bm_cmd_prepare);
175
176
177/*
178 * Verify the ack data received
179 *
180 * Given a reply to a boot mode command, chew it and verify everything
181 * is ok.
182 *
183 * @opcode: opcode which generated this ack. For error messages.
184 * @ack: pointer to ack data we received
185 * @ack_size: size of that data buffer
186 * @flags: I2400M_BM_CMD_* flags we called the command with.
187 *
188 * Way too long function -- maybe it should be further split
189 */
190static
191ssize_t __i2400m_bm_ack_verify(struct i2400m *i2400m, int opcode,
192 struct i2400m_bootrom_header *ack,
193 size_t ack_size, int flags)
194{
195 ssize_t result = -ENOMEM;
196 struct device *dev = i2400m_dev(i2400m);
197
198 d_fnstart(8, dev, "(i2400m %p opcode %d ack %p size %zu)\n",
199 i2400m, opcode, ack, ack_size);
200 if (ack_size < sizeof(*ack)) {
201 result = -EIO;
202 dev_err(dev, "boot-mode cmd %d: HW BUG? notification didn't "
203 "return enough data (%zu bytes vs %zu expected)\n",
204 opcode, ack_size, sizeof(*ack));
205 goto error_ack_short;
206 }
207 if (ack_size == sizeof(i2400m_NBOOT_BARKER)
208 && memcmp(ack, i2400m_NBOOT_BARKER, sizeof(*ack)) == 0) {
209 result = -ERESTARTSYS;
210 i2400m->sboot = 0;
211 d_printf(6, dev, "boot-mode cmd %d: "
212 "HW non-signed boot barker\n", opcode);
213 goto error_reboot;
214 }
215 if (ack_size == sizeof(i2400m_SBOOT_BARKER)
216 && memcmp(ack, i2400m_SBOOT_BARKER, sizeof(*ack)) == 0) {
217 result = -ERESTARTSYS;
218 i2400m->sboot = 1;
219 d_printf(6, dev, "boot-mode cmd %d: HW signed reboot barker\n",
220 opcode);
221 goto error_reboot;
222 }
223 if (ack_size == sizeof(i2400m_ACK_BARKER)
224 && memcmp(ack, i2400m_ACK_BARKER, sizeof(*ack)) == 0) {
225 result = -EISCONN;
226 d_printf(3, dev, "boot-mode cmd %d: HW reboot ack barker\n",
227 opcode);
228 goto error_reboot_ack;
229 }
230 result = 0;
231 if (flags & I2400M_BM_CMD_RAW)
232 goto out_raw;
233 ack->data_size = le32_to_cpu(ack->data_size);
234 ack->target_addr = le32_to_cpu(ack->target_addr);
235 ack->block_checksum = le32_to_cpu(ack->block_checksum);
236 d_printf(5, dev, "boot-mode cmd %d: notification for opcode %u "
237 "response %u csum %u rr %u da %u\n",
238 opcode, i2400m_brh_get_opcode(ack),
239 i2400m_brh_get_response(ack),
240 i2400m_brh_get_use_checksum(ack),
241 i2400m_brh_get_response_required(ack),
242 i2400m_brh_get_direct_access(ack));
243 result = -EIO;
244 if (i2400m_brh_get_signature(ack) != 0xcbbc) {
245 dev_err(dev, "boot-mode cmd %d: HW BUG? wrong signature "
246 "0x%04x\n", opcode, i2400m_brh_get_signature(ack));
247 goto error_ack_signature;
248 }
249 if (opcode != -1 && opcode != i2400m_brh_get_opcode(ack)) {
250 dev_err(dev, "boot-mode cmd %d: HW BUG? "
251 "received response for opcode %u, expected %u\n",
252 opcode, i2400m_brh_get_opcode(ack), opcode);
253 goto error_ack_opcode;
254 }
255 if (i2400m_brh_get_response(ack) != 0) { /* failed? */
256 dev_err(dev, "boot-mode cmd %d: error; hw response %u\n",
257 opcode, i2400m_brh_get_response(ack));
258 goto error_ack_failed;
259 }
260 if (ack_size < ack->data_size + sizeof(*ack)) {
261 dev_err(dev, "boot-mode cmd %d: SW BUG "
262 "driver provided only %zu bytes for %zu bytes "
263 "of data\n", opcode, ack_size,
264 (size_t) le32_to_cpu(ack->data_size) + sizeof(*ack));
265 goto error_ack_short_buffer;
266 }
267 result = ack_size;
268 /* Don't you love this stack of empty targets? Well, I don't
269 * either, but it helps track exactly who comes in here and
270 * why :) */
271error_ack_short_buffer:
272error_ack_failed:
273error_ack_opcode:
274error_ack_signature:
275out_raw:
276error_reboot_ack:
277error_reboot:
278error_ack_short:
279 d_fnend(8, dev, "(i2400m %p opcode %d ack %p size %zu) = %d\n",
280 i2400m, opcode, ack, ack_size, (int) result);
281 return result;
282}
283
284
285/**
286 * i2400m_bm_cmd - Execute a boot mode command
287 *
288 * @cmd: buffer containing the command data (pointing at the header).
289 * This data can be ANYWHERE (for USB, we will copy it to an
290 * specific buffer). Make sure everything is in proper little
291 * endian.
292 *
293 * A raw buffer can be also sent, just cast it and set flags to
294 * I2400M_BM_CMD_RAW.
295 *
296 * This function will generate a checksum for you if the
297 * checksum bit in the command is set (unless I2400M_BM_CMD_RAW
298 * is set).
299 *
300 * You can use the i2400m->bm_cmd_buf to stage your commands and
301 * send them.
302 *
303 * If NULL, no command is sent (we just wait for an ack).
304 *
305 * @cmd_size: size of the command. Will be auto padded to the
306 * bus-specific drivers padding requirements.
307 *
308 * @ack: buffer where to place the acknowledgement. If it is a regular
309 * command response, all fields will be returned with the right,
310 * native endianess.
311 *
312 * You *cannot* use i2400m->bm_ack_buf for this buffer.
313 *
314 * @ack_size: size of @ack, 16 aligned; you need to provide at least
315 * sizeof(*ack) bytes and then enough to contain the return data
316 * from the command
317 *
318 * @flags: see I2400M_BM_CMD_* above.
319 *
320 * @returns: bytes received by the notification; if < 0, an errno code
321 * denoting an error or:
322 *
323 * -ERESTARTSYS The device has rebooted
324 *
325 * Executes a boot-mode command and waits for a response, doing basic
326 * validation on it; if a zero length response is received, it retries
327 * waiting for a response until a non-zero one is received (timing out
328 * after %I2400M_BOOT_RETRIES retries).
329 */
330static
331ssize_t i2400m_bm_cmd(struct i2400m *i2400m,
332 const struct i2400m_bootrom_header *cmd, size_t cmd_size,
333 struct i2400m_bootrom_header *ack, size_t ack_size,
334 int flags)
335{
336 ssize_t result = -ENOMEM, rx_bytes;
337 struct device *dev = i2400m_dev(i2400m);
338 int opcode = cmd == NULL ? -1 : i2400m_brh_get_opcode(cmd);
339
340 d_fnstart(6, dev, "(i2400m %p cmd %p size %zu ack %p size %zu)\n",
341 i2400m, cmd, cmd_size, ack, ack_size);
342 BUG_ON(ack_size < sizeof(*ack));
343 BUG_ON(i2400m->boot_mode == 0);
344
345 if (cmd != NULL) { /* send the command */
346 memcpy(i2400m->bm_cmd_buf, cmd, cmd_size);
347 result = i2400m->bus_bm_cmd_send(i2400m, cmd, cmd_size, flags);
348 if (result < 0)
349 goto error_cmd_send;
350 if ((flags & I2400M_BM_CMD_RAW) == 0)
351 d_printf(5, dev,
352 "boot-mode cmd %d csum %u rr %u da %u: "
353 "addr 0x%04x size %u block csum 0x%04x\n",
354 opcode, i2400m_brh_get_use_checksum(cmd),
355 i2400m_brh_get_response_required(cmd),
356 i2400m_brh_get_direct_access(cmd),
357 cmd->target_addr, cmd->data_size,
358 cmd->block_checksum);
359 }
360 result = i2400m->bus_bm_wait_for_ack(i2400m, ack, ack_size);
361 if (result < 0) {
362 dev_err(dev, "boot-mode cmd %d: error waiting for an ack: %d\n",
363 opcode, (int) result); /* bah, %zd doesn't work */
364 goto error_wait_for_ack;
365 }
366 rx_bytes = result;
367 /* verify the ack and read more if neccessary [result is the
368 * final amount of bytes we get in the ack] */
369 result = __i2400m_bm_ack_verify(i2400m, opcode, ack, ack_size, flags);
370 if (result < 0)
371 goto error_bad_ack;
372 /* Don't you love this stack of empty targets? Well, I don't
373 * either, but it helps track exactly who comes in here and
374 * why :) */
375 result = rx_bytes;
376error_bad_ack:
377error_wait_for_ack:
378error_cmd_send:
379 d_fnend(6, dev, "(i2400m %p cmd %p size %zu ack %p size %zu) = %d\n",
380 i2400m, cmd, cmd_size, ack, ack_size, (int) result);
381 return result;
382}
383
384
385/**
386 * i2400m_download_chunk - write a single chunk of data to the device's memory
387 *
388 * @i2400m: device descriptor
389 * @buf: the buffer to write
390 * @buf_len: length of the buffer to write
391 * @addr: address in the device memory space
392 * @direct: bootrom write mode
393 * @do_csum: should a checksum validation be performed
394 */
395static int i2400m_download_chunk(struct i2400m *i2400m, const void *chunk,
396 size_t __chunk_len, unsigned long addr,
397 unsigned int direct, unsigned int do_csum)
398{
399 int ret;
400 size_t chunk_len = ALIGN(__chunk_len, I2400M_PL_PAD);
401 struct device *dev = i2400m_dev(i2400m);
402 struct {
403 struct i2400m_bootrom_header cmd;
404 u8 cmd_payload[chunk_len];
405 } __attribute__((packed)) *buf;
406 struct i2400m_bootrom_header ack;
407
408 d_fnstart(5, dev, "(i2400m %p chunk %p __chunk_len %zu addr 0x%08lx "
409 "direct %u do_csum %u)\n", i2400m, chunk, __chunk_len,
410 addr, direct, do_csum);
411 buf = i2400m->bm_cmd_buf;
412 memcpy(buf->cmd_payload, chunk, __chunk_len);
413 memset(buf->cmd_payload + __chunk_len, 0xad, chunk_len - __chunk_len);
414
415 buf->cmd.command = i2400m_brh_command(I2400M_BRH_WRITE,
416 __chunk_len & 0x3 ? 0 : do_csum,
417 __chunk_len & 0xf ? 0 : direct);
418 buf->cmd.target_addr = cpu_to_le32(addr);
419 buf->cmd.data_size = cpu_to_le32(__chunk_len);
420 ret = i2400m_bm_cmd(i2400m, &buf->cmd, sizeof(buf->cmd) + chunk_len,
421 &ack, sizeof(ack), 0);
422 if (ret >= 0)
423 ret = 0;
424 d_fnend(5, dev, "(i2400m %p chunk %p __chunk_len %zu addr 0x%08lx "
425 "direct %u do_csum %u) = %d\n", i2400m, chunk, __chunk_len,
426 addr, direct, do_csum, ret);
427 return ret;
428}
429
430
431/*
432 * Download a BCF file's sections to the device
433 *
434 * @i2400m: device descriptor
435 * @bcf: pointer to firmware data (followed by the payloads). Assumed
436 * verified and consistent.
437 * @bcf_len: length (in bytes) of the @bcf buffer.
438 *
439 * Returns: < 0 errno code on error or the offset to the jump instruction.
440 *
441 * Given a BCF file, downloads each section (a command and a payload)
442 * to the device's address space. Actually, it just executes each
443 * command i the BCF file.
444 *
445 * The section size has to be aligned to 4 bytes AND the padding has
446 * to be taken from the firmware file, as the signature takes it into
447 * account.
448 */
449static
450ssize_t i2400m_dnload_bcf(struct i2400m *i2400m,
451 const struct i2400m_bcf_hdr *bcf, size_t bcf_len)
452{
453 ssize_t ret;
454 struct device *dev = i2400m_dev(i2400m);
455 size_t offset, /* iterator offset */
456 data_size, /* Size of the data payload */
457 section_size, /* Size of the whole section (cmd + payload) */
458 section = 1;
459 const struct i2400m_bootrom_header *bh;
460 struct i2400m_bootrom_header ack;
461
462 d_fnstart(3, dev, "(i2400m %p bcf %p bcf_len %zu)\n",
463 i2400m, bcf, bcf_len);
464 /* Iterate over the command blocks in the BCF file that start
465 * after the header */
466 offset = le32_to_cpu(bcf->header_len) * sizeof(u32);
467 while (1) { /* start sending the file */
468 bh = (void *) bcf + offset;
469 data_size = le32_to_cpu(bh->data_size);
470 section_size = ALIGN(sizeof(*bh) + data_size, 4);
471 d_printf(7, dev,
472 "downloading section #%zu (@%zu %zu B) to 0x%08x\n",
473 section, offset, sizeof(*bh) + data_size,
474 le32_to_cpu(bh->target_addr));
475 if (i2400m_brh_get_opcode(bh) == I2400M_BRH_SIGNED_JUMP) {
476 /* Secure boot needs to stop here */
477 d_printf(5, dev, "signed jump found @%zu\n", offset);
478 break;
479 }
480 if (offset + section_size == bcf_len)
481 /* Non-secure boot stops here */
482 break;
483 if (offset + section_size > bcf_len) {
484 dev_err(dev, "fw %s: bad section #%zu, "
485 "end (@%zu) beyond EOF (@%zu)\n",
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +0000486 i2400m->fw_name, section,
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -0800487 offset + section_size, bcf_len);
488 ret = -EINVAL;
489 goto error_section_beyond_eof;
490 }
491 __i2400m_msleep(20);
492 ret = i2400m_bm_cmd(i2400m, bh, section_size,
493 &ack, sizeof(ack), I2400M_BM_CMD_RAW);
494 if (ret < 0) {
495 dev_err(dev, "fw %s: section #%zu (@%zu %zu B) "
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +0000496 "failed %d\n", i2400m->fw_name, section,
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -0800497 offset, sizeof(*bh) + data_size, (int) ret);
498 goto error_send;
499 }
500 offset += section_size;
501 section++;
502 }
503 ret = offset;
504error_section_beyond_eof:
505error_send:
506 d_fnend(3, dev, "(i2400m %p bcf %p bcf_len %zu) = %d\n",
507 i2400m, bcf, bcf_len, (int) ret);
508 return ret;
509}
510
511
512/*
513 * Do the final steps of uploading firmware
514 *
515 * Depending on the boot mode (signed vs non-signed), different
516 * actions need to be taken.
517 */
518static
519int i2400m_dnload_finalize(struct i2400m *i2400m,
520 const struct i2400m_bcf_hdr *bcf, size_t offset)
521{
522 int ret = 0;
523 struct device *dev = i2400m_dev(i2400m);
524 struct i2400m_bootrom_header *cmd, ack;
525 struct {
526 struct i2400m_bootrom_header cmd;
527 u8 cmd_pl[0];
528 } __attribute__((packed)) *cmd_buf;
529 size_t signature_block_offset, signature_block_size;
530
531 d_fnstart(3, dev, "offset %zu\n", offset);
532 cmd = (void *) bcf + offset;
533 if (i2400m->sboot == 0) {
534 struct i2400m_bootrom_header jump_ack;
535 d_printf(3, dev, "unsecure boot, jumping to 0x%08x\n",
536 le32_to_cpu(cmd->target_addr));
537 i2400m_brh_set_opcode(cmd, I2400M_BRH_JUMP);
538 cmd->data_size = 0;
539 ret = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd),
540 &jump_ack, sizeof(jump_ack), 0);
541 } else {
542 d_printf(3, dev, "secure boot, jumping to 0x%08x\n",
543 le32_to_cpu(cmd->target_addr));
544 cmd_buf = i2400m->bm_cmd_buf;
545 memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd));
546 signature_block_offset =
547 sizeof(*bcf)
548 + le32_to_cpu(bcf->key_size) * sizeof(u32)
549 + le32_to_cpu(bcf->exponent_size) * sizeof(u32);
550 signature_block_size =
551 le32_to_cpu(bcf->modulus_size) * sizeof(u32);
552 memcpy(cmd_buf->cmd_pl, (void *) bcf + signature_block_offset,
553 signature_block_size);
554 ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd,
555 sizeof(cmd_buf->cmd) + signature_block_size,
556 &ack, sizeof(ack), I2400M_BM_CMD_RAW);
557 }
558 d_fnend(3, dev, "returning %d\n", ret);
559 return ret;
560}
561
562
563/**
564 * i2400m_bootrom_init - Reboots a powered device into boot mode
565 *
566 * @i2400m: device descriptor
567 * @flags:
568 * I2400M_BRI_SOFT: a reboot notification has been seen
569 * already, so don't wait for it.
570 *
571 * I2400M_BRI_NO_REBOOT: Don't send a reboot command, but wait
572 * for a reboot barker notification. This is a one shot; if
573 * the state machine needs to send a reboot command it will.
574 *
575 * Returns:
576 *
577 * < 0 errno code on error, 0 if ok.
578 *
579 * i2400m->sboot set to 0 for unsecure boot process, 1 for secure
580 * boot process.
581 *
582 * Description:
583 *
584 * Tries hard enough to put the device in boot-mode. There are two
585 * main phases to this:
586 *
587 * a. (1) send a reboot command and (2) get a reboot barker
588 * b. (1) ack the reboot sending a reboot barker and (2) getting an
589 * ack barker in return
590 *
591 * We want to skip (a) in some cases [soft]. The state machine is
592 * horrible, but it is basically: on each phase, send what has to be
593 * sent (if any), wait for the answer and act on the answer. We might
594 * have to backtrack and retry, so we keep a max tries counter for
595 * that.
596 *
597 * If we get a timeout after sending a warm reset, we do it again.
598 */
599int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags)
600{
601 int result;
602 struct device *dev = i2400m_dev(i2400m);
603 struct i2400m_bootrom_header *cmd;
604 struct i2400m_bootrom_header ack;
605 int count = I2400M_BOOT_RETRIES;
606 int ack_timeout_cnt = 1;
607
608 BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_NBOOT_BARKER));
609 BUILD_BUG_ON(sizeof(ack) != sizeof(i2400m_ACK_BARKER));
610
611 d_fnstart(4, dev, "(i2400m %p flags 0x%08x)\n", i2400m, flags);
612 result = -ENOMEM;
613 cmd = i2400m->bm_cmd_buf;
614 if (flags & I2400M_BRI_SOFT)
615 goto do_reboot_ack;
616do_reboot:
617 if (--count < 0)
618 goto error_timeout;
619 d_printf(4, dev, "device reboot: reboot command [%d # left]\n",
620 count);
621 if ((flags & I2400M_BRI_NO_REBOOT) == 0)
622 i2400m->bus_reset(i2400m, I2400M_RT_WARM);
623 result = i2400m_bm_cmd(i2400m, NULL, 0, &ack, sizeof(ack),
624 I2400M_BM_CMD_RAW);
625 flags &= ~I2400M_BRI_NO_REBOOT;
626 switch (result) {
627 case -ERESTARTSYS:
628 d_printf(4, dev, "device reboot: got reboot barker\n");
629 break;
630 case -EISCONN: /* we don't know how it got here...but we follow it */
631 d_printf(4, dev, "device reboot: got ack barker - whatever\n");
632 goto do_reboot;
633 case -ETIMEDOUT: /* device has timed out, we might be in boot
634 * mode already and expecting an ack, let's try
635 * that */
636 dev_info(dev, "warm reset timed out, trying an ack\n");
637 goto do_reboot_ack;
638 case -EPROTO:
639 case -ESHUTDOWN: /* dev is gone */
640 case -EINTR: /* user cancelled */
641 goto error_dev_gone;
642 default:
643 dev_err(dev, "device reboot: error %d while waiting "
644 "for reboot barker - rebooting\n", result);
645 goto do_reboot;
646 }
647 /* At this point we ack back with 4 REBOOT barkers and expect
648 * 4 ACK barkers. This is ugly, as we send a raw command --
649 * hence the cast. _bm_cmd() will catch the reboot ack
650 * notification and report it as -EISCONN. */
651do_reboot_ack:
652 d_printf(4, dev, "device reboot ack: sending ack [%d # left]\n", count);
653 if (i2400m->sboot == 0)
654 memcpy(cmd, i2400m_NBOOT_BARKER,
655 sizeof(i2400m_NBOOT_BARKER));
656 else
657 memcpy(cmd, i2400m_SBOOT_BARKER,
658 sizeof(i2400m_SBOOT_BARKER));
659 result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd),
660 &ack, sizeof(ack), I2400M_BM_CMD_RAW);
661 switch (result) {
662 case -ERESTARTSYS:
663 d_printf(4, dev, "reboot ack: got reboot barker - retrying\n");
664 if (--count < 0)
665 goto error_timeout;
666 goto do_reboot_ack;
667 case -EISCONN:
668 d_printf(4, dev, "reboot ack: got ack barker - good\n");
669 break;
670 case -ETIMEDOUT: /* no response, maybe it is the other type? */
671 if (ack_timeout_cnt-- >= 0) {
672 d_printf(4, dev, "reboot ack timedout: "
673 "trying the other type?\n");
674 i2400m->sboot = !i2400m->sboot;
675 goto do_reboot_ack;
676 } else {
677 dev_err(dev, "reboot ack timedout too long: "
678 "trying reboot\n");
679 goto do_reboot;
680 }
681 break;
682 case -EPROTO:
683 case -ESHUTDOWN: /* dev is gone */
684 goto error_dev_gone;
685 default:
686 dev_err(dev, "device reboot ack: error %d while waiting for "
687 "reboot ack barker - rebooting\n", result);
688 goto do_reboot;
689 }
690 d_printf(2, dev, "device reboot ack: got ack barker - boot done\n");
691 result = 0;
692exit_timeout:
693error_dev_gone:
694 d_fnend(4, dev, "(i2400m %p flags 0x%08x) = %d\n",
695 i2400m, flags, result);
696 return result;
697
698error_timeout:
699 dev_err(dev, "Timed out waiting for reboot ack, resetting\n");
700 i2400m->bus_reset(i2400m, I2400M_RT_BUS);
701 result = -ETIMEDOUT;
702 goto exit_timeout;
703}
704
705
706/*
707 * Read the MAC addr
708 *
709 * The position this function reads is fixed in device memory and
710 * always available, even without firmware.
711 *
712 * Note we specify we want to read only six bytes, but provide space
713 * for 16, as we always get it rounded up.
714 */
715int i2400m_read_mac_addr(struct i2400m *i2400m)
716{
717 int result;
718 struct device *dev = i2400m_dev(i2400m);
719 struct net_device *net_dev = i2400m->wimax_dev.net_dev;
720 struct i2400m_bootrom_header *cmd;
721 struct {
722 struct i2400m_bootrom_header ack;
723 u8 ack_pl[16];
724 } __attribute__((packed)) ack_buf;
725
726 d_fnstart(5, dev, "(i2400m %p)\n", i2400m);
727 cmd = i2400m->bm_cmd_buf;
728 cmd->command = i2400m_brh_command(I2400M_BRH_READ, 0, 1);
729 cmd->target_addr = cpu_to_le32(0x00203fe8);
730 cmd->data_size = cpu_to_le32(6);
731 result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd),
732 &ack_buf.ack, sizeof(ack_buf), 0);
733 if (result < 0) {
734 dev_err(dev, "BM: read mac addr failed: %d\n", result);
735 goto error_read_mac;
736 }
737 d_printf(2, dev,
738 "mac addr is %02x:%02x:%02x:%02x:%02x:%02x\n",
739 ack_buf.ack_pl[0], ack_buf.ack_pl[1],
740 ack_buf.ack_pl[2], ack_buf.ack_pl[3],
741 ack_buf.ack_pl[4], ack_buf.ack_pl[5]);
742 if (i2400m->bus_bm_mac_addr_impaired == 1) {
743 ack_buf.ack_pl[0] = 0x00;
744 ack_buf.ack_pl[1] = 0x16;
745 ack_buf.ack_pl[2] = 0xd3;
746 get_random_bytes(&ack_buf.ack_pl[3], 3);
747 dev_err(dev, "BM is MAC addr impaired, faking MAC addr to "
748 "mac addr is %02x:%02x:%02x:%02x:%02x:%02x\n",
749 ack_buf.ack_pl[0], ack_buf.ack_pl[1],
750 ack_buf.ack_pl[2], ack_buf.ack_pl[3],
751 ack_buf.ack_pl[4], ack_buf.ack_pl[5]);
752 result = 0;
753 }
754 net_dev->addr_len = ETH_ALEN;
755 memcpy(net_dev->perm_addr, ack_buf.ack_pl, ETH_ALEN);
756 memcpy(net_dev->dev_addr, ack_buf.ack_pl, ETH_ALEN);
757error_read_mac:
758 d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, result);
759 return result;
760}
761
762
763/*
764 * Initialize a non signed boot
765 *
766 * This implies sending some magic values to the device's memory. Note
767 * we convert the values to little endian in the same array
768 * declaration.
769 */
770static
771int i2400m_dnload_init_nonsigned(struct i2400m *i2400m)
772{
773#define POKE(a, d) { \
Harvey Harrisonee437772009-02-01 00:43:54 -0800774 .address = cpu_to_le32(a), \
775 .data = cpu_to_le32(d) \
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -0800776}
777 static const struct {
778 __le32 address;
779 __le32 data;
780 } i2400m_pokes[] = {
781 POKE(0x081A58, 0xA7810230),
782 POKE(0x080040, 0x00000000),
783 POKE(0x080048, 0x00000082),
784 POKE(0x08004C, 0x0000081F),
785 POKE(0x080054, 0x00000085),
786 POKE(0x080058, 0x00000180),
787 POKE(0x08005C, 0x00000018),
788 POKE(0x080060, 0x00000010),
789 POKE(0x080574, 0x00000001),
790 POKE(0x080550, 0x00000005),
791 POKE(0xAE0000, 0x00000000),
792 };
793#undef POKE
794 unsigned i;
795 int ret;
796 struct device *dev = i2400m_dev(i2400m);
797
798 dev_warn(dev, "WARNING!!! non-signed boot UNTESTED PATH!\n");
799
800 d_fnstart(5, dev, "(i2400m %p)\n", i2400m);
801 for (i = 0; i < ARRAY_SIZE(i2400m_pokes); i++) {
802 ret = i2400m_download_chunk(i2400m, &i2400m_pokes[i].data,
803 sizeof(i2400m_pokes[i].data),
804 i2400m_pokes[i].address, 1, 1);
805 if (ret < 0)
806 break;
807 }
808 d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret);
809 return ret;
810}
811
812
813/*
814 * Initialize the signed boot process
815 *
816 * @i2400m: device descriptor
817 *
818 * @bcf_hdr: pointer to the firmware header; assumes it is fully in
819 * memory (it has gone through basic validation).
820 *
821 * Returns: 0 if ok, < 0 errno code on error, -ERESTARTSYS if the hw
822 * rebooted.
823 *
824 * This writes the firmware BCF header to the device using the
825 * HASH_PAYLOAD_ONLY command.
826 */
827static
828int i2400m_dnload_init_signed(struct i2400m *i2400m,
829 const struct i2400m_bcf_hdr *bcf_hdr)
830{
831 int ret;
832 struct device *dev = i2400m_dev(i2400m);
833 struct {
834 struct i2400m_bootrom_header cmd;
835 struct i2400m_bcf_hdr cmd_pl;
836 } __attribute__((packed)) *cmd_buf;
837 struct i2400m_bootrom_header ack;
838
839 d_fnstart(5, dev, "(i2400m %p bcf_hdr %p)\n", i2400m, bcf_hdr);
840 cmd_buf = i2400m->bm_cmd_buf;
841 cmd_buf->cmd.command =
842 i2400m_brh_command(I2400M_BRH_HASH_PAYLOAD_ONLY, 0, 0);
843 cmd_buf->cmd.target_addr = 0;
844 cmd_buf->cmd.data_size = cpu_to_le32(sizeof(cmd_buf->cmd_pl));
845 memcpy(&cmd_buf->cmd_pl, bcf_hdr, sizeof(*bcf_hdr));
846 ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd, sizeof(*cmd_buf),
847 &ack, sizeof(ack), 0);
848 if (ret >= 0)
849 ret = 0;
850 d_fnend(5, dev, "(i2400m %p bcf_hdr %p) = %d\n", i2400m, bcf_hdr, ret);
851 return ret;
852}
853
854
855/*
856 * Initialize the firmware download at the device size
857 *
858 * Multiplex to the one that matters based on the device's mode
859 * (signed or non-signed).
860 */
861static
862int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf)
863{
864 int result;
865 struct device *dev = i2400m_dev(i2400m);
866 u32 module_id = le32_to_cpu(bcf->module_id);
867
868 if (i2400m->sboot == 0
869 && (module_id & I2400M_BCF_MOD_ID_POKES) == 0) {
870 /* non-signed boot process without pokes */
871 result = i2400m_dnload_init_nonsigned(i2400m);
872 if (result == -ERESTARTSYS)
873 return result;
874 if (result < 0)
875 dev_err(dev, "fw %s: non-signed download "
876 "initialization failed: %d\n",
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +0000877 i2400m->fw_name, result);
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -0800878 } else if (i2400m->sboot == 0
879 && (module_id & I2400M_BCF_MOD_ID_POKES)) {
880 /* non-signed boot process with pokes, nothing to do */
881 result = 0;
882 } else { /* signed boot process */
883 result = i2400m_dnload_init_signed(i2400m, bcf);
884 if (result == -ERESTARTSYS)
885 return result;
886 if (result < 0)
887 dev_err(dev, "fw %s: signed boot download "
888 "initialization failed: %d\n",
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +0000889 i2400m->fw_name, result);
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -0800890 }
891 return result;
892}
893
894
895/*
896 * Run quick consistency tests on the firmware file
897 *
898 * Check for the firmware being made for the i2400m device,
899 * etc...These checks are mostly informative, as the device will make
900 * them too; but the driver's response is more informative on what
901 * went wrong.
902 */
903static
904int i2400m_fw_check(struct i2400m *i2400m,
905 const struct i2400m_bcf_hdr *bcf,
906 size_t bcf_size)
907{
908 int result;
909 struct device *dev = i2400m_dev(i2400m);
910 unsigned module_type, header_len, major_version, minor_version,
911 module_id, module_vendor, date, size;
912
913 /* Check hard errors */
914 result = -EINVAL;
915 if (bcf_size < sizeof(*bcf)) { /* big enough header? */
916 dev_err(dev, "firmware %s too short: "
917 "%zu B vs %zu (at least) expected\n",
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +0000918 i2400m->fw_name, bcf_size, sizeof(*bcf));
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -0800919 goto error;
920 }
921
922 module_type = bcf->module_type;
923 header_len = sizeof(u32) * le32_to_cpu(bcf->header_len);
924 major_version = le32_to_cpu(bcf->header_version) & 0xffff0000 >> 16;
925 minor_version = le32_to_cpu(bcf->header_version) & 0x0000ffff;
926 module_id = le32_to_cpu(bcf->module_id);
927 module_vendor = le32_to_cpu(bcf->module_vendor);
928 date = le32_to_cpu(bcf->date);
929 size = sizeof(u32) * le32_to_cpu(bcf->size);
930
931 if (bcf_size != size) { /* annoyingly paranoid */
932 dev_err(dev, "firmware %s: bad size, got "
933 "%zu B vs %u expected\n",
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +0000934 i2400m->fw_name, bcf_size, size);
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -0800935 goto error;
936 }
937
938 d_printf(2, dev, "type 0x%x id 0x%x vendor 0x%x; header v%u.%u (%zu B) "
939 "date %08x (%zu B)\n",
940 module_type, module_id, module_vendor,
941 major_version, minor_version, (size_t) header_len,
942 date, (size_t) size);
943
944 if (module_type != 6) { /* built for the right hardware? */
945 dev_err(dev, "bad fw %s: unexpected module type 0x%x; "
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +0000946 "aborting\n", i2400m->fw_name, module_type);
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -0800947 goto error;
948 }
949
950 /* Check soft-er errors */
951 result = 0;
952 if (module_vendor != 0x8086)
953 dev_err(dev, "bad fw %s? unexpected vendor 0x%04x\n",
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +0000954 i2400m->fw_name, module_vendor);
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -0800955 if (date < 0x20080300)
956 dev_err(dev, "bad fw %s? build date too old %08x\n",
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +0000957 i2400m->fw_name, date);
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -0800958error:
959 return result;
960}
961
962
963/*
964 * Download the firmware to the device
965 *
966 * @i2400m: device descriptor
967 * @bcf: pointer to loaded (and minimally verified for consistency)
968 * firmware
969 * @bcf_size: size of the @bcf buffer (header plus payloads)
970 *
971 * The process for doing this is described in this file's header.
972 *
973 * Note we only reinitialize boot-mode if the flags say so. Some hw
974 * iterations need it, some don't. In any case, if we loop, we always
975 * need to reinitialize the boot room, hence the flags modification.
976 */
977static
978int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf,
979 size_t bcf_size, enum i2400m_bri flags)
980{
981 int ret = 0;
982 struct device *dev = i2400m_dev(i2400m);
983 int count = I2400M_BOOT_RETRIES;
984
985 d_fnstart(5, dev, "(i2400m %p bcf %p size %zu)\n",
986 i2400m, bcf, bcf_size);
987 i2400m->boot_mode = 1;
988hw_reboot:
989 if (count-- == 0) {
990 ret = -ERESTARTSYS;
991 dev_err(dev, "device rebooted too many times, aborting\n");
992 goto error_too_many_reboots;
993 }
994 if (flags & I2400M_BRI_MAC_REINIT) {
995 ret = i2400m_bootrom_init(i2400m, flags);
996 if (ret < 0) {
997 dev_err(dev, "bootrom init failed: %d\n", ret);
998 goto error_bootrom_init;
999 }
1000 }
1001 flags |= I2400M_BRI_MAC_REINIT;
1002
1003 /*
1004 * Initialize the download, push the bytes to the device and
1005 * then jump to the new firmware. Note @ret is passed with the
1006 * offset of the jump instruction to _dnload_finalize()
1007 */
1008 ret = i2400m_dnload_init(i2400m, bcf); /* Init device's dnload */
1009 if (ret == -ERESTARTSYS)
1010 goto error_dev_rebooted;
1011 if (ret < 0)
1012 goto error_dnload_init;
1013
1014 ret = i2400m_dnload_bcf(i2400m, bcf, bcf_size);
1015 if (ret == -ERESTARTSYS)
1016 goto error_dev_rebooted;
1017 if (ret < 0) {
1018 dev_err(dev, "fw %s: download failed: %d\n",
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +00001019 i2400m->fw_name, ret);
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -08001020 goto error_dnload_bcf;
1021 }
1022
1023 ret = i2400m_dnload_finalize(i2400m, bcf, ret);
1024 if (ret == -ERESTARTSYS)
1025 goto error_dev_rebooted;
1026 if (ret < 0) {
1027 dev_err(dev, "fw %s: "
1028 "download finalization failed: %d\n",
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +00001029 i2400m->fw_name, ret);
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -08001030 goto error_dnload_finalize;
1031 }
1032
1033 d_printf(2, dev, "fw %s successfully uploaded\n",
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +00001034 i2400m->fw_name);
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -08001035 i2400m->boot_mode = 0;
1036error_dnload_finalize:
1037error_dnload_bcf:
1038error_dnload_init:
1039error_bootrom_init:
1040error_too_many_reboots:
1041 d_fnend(5, dev, "(i2400m %p bcf %p size %zu) = %d\n",
1042 i2400m, bcf, bcf_size, ret);
1043 return ret;
1044
1045error_dev_rebooted:
1046 dev_err(dev, "device rebooted, %d tries left\n", count);
1047 /* we got the notification already, no need to wait for it again */
1048 flags |= I2400M_BRI_SOFT;
1049 goto hw_reboot;
1050}
1051
1052
1053/**
1054 * i2400m_dev_bootstrap - Bring the device to a known state and upload firmware
1055 *
1056 * @i2400m: device descriptor
1057 *
1058 * Returns: >= 0 if ok, < 0 errno code on error.
1059 *
1060 * This sets up the firmware upload environment, loads the firmware
1061 * file from disk, verifies and then calls the firmware upload process
1062 * per se.
1063 *
1064 * Can be called either from probe, or after a warm reset. Can not be
1065 * called from within an interrupt. All the flow in this code is
1066 * single-threade; all I/Os are synchronous.
1067 */
1068int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags)
1069{
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +00001070 int ret = 0, itr = 0;
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -08001071 struct device *dev = i2400m_dev(i2400m);
1072 const struct firmware *fw;
1073 const struct i2400m_bcf_hdr *bcf; /* Firmware data */
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +00001074 const char *fw_name;
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -08001075
1076 d_fnstart(5, dev, "(i2400m %p)\n", i2400m);
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -08001077
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +00001078 /* Load firmware files to memory. */
1079 itr = 0;
1080 while(1) {
1081 fw_name = i2400m->bus_fw_names[itr];
1082 if (fw_name == NULL) {
1083 dev_err(dev, "Could not find a usable firmware image\n");
1084 ret = -ENOENT;
1085 goto error_no_fw;
1086 }
1087 ret = request_firmware(&fw, fw_name, dev);
1088 if (ret == 0)
1089 break; /* got it */
1090 if (ret < 0)
1091 dev_err(dev, "fw %s: cannot load file: %d\n",
1092 fw_name, ret);
1093 itr++;
1094 }
1095
1096 bcf = (void *) fw->data;
1097 i2400m->fw_name = fw_name;
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -08001098 ret = i2400m_fw_check(i2400m, bcf, fw->size);
1099 if (ret < 0)
1100 goto error_fw_bad;
1101 ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags);
1102error_fw_bad:
1103 release_firmware(fw);
Inaky Perez-Gonzalez1039abb2009-02-28 23:42:47 +00001104error_no_fw:
Inaky Perez-Gonzalez467cc392008-12-20 16:57:46 -08001105 d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret);
1106 return ret;
1107}
1108EXPORT_SYMBOL_GPL(i2400m_dev_bootstrap);