diff options
author | Paul Kocialkowski <contact@paulk.fr> | 2012-07-05 14:03:19 +0200 |
---|---|---|
committer | Paul Kocialkowski <contact@paulk.fr> | 2012-07-05 14:03:19 +0200 |
commit | 2eea520884474f4eefcf03815d52dfaac9e03cce (patch) | |
tree | 72e45c68995b1598fdf3b40ca88a464b3cebfdc2 /samsung-ipc/device/maguro/maguro_loader.c | |
parent | e94080b52dd1b205b107c30d9535feb9fdc4ff7f (diff) | |
download | external_libsamsung-ipc-2eea520884474f4eefcf03815d52dfaac9e03cce.zip external_libsamsung-ipc-2eea520884474f4eefcf03815d52dfaac9e03cce.tar.gz external_libsamsung-ipc-2eea520884474f4eefcf03815d52dfaac9e03cce.tar.bz2 |
XMM6260: Moved device-specific files to galaxys2 and maguro folders
Warning: this commit breaks code compilation!
Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
Diffstat (limited to 'samsung-ipc/device/maguro/maguro_loader.c')
-rw-r--r-- | samsung-ipc/device/maguro/maguro_loader.c | 796 |
1 files changed, 796 insertions, 0 deletions
diff --git a/samsung-ipc/device/maguro/maguro_loader.c b/samsung-ipc/device/maguro/maguro_loader.c new file mode 100644 index 0000000..3ea973f --- /dev/null +++ b/samsung-ipc/device/maguro/maguro_loader.c @@ -0,0 +1,796 @@ +/* + * Firmware loader for Samsung I9100 and I9250 + * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com> + * + * based on the incomplete C++ implementation which is + * Copyright (C) 2012 Sergey Gridasov <grindars@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "modemctl_common.h" +#include "ipc_private.h" +#include "fwloader_i9250.h" + +/* + * Locations of the firmware components in the Samsung firmware + */ + +struct xmm6260_radio_part i9250_radio_parts[] = { + [PSI] = { + .offset = 0, + .length = 0xf000, + }, + [EBL] = { + .offset = 0xf000, + .length = 0x19000, + }, + [SECURE_IMAGE] = { + .offset = 0x9ff800, + .length = 0x800, + }, + [FIRMWARE] = { + .offset = 0x28000, + .length = 0x9d8000, + }, + [NVDATA] = { + .offset = 0xa00000, + .length = 2 << 20, + } +}; + +struct i9250_boot_cmd_desc i9250_boot_cmd_desc[] = { + [SetPortConf] = { + .code = 0x86, + .long_tail = 1, + }, + [ReqSecStart] = { + .code = 0x204, + .long_tail = 1, + }, + [ReqSecEnd] = { + .code = 0x205, + }, + [ReqForceHwReset] = { + .code = 0x208, + .long_tail = 1, + .no_ack = 1, + }, + [ReqFlashSetAddress] = { + .code = 0x802, + .long_tail = 1, + }, + [ReqFlashWriteBlock] = { + .code = 0x804, + }, +}; + +static int i9250_send_image(struct ipc_client *client, + struct modemctl_io_data *io_data, enum xmm6260_image type) +{ + int ret = -1; + + if (type >= ARRAY_SIZE(i9250_radio_parts)) { + ipc_client_log(client, "Error: bad image type %x", type); + goto fail; + } + + size_t length = i9250_radio_parts[type].length; + size_t offset = i9250_radio_parts[type].offset; + + size_t start = offset; + size_t end = length + start; + + unsigned char crc = calculateCRC(io_data->radio_data, offset, length); + + //dump some image bytes + ipc_client_log(client, "image start"); + + size_t chunk_size = 0xdfc; + + while (start < end) { + size_t remaining = end - start; + size_t curr_chunk = chunk_size < remaining ? chunk_size : remaining; + ret = write(io_data->boot_fd, io_data->radio_data + start, curr_chunk); + if (ret < 0) { + ipc_client_log(client, "Error: failed to write image chunk"); + goto fail; + } + start += ret; + } + ipc_client_log(client, "sent image type=%d", type); + + if (type == EBL) { + if ((ret = write(io_data->boot_fd, &crc, 1)) < 1) { + ipc_client_log(client, "Error: failed to write EBL CRC"); + goto fail; + } + else { + ipc_client_log(client, "wrote EBL CRC %02x", crc); + } + goto done; + } + + uint32_t crc32 = (crc << 24) | 0xffffff; + if ((ret = write(io_data->boot_fd, &crc32, 4)) != 4) { + ipc_client_log(client, "Error: failed to write CRC"); + goto fail; + } + else { + ipc_client_log(client, "wrote CRC %x", crc); + } + +done: + ret = 0; + +fail: + return ret; +} + +static int i9250_send_psi(struct ipc_client *client, + struct modemctl_io_data *io_data) +{ + int ret = -1; + + if ((ret = write(io_data->boot_fd, I9250_PSI_START_MAGIC, 4)) < 0) { + ipc_client_log(client, "%s: failed to write header, ret %d", __func__, ret); + goto fail; + } + + if ((ret = i9250_send_image(client, io_data, PSI)) < 0) { + ipc_client_log(client, "Error: failed to send PSI image"); + goto fail; + } + + char expected_acks[4][4] = { + "\xff\xff\xff\x01", + "\xff\xff\xff\x01", + "\x02\x00\x00\x00", + "\x01\xdd\x00\x00", + }; + + unsigned i; + for (i = 0; i < ARRAY_SIZE(expected_acks); i++) { + ret = expect_data(io_data->boot_fd, expected_acks[i], 4); + if (ret < 0) { + ipc_client_log(client, "failed to wait for ack %d", i); + goto fail; + } + } + ipc_client_log(client, "received PSI ACK"); + + return 0; + +fail: + return ret; +} + +static int i9250_send_ebl(struct ipc_client *client, + struct modemctl_io_data *io_data) +{ + int ret; + int fd = io_data->boot_fd; + unsigned length = i9250_radio_parts[EBL].length; + + if ((ret = write(fd, "\x04\x00\x00\x00", 4)) != 4) { + ipc_client_log(client, "Error: failed to write length of EBL length ('4') "); + goto fail; + } + + if ((ret = write(fd, &length, sizeof(length))) != sizeof(length)) { + ipc_client_log(client, "Error: failed to write EBL length"); + goto fail; + } + + if ((ret = expect_data(fd, I9250_GENERAL_ACK, 4)) < 0) { + ipc_client_log(client, "Error: failed to wait for EBL length ACK"); + goto fail; + } + + if ((ret = expect_data(fd, I9250_EBL_HDR_ACK_MAGIC, 4)) < 0) { + ipc_client_log(client, "Error: failed to wait for EBL header ACK"); + goto fail; + } + + length++; + if ((ret = write(fd, &length, sizeof(length))) != sizeof(length)) { + ipc_client_log(client, "Error: failed to write EBL length + 1"); + goto fail; + } + + if ((ret = i9250_send_image(client, io_data, EBL)) < 0) { + ipc_client_log(client, "Error: failed to send EBL image"); + goto fail; + } + else { + ipc_client_log(client, "sent EBL image, waiting for ACK"); + } + + if ((ret = expect_data(fd, I9250_GENERAL_ACK, 4)) < 0) { + ipc_client_log(client, "Error: failed to wait for EBL image general ACK"); + goto fail; + } + + if ((ret = expect_data(fd, I9250_EBL_IMG_ACK_MAGIC, 4)) < 0) { + ipc_client_log(client, "Error: failed to wait for EBL image ACK"); + goto fail; + } + else { + ipc_client_log(client, "got EBL ACK"); + } + + return 0; + +fail: + return ret; +} + +static int i9250_boot_cmd(struct ipc_client *client, + struct modemctl_io_data *io_data, enum xmm6260_boot_cmd cmd, + void *data, size_t data_size) +{ + int ret = 0; + char *cmd_data = 0; + if (cmd >= ARRAY_SIZE(i9250_boot_cmd_desc)) { + ipc_client_log(client, "Error: bad command %x\n", cmd); + goto done_or_fail; + } + + unsigned cmd_code = i9250_boot_cmd_desc[cmd].code; + + uint16_t checksum = (data_size & 0xffff) + cmd_code; + unsigned char *ptr = (unsigned char*)data; + size_t i; + for (i = 0; i < data_size; i++) { + checksum += ptr[i]; + } + + DECLARE_BOOT_CMD_HEADER(header, cmd_code, data_size); + DECLARE_BOOT_TAIL_HEADER(tail, checksum); + + size_t tail_size = sizeof(tail); + if (!i9250_boot_cmd_desc[cmd].long_tail) { + tail_size -= 2; + } + + size_t cmd_buffer_size = data_size + sizeof(header) + tail_size; + ipc_client_log(client, "data_size %d [%d] checksum 0x%x", data_size, cmd_buffer_size, checksum); + + cmd_data = (char*)malloc(cmd_buffer_size); + if (!cmd_data) { + ipc_client_log(client, "Error: failed to allocate command buffer"); + ret = -ENOMEM; + goto done_or_fail; + } + memset(cmd_data, 0, cmd_buffer_size); + memcpy(cmd_data, &header, sizeof(header)); + memcpy(cmd_data + sizeof(header), data, data_size); + memcpy(cmd_data + sizeof(header) + data_size, &tail, tail_size); + + if ((ret = write(io_data->boot_fd, cmd_data, cmd_buffer_size)) < 0) { + ipc_client_log(client, "Error: failed to write command to socket"); + goto done_or_fail; + } + + if ((unsigned)ret < cmd_buffer_size) { + ipc_client_log(client, "Error: written %d bytes of %d", ret, cmd_buffer_size); + ret = -EINVAL; + goto done_or_fail; + } + + if (i9250_boot_cmd_desc[cmd].no_ack) { + ipc_client_log(client, "not waiting for ACK"); + goto done_or_fail; + } + + uint32_t ack_length; + if ((ret = expect_read(io_data->boot_fd, &ack_length, 4)) < 0) { + ipc_client_log(client, "Error: failed to receive ack header length"); + goto done_or_fail; + } + + if (ack_length + 4 > cmd_buffer_size) { + free(cmd_data); + cmd_data = NULL; + cmd_data = malloc(ack_length + 4); + if (!cmd_data) { + ipc_client_log(client, "Error: failed to allocate the buffer for ack data"); + goto done_or_fail; + } + } + memset(cmd_data, 0, ack_length); + memcpy(cmd_data, &ack_length, 4); + for (i = 0; i < (ack_length + 3) / 4; i++) { + if ((ret = expect_read(io_data->boot_fd, cmd_data + ((i + 1) << 2), 4)) < 0) { + ipc_client_log(client, "Error: failed to receive ack chunk"); + goto done_or_fail; + } + } + + ipc_client_log(client, "received ack"); + + struct i9250_boot_cmd_header *ack_hdr = (struct i9250_boot_cmd_header*)cmd_data; + struct i9250_boot_tail_header *ack_tail = (struct i9250_boot_tail_header*) + (cmd_data + ack_length + 4 - sizeof(struct i9250_boot_tail_header)); + + ipc_client_log(client, "ack code 0x%x checksum 0x%x", ack_hdr->cmd, ack_tail->checksum); + if (ack_hdr->cmd != header.cmd) { + ipc_client_log(client, "Error: request and ack command codes do not match"); + ret = -1; + goto done_or_fail; + } + + ret = 0; + +done_or_fail: + + if (cmd_data) { + free(cmd_data); + } + + return ret; +} + +static int i9250_boot_info_ack(struct ipc_client *client, + struct modemctl_io_data *io_data) +{ + int ret = -1; + uint32_t boot_info_length; + char *boot_info = 0; + + + if ((ret = expect_read(io_data->boot_fd, &boot_info_length, 4)) < 0) { + ipc_client_log(client, "Error: failed to receive boot info length"); + goto fail; + } + + ipc_client_log(client, "Boot Info length=0x%x", boot_info_length); + + boot_info = (char*)malloc(boot_info_length); + if (!boot_info) { + ipc_client_log(client, "Error: failed to allocate memory for boot info"); + goto fail; + } + + memset(boot_info, 0, boot_info_length); + + size_t boot_chunk = 4; + size_t boot_chunk_count = (boot_info_length + boot_chunk - 1) / boot_chunk; + unsigned i; + for (i = 0; i < boot_chunk_count; i++) { + ret = expect_read(io_data->boot_fd, boot_info + (i * boot_chunk), boot_chunk); + if (ret < 0) { + ipc_client_log(client, "Error: failed to receive Boot Info chunk %i ret=%d", i, ret); + goto fail; + } + } + + ipc_client_log(client, "received Boot Info"); + + ret = i9250_boot_cmd(client, io_data, SetPortConf, boot_info, boot_info_length); + if (ret < 0) { + ipc_client_log(client, "Error: failed to send SetPortConf command"); + goto fail; + } + else { + ipc_client_log(client, "sent SetPortConf command"); + } + + ret = 0; + +fail: + if (boot_info) { + free(boot_info); + } + + return ret; +} + +static int i9250_send_image_data(struct ipc_client *client, + struct modemctl_io_data *io_data, uint32_t addr, + void *data, int data_len) +{ + int ret = 0; + int count = 0; + char *data_p = (char *) data; + + if ((ret = i9250_boot_cmd(client, io_data, ReqFlashSetAddress, &addr, 4)) < 0) { + ipc_client_log(client, "Error: failed to send ReqFlashSetAddress"); + goto fail; + } + else { + ipc_client_log(client, "sent ReqFlashSetAddress"); + } + + while (count < data_len) { + int rest = data_len - count; + int chunk = rest < SEC_DOWNLOAD_CHUNK ? rest : SEC_DOWNLOAD_CHUNK; + + ret = i9250_boot_cmd(client, io_data, ReqFlashWriteBlock, data_p, chunk); + if (ret < 0) { + ipc_client_log(client, "Error: failed to send data chunk"); + goto fail; + } + + data_p += chunk; + count += chunk; + } + + usleep(SEC_DOWNLOAD_DELAY_US); + +fail: + return ret; +} + +static int i9250_send_image_addr(struct ipc_client *client, + struct modemctl_io_data *io_data, uint32_t addr, enum xmm6260_image type) +{ + uint32_t offset = i9250_radio_parts[type].offset; + uint32_t length = i9250_radio_parts[type].length; + char *start = io_data->radio_data + offset; + int ret = 0; + + ret = i9250_send_image_data(client, io_data, addr, start, length); + + return ret; +} + +static int i9250_send_mps_data(struct ipc_client *client, + struct modemctl_io_data *io_data) +{ + int ret = 0; + int mps_fd = -1; + char mps_data[I9250_MPS_LENGTH] = {}; + uint32_t addr = I9250_MPS_LOAD_ADDR; + + mps_fd = open(I9250_MPS_IMAGE_PATH, O_RDONLY); + if (mps_fd < 0) { + ipc_client_log(client, "Error: failed to open MPS data"); + } + else { + read(mps_fd, mps_data, I9250_MPS_LENGTH); + } + + if ((ret = i9250_boot_cmd(client, io_data, ReqFlashSetAddress, &addr, 4)) < 0) { + ipc_client_log(client, "Error: failed to send ReqFlashSetAddress"); + goto fail; + } + else { + ipc_client_log(client, "sent ReqFlashSetAddress"); + } + + if ((ret = i9250_boot_cmd(client, io_data, ReqFlashWriteBlock, + mps_data, I9250_MPS_LENGTH)) < 0) { + ipc_client_log(client, "Error: failed to write MPS data to modem"); + goto fail; + } + + +fail: + if (mps_fd >= 0) { + close(mps_fd); + } + + return ret; +} + +static int i9250_send_image_addrs(struct ipc_client *client, + struct modemctl_io_data *io_data) +{ + int ret = 0; + + uint32_t sec_off = i9250_radio_parts[SECURE_IMAGE].offset; + uint32_t sec_len = i9250_radio_parts[SECURE_IMAGE].length; + void *sec_img = io_data->radio_data + sec_off; + void *nv_data = NULL; + + if ((ret = i9250_boot_cmd(client, io_data, ReqSecStart, sec_img, sec_len)) < 0) { + ipc_client_log(client, "Error: failed to write ReqSecStart"); + goto fail; + } + else { + ipc_client_log(client, "sent ReqSecStart"); + } + + if ((ret = i9250_send_image_addr(client, io_data, FW_LOAD_ADDR, FIRMWARE)) < 0) { + ipc_client_log(client, "Error: failed to send FIRMWARE image"); + goto fail; + } + else { + ipc_client_log(client, "sent FIRMWARE image"); + } + + nv_data_check(client); + nv_data_md5_check(client); + + nv_data = ipc_file_read(client, nv_data_path(client), 2 << 20, 1024); + if (nv_data == NULL) { + ipc_client_log(client, "Error: failed to read NVDATA image"); + goto fail; + } + + if ((ret = i9250_send_image_data(client, io_data, NVDATA_LOAD_ADDR, nv_data, 2 << 20)) < 0) { + ipc_client_log(client, "Error: failed to send NVDATA image"); + goto fail; + } + else { + ipc_client_log(client, "sent NVDATA image"); + } + + free(nv_data); + + if ((ret = i9250_send_mps_data(client, io_data)) < 0) { + ipc_client_log(client, "Error: failed to send MPS data"); + goto fail; + } + else { + ipc_client_log(client, "sent MPS data"); + } + + if ((ret = i9250_boot_cmd(client, io_data, ReqSecEnd, + BL_END_MAGIC, BL_END_MAGIC_LEN)) < 0) + { + ipc_client_log(client, "Error: failed to write ReqSecEnd"); + goto fail; + } + else { + ipc_client_log(client, "sent ReqSecEnd"); + } + + ret = i9250_boot_cmd(client, io_data, ReqForceHwReset, + BL_RESET_MAGIC, BL_RESET_MAGIC_LEN); + if (ret < 0) { + ipc_client_log(client, "Error: failed to write ReqForceHwReset"); + goto fail; + } + else { + ipc_client_log(client, "sent ReqForceHwReset"); + } + +fail: + return ret; +} + +static int i9250_reboot_modem(struct ipc_client *client, + struct modemctl_io_data *io_data, bool hard) +{ + int ret; + + if (!hard) { + return 0; + } + + /* + * Disable the hardware to ensure consistent state + */ + if ((ret = modemctl_modem_power(client, io_data, false)) < 0) { + ipc_client_log(client, "Error: failed to disable modem power"); + goto fail; + } + else { + ipc_client_log(client, "disabled modem power"); + } + + if ((ret = modemctl_modem_boot_power(client, io_data, false)) < 0) { + ipc_client_log(client, "Error: failed to disable modem boot power"); + goto fail; + } + else { + ipc_client_log(client, "disabled modem boot power"); + } + + /* + * Now, initialize the hardware + */ + if ((ret = modemctl_modem_boot_power(client, io_data, true)) < 0) { + ipc_client_log(client, "Error: failed to enable modem boot power"); + goto fail; + } + else { + ipc_client_log(client, "enabled modem boot power"); + } + + if ((ret = modemctl_modem_power(client, io_data, true)) < 0) { + ipc_client_log(client, "Error: failed to enable modem power"); + goto fail; + } + else { + ipc_client_log(client, "enabled modem power"); + } + +fail: + return ret; +} + +int i9250_boot_modem(struct ipc_client *client) +{ + int ret = -1; + struct modemctl_io_data io_data; + memset(&io_data, 0, sizeof(client, io_data)); + + io_data.radio_fd = open(I9250_RADIO_IMAGE, O_RDONLY); + if (io_data.radio_fd < 0) { + ipc_client_log(client, "Error: failed to open radio firmware"); + goto fail; + } + else { + ipc_client_log(client, "opened radio image %s, fd=%d", I9250_RADIO_IMAGE, io_data.radio_fd); + } + + if (fstat(io_data.radio_fd, &io_data.radio_stat) < 0) { + ipc_client_log(client, "Error: failed to stat radio image, error %s", strerror(errno)); + goto fail; + } + + io_data.radio_data = mmap(0, RADIO_MAP_SIZE, PROT_READ, MAP_SHARED, + io_data.radio_fd, 0); + if (io_data.radio_data == MAP_FAILED) { + ipc_client_log(client, "Error: failed to mmap radio image, error %s", strerror(errno)); + goto fail; + } + + io_data.boot_fd = open(BOOT_DEV, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (io_data.boot_fd < 0) { + ipc_client_log(client, "Error: failed to open boot device"); + goto fail; + } + else { + ipc_client_log(client, "opened boot device %s, fd=%d", BOOT_DEV, io_data.boot_fd); + } + + if (i9250_reboot_modem(client, &io_data, true) < 0) { + ipc_client_log(client, "Error: failed to hard reset modem"); + goto fail; + } + else { + ipc_client_log(client, "modem hard reset done"); + } + + /* + * Now, actually load the firmware + */ + int i; + for (i = 0; i < 2; i++) { + if (write(io_data.boot_fd, "ATAT", 4) != 4) { + ipc_client_log(client, "Error: failed to write ATAT to boot socket"); + goto fail; + } + else { + ipc_client_log(client, "written ATAT to boot socket, waiting for ACK"); + } + + if (expect(io_data.boot_fd, 100) < 0) { + ipc_client_log(client, "failed to select before next ACK, ignoring"); + } + } + + //FIXME: make sure it does not timeout or add the retry in the ril library + + if ((ret = expect(io_data.boot_fd, 100)) < 0) { + ipc_client_log(client, "Error: failed to wait for bootloader ready state"); + goto fail; + } + else { + ipc_client_log(client, "ready for PSI upload"); + } + + ret = -ETIMEDOUT; + for (i = 0; i < I9250_BOOT_REPLY_MAX; i++) { + uint32_t id_buf; + if ((ret = expect_read(io_data.boot_fd, (void*)&id_buf, 4)) != 4) { + ipc_client_log(client, "Error: failed receiving bootloader reply"); + goto fail; + } + ipc_client_log(client, "got bootloader reply %08x", id_buf); + if (id_buf == I9250_BOOT_LAST_MARKER) { + ret = 0; + break; + } + } + + if (ret < 0) { + ipc_client_log(client, "Error: bootloader id marker not received"); + goto fail; + } + else { + ipc_client_log(client, "got bootloader id marker"); + } + + if ((ret = i9250_send_psi(client, &io_data)) < 0) { + ipc_client_log(client, "Error: failed to upload PSI"); + goto fail; + } + else { + ipc_client_log(client, "PSI download complete"); + } + + close(io_data.boot_fd); + io_data.boot_fd = open(I9250_SECOND_BOOT_DEV, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (io_data.boot_fd < 0) { + ipc_client_log(client, "Error: failed to open " I9250_SECOND_BOOT_DEV " control device"); + goto fail; + } + else { + ipc_client_log(client, "opened second boot device %s, fd=%d", I9250_SECOND_BOOT_DEV, io_data.boot_fd); + } + + //RpsiCmdLoadAndExecute + if ((ret = write(io_data.boot_fd, I9250_PSI_CMD_EXEC, 4)) < 0) { + ipc_client_log(client, "Error: failed writing cmd_load_exe_EBL"); + goto fail; + } + if ((ret = write(io_data.boot_fd, I9250_PSI_EXEC_DATA, 8)) < 0) { + ipc_client_log(client, "Error: failed writing 8 bytes to boot1"); + goto fail; + } + + if ((ret = expect_data(io_data.boot_fd, I9250_GENERAL_ACK, 4)) < 0) { + ipc_client_log(client, "Error: failed to receive cmd_load_exe_EBL ack"); + goto fail; + } + + if ((ret = expect_data(io_data.boot_fd, I9250_PSI_READY_ACK, 4)) < 0) { + ipc_client_log(client, "Error: failed to receive PSI ready ack"); + goto fail; + } + + if ((ret = i9250_send_ebl(client, &io_data)) < 0) { + ipc_client_log(client, "Error: failed to upload EBL"); + goto fail; + } + else { + ipc_client_log(client, "EBL download complete"); + } + + if ((ret = i9250_boot_info_ack(client, &io_data)) < 0) { + ipc_client_log(client, "Error: failed to receive Boot Info"); + goto fail; + } + else { + ipc_client_log(client, "Boot Info ACK done"); + } + + if ((ret = i9250_send_image_addrs(client, &io_data)) < 0) { + ipc_client_log(client, "Error: failed to upload Secure Image"); + goto fail; + } + else { + ipc_client_log(client, "Secure Image download complete"); + } + + if ((ret = modemctl_wait_modem_online(client, &io_data))) { + ipc_client_log(client, "Error: failed to wait for modem to become online"); + goto fail; + } + + ipc_client_log(client, "Modem is online!"); + ret = 0; + +fail: + if (io_data.radio_data != MAP_FAILED) { + munmap(io_data.radio_data, RADIO_MAP_SIZE); + } + + if (io_data.radio_fd >= 0) { + close(io_data.radio_fd); + } + + if (io_data.boot_fd >= 0) { + close(io_data.boot_fd); + } + + return ret; +} + +// vim:ts=4:sw=4:expandtab |