From 2eea520884474f4eefcf03815d52dfaac9e03cce Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 5 Jul 2012 14:03:19 +0200 Subject: XMM6260: Moved device-specific files to galaxys2 and maguro folders Warning: this commit breaks code compilation! Signed-off-by: Paul Kocialkowski --- samsung-ipc/device/galaxys2/galaxys2_loader.c | 743 ++++++++++++++++++++++++ samsung-ipc/device/galaxys2/galaxys2_loader.h | 75 +++ samsung-ipc/device/maguro/maguro_loader.c | 796 ++++++++++++++++++++++++++ samsung-ipc/device/maguro/maguro_loader.h | 97 ++++ samsung-ipc/device/xmm6260/fwloader_i9100.c | 743 ------------------------ samsung-ipc/device/xmm6260/fwloader_i9100.h | 75 --- samsung-ipc/device/xmm6260/fwloader_i9250.c | 796 -------------------------- samsung-ipc/device/xmm6260/fwloader_i9250.h | 97 ---- samsung-ipc/device/xmm6260/modemctl.c | 170 ++++++ samsung-ipc/device/xmm6260/modemctl.h | 174 ++++++ samsung-ipc/device/xmm6260/modemctl_common.c | 170 ------ samsung-ipc/device/xmm6260/modemctl_common.h | 174 ------ 12 files changed, 2055 insertions(+), 2055 deletions(-) create mode 100644 samsung-ipc/device/galaxys2/galaxys2_loader.c create mode 100644 samsung-ipc/device/galaxys2/galaxys2_loader.h create mode 100644 samsung-ipc/device/maguro/maguro_loader.c create mode 100644 samsung-ipc/device/maguro/maguro_loader.h delete mode 100644 samsung-ipc/device/xmm6260/fwloader_i9100.c delete mode 100644 samsung-ipc/device/xmm6260/fwloader_i9100.h delete mode 100644 samsung-ipc/device/xmm6260/fwloader_i9250.c delete mode 100644 samsung-ipc/device/xmm6260/fwloader_i9250.h create mode 100644 samsung-ipc/device/xmm6260/modemctl.c create mode 100644 samsung-ipc/device/xmm6260/modemctl.h delete mode 100644 samsung-ipc/device/xmm6260/modemctl_common.c delete mode 100644 samsung-ipc/device/xmm6260/modemctl_common.h diff --git a/samsung-ipc/device/galaxys2/galaxys2_loader.c b/samsung-ipc/device/galaxys2/galaxys2_loader.c new file mode 100644 index 0000000..0c6d80d --- /dev/null +++ b/samsung-ipc/device/galaxys2/galaxys2_loader.c @@ -0,0 +1,743 @@ +/* + * Firmware loader for Samsung I9100 and I9250 + * Copyright (C) 2012 Alexander Tarasikov + * + * based on the incomplete C++ implementation which is + * Copyright (C) 2012 Sergey Gridasov + * + * 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 . + */ + +#include "modemctl_common.h" +#include "ipc_private.h" +#include "fwloader_i9100.h" + +/* + * Locations of the firmware components in the Samsung firmware + */ + +struct xmm6260_radio_part i9100_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 i9100_boot_cmd_desc i9100_boot_cmd_desc[] = { + [SetPortConf] = { + .code = 0x86, + .data_size = 0x800, + .need_ack = 1, + }, + [ReqSecStart] = { + .code = 0x204, + .data_size = 0x4000, + .need_ack = 1, + }, + [ReqSecEnd] = { + .code = 0x205, + .data_size = 0x4000, + .need_ack = 1, + }, + [ReqForceHwReset] = { + .code = 0x208, + .data_size = 0x4000, + .need_ack = 0, + }, + [ReqFlashSetAddress] = { + .code = 0x802, + .data_size = 0x4000, + .need_ack = 1, + }, + [ReqFlashWriteBlock] = { + .code = 0x804, + .data_size = 0x4000, + .need_ack = 0, + } +}; + +static int i9100_send_image(struct ipc_client *client, + struct modemctl_io_data *io_data, enum xmm6260_image type) +{ + int ret = -1; + + if (type >= io_data->radio_parts_count) { + ipc_client_log(client, "Error: bad image type %x", type); + goto fail; + } + + size_t length = io_data->radio_parts[type].length; + size_t offset = io_data->radio_parts[type].offset; + + size_t start = offset; + size_t end = length + start; + + //dump some image bytes + ipc_client_log(client, "image start"); +// hexdump(io_data->radio_data + start, length); + + while (start < end) { + ret = write(io_data->boot_fd, io_data->radio_data + start, end - start); + if (ret < 0) { + ipc_client_log(client, "failed to write image chunk"); + goto fail; + } + start += ret; + } + + unsigned char crc = calculateCRC(io_data->radio_data, offset, length); + + if ((ret = write(io_data->boot_fd, &crc, 1)) < 1) { + ipc_client_log(client, "failed to write CRC"); + goto fail; + } + else { + ipc_client_log(client, "wrote CRC %x", crc); + } + + return 0; + +fail: + return ret; +} + +static int i9100_send_psi(struct ipc_client *client, struct modemctl_io_data *io_data) +{ + size_t length = i9100_radio_parts[PSI].length; + + struct i9100_psi_header hdr = { + .magic = XMM_PSI_MAGIC, + .length = length, + .padding = 0xff, + }; + int ret = -1; + + if ((ret = write(io_data->boot_fd, &hdr, sizeof(hdr))) != sizeof(hdr)) { + ipc_client_log(client, "%s: failed to write header, ret %d", __func__, ret); + goto fail; + } + + if ((ret = i9100_send_image(client, io_data, PSI)) < 0) { + ipc_client_log(client, "Error: failed to send PSI image"); + goto fail; + } + + int i; + for (i = 0; i < 22; i++) { + char ack; + if (expect_read(io_data->boot_fd, &ack, 1) < 1) { + ipc_client_log(client, "failed to read ACK byte %d", i); + goto fail; + } + } + + if ((ret = expect_data(io_data->boot_fd, "\x1", 1)) < 0) { + ipc_client_log(client, "failed to wait for first ACK"); + goto fail; + } + + if ((ret = expect_data(io_data->boot_fd, "\x1", 1)) < 0) { + ipc_client_log(client, "failed to wait for second ACK"); + goto fail; + } + + if ((ret = expect_data(io_data->boot_fd, PSI_ACK_MAGIC, 2)) < 0) { + ipc_client_log(client, "Error: failed to receive PSI ACK"); + goto fail; + } + else { + ipc_client_log(client, "received PSI ACK"); + } + + return 0; + +fail: + return ret; +} + +static int i9100_send_ebl(struct ipc_client *client, struct modemctl_io_data *io_data) +{ + int ret; + int fd = io_data->boot_fd; + unsigned length = i9100_radio_parts[EBL].length; + + if ((ret = write(fd, &length, sizeof(length))) < 0) { + ipc_client_log(client, "Error: failed to write EBL length"); + goto fail; + } + + if ((ret = expect_data(fd, EBL_HDR_ACK_MAGIC, 2)) < 0) { + ipc_client_log(client, "Error: failed to wait for EBL header ACK"); + goto fail; + } + + if ((ret = i9100_send_image(client, io_data, EBL)) < 0) { + ipc_client_log(client, "Error: failed to send EBL image"); + goto fail; + } + + if ((ret = expect_data(fd, EBL_IMG_ACK_MAGIC, 2)) < 0) { + ipc_client_log(client, "Error: failed to wait for EBL image ACK"); + goto fail; + } + + return 0; + +fail: + return ret; +} + +static int i9100_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(i9100_boot_cmd_desc)) { + ipc_client_log(client, "Error: bad command %x\n", cmd); + goto done_or_fail; + } + + unsigned cmd_code = i9100_boot_cmd_desc[cmd].code; + + uint16_t magic = (data_size & 0xffff) + cmd_code; + unsigned char *ptr = (unsigned char*)data; + size_t i; + for (i = 0; i < data_size; i++) { + magic += ptr[i]; + } + + struct i9100_boot_cmd header = { + .check = magic, + .cmd = cmd_code, + .data_size = data_size, + }; + + size_t cmd_size = i9100_boot_cmd_desc[cmd].data_size; + size_t buf_size = cmd_size + sizeof(header); + + cmd_data = (char*)malloc(buf_size); + if (!cmd_data) { + ipc_client_log(client, "Error: failed to allocate command buffer"); + ret = -ENOMEM; + goto done_or_fail; + } + memset(cmd_data, 0, buf_size); + memcpy(cmd_data, &header, sizeof(header)); + memcpy(cmd_data + sizeof(header), data, data_size); + + if ((ret = write(io_data->boot_fd, cmd_data, buf_size)) < 0) { + ipc_client_log(client, "Error: failed to write command to socket"); + goto done_or_fail; + } + + if ((unsigned)ret != buf_size) { + ipc_client_log(client, "Error: written %d bytes of %d", ret, buf_size); + ret = -EINVAL; + goto done_or_fail; + } + + if (!i9100_boot_cmd_desc[cmd].need_ack) { + ret = 0; + goto done_or_fail; + } + + struct i9100_boot_cmd ack = { + .check = 0, + }; + if ((ret = expect_read(io_data->boot_fd, &ack, sizeof(ack))) < 0) { + ipc_client_log(client, "Error: failed to receive ack for cmd %x", header.cmd); + goto done_or_fail; + } + + if (ret != sizeof(ack)) { + ipc_client_log(client, "Error: received %x bytes of %x for ack", ret, sizeof(ack)); + ret = -EINVAL; + goto done_or_fail; + } + + if (ack.cmd != header.cmd) { + ipc_client_log(client, "Error: ack cmd %x does not match request %x", ack.cmd, header.cmd); + ret = -EINVAL; + goto done_or_fail; + } + + if ((ret = expect_read(io_data->boot_fd, cmd_data, cmd_size)) < 0) { + ipc_client_log(client, "Error: failed to receive reply data"); + goto done_or_fail; + } + + if ((unsigned)ret != cmd_size) { + ipc_client_log(client, "Error: received %x bytes of %x for reply data", ret, cmd_size); + ret = -EINVAL; + goto done_or_fail; + } + +done_or_fail: + + if (cmd_data) { + free(cmd_data); + } + + return ret; +} + +static int i9100_boot_info_ack(struct ipc_client *client, + struct modemctl_io_data *io_data) +{ + int ret; + struct i9100_boot_info info; + + if ((ret = expect_read(io_data->boot_fd, &info, sizeof(info))) != sizeof(info)) { + ipc_client_log(client, "Error: failed to receive Boot Info ret=%d", ret); + ret = -1; + goto fail; + } + else { + ipc_client_log(client, "received Boot Info"); + } + + if ((ret = i9100_boot_cmd(client, io_data, SetPortConf, &info, sizeof(info))) < 0) { + ipc_client_log(client, "Error: failed to send SetPortConf command"); + goto fail; + } + else { + ipc_client_log(client, "sent SetPortConf command"); + } + + return 0; + +fail: + return ret; +} + +static int i9100_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 = i9100_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 = i9100_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 i9100_send_image_addr(struct ipc_client *client, + struct modemctl_io_data *io_data, uint32_t addr, enum xmm6260_image type) +{ + uint32_t offset = i9100_radio_parts[type].offset; + uint32_t length = i9100_radio_parts[type].length; + char *start = io_data->radio_data + offset; + int ret = 0; + + ret = i9100_send_image_data(client, io_data, addr, start, length); + + return ret; +} + +static int i9100_send_secure_images(struct ipc_client *client, + struct modemctl_io_data *io_data) +{ + int ret = 0; + + uint32_t sec_off = i9100_radio_parts[SECURE_IMAGE].offset; + uint32_t sec_len = i9100_radio_parts[SECURE_IMAGE].length; + void *sec_img = io_data->radio_data + sec_off; + void *nv_data = NULL; + + if ((ret = i9100_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 = i9100_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 = i9100_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 = i9100_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 = i9100_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; +} + +/* + * i9200 (Galaxy S2) board-specific code + */ + +/* + * Power management + */ +static int i9100_ehci_setpower(struct ipc_client *client, bool enabled) { + int ret = -1; + + ipc_client_log(client, "%s: enabled=%d", __func__, enabled); + + int ehci_fd = open(I9100_EHCI_PATH, O_RDWR); + if (ehci_fd < 0) { + ipc_client_log(client, "Error: failed to open EHCI fd"); + ret = -ENODEV; + goto fail; + } + else { + ipc_client_log(client, "opened EHCI %s: fd=%d", I9100_EHCI_PATH, ehci_fd); + } + + ret = write(ehci_fd, enabled ? "1" : "0", 1); + + //must write exactly one byte + if (ret <= 0) { + ipc_client_log(client, "Error: failed to set EHCI power"); + } + else { + ipc_client_log(client, "set EHCI power"); + } + +fail: + if (ehci_fd >= 0) { + close(ehci_fd); + } + + return ret; +} + +static int i9100_reboot_modem(struct ipc_client *client, + struct modemctl_io_data *io_data, bool hard) { + int ret; + + //wait for link to become ready before redetection + if (!hard) { + if ((ret = modemctl_wait_link_ready(client, io_data)) < 0) { + ipc_client_log(client, "Error: failed to wait for link to get ready for redetection"); + goto fail; + } + else { + ipc_client_log(client, "link ready for redetection"); + } + } + + /* + * Disable the hardware to ensure consistent state + */ + if (hard) { + if ((ret = modemctl_modem_power(client, io_data, false)) < 0) { + ipc_client_log(client, "Error: failed to disable xmm6260 power"); + goto fail; + } + else { + ipc_client_log(client, "disabled xmm6260 power"); + } + } + + if ((ret = modemctl_link_set_enabled(client, io_data, false)) < 0) { + ipc_client_log(client, "Error: failed to disable I9100 HSIC link"); + goto fail; + } + else { + ipc_client_log(client, "disabled I9100 HSIC link"); + } + + if ((ret = i9100_ehci_setpower(client, false)) < 0) { + ipc_client_log(client, "Error: failed to disable I9100 EHCI"); + goto fail; + } + else { + ipc_client_log(client, "disabled I9100 EHCI"); + } + + if ((ret = modemctl_link_set_active(client, io_data, false)) < 0) { + ipc_client_log(client, "Error: failed to deactivate I9100 HSIC link"); + goto fail; + } + else { + ipc_client_log(client, "deactivated I9100 HSIC link"); + } + + /* + * Now, initialize the hardware + */ + + if ((ret = modemctl_link_set_enabled(client, io_data, true)) < 0) { + ipc_client_log(client, "Error: failed to enable I9100 HSIC link"); + goto fail; + } + else { + ipc_client_log(client, "enabled I9100 HSIC link"); + } + + if ((ret = i9100_ehci_setpower(client, true)) < 0) { + ipc_client_log(client, "Error: failed to enable I9100 EHCI"); + goto fail; + } + else { + ipc_client_log(client, "enabled I9100 EHCI"); + } + + if ((ret = modemctl_link_set_active(client, io_data, true)) < 0) { + ipc_client_log(client, "Error: failed to activate I9100 HSIC link"); + goto fail; + } + else { + ipc_client_log(client, "activated I9100 HSIC link"); + } + + if (hard) { + if ((ret = modemctl_modem_power(client, io_data, true)) < 0) { + ipc_client_log(client, "Error: failed to enable xmm6260 power"); + goto fail; + } + else { + ipc_client_log(client, "enabled xmm6260 power"); + } + } + + if ((ret = modemctl_wait_link_ready(client, io_data)) < 0) { + ipc_client_log(client, "Error: failed to wait for link to get ready"); + goto fail; + } + else { + ipc_client_log(client, "link ready"); + } + +fail: + return ret; +} + +int i9100_boot_modem(struct ipc_client *client) { + int ret = 0; + struct modemctl_io_data io_data; + memset(&io_data, 0, sizeof(client, io_data)); + + io_data.radio_parts = i9100_radio_parts; + io_data.radio_parts_count = ARRAY_SIZE(i9100_radio_parts); + + io_data.radio_fd = open(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", 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); + } + + io_data.link_fd = open(LINK_PM, O_RDWR); + if (io_data.link_fd < 0) { + ipc_client_log(client, "Error: failed to open link device"); + goto fail; + } + else { + ipc_client_log(client, "opened link device %s, fd=%d", LINK_PM, io_data.link_fd); + } + + if (i9100_reboot_modem(client, &io_data, true)) { + 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 + */ + 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"); + } + + char buf[2]; + if (expect_read(io_data.boot_fd, buf, 1) < 0) { + ipc_client_log(client, "Error: failed to receive bootloader ACK"); + goto fail; + } + if (expect_read(io_data.boot_fd, buf + 1, 1) < 0) { + ipc_client_log(client, "Error: failed to receive chip IP ACK"); + goto fail; + } + ipc_client_log(client, "receive ID: [%02x %02x]", buf[0], buf[1]); + + if ((ret = i9100_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"); + } + + if ((ret = i9100_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 = i9100_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 = i9100_send_secure_images(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"); + } + + usleep(POST_BOOT_TIMEOUT_US); + + if ((ret = i9100_reboot_modem(client, &io_data, false))) { + ipc_client_log(client, "Error: failed to soft reset modem"); + goto fail; + } + else { + ipc_client_log(client, "modem soft reset done"); + } + + 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.link_fd >= 0) { + close(io_data.link_fd); + } + + 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 diff --git a/samsung-ipc/device/galaxys2/galaxys2_loader.h b/samsung-ipc/device/galaxys2/galaxys2_loader.h new file mode 100644 index 0000000..06dadd7 --- /dev/null +++ b/samsung-ipc/device/galaxys2/galaxys2_loader.h @@ -0,0 +1,75 @@ +/* + * Firmware loader for Samsung I9100 and I9250 + * Copyright (C) 2012 Alexander Tarasikov + * + * based on the incomplete C++ implementation which is + * Copyright (C) 2012 Sergey Gridasov + * + * 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 . + */ + +#ifndef __FWLOADER_I9100_IPC_H__ +#define __FWLOADER_I9100_IPC_H__ + +#define RADIO_IMAGE "/dev/block/mmcblk0p8" +#define I9100_EHCI_PATH "/sys/devices/platform/s5p-ehci/ehci_power" + +#define LINK_POLL_DELAY_US (50 * 1000) +#define LINK_TIMEOUT_MS 2000 + +#define XMM_PSI_MAGIC 0x30 +#define PSI_ACK_MAGIC "\x00\xaa" + +#define EBL_HDR_ACK_MAGIC "\xcc\xcc" +#define EBL_IMG_ACK_MAGIC "\x51\xa5" + +#define BL_END_MAGIC "\x00\x00" +#define BL_END_MAGIC_LEN 2 + +#define BL_RESET_MAGIC "\x01\x10\x11\x00" +#define BL_RESET_MAGIC_LEN 4 + +#define SEC_DOWNLOAD_CHUNK 16384 +#define SEC_DOWNLOAD_DELAY_US (500 * 1000) + +#define POST_BOOT_TIMEOUT_US (600 * 1000) + +#define FW_LOAD_ADDR 0x60300000 +#define NVDATA_LOAD_ADDR 0x60e80000 + +struct i9100_boot_cmd_desc { + unsigned code; + size_t data_size; + bool need_ack; +}; + +struct i9100_psi_header { + uint8_t magic; + uint16_t length; + uint8_t padding; +} __attribute__((packed)); + +struct i9100_boot_info { + uint8_t data[76]; +} __attribute__((packed)); + +struct i9100_boot_cmd { + uint16_t check; + uint16_t cmd; + uint32_t data_size; +} __attribute__((packed)); + +#endif + +// vim:ts=4:sw=4:expandtab 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 + * + * based on the incomplete C++ implementation which is + * Copyright (C) 2012 Sergey Gridasov + * + * 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 . + */ + +#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 diff --git a/samsung-ipc/device/maguro/maguro_loader.h b/samsung-ipc/device/maguro/maguro_loader.h new file mode 100644 index 0000000..7fa1782 --- /dev/null +++ b/samsung-ipc/device/maguro/maguro_loader.h @@ -0,0 +1,97 @@ +/* + * Firmware loader for Samsung I9100 and I9250 + * Copyright (C) 2012 Alexander Tarasikov + * + * based on the incomplete C++ implementation which is + * Copyright (C) 2012 Sergey Gridasov + * + * 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 . + */ + +#ifndef __FWLOADER_I9250_IPC_H__ +#define __FWLOADER_I9250_IPC_H__ + +#define I9250_RADIO_IMAGE "/dev/block/platform/omap/omap_hsmmc.0/by-name/radio" +#define I9250_SECOND_BOOT_DEV "/dev/umts_boot1" + +#define I9250_BOOT_LAST_MARKER 0x0030ffff +#define I9250_BOOT_REPLY_MAX 20 + +#define I9250_GENERAL_ACK "\x02\x00\x00\x00" + +#define I9250_PSI_START_MAGIC "\xff\xf0\x00\x30" +#define I9250_PSI_CMD_EXEC "\x08\x00\x00\x00" +#define I9250_PSI_EXEC_DATA "\x00\x00\x00\x00\x02\x00\x02\x00" +#define I9250_PSI_READY_ACK "\x00\xaa\x00\x00" + +#define I9250_EBL_IMG_ACK_MAGIC "\x51\xa5\x00\x00" +#define I9250_EBL_HDR_ACK_MAGIC "\xcc\xcc\x00\x00" + +#define I9250_MPS_IMAGE_PATH "/factory/imei/mps_code.dat" +#define I9250_MPS_LOAD_ADDR 0x61080000 +#define I9250_MPS_LENGTH 3 + +#define SEC_DOWNLOAD_CHUNK 0xdfc2 +#define SEC_DOWNLOAD_DELAY_US (500 * 1000) + + #define FW_LOAD_ADDR 0x60300000 +#define NVDATA_LOAD_ADDR 0x60e80000 + +#define BL_END_MAGIC "\x00\x00" +#define BL_END_MAGIC_LEN 2 + +#define BL_RESET_MAGIC "\x01\x10\x11\x00" +#define BL_RESET_MAGIC_LEN 4 + +/* + * on I9250, all commands need ACK and we do not need to + * allocate a fixed size buffer + */ +struct i9250_boot_cmd_desc { + unsigned code; + bool long_tail; + bool no_ack; +}; + +struct i9250_boot_cmd_header { + uint32_t total_size; + uint16_t hdr_magic; + uint16_t cmd; + uint16_t data_size; +} __attribute__((packed)); + +#define DECLARE_BOOT_CMD_HEADER(name, code, size) \ +struct i9250_boot_cmd_header name = {\ + .total_size = size + 10,\ + .hdr_magic = 2,\ + .cmd = code,\ + .data_size = size,\ +} + +struct i9250_boot_tail_header { + uint16_t checksum; + uint16_t tail_magic; + uint8_t unknown[2]; +} __attribute__((packed)); + +#define DECLARE_BOOT_TAIL_HEADER(name, checksum) \ +struct i9250_boot_tail_header name = {\ + .checksum = checksum,\ + .tail_magic = 3,\ + .unknown = "\xea\xea",\ +} + +#endif + +// vim:ts=4:sw=4:expandtab diff --git a/samsung-ipc/device/xmm6260/fwloader_i9100.c b/samsung-ipc/device/xmm6260/fwloader_i9100.c deleted file mode 100644 index 0c6d80d..0000000 --- a/samsung-ipc/device/xmm6260/fwloader_i9100.c +++ /dev/null @@ -1,743 +0,0 @@ -/* - * Firmware loader for Samsung I9100 and I9250 - * Copyright (C) 2012 Alexander Tarasikov - * - * based on the incomplete C++ implementation which is - * Copyright (C) 2012 Sergey Gridasov - * - * 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 . - */ - -#include "modemctl_common.h" -#include "ipc_private.h" -#include "fwloader_i9100.h" - -/* - * Locations of the firmware components in the Samsung firmware - */ - -struct xmm6260_radio_part i9100_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 i9100_boot_cmd_desc i9100_boot_cmd_desc[] = { - [SetPortConf] = { - .code = 0x86, - .data_size = 0x800, - .need_ack = 1, - }, - [ReqSecStart] = { - .code = 0x204, - .data_size = 0x4000, - .need_ack = 1, - }, - [ReqSecEnd] = { - .code = 0x205, - .data_size = 0x4000, - .need_ack = 1, - }, - [ReqForceHwReset] = { - .code = 0x208, - .data_size = 0x4000, - .need_ack = 0, - }, - [ReqFlashSetAddress] = { - .code = 0x802, - .data_size = 0x4000, - .need_ack = 1, - }, - [ReqFlashWriteBlock] = { - .code = 0x804, - .data_size = 0x4000, - .need_ack = 0, - } -}; - -static int i9100_send_image(struct ipc_client *client, - struct modemctl_io_data *io_data, enum xmm6260_image type) -{ - int ret = -1; - - if (type >= io_data->radio_parts_count) { - ipc_client_log(client, "Error: bad image type %x", type); - goto fail; - } - - size_t length = io_data->radio_parts[type].length; - size_t offset = io_data->radio_parts[type].offset; - - size_t start = offset; - size_t end = length + start; - - //dump some image bytes - ipc_client_log(client, "image start"); -// hexdump(io_data->radio_data + start, length); - - while (start < end) { - ret = write(io_data->boot_fd, io_data->radio_data + start, end - start); - if (ret < 0) { - ipc_client_log(client, "failed to write image chunk"); - goto fail; - } - start += ret; - } - - unsigned char crc = calculateCRC(io_data->radio_data, offset, length); - - if ((ret = write(io_data->boot_fd, &crc, 1)) < 1) { - ipc_client_log(client, "failed to write CRC"); - goto fail; - } - else { - ipc_client_log(client, "wrote CRC %x", crc); - } - - return 0; - -fail: - return ret; -} - -static int i9100_send_psi(struct ipc_client *client, struct modemctl_io_data *io_data) -{ - size_t length = i9100_radio_parts[PSI].length; - - struct i9100_psi_header hdr = { - .magic = XMM_PSI_MAGIC, - .length = length, - .padding = 0xff, - }; - int ret = -1; - - if ((ret = write(io_data->boot_fd, &hdr, sizeof(hdr))) != sizeof(hdr)) { - ipc_client_log(client, "%s: failed to write header, ret %d", __func__, ret); - goto fail; - } - - if ((ret = i9100_send_image(client, io_data, PSI)) < 0) { - ipc_client_log(client, "Error: failed to send PSI image"); - goto fail; - } - - int i; - for (i = 0; i < 22; i++) { - char ack; - if (expect_read(io_data->boot_fd, &ack, 1) < 1) { - ipc_client_log(client, "failed to read ACK byte %d", i); - goto fail; - } - } - - if ((ret = expect_data(io_data->boot_fd, "\x1", 1)) < 0) { - ipc_client_log(client, "failed to wait for first ACK"); - goto fail; - } - - if ((ret = expect_data(io_data->boot_fd, "\x1", 1)) < 0) { - ipc_client_log(client, "failed to wait for second ACK"); - goto fail; - } - - if ((ret = expect_data(io_data->boot_fd, PSI_ACK_MAGIC, 2)) < 0) { - ipc_client_log(client, "Error: failed to receive PSI ACK"); - goto fail; - } - else { - ipc_client_log(client, "received PSI ACK"); - } - - return 0; - -fail: - return ret; -} - -static int i9100_send_ebl(struct ipc_client *client, struct modemctl_io_data *io_data) -{ - int ret; - int fd = io_data->boot_fd; - unsigned length = i9100_radio_parts[EBL].length; - - if ((ret = write(fd, &length, sizeof(length))) < 0) { - ipc_client_log(client, "Error: failed to write EBL length"); - goto fail; - } - - if ((ret = expect_data(fd, EBL_HDR_ACK_MAGIC, 2)) < 0) { - ipc_client_log(client, "Error: failed to wait for EBL header ACK"); - goto fail; - } - - if ((ret = i9100_send_image(client, io_data, EBL)) < 0) { - ipc_client_log(client, "Error: failed to send EBL image"); - goto fail; - } - - if ((ret = expect_data(fd, EBL_IMG_ACK_MAGIC, 2)) < 0) { - ipc_client_log(client, "Error: failed to wait for EBL image ACK"); - goto fail; - } - - return 0; - -fail: - return ret; -} - -static int i9100_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(i9100_boot_cmd_desc)) { - ipc_client_log(client, "Error: bad command %x\n", cmd); - goto done_or_fail; - } - - unsigned cmd_code = i9100_boot_cmd_desc[cmd].code; - - uint16_t magic = (data_size & 0xffff) + cmd_code; - unsigned char *ptr = (unsigned char*)data; - size_t i; - for (i = 0; i < data_size; i++) { - magic += ptr[i]; - } - - struct i9100_boot_cmd header = { - .check = magic, - .cmd = cmd_code, - .data_size = data_size, - }; - - size_t cmd_size = i9100_boot_cmd_desc[cmd].data_size; - size_t buf_size = cmd_size + sizeof(header); - - cmd_data = (char*)malloc(buf_size); - if (!cmd_data) { - ipc_client_log(client, "Error: failed to allocate command buffer"); - ret = -ENOMEM; - goto done_or_fail; - } - memset(cmd_data, 0, buf_size); - memcpy(cmd_data, &header, sizeof(header)); - memcpy(cmd_data + sizeof(header), data, data_size); - - if ((ret = write(io_data->boot_fd, cmd_data, buf_size)) < 0) { - ipc_client_log(client, "Error: failed to write command to socket"); - goto done_or_fail; - } - - if ((unsigned)ret != buf_size) { - ipc_client_log(client, "Error: written %d bytes of %d", ret, buf_size); - ret = -EINVAL; - goto done_or_fail; - } - - if (!i9100_boot_cmd_desc[cmd].need_ack) { - ret = 0; - goto done_or_fail; - } - - struct i9100_boot_cmd ack = { - .check = 0, - }; - if ((ret = expect_read(io_data->boot_fd, &ack, sizeof(ack))) < 0) { - ipc_client_log(client, "Error: failed to receive ack for cmd %x", header.cmd); - goto done_or_fail; - } - - if (ret != sizeof(ack)) { - ipc_client_log(client, "Error: received %x bytes of %x for ack", ret, sizeof(ack)); - ret = -EINVAL; - goto done_or_fail; - } - - if (ack.cmd != header.cmd) { - ipc_client_log(client, "Error: ack cmd %x does not match request %x", ack.cmd, header.cmd); - ret = -EINVAL; - goto done_or_fail; - } - - if ((ret = expect_read(io_data->boot_fd, cmd_data, cmd_size)) < 0) { - ipc_client_log(client, "Error: failed to receive reply data"); - goto done_or_fail; - } - - if ((unsigned)ret != cmd_size) { - ipc_client_log(client, "Error: received %x bytes of %x for reply data", ret, cmd_size); - ret = -EINVAL; - goto done_or_fail; - } - -done_or_fail: - - if (cmd_data) { - free(cmd_data); - } - - return ret; -} - -static int i9100_boot_info_ack(struct ipc_client *client, - struct modemctl_io_data *io_data) -{ - int ret; - struct i9100_boot_info info; - - if ((ret = expect_read(io_data->boot_fd, &info, sizeof(info))) != sizeof(info)) { - ipc_client_log(client, "Error: failed to receive Boot Info ret=%d", ret); - ret = -1; - goto fail; - } - else { - ipc_client_log(client, "received Boot Info"); - } - - if ((ret = i9100_boot_cmd(client, io_data, SetPortConf, &info, sizeof(info))) < 0) { - ipc_client_log(client, "Error: failed to send SetPortConf command"); - goto fail; - } - else { - ipc_client_log(client, "sent SetPortConf command"); - } - - return 0; - -fail: - return ret; -} - -static int i9100_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 = i9100_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 = i9100_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 i9100_send_image_addr(struct ipc_client *client, - struct modemctl_io_data *io_data, uint32_t addr, enum xmm6260_image type) -{ - uint32_t offset = i9100_radio_parts[type].offset; - uint32_t length = i9100_radio_parts[type].length; - char *start = io_data->radio_data + offset; - int ret = 0; - - ret = i9100_send_image_data(client, io_data, addr, start, length); - - return ret; -} - -static int i9100_send_secure_images(struct ipc_client *client, - struct modemctl_io_data *io_data) -{ - int ret = 0; - - uint32_t sec_off = i9100_radio_parts[SECURE_IMAGE].offset; - uint32_t sec_len = i9100_radio_parts[SECURE_IMAGE].length; - void *sec_img = io_data->radio_data + sec_off; - void *nv_data = NULL; - - if ((ret = i9100_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 = i9100_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 = i9100_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 = i9100_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 = i9100_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; -} - -/* - * i9200 (Galaxy S2) board-specific code - */ - -/* - * Power management - */ -static int i9100_ehci_setpower(struct ipc_client *client, bool enabled) { - int ret = -1; - - ipc_client_log(client, "%s: enabled=%d", __func__, enabled); - - int ehci_fd = open(I9100_EHCI_PATH, O_RDWR); - if (ehci_fd < 0) { - ipc_client_log(client, "Error: failed to open EHCI fd"); - ret = -ENODEV; - goto fail; - } - else { - ipc_client_log(client, "opened EHCI %s: fd=%d", I9100_EHCI_PATH, ehci_fd); - } - - ret = write(ehci_fd, enabled ? "1" : "0", 1); - - //must write exactly one byte - if (ret <= 0) { - ipc_client_log(client, "Error: failed to set EHCI power"); - } - else { - ipc_client_log(client, "set EHCI power"); - } - -fail: - if (ehci_fd >= 0) { - close(ehci_fd); - } - - return ret; -} - -static int i9100_reboot_modem(struct ipc_client *client, - struct modemctl_io_data *io_data, bool hard) { - int ret; - - //wait for link to become ready before redetection - if (!hard) { - if ((ret = modemctl_wait_link_ready(client, io_data)) < 0) { - ipc_client_log(client, "Error: failed to wait for link to get ready for redetection"); - goto fail; - } - else { - ipc_client_log(client, "link ready for redetection"); - } - } - - /* - * Disable the hardware to ensure consistent state - */ - if (hard) { - if ((ret = modemctl_modem_power(client, io_data, false)) < 0) { - ipc_client_log(client, "Error: failed to disable xmm6260 power"); - goto fail; - } - else { - ipc_client_log(client, "disabled xmm6260 power"); - } - } - - if ((ret = modemctl_link_set_enabled(client, io_data, false)) < 0) { - ipc_client_log(client, "Error: failed to disable I9100 HSIC link"); - goto fail; - } - else { - ipc_client_log(client, "disabled I9100 HSIC link"); - } - - if ((ret = i9100_ehci_setpower(client, false)) < 0) { - ipc_client_log(client, "Error: failed to disable I9100 EHCI"); - goto fail; - } - else { - ipc_client_log(client, "disabled I9100 EHCI"); - } - - if ((ret = modemctl_link_set_active(client, io_data, false)) < 0) { - ipc_client_log(client, "Error: failed to deactivate I9100 HSIC link"); - goto fail; - } - else { - ipc_client_log(client, "deactivated I9100 HSIC link"); - } - - /* - * Now, initialize the hardware - */ - - if ((ret = modemctl_link_set_enabled(client, io_data, true)) < 0) { - ipc_client_log(client, "Error: failed to enable I9100 HSIC link"); - goto fail; - } - else { - ipc_client_log(client, "enabled I9100 HSIC link"); - } - - if ((ret = i9100_ehci_setpower(client, true)) < 0) { - ipc_client_log(client, "Error: failed to enable I9100 EHCI"); - goto fail; - } - else { - ipc_client_log(client, "enabled I9100 EHCI"); - } - - if ((ret = modemctl_link_set_active(client, io_data, true)) < 0) { - ipc_client_log(client, "Error: failed to activate I9100 HSIC link"); - goto fail; - } - else { - ipc_client_log(client, "activated I9100 HSIC link"); - } - - if (hard) { - if ((ret = modemctl_modem_power(client, io_data, true)) < 0) { - ipc_client_log(client, "Error: failed to enable xmm6260 power"); - goto fail; - } - else { - ipc_client_log(client, "enabled xmm6260 power"); - } - } - - if ((ret = modemctl_wait_link_ready(client, io_data)) < 0) { - ipc_client_log(client, "Error: failed to wait for link to get ready"); - goto fail; - } - else { - ipc_client_log(client, "link ready"); - } - -fail: - return ret; -} - -int i9100_boot_modem(struct ipc_client *client) { - int ret = 0; - struct modemctl_io_data io_data; - memset(&io_data, 0, sizeof(client, io_data)); - - io_data.radio_parts = i9100_radio_parts; - io_data.radio_parts_count = ARRAY_SIZE(i9100_radio_parts); - - io_data.radio_fd = open(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", 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); - } - - io_data.link_fd = open(LINK_PM, O_RDWR); - if (io_data.link_fd < 0) { - ipc_client_log(client, "Error: failed to open link device"); - goto fail; - } - else { - ipc_client_log(client, "opened link device %s, fd=%d", LINK_PM, io_data.link_fd); - } - - if (i9100_reboot_modem(client, &io_data, true)) { - 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 - */ - 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"); - } - - char buf[2]; - if (expect_read(io_data.boot_fd, buf, 1) < 0) { - ipc_client_log(client, "Error: failed to receive bootloader ACK"); - goto fail; - } - if (expect_read(io_data.boot_fd, buf + 1, 1) < 0) { - ipc_client_log(client, "Error: failed to receive chip IP ACK"); - goto fail; - } - ipc_client_log(client, "receive ID: [%02x %02x]", buf[0], buf[1]); - - if ((ret = i9100_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"); - } - - if ((ret = i9100_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 = i9100_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 = i9100_send_secure_images(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"); - } - - usleep(POST_BOOT_TIMEOUT_US); - - if ((ret = i9100_reboot_modem(client, &io_data, false))) { - ipc_client_log(client, "Error: failed to soft reset modem"); - goto fail; - } - else { - ipc_client_log(client, "modem soft reset done"); - } - - 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.link_fd >= 0) { - close(io_data.link_fd); - } - - 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 diff --git a/samsung-ipc/device/xmm6260/fwloader_i9100.h b/samsung-ipc/device/xmm6260/fwloader_i9100.h deleted file mode 100644 index 06dadd7..0000000 --- a/samsung-ipc/device/xmm6260/fwloader_i9100.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Firmware loader for Samsung I9100 and I9250 - * Copyright (C) 2012 Alexander Tarasikov - * - * based on the incomplete C++ implementation which is - * Copyright (C) 2012 Sergey Gridasov - * - * 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 . - */ - -#ifndef __FWLOADER_I9100_IPC_H__ -#define __FWLOADER_I9100_IPC_H__ - -#define RADIO_IMAGE "/dev/block/mmcblk0p8" -#define I9100_EHCI_PATH "/sys/devices/platform/s5p-ehci/ehci_power" - -#define LINK_POLL_DELAY_US (50 * 1000) -#define LINK_TIMEOUT_MS 2000 - -#define XMM_PSI_MAGIC 0x30 -#define PSI_ACK_MAGIC "\x00\xaa" - -#define EBL_HDR_ACK_MAGIC "\xcc\xcc" -#define EBL_IMG_ACK_MAGIC "\x51\xa5" - -#define BL_END_MAGIC "\x00\x00" -#define BL_END_MAGIC_LEN 2 - -#define BL_RESET_MAGIC "\x01\x10\x11\x00" -#define BL_RESET_MAGIC_LEN 4 - -#define SEC_DOWNLOAD_CHUNK 16384 -#define SEC_DOWNLOAD_DELAY_US (500 * 1000) - -#define POST_BOOT_TIMEOUT_US (600 * 1000) - -#define FW_LOAD_ADDR 0x60300000 -#define NVDATA_LOAD_ADDR 0x60e80000 - -struct i9100_boot_cmd_desc { - unsigned code; - size_t data_size; - bool need_ack; -}; - -struct i9100_psi_header { - uint8_t magic; - uint16_t length; - uint8_t padding; -} __attribute__((packed)); - -struct i9100_boot_info { - uint8_t data[76]; -} __attribute__((packed)); - -struct i9100_boot_cmd { - uint16_t check; - uint16_t cmd; - uint32_t data_size; -} __attribute__((packed)); - -#endif - -// vim:ts=4:sw=4:expandtab diff --git a/samsung-ipc/device/xmm6260/fwloader_i9250.c b/samsung-ipc/device/xmm6260/fwloader_i9250.c deleted file mode 100644 index 3ea973f..0000000 --- a/samsung-ipc/device/xmm6260/fwloader_i9250.c +++ /dev/null @@ -1,796 +0,0 @@ -/* - * Firmware loader for Samsung I9100 and I9250 - * Copyright (C) 2012 Alexander Tarasikov - * - * based on the incomplete C++ implementation which is - * Copyright (C) 2012 Sergey Gridasov - * - * 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 . - */ - -#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 diff --git a/samsung-ipc/device/xmm6260/fwloader_i9250.h b/samsung-ipc/device/xmm6260/fwloader_i9250.h deleted file mode 100644 index 7fa1782..0000000 --- a/samsung-ipc/device/xmm6260/fwloader_i9250.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Firmware loader for Samsung I9100 and I9250 - * Copyright (C) 2012 Alexander Tarasikov - * - * based on the incomplete C++ implementation which is - * Copyright (C) 2012 Sergey Gridasov - * - * 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 . - */ - -#ifndef __FWLOADER_I9250_IPC_H__ -#define __FWLOADER_I9250_IPC_H__ - -#define I9250_RADIO_IMAGE "/dev/block/platform/omap/omap_hsmmc.0/by-name/radio" -#define I9250_SECOND_BOOT_DEV "/dev/umts_boot1" - -#define I9250_BOOT_LAST_MARKER 0x0030ffff -#define I9250_BOOT_REPLY_MAX 20 - -#define I9250_GENERAL_ACK "\x02\x00\x00\x00" - -#define I9250_PSI_START_MAGIC "\xff\xf0\x00\x30" -#define I9250_PSI_CMD_EXEC "\x08\x00\x00\x00" -#define I9250_PSI_EXEC_DATA "\x00\x00\x00\x00\x02\x00\x02\x00" -#define I9250_PSI_READY_ACK "\x00\xaa\x00\x00" - -#define I9250_EBL_IMG_ACK_MAGIC "\x51\xa5\x00\x00" -#define I9250_EBL_HDR_ACK_MAGIC "\xcc\xcc\x00\x00" - -#define I9250_MPS_IMAGE_PATH "/factory/imei/mps_code.dat" -#define I9250_MPS_LOAD_ADDR 0x61080000 -#define I9250_MPS_LENGTH 3 - -#define SEC_DOWNLOAD_CHUNK 0xdfc2 -#define SEC_DOWNLOAD_DELAY_US (500 * 1000) - - #define FW_LOAD_ADDR 0x60300000 -#define NVDATA_LOAD_ADDR 0x60e80000 - -#define BL_END_MAGIC "\x00\x00" -#define BL_END_MAGIC_LEN 2 - -#define BL_RESET_MAGIC "\x01\x10\x11\x00" -#define BL_RESET_MAGIC_LEN 4 - -/* - * on I9250, all commands need ACK and we do not need to - * allocate a fixed size buffer - */ -struct i9250_boot_cmd_desc { - unsigned code; - bool long_tail; - bool no_ack; -}; - -struct i9250_boot_cmd_header { - uint32_t total_size; - uint16_t hdr_magic; - uint16_t cmd; - uint16_t data_size; -} __attribute__((packed)); - -#define DECLARE_BOOT_CMD_HEADER(name, code, size) \ -struct i9250_boot_cmd_header name = {\ - .total_size = size + 10,\ - .hdr_magic = 2,\ - .cmd = code,\ - .data_size = size,\ -} - -struct i9250_boot_tail_header { - uint16_t checksum; - uint16_t tail_magic; - uint8_t unknown[2]; -} __attribute__((packed)); - -#define DECLARE_BOOT_TAIL_HEADER(name, checksum) \ -struct i9250_boot_tail_header name = {\ - .checksum = checksum,\ - .tail_magic = 3,\ - .unknown = "\xea\xea",\ -} - -#endif - -// vim:ts=4:sw=4:expandtab diff --git a/samsung-ipc/device/xmm6260/modemctl.c b/samsung-ipc/device/xmm6260/modemctl.c new file mode 100644 index 0000000..489f9fe --- /dev/null +++ b/samsung-ipc/device/xmm6260/modemctl.c @@ -0,0 +1,170 @@ +/* + * Firmware loader for Samsung I9100 and I9250 + * Copyright (C) 2012 Alexander Tarasikov + * + * based on the incomplete C++ implementation which is + * Copyright (C) 2012 Sergey Gridasov + * + * 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 . + */ + +//modemctl shared code +#include "modemctl_common.h" + +/* + * modemctl generic functions + */ +int modemctl_link_set_active(struct ipc_client *client, + struct modemctl_io_data *io_data, bool enabled) { + unsigned status = enabled; + int ret; + unsigned long ioctl_code; + + ioctl_code = IOCTL_LINK_CONTROL_ACTIVE; + ret = ioctl(io_data->link_fd, ioctl_code, &status); + + if (ret < 0) { + ipc_client_log(client, "failed to set link active to %d", enabled); + goto fail; + } + + return 0; +fail: + return ret; +} + +int modemctl_link_set_enabled(struct ipc_client *client, + struct modemctl_io_data *io_data, bool enabled) { + unsigned status = enabled; + int ret; + unsigned long ioctl_code; + + ioctl_code = IOCTL_LINK_CONTROL_ENABLE; + ret = ioctl(io_data->link_fd, ioctl_code, &status); + + if (ret < 0) { + ipc_client_log(client, "failed to set link state to %d", enabled); + goto fail; + } + + return 0; +fail: + return ret; +} + +int modemctl_wait_link_ready(struct ipc_client *client, + struct modemctl_io_data *io_data) { + int ret; + + struct timeval tv_start = {}; + struct timeval tv_end = {}; + + gettimeofday(&tv_start, 0);; + + //link wakeup timeout in milliseconds + long diff = 0; + + do { + ret = ioctl(io_data->link_fd, IOCTL_LINK_CONNECTED, 0); + if (ret < 0) { + goto fail; + } + + if (ret == 1) { + return 0; + } + + usleep(LINK_POLL_DELAY_US); + gettimeofday(&tv_end, 0);; + + diff = (tv_end.tv_sec - tv_start.tv_sec) * 1000; + diff += (tv_end.tv_usec - tv_start.tv_usec) / 1000; + } while (diff < LINK_TIMEOUT_MS); + + ret = -ETIMEDOUT; + +fail: + return ret; +} + +int modemctl_wait_modem_online(struct ipc_client *client, + struct modemctl_io_data *io_data) { + int ret; + + struct timeval tv_start = {}; + struct timeval tv_end = {}; + + gettimeofday(&tv_start, 0);; + + //link wakeup timeout in milliseconds + long diff = 0; + + do { + ret = ioctl(io_data->boot_fd, IOCTL_MODEM_STATUS, 0); + if (ret < 0) { + goto fail; + } + + if (ret == STATE_ONLINE) { + return 0; + } + + usleep(LINK_POLL_DELAY_US); + gettimeofday(&tv_end, 0);; + + diff = (tv_end.tv_sec - tv_start.tv_sec) * 1000; + diff += (tv_end.tv_usec - tv_start.tv_usec) / 1000; + } while (diff < LINK_TIMEOUT_MS); + + ret = -ETIMEDOUT; + +fail: + return ret; +} + +int modemctl_modem_power(struct ipc_client *client, + struct modemctl_io_data *io_data, bool enabled) { + if (enabled) { + return ioctl(io_data->boot_fd, IOCTL_MODEM_ON, 0); + } + else { + return ioctl(io_data->boot_fd, IOCTL_MODEM_OFF, 0); + } + return -1; +} + +int modemctl_modem_boot_power(struct ipc_client *client, + struct modemctl_io_data *io_data, bool enabled) { + if (enabled) { + return ioctl(io_data->boot_fd, IOCTL_MODEM_BOOT_ON, 0); + } + else { + return ioctl(io_data->boot_fd, IOCTL_MODEM_BOOT_OFF, 0); + } + return -1; +} + +unsigned char calculateCRC(void* data, size_t offset, size_t length) +{ + unsigned char crc = 0; + unsigned char *ptr = (unsigned char*)(data + offset); + + while (length--) { + crc ^= *ptr++; + } + + return crc; +} + +// vim:ts=4:sw=4:expandtab diff --git a/samsung-ipc/device/xmm6260/modemctl.h b/samsung-ipc/device/xmm6260/modemctl.h new file mode 100644 index 0000000..87c9555 --- /dev/null +++ b/samsung-ipc/device/xmm6260/modemctl.h @@ -0,0 +1,174 @@ +/* + * Firmware loader for Samsung I9100 and I9250 + * Copyright (C) 2012 Alexander Tarasikov + * + * based on the incomplete C++ implementation which is + * Copyright (C) 2012 Sergey Gridasov + * + * 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 . + */ + +#ifndef __MODEMCTL_COMMON_H__ +#define __MODEMCTL_COMMON_H__ + +#include + +#include "common.h" +#include "io_helpers.h" + +//Samsung IOCTLs +#include "modem_prj.h" + +#define MODEM_DEVICE(x) ("/dev/" #x) +#define LINK_PM MODEM_DEVICE(link_pm) +#define MODEM_DEV MODEM_DEVICE(modem_br) +#define BOOT_DEV MODEM_DEVICE(umts_boot0) +#define IPC_DEV MODEM_DEVICE(umts_ipc0) +#define RFS_DEV MODEM_DEVICE(umts_rfs0) + +#define LINK_POLL_DELAY_US (50 * 1000) +#define LINK_TIMEOUT_MS 2000 + +#define RADIO_MAP_SIZE (16 << 20) + +struct xmm6260_radio_part { + size_t offset; + size_t length; +}; + +/* + * Components of the Samsung XMM6260 firmware + */ +enum xmm6260_image { + PSI, + EBL, + SECURE_IMAGE, + FIRMWARE, + NVDATA, +}; + +/* + * Bootloader control interface definitions + */ + +enum xmm6260_boot_cmd { + SetPortConf, + + ReqSecStart, + ReqSecEnd, + ReqForceHwReset, + + ReqFlashSetAddress, + ReqFlashWriteBlock, +}; + +struct modemctl_io_data { + int link_fd; + int boot_fd; + + int radio_fd; + char *radio_data; + struct stat radio_stat; + + struct xmm6260_radio_part *radio_parts; + int radio_parts_count; +}; + +/* + * Function prototypes + */ + +/* + * @brief Activates the modem <-> cpu link data transfer + * + * @param client [in] ipc client + * @param io_data [in] modemctl-specific data + * @param enabled [in] whether to enable or disable link data transport + * @return Negative value indicating error code + * @return ioctl call result + */ +int modemctl_link_set_active(struct ipc_client *client, + struct modemctl_io_data *io_data, bool enabled); + +/* + * @brief Activates the modem <-> cpu link connection + * + * @param client [in] ipc client + * @param io_data [in] modemctl-specific data + * @param enabled [in] the state to set link to + * @return Negative value indicating error code + * @return ioctl call result + */ +int modemctl_link_set_enabled(struct ipc_client *client, + struct modemctl_io_data *io_data, bool enabled); + +/* + * @brief Poll the link until it gets ready or times out + * + * @param client [in] ipc client + * @param io_data [in] modemctl-specific data + * @return Negative value indicating error code + * @return ioctl call result + */ +int modemctl_wait_link_ready(struct ipc_client *client, + struct modemctl_io_data *io_data); + +/* + * @brief Poll the modem until it gets online or times out + * + * @param client [in] ipc client + * @param io_data [in] modemctl-specific data + * @return Negative value indicating error code + * @return ioctl call result + */ +int modemctl_wait_modem_online(struct ipc_client *client, + struct modemctl_io_data *io_data); + +/* + * @brief Sets the modem power + * + * @param client [in] ipc client + * @param io_data [in] modemctl-specific data + * @param enabled [in] whether to enable or disable modem power + * @return Negative value indicating error code + * @return ioctl call result + */ +int modemctl_modem_power(struct ipc_client *client, + struct modemctl_io_data *io_data, bool enabled); + +/* + * @brief Sets the modem bootloader power/UART configuration + * + * @param client [in] ipc client + * @param io_data [in] modemctl-specific data + * @param enabled [in] whether to enable or disable power + * @return Negative value indicating error code + * @return ioctl call result + */ +int modemctl_modem_boot_power(struct ipc_client *client, + struct modemctl_io_data *io_data, bool enabled); + +/* + * @brief Calculate the checksum for the XMM6260 bootloader protocol + * + * @param data [in] the data to calculate the checksum for + * @param offset [in] number of bytes to skip + * @param length [in] length of data in bytes + * @return checksum value + */ +unsigned char calculateCRC(void* data, size_t offset, size_t length); + +#endif //__MODEMCTL_COMMON_H__ + +// vim:ts=4:sw=4:expandtab diff --git a/samsung-ipc/device/xmm6260/modemctl_common.c b/samsung-ipc/device/xmm6260/modemctl_common.c deleted file mode 100644 index 489f9fe..0000000 --- a/samsung-ipc/device/xmm6260/modemctl_common.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Firmware loader for Samsung I9100 and I9250 - * Copyright (C) 2012 Alexander Tarasikov - * - * based on the incomplete C++ implementation which is - * Copyright (C) 2012 Sergey Gridasov - * - * 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 . - */ - -//modemctl shared code -#include "modemctl_common.h" - -/* - * modemctl generic functions - */ -int modemctl_link_set_active(struct ipc_client *client, - struct modemctl_io_data *io_data, bool enabled) { - unsigned status = enabled; - int ret; - unsigned long ioctl_code; - - ioctl_code = IOCTL_LINK_CONTROL_ACTIVE; - ret = ioctl(io_data->link_fd, ioctl_code, &status); - - if (ret < 0) { - ipc_client_log(client, "failed to set link active to %d", enabled); - goto fail; - } - - return 0; -fail: - return ret; -} - -int modemctl_link_set_enabled(struct ipc_client *client, - struct modemctl_io_data *io_data, bool enabled) { - unsigned status = enabled; - int ret; - unsigned long ioctl_code; - - ioctl_code = IOCTL_LINK_CONTROL_ENABLE; - ret = ioctl(io_data->link_fd, ioctl_code, &status); - - if (ret < 0) { - ipc_client_log(client, "failed to set link state to %d", enabled); - goto fail; - } - - return 0; -fail: - return ret; -} - -int modemctl_wait_link_ready(struct ipc_client *client, - struct modemctl_io_data *io_data) { - int ret; - - struct timeval tv_start = {}; - struct timeval tv_end = {}; - - gettimeofday(&tv_start, 0);; - - //link wakeup timeout in milliseconds - long diff = 0; - - do { - ret = ioctl(io_data->link_fd, IOCTL_LINK_CONNECTED, 0); - if (ret < 0) { - goto fail; - } - - if (ret == 1) { - return 0; - } - - usleep(LINK_POLL_DELAY_US); - gettimeofday(&tv_end, 0);; - - diff = (tv_end.tv_sec - tv_start.tv_sec) * 1000; - diff += (tv_end.tv_usec - tv_start.tv_usec) / 1000; - } while (diff < LINK_TIMEOUT_MS); - - ret = -ETIMEDOUT; - -fail: - return ret; -} - -int modemctl_wait_modem_online(struct ipc_client *client, - struct modemctl_io_data *io_data) { - int ret; - - struct timeval tv_start = {}; - struct timeval tv_end = {}; - - gettimeofday(&tv_start, 0);; - - //link wakeup timeout in milliseconds - long diff = 0; - - do { - ret = ioctl(io_data->boot_fd, IOCTL_MODEM_STATUS, 0); - if (ret < 0) { - goto fail; - } - - if (ret == STATE_ONLINE) { - return 0; - } - - usleep(LINK_POLL_DELAY_US); - gettimeofday(&tv_end, 0);; - - diff = (tv_end.tv_sec - tv_start.tv_sec) * 1000; - diff += (tv_end.tv_usec - tv_start.tv_usec) / 1000; - } while (diff < LINK_TIMEOUT_MS); - - ret = -ETIMEDOUT; - -fail: - return ret; -} - -int modemctl_modem_power(struct ipc_client *client, - struct modemctl_io_data *io_data, bool enabled) { - if (enabled) { - return ioctl(io_data->boot_fd, IOCTL_MODEM_ON, 0); - } - else { - return ioctl(io_data->boot_fd, IOCTL_MODEM_OFF, 0); - } - return -1; -} - -int modemctl_modem_boot_power(struct ipc_client *client, - struct modemctl_io_data *io_data, bool enabled) { - if (enabled) { - return ioctl(io_data->boot_fd, IOCTL_MODEM_BOOT_ON, 0); - } - else { - return ioctl(io_data->boot_fd, IOCTL_MODEM_BOOT_OFF, 0); - } - return -1; -} - -unsigned char calculateCRC(void* data, size_t offset, size_t length) -{ - unsigned char crc = 0; - unsigned char *ptr = (unsigned char*)(data + offset); - - while (length--) { - crc ^= *ptr++; - } - - return crc; -} - -// vim:ts=4:sw=4:expandtab diff --git a/samsung-ipc/device/xmm6260/modemctl_common.h b/samsung-ipc/device/xmm6260/modemctl_common.h deleted file mode 100644 index 87c9555..0000000 --- a/samsung-ipc/device/xmm6260/modemctl_common.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Firmware loader for Samsung I9100 and I9250 - * Copyright (C) 2012 Alexander Tarasikov - * - * based on the incomplete C++ implementation which is - * Copyright (C) 2012 Sergey Gridasov - * - * 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 . - */ - -#ifndef __MODEMCTL_COMMON_H__ -#define __MODEMCTL_COMMON_H__ - -#include - -#include "common.h" -#include "io_helpers.h" - -//Samsung IOCTLs -#include "modem_prj.h" - -#define MODEM_DEVICE(x) ("/dev/" #x) -#define LINK_PM MODEM_DEVICE(link_pm) -#define MODEM_DEV MODEM_DEVICE(modem_br) -#define BOOT_DEV MODEM_DEVICE(umts_boot0) -#define IPC_DEV MODEM_DEVICE(umts_ipc0) -#define RFS_DEV MODEM_DEVICE(umts_rfs0) - -#define LINK_POLL_DELAY_US (50 * 1000) -#define LINK_TIMEOUT_MS 2000 - -#define RADIO_MAP_SIZE (16 << 20) - -struct xmm6260_radio_part { - size_t offset; - size_t length; -}; - -/* - * Components of the Samsung XMM6260 firmware - */ -enum xmm6260_image { - PSI, - EBL, - SECURE_IMAGE, - FIRMWARE, - NVDATA, -}; - -/* - * Bootloader control interface definitions - */ - -enum xmm6260_boot_cmd { - SetPortConf, - - ReqSecStart, - ReqSecEnd, - ReqForceHwReset, - - ReqFlashSetAddress, - ReqFlashWriteBlock, -}; - -struct modemctl_io_data { - int link_fd; - int boot_fd; - - int radio_fd; - char *radio_data; - struct stat radio_stat; - - struct xmm6260_radio_part *radio_parts; - int radio_parts_count; -}; - -/* - * Function prototypes - */ - -/* - * @brief Activates the modem <-> cpu link data transfer - * - * @param client [in] ipc client - * @param io_data [in] modemctl-specific data - * @param enabled [in] whether to enable or disable link data transport - * @return Negative value indicating error code - * @return ioctl call result - */ -int modemctl_link_set_active(struct ipc_client *client, - struct modemctl_io_data *io_data, bool enabled); - -/* - * @brief Activates the modem <-> cpu link connection - * - * @param client [in] ipc client - * @param io_data [in] modemctl-specific data - * @param enabled [in] the state to set link to - * @return Negative value indicating error code - * @return ioctl call result - */ -int modemctl_link_set_enabled(struct ipc_client *client, - struct modemctl_io_data *io_data, bool enabled); - -/* - * @brief Poll the link until it gets ready or times out - * - * @param client [in] ipc client - * @param io_data [in] modemctl-specific data - * @return Negative value indicating error code - * @return ioctl call result - */ -int modemctl_wait_link_ready(struct ipc_client *client, - struct modemctl_io_data *io_data); - -/* - * @brief Poll the modem until it gets online or times out - * - * @param client [in] ipc client - * @param io_data [in] modemctl-specific data - * @return Negative value indicating error code - * @return ioctl call result - */ -int modemctl_wait_modem_online(struct ipc_client *client, - struct modemctl_io_data *io_data); - -/* - * @brief Sets the modem power - * - * @param client [in] ipc client - * @param io_data [in] modemctl-specific data - * @param enabled [in] whether to enable or disable modem power - * @return Negative value indicating error code - * @return ioctl call result - */ -int modemctl_modem_power(struct ipc_client *client, - struct modemctl_io_data *io_data, bool enabled); - -/* - * @brief Sets the modem bootloader power/UART configuration - * - * @param client [in] ipc client - * @param io_data [in] modemctl-specific data - * @param enabled [in] whether to enable or disable power - * @return Negative value indicating error code - * @return ioctl call result - */ -int modemctl_modem_boot_power(struct ipc_client *client, - struct modemctl_io_data *io_data, bool enabled); - -/* - * @brief Calculate the checksum for the XMM6260 bootloader protocol - * - * @param data [in] the data to calculate the checksum for - * @param offset [in] number of bytes to skip - * @param length [in] length of data in bytes - * @return checksum value - */ -unsigned char calculateCRC(void* data, size_t offset, size_t length); - -#endif //__MODEMCTL_COMMON_H__ - -// vim:ts=4:sw=4:expandtab -- cgit v1.1