/** * i2400m_bm_cmd - Execute a boot mode command * * @cmd: buffer containing the command data (pointing at the header). * This data can be ANYWHERE (for USB, we will copy it to an * specific buffer). Make sure everything is in proper little * endian. * * A raw buffer can be also sent, just cast it and set flags to * I2400M_BM_CMD_RAW. * * This function will generate a checksum for you if the * checksum bit in the command is set (unless I2400M_BM_CMD_RAW * is set). * * You can use the i2400m->bm_cmd_buf to stage your commands and * send them. * * If NULL, no command is sent (we just wait for an ack). * * @cmd_size: size of the command. Will be auto padded to the * bus-specific drivers padding requirements. * * @ack: buffer where to place the acknowledgement. If it is a regular * command response, all fields will be returned with the right, * native endianess. * * You *cannot* use i2400m->bm_ack_buf for this buffer. * * @ack_size: size of @ack, 16 aligned; you need to provide at least * sizeof(*ack) bytes and then enough to contain the return data * from the command * * @flags: see I2400M_BM_CMD_* above. * * @returns: bytes received by the notification; if < 0, an errno code * denoting an error or: * * -ERESTARTSYS The device has rebooted * * Executes a boot-mode command and waits for a response, doing basic * validation on it; if a zero length response is received, it retries * waiting for a response until a non-zero one is received (timing out * after %I2400M_BOOT_RETRIES retries). */ static ssize_t i2400m_bm_cmd(struct i2400m *i2400m, const struct i2400m_bootrom_header *cmd, size_t cmd_size, struct i2400m_bootrom_header *ack, size_t ack_size, int flags) { ssize_t result = -ENOMEM, rx_bytes; struct device *dev = i2400m_dev(i2400m); int opcode = cmd == NULL ? -1 : i2400m_brh_get_opcode(cmd); d_fnstart(6, dev, "(i2400m %p cmd %p size %zu ack %p size %zu)\n", i2400m, cmd, cmd_size, ack, ack_size); BUG_ON(ack_size < sizeof(*ack)); BUG_ON(i2400m->boot_mode == 0); if (cmd != NULL) { /* send the command */ result = i2400m->bus_bm_cmd_send(i2400m, cmd, cmd_size, flags); if (result < 0) goto error_cmd_send; if ((flags & I2400M_BM_CMD_RAW) == 0) d_printf(5, dev, "boot-mode cmd %d csum %u rr %u da %u: " "addr 0x%04x size %u block csum 0x%04x\n", opcode, i2400m_brh_get_use_checksum(cmd), i2400m_brh_get_response_required(cmd), i2400m_brh_get_direct_access(cmd), cmd->target_addr, cmd->data_size, cmd->block_checksum); } result = i2400m->bus_bm_wait_for_ack(i2400m, ack, ack_size); if (result < 0) { dev_err(dev, "boot-mode cmd %d: error waiting for an ack: %d\n", opcode, (int) result); /* bah, %zd doesn't work */ goto error_wait_for_ack; } rx_bytes = result; /* verify the ack and read more if necessary [result is the * final amount of bytes we get in the ack] */ result = __i2400m_bm_ack_verify(i2400m, opcode, ack, ack_size, flags); if (result < 0) goto error_bad_ack; /* Don't you love this stack of empty targets? Well, I don't * either, but it helps track exactly who comes in here and * why :) */ result = rx_bytes; error_bad_ack: error_wait_for_ack: error_cmd_send: d_fnend(6, dev, "(i2400m %p cmd %p size %zu ack %p size %zu) = %d\n", i2400m, cmd, cmd_size, ack, ack_size, (int) result); return result; }
ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *i2400m, const struct i2400m_bootrom_header *_cmd, size_t cmd_size, int flags) { ssize_t result; struct device *dev = i2400m_dev(i2400m); struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); int opcode = _cmd == NULL ? -1 : i2400m_brh_get_opcode(_cmd); struct i2400m_bootrom_header *cmd; size_t cmd_size_a = ALIGN(cmd_size, I2400MS_BLK_SIZE); d_fnstart(5, dev, "(i2400m %p cmd %p size %zu)\n", i2400m, _cmd, cmd_size); result = -E2BIG; if (cmd_size > I2400M_BM_CMD_BUF_SIZE) goto error_too_big; if (_cmd != i2400m->bm_cmd_buf) memmove(i2400m->bm_cmd_buf, _cmd, cmd_size); cmd = i2400m->bm_cmd_buf; if (cmd_size_a > cmd_size) memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size); if ((flags & I2400M_BM_CMD_RAW) == 0) { if (WARN_ON(i2400m_brh_get_response_required(cmd) == 0)) dev_warn(dev, "SW BUG: response_required == 0\n"); i2400m_bm_cmd_prepare(cmd); } d_printf(4, dev, "BM cmd %d: %zu bytes (%zu padded)\n", opcode, cmd_size, cmd_size_a); d_dump(5, dev, cmd, cmd_size); sdio_claim_host(i2400ms->func); result = sdio_memcpy_toio(i2400ms->func, I2400MS_DATA_ADDR, i2400m->bm_cmd_buf, cmd_size_a); sdio_release_host(i2400ms->func); if (result < 0) { dev_err(dev, "BM cmd %d: cannot send: %ld\n", opcode, (long) result); goto error_cmd_send; } result = cmd_size; error_cmd_send: error_too_big: d_fnend(5, dev, "(i2400m %p cmd %p size %zu) = %d\n", i2400m, _cmd, cmd_size, (int) result); return result; }
/* * Send a boot-mode command over the bulk-out pipe * * Command can be a raw command, which requires no preparation (and * which might not even be following the command format). Checks that * the right amount of data was transferred. * * To satisfy USB requirements (no onstack, vmalloc or in data segment * buffers), we copy the command to i2400m->bm_cmd_buf and send it from * there. * * @flags: pass thru from i2400m_bm_cmd() * @return: cmd_size if ok, < 0 errno code on error. */ ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *i2400m, const struct i2400m_bootrom_header *_cmd, size_t cmd_size, int flags) { ssize_t result; struct device *dev = i2400m_dev(i2400m); struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); int opcode = _cmd == NULL ? -1 : i2400m_brh_get_opcode(_cmd); struct i2400m_bootrom_header *cmd; size_t cmd_size_a = ALIGN(cmd_size, 16); /* USB restriction */ d_fnstart(8, dev, "(i2400m %p cmd %p size %zu)\n", i2400m, _cmd, cmd_size); result = -E2BIG; if (cmd_size > I2400M_BM_CMD_BUF_SIZE) goto error_too_big; if (_cmd != i2400m->bm_cmd_buf) memmove(i2400m->bm_cmd_buf, _cmd, cmd_size); cmd = i2400m->bm_cmd_buf; if (cmd_size_a > cmd_size) /* Zero pad space */ memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size); if ((flags & I2400M_BM_CMD_RAW) == 0) { if (WARN_ON(i2400m_brh_get_response_required(cmd) == 0)) dev_warn(dev, "SW BUG: response_required == 0\n"); i2400m_bm_cmd_prepare(cmd); } result = i2400mu_tx_bulk_out(i2400mu, i2400m->bm_cmd_buf, cmd_size); if (result < 0) { dev_err(dev, "boot-mode cmd %d: cannot send: %zd\n", opcode, result); goto error_cmd_send; } if (result != cmd_size) { /* all was transferred? */ dev_err(dev, "boot-mode cmd %d: incomplete transfer " "(%zu vs %zu submitted)\n", opcode, result, cmd_size); result = -EIO; goto error_cmd_size; } error_cmd_size: error_cmd_send: error_too_big: d_fnend(8, dev, "(i2400m %p cmd %p size %zu) = %zd\n", i2400m, _cmd, cmd_size, result); return result; }
/* * Verify the ack data received * * Given a reply to a boot mode command, chew it and verify everything * is ok. * * @opcode: opcode which generated this ack. For error messages. * @ack: pointer to ack data we received * @ack_size: size of that data buffer * @flags: I2400M_BM_CMD_* flags we called the command with. * * Way too long function -- maybe it should be further split */ static ssize_t __i2400m_bm_ack_verify(struct i2400m *i2400m, int opcode, struct i2400m_bootrom_header *ack, size_t ack_size, int flags) { ssize_t result = -ENOMEM; struct device *dev = i2400m_dev(i2400m); d_fnstart(8, dev, "(i2400m %p opcode %d ack %p size %zu)\n", i2400m, opcode, ack, ack_size); if (ack_size < sizeof(*ack)) { result = -EIO; dev_err(dev, "boot-mode cmd %d: HW BUG? notification didn't " "return enough data (%zu bytes vs %zu expected)\n", opcode, ack_size, sizeof(*ack)); goto error_ack_short; } result = i2400m_is_boot_barker(i2400m, ack, ack_size); if (result >= 0) { result = -ERESTARTSYS; d_printf(6, dev, "boot-mode cmd %d: HW boot barker\n", opcode); goto error_reboot; } if (ack_size == sizeof(i2400m_ACK_BARKER) && memcmp(ack, i2400m_ACK_BARKER, sizeof(*ack)) == 0) { result = -EISCONN; d_printf(3, dev, "boot-mode cmd %d: HW reboot ack barker\n", opcode); goto error_reboot_ack; } result = 0; if (flags & I2400M_BM_CMD_RAW) goto out_raw; ack->data_size = le32_to_cpu(ack->data_size); ack->target_addr = le32_to_cpu(ack->target_addr); ack->block_checksum = le32_to_cpu(ack->block_checksum); d_printf(5, dev, "boot-mode cmd %d: notification for opcode %u " "response %u csum %u rr %u da %u\n", opcode, i2400m_brh_get_opcode(ack), i2400m_brh_get_response(ack), i2400m_brh_get_use_checksum(ack), i2400m_brh_get_response_required(ack), i2400m_brh_get_direct_access(ack)); result = -EIO; if (i2400m_brh_get_signature(ack) != 0xcbbc) { dev_err(dev, "boot-mode cmd %d: HW BUG? wrong signature " "0x%04x\n", opcode, i2400m_brh_get_signature(ack)); goto error_ack_signature; } if (opcode != -1 && opcode != i2400m_brh_get_opcode(ack)) { dev_err(dev, "boot-mode cmd %d: HW BUG? " "received response for opcode %u, expected %u\n", opcode, i2400m_brh_get_opcode(ack), opcode); goto error_ack_opcode; } if (i2400m_brh_get_response(ack) != 0) { /* failed? */ dev_err(dev, "boot-mode cmd %d: error; hw response %u\n", opcode, i2400m_brh_get_response(ack)); goto error_ack_failed; } if (ack_size < ack->data_size + sizeof(*ack)) { dev_err(dev, "boot-mode cmd %d: SW BUG " "driver provided only %zu bytes for %zu bytes " "of data\n", opcode, ack_size, (size_t) le32_to_cpu(ack->data_size) + sizeof(*ack)); goto error_ack_short_buffer; } result = ack_size; /* Don't you love this stack of empty targets? Well, I don't * either, but it helps track exactly who comes in here and * why :) */ error_ack_short_buffer: error_ack_failed: error_ack_opcode: error_ack_signature: out_raw: error_reboot_ack: error_reboot: error_ack_short: d_fnend(8, dev, "(i2400m %p opcode %d ack %p size %zu) = %d\n", i2400m, opcode, ack, ack_size, (int) result); return result; }