aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk18
-rw-r--r--samsung-ipc/device/xmm6260/common.h50
-rw-r--r--samsung-ipc/device/xmm6260/fwloader_i9100.c776
-rw-r--r--samsung-ipc/device/xmm6260/fwloader_i9250.c883
-rw-r--r--samsung-ipc/device/xmm6260/io_helpers.c89
-rw-r--r--samsung-ipc/device/xmm6260/io_helpers.h70
-rw-r--r--samsung-ipc/device/xmm6260/log.c44
-rw-r--r--samsung-ipc/device/xmm6260/log.h54
-rw-r--r--samsung-ipc/device/xmm6260/modem_prj.h127
-rw-r--r--samsung-ipc/device/xmm6260/modemctl_common.c116
-rw-r--r--samsung-ipc/device/xmm6260/modemctl_common.h148
-rw-r--r--samsung-ipc/device/xmm6260/xmm6260_ipc.c362
-rw-r--r--samsung-ipc/device/xmm6260/xmm6260_ipc.h31
-rw-r--r--samsung-ipc/ipc_devices.c7
-rw-r--r--samsung-ipc/ipc_devices.h8
15 files changed, 2782 insertions, 1 deletions
diff --git a/Android.mk b/Android.mk
index 70109d0..8ccda02 100644
--- a/Android.mk
+++ b/Android.mk
@@ -10,6 +10,11 @@ LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS += -Iexternal/openssl/include
LOCAL_LDFLAGS += -lcrypto
+ifeq ($(TARGET_DEVICE),maguro)
+ LOCAL_CFLAGS += -DDEVICE_IPC_V4
+ samsung-ipc_device := xmm6260
+endif
+
ifeq ($(TARGET_DEVICE),crespo)
LOCAL_CFLAGS += -DDEVICE_IPC_V4
samsung-ipc_device := crespo
@@ -47,7 +52,13 @@ samsung-ipc_files := \
samsung-ipc/sec.c \
samsung-ipc/device/h1/h1_ipc.c \
samsung-ipc/device/crespo/crespo_ipc.c \
- samsung-ipc/device/aries/aries_ipc.c
+ samsung-ipc/device/aries/aries_ipc.c \
+ samsung-ipc/device/xmm6260/xmm6260_ipc.c \
+ samsung-ipc/device/xmm6260/fwloader_i9100.c \
+ samsung-ipc/device/xmm6260/fwloader_i9250.c \
+ samsung-ipc/device/xmm6260/io_helpers.c \
+ samsung-ipc/device/xmm6260/log.c \
+ samsung-ipc/device/xmm6260/modemctl_common.c
LOCAL_SRC_FILES := $(samsung-ipc_files)
LOCAL_CFLAGS += -DIPC_DEVICE_EXPLICIT=\"$(samsung-ipc_device)\"
@@ -65,6 +76,11 @@ include $(CLEAR_VARS)
LOCAL_MODULE := ipc-modemctrl
LOCAL_MODULE_TAGS := optional
+ifeq ($(TARGET_DEVICE),maguro)
+ LOCAL_CFLAGS += -DDEVICE_IPC_V4
+ samsung-ipc_device := xmm6260
+endif
+
ifeq ($(TARGET_DEVICE),crespo)
LOCAL_CFLAGS += -DDEVICE_IPC_V4
samsung-ipc_device := crespo
diff --git a/samsung-ipc/device/xmm6260/common.h b/samsung-ipc/device/xmm6260/common.h
new file mode 100644
index 0000000..9b5e66a
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/common.h
@@ -0,0 +1,50 @@
+/*
+ * common.h: common system headers includes and config defines
+ * This file is part of:
+ *
+ * Firmware loader for Samsung I9100 and I9250
+ * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com>
+ *
+ * based on the incomplete C++ implementation which is
+ * Copyright (C) 2012 Sergey Gridasov <grindars@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <getopt.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+//for timeval
+#include <sys/time.h>
+
+//for mmap
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+#define DEBUG 1
+
+#endif //__COMMON_H__
diff --git a/samsung-ipc/device/xmm6260/fwloader_i9100.c b/samsung-ipc/device/xmm6260/fwloader_i9100.c
new file mode 100644
index 0000000..b083610
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/fwloader_i9100.c
@@ -0,0 +1,776 @@
+/*
+ * Firmware loader for Samsung I9100 and I9250
+ * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com>
+ *
+ * based on the incomplete C++ implementation which is
+ * Copyright (C) 2012 Sergey Gridasov <grindars@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "modemctl_common.h"
+
+/*
+ * I9100 specific implementation
+ */
+#define RADIO_IMAGE "/dev/block/mmcblk0p8"
+#define NVDATA_IMAGE "/efs/nv_data.bin"
+#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
+
+/*
+ * Locations of the firmware components in the Samsung firmware
+ */
+static struct xmm6260_offset {
+ size_t offset;
+ size_t length;
+} 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 {
+ unsigned code;
+ size_t data_size;
+ bool need_ack;
+} 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,
+ },
+};
+
+typedef struct {
+ uint8_t magic;
+ uint16_t length;
+ uint8_t padding;
+} __attribute__((packed)) psi_header_t;
+
+typedef struct {
+ uint8_t data[76];
+} __attribute__((packed)) boot_info_t;
+
+typedef struct {
+ uint16_t check;
+ uint16_t cmd;
+ uint32_t data_size;
+} __attribute__((packed)) bootloader_cmd_t;
+
+/*
+ * Bootloader protocol
+ */
+static 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;
+}
+
+static int send_image(fwloader_context *ctx, enum xmm6260_image type) {
+ int ret;
+
+ if (type >= ARRAY_SIZE(i9100_radio_parts)) {
+ _e("bad image type %x", type);
+ goto fail;
+ }
+
+ size_t length = i9100_radio_parts[type].length;
+ size_t offset = i9100_radio_parts[type].offset;
+
+ size_t start = offset;
+ size_t end = length + start;
+
+ //dump some image bytes
+ _d("image start");
+ hexdump(ctx->radio_data + start, length);
+
+ while (start < end) {
+ ret = write(ctx->boot_fd, ctx->radio_data + start, end - start);
+ if (ret < 0) {
+ _d("failed to write image chunk");
+ goto fail;
+ }
+ start += ret;
+ }
+
+ unsigned char crc = calculateCRC(ctx->radio_data, offset, length);
+
+ if ((ret = write(ctx->boot_fd, &crc, 1)) < 1) {
+ _d("failed to write CRC");
+ goto fail;
+ }
+ else {
+ _d("wrote CRC %x", crc);
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int send_PSI(fwloader_context *ctx) {
+ size_t length = i9100_radio_parts[PSI].length;
+
+ psi_header_t hdr = {
+ .magic = XMM_PSI_MAGIC,
+ .length = length,
+ .padding = 0xff,
+ };
+ int ret = -1;
+
+ if ((ret = write(ctx->boot_fd, &hdr, sizeof(hdr))) != sizeof(hdr)) {
+ _d("%s: failed to write header, ret %d", __func__, ret);
+ goto fail;
+ }
+
+ if ((ret = send_image(ctx, PSI)) < 0) {
+ _e("failed to send PSI image");
+ goto fail;
+ }
+
+ int i;
+ for (i = 0; i < 22; i++) {
+ char ack;
+ if (receive(ctx->boot_fd, &ack, 1) < 1) {
+ _d("failed to read ACK byte %d", i);
+ goto fail;
+ }
+ _d("%02x ", ack);
+ }
+
+ if ((ret = expect_data(ctx->boot_fd, "\x1", 1)) < 0) {
+ _d("failed to wait for first ACK");
+ goto fail;
+ }
+
+ if ((ret = expect_data(ctx->boot_fd, "\x1", 1)) < 0) {
+ _d("failed to wait for second ACK");
+ goto fail;
+ }
+
+ if ((ret = expect_data(ctx->boot_fd, PSI_ACK_MAGIC, 2)) < 0) {
+ _e("failed to receive PSI ACK");
+ goto fail;
+ }
+ else {
+ _d("received PSI ACK");
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int send_EBL(fwloader_context *ctx) {
+ int ret;
+ int fd = ctx->boot_fd;
+ unsigned length = i9100_radio_parts[EBL].length;
+
+ if ((ret = write(fd, &length, sizeof(length))) < 0) {
+ _e("failed to write EBL length");
+ goto fail;
+ }
+
+ if ((ret = expect_data(fd, EBL_HDR_ACK_MAGIC, 2)) < 0) {
+ _e("failed to wait for EBL header ACK");
+ goto fail;
+ }
+
+ if ((ret = send_image(ctx, EBL)) < 0) {
+ _e("failed to send EBL image");
+ goto fail;
+ }
+
+ if ((ret = expect_data(fd, EBL_IMG_ACK_MAGIC, 2)) < 0) {
+ _e("failed to wait for EBL image ACK");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int bootloader_cmd(fwloader_context *ctx, enum xmm6260_boot_cmd cmd,
+ void *data, size_t data_size)
+{
+ int ret = 0;
+ if (cmd >= ARRAY_SIZE(i9100_boot_cmd_desc)) {
+ _e("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];
+ }
+
+ bootloader_cmd_t 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);
+
+ char *cmd_data = (char*)malloc(buf_size);
+ if (!cmd_data) {
+ _e("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);
+
+ _d("bootloader cmd packet");
+ hexdump(cmd_data, buf_size);
+
+ if ((ret = write(ctx->boot_fd, cmd_data, buf_size)) < 0) {
+ _e("failed to write command to socket");
+ goto done_or_fail;
+ }
+
+ if (ret != buf_size) {
+ _e("written %d bytes of %d", ret, buf_size);
+ ret = -EINVAL;
+ goto done_or_fail;
+ }
+
+ _d("sent command %x magic=%x", header.cmd, header.check);
+
+ if (!i9100_boot_cmd_desc[cmd].need_ack) {
+ ret = 0;
+ goto done_or_fail;
+ }
+
+ bootloader_cmd_t ack = {};
+ if ((ret = receive(ctx->boot_fd, &ack, sizeof(ack))) < 0) {
+ _e("failed to receive ack for cmd %x", header.cmd);
+ goto done_or_fail;
+ }
+
+ if (ret != sizeof(ack)) {
+ _e("received %x bytes of %x for ack", ret, sizeof(ack));
+ ret = -EINVAL;
+ goto done_or_fail;
+ }
+
+ hexdump(&ack, sizeof(ack));
+
+ if (ack.cmd != header.cmd) {
+ _e("ack cmd %x does not match request %x", ack.cmd, header.cmd);
+ ret = -EINVAL;
+ goto done_or_fail;
+ }
+
+ if ((ret = receive(ctx->boot_fd, cmd_data, cmd_size)) < 0) {
+ _e("failed to receive reply data");
+ goto done_or_fail;
+ }
+
+ if (ret != cmd_size) {
+ _e("received %x bytes of %x for reply data", ret, cmd_size);
+ ret = -EINVAL;
+ goto done_or_fail;
+ }
+ hexdump(cmd_data, cmd_size);
+
+done_or_fail:
+
+ if (cmd_data) {
+ free(cmd_data);
+ }
+
+ return ret;
+}
+
+static int ack_BootInfo(fwloader_context *ctx) {
+ int ret;
+ boot_info_t info;
+
+ if ((ret = receive(ctx->boot_fd, &info, sizeof(info))) != sizeof(info)) {
+ _e("failed to receive Boot Info ret=%d", ret);
+ ret = -1;
+ goto fail;
+ }
+ else {
+ _d("received Boot Info");
+ hexdump(&info, sizeof(info));
+ }
+
+ if ((ret = bootloader_cmd(ctx, SetPortConf, &info, sizeof(info))) < 0) {
+ _e("failed to send SetPortConf command");
+ goto fail;
+ }
+ else {
+ _d("sent SetPortConf command");
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int send_image_addr(fwloader_context *ctx, uint32_t addr,
+ enum xmm6260_image type)
+{
+ int ret = 0;
+ if ((ret = bootloader_cmd(ctx, ReqFlashSetAddress, &addr, 4)) < 0) {
+ _e("failed to send ReqFlashSetAddress");
+ goto fail;
+ }
+ else {
+ _d("sent ReqFlashSetAddress");
+ }
+
+ uint32_t offset = i9100_radio_parts[type].offset;
+ uint32_t length = i9100_radio_parts[type].length;
+
+ char *start = ctx->radio_data + offset;
+ char *end = start + length;
+
+ while (start < end) {
+ unsigned rest = end - start;
+ unsigned chunk = rest < SEC_DOWNLOAD_CHUNK ? rest : SEC_DOWNLOAD_CHUNK;
+
+ ret = bootloader_cmd(ctx, ReqFlashWriteBlock, start, chunk);
+ if (ret < 0) {
+ _e("failed to send data chunk");
+ goto fail;
+ }
+
+ start += chunk;
+ }
+
+ usleep(SEC_DOWNLOAD_DELAY_US);
+
+fail:
+ return ret;
+}
+
+static int send_SecureImage(fwloader_context *ctx) {
+ 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 = ctx->radio_data + sec_off;
+
+ if ((ret = bootloader_cmd(ctx, ReqSecStart, sec_img, sec_len)) < 0) {
+ _e("failed to write ReqSecStart");
+ goto fail;
+ }
+ else {
+ _d("sent ReqSecStart");
+ }
+
+ if ((ret = send_image_addr(ctx, FW_LOAD_ADDR, FIRMWARE)) < 0) {
+ _e("failed to send FIRMWARE image");
+ goto fail;
+ }
+ else {
+ _d("sent FIRMWARE image");
+ }
+
+ if ((ret = send_image_addr(ctx, NVDATA_LOAD_ADDR, NVDATA)) < 0) {
+ _e("failed to send NVDATA image");
+ goto fail;
+ }
+ else {
+ _d("sent NVDATA image");
+ }
+
+ if ((ret = bootloader_cmd(ctx, ReqSecEnd,
+ BL_END_MAGIC, BL_END_MAGIC_LEN)) < 0)
+ {
+ _e("failed to write ReqSecEnd");
+ goto fail;
+ }
+ else {
+ _d("sent ReqSecEnd");
+ }
+
+ ret = bootloader_cmd(ctx, ReqForceHwReset,
+ BL_RESET_MAGIC, BL_RESET_MAGIC_LEN);
+ if (ret < 0) {
+ _e("failed to write ReqForceHwReset");
+ goto fail;
+ }
+ else {
+ _d("sent ReqForceHwReset");
+ }
+
+fail:
+ return ret;
+}
+
+/*
+ * i9200 (Galaxy S2) board-specific code
+ */
+
+/*
+ * Power management
+ */
+static int i9100_ehci_setpower(bool enabled) {
+ int ret;
+
+ _d("%s: enabled=%d", __func__, enabled);
+
+ int ehci_fd = open(I9100_EHCI_PATH, O_RDWR);
+ if (ehci_fd < 0) {
+ _e("failed to open EHCI fd");
+ goto fail;
+ }
+ else {
+ _d("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) {
+ _e("failed to set EHCI power");
+ }
+ else {
+ _d("set EHCI power");
+ }
+
+fail:
+ if (ehci_fd >= 0) {
+ close(ehci_fd);
+ }
+
+ return ret;
+}
+
+static int reboot_modem_i9100(fwloader_context *ctx, bool hard) {
+ int ret;
+
+ //wait for link to become ready before redetection
+ if (!hard) {
+ if ((ret = modemctl_wait_link_ready(ctx)) < 0) {
+ _e("failed to wait for link to get ready for redetection");
+ goto fail;
+ }
+ else {
+ _d("link ready for redetection");
+ }
+ }
+
+ /*
+ * Disable the hardware to ensure consistent state
+ */
+ if (hard) {
+ if ((ret = modemctl_modem_power(ctx, false)) < 0) {
+ _e("failed to disable xmm6260 power");
+ goto fail;
+ }
+ else {
+ _d("disabled xmm6260 power");
+ }
+ }
+
+ if ((ret = modemctl_link_set_enabled(ctx, false)) < 0) {
+ _e("failed to disable I9100 HSIC link");
+ goto fail;
+ }
+ else {
+ _d("disabled I9100 HSIC link");
+ }
+
+ if ((ret = i9100_ehci_setpower(false)) < 0) {
+ _e("failed to disable I9100 EHCI");
+ goto fail;
+ }
+ else {
+ _d("disabled I9100 EHCI");
+ }
+
+ if ((ret = modemctl_link_set_active(ctx, false)) < 0) {
+ _e("failed to deactivate I9100 HSIC link");
+ goto fail;
+ }
+ else {
+ _d("deactivated I9100 HSIC link");
+ }
+
+ /*
+ * Now, initialize the hardware
+ */
+
+ if ((ret = modemctl_link_set_enabled(ctx, true)) < 0) {
+ _e("failed to enable I9100 HSIC link");
+ goto fail;
+ }
+ else {
+ _d("enabled I9100 HSIC link");
+ }
+
+ if ((ret = i9100_ehci_setpower(true)) < 0) {
+ _e("failed to enable I9100 EHCI");
+ goto fail;
+ }
+ else {
+ _d("enabled I9100 EHCI");
+ }
+
+ if ((ret = modemctl_link_set_active(ctx, true)) < 0) {
+ _e("failed to activate I9100 HSIC link");
+ goto fail;
+ }
+ else {
+ _d("activated I9100 HSIC link");
+ }
+
+ if (hard) {
+ if ((ret = modemctl_modem_power(ctx, true)) < 0) {
+ _e("failed to enable xmm6260 power");
+ goto fail;
+ }
+ else {
+ _d("enabled xmm6260 power");
+ }
+ }
+
+ if ((ret = modemctl_wait_link_ready(ctx)) < 0) {
+ _e("failed to wait for link to get ready");
+ goto fail;
+ }
+ else {
+ _d("link ready");
+ }
+
+fail:
+ return ret;
+}
+
+int boot_modem_i9100(void) {
+ int ret;
+ fwloader_context ctx;
+ memset(&ctx, 0, sizeof(ctx));
+
+ ctx.radio_fd = open(RADIO_IMAGE, O_RDONLY);
+ if (ctx.radio_fd < 0) {
+ _e("failed to open radio firmware");
+ goto fail;
+ }
+ else {
+ _d("opened radio image %s, fd=%d", RADIO_IMAGE, ctx.radio_fd);
+ }
+
+ if (fstat(ctx.radio_fd, &ctx.radio_stat) < 0) {
+ _e("failed to stat radio image, error %s", strerror(errno));
+ goto fail;
+ }
+
+ ctx.radio_data = mmap(0, RADIO_MAP_SIZE, PROT_READ, MAP_SHARED,
+ ctx.radio_fd, 0);
+ if (ctx.radio_data == MAP_FAILED) {
+ _e("failed to mmap radio image, error %s", strerror(errno));
+ goto fail;
+ }
+
+ ctx.boot_fd = open(BOOT_DEV, O_RDWR);
+ if (ctx.boot_fd < 0) {
+ _e("failed to open boot device");
+ goto fail;
+ }
+ else {
+ _d("opened boot device %s, fd=%d", BOOT_DEV, ctx.boot_fd);
+ }
+
+ ctx.link_fd = open(LINK_PM, O_RDWR);
+ if (ctx.link_fd < 0) {
+ _e("failed to open link device");
+ goto fail;
+ }
+ else {
+ _d("opened link device %s, fd=%d", LINK_PM, ctx.link_fd);
+ }
+
+ if (reboot_modem_i9100(&ctx, true)) {
+ _e("failed to hard reset modem");
+ goto fail;
+ }
+ else {
+ _d("modem hard reset done");
+ }
+
+ /*
+ * Now, actually load the firmware
+ */
+ if (write(ctx.boot_fd, "ATAT", 4) != 4) {
+ _e("failed to write ATAT to boot socket");
+ goto fail;
+ }
+ else {
+ _d("written ATAT to boot socket, waiting for ACK");
+ }
+
+ char buf[2];
+ if (receive(ctx.boot_fd, buf, 1) < 0) {
+ _e("failed to receive bootloader ACK");
+ goto fail;
+ }
+ if (receive(ctx.boot_fd, buf + 1, 1) < 0) {
+ _e("failed to receive chip IP ACK");
+ goto fail;
+ }
+ _i("receive ID: [%02x %02x]", buf[0], buf[1]);
+
+ if ((ret = send_PSI(&ctx)) < 0) {
+ _e("failed to upload PSI");
+ goto fail;
+ }
+ else {
+ _d("PSI download complete");
+ }
+
+ if ((ret = send_EBL(&ctx)) < 0) {
+ _e("failed to upload EBL");
+ goto fail;
+ }
+ else {
+ _d("EBL download complete");
+ }
+
+ if ((ret = ack_BootInfo(&ctx)) < 0) {
+ _e("failed to receive Boot Info");
+ goto fail;
+ }
+ else {
+ _d("Boot Info ACK done");
+ }
+
+ if ((ret = send_SecureImage(&ctx)) < 0) {
+ _e("failed to upload Secure Image");
+ goto fail;
+ }
+ else {
+ _d("Secure Image download complete");
+ }
+
+ usleep(POST_BOOT_TIMEOUT_US);
+
+ if ((ret = reboot_modem_i9100(&ctx, false))) {
+ _e("failed to soft reset modem");
+ goto fail;
+ }
+ else {
+ _d("modem soft reset done");
+ }
+
+ _i("online");
+
+fail:
+ if (ctx.radio_data != MAP_FAILED) {
+ munmap(ctx.radio_data, RADIO_MAP_SIZE);
+ }
+
+ if (ctx.link_fd >= 0) {
+ close(ctx.link_fd);
+ }
+
+ if (ctx.radio_fd >= 0) {
+ close(ctx.radio_fd);
+ }
+
+ if (ctx.boot_fd >= 0) {
+ close(ctx.boot_fd);
+ }
+
+ return ret;
+}
+
diff --git a/samsung-ipc/device/xmm6260/fwloader_i9250.c b/samsung-ipc/device/xmm6260/fwloader_i9250.c
new file mode 100644
index 0000000..23cfd42
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/fwloader_i9250.c
@@ -0,0 +1,883 @@
+/*
+ * Firmware loader for Samsung I9100 and I9250
+ * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com>
+ *
+ * based on the incomplete C++ implementation which is
+ * Copyright (C) 2012 Sergey Gridasov <grindars@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "modemctl_common.h"
+
+/*
+ * I9250 specific implementation
+ */
+
+/*
+ * Locations of the firmware components in the Samsung firmware
+ */
+static struct xmm6260_offset {
+ size_t offset;
+ size_t length;
+} 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,
+ }
+};
+
+/*
+ * on I9250, all commands need ACK and we do not need to
+ * allocate a fixed size buffer
+ */
+
+struct {
+ unsigned code;
+ bool long_tail;
+ bool no_ack;
+} 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,
+ },
+};
+
+typedef struct {
+ uint8_t magic;
+ uint16_t length;
+ uint8_t padding;
+} __attribute__((packed)) psi_header_t;
+
+typedef struct {
+ uint8_t data[76];
+} __attribute__((packed)) boot_info_t;
+
+typedef struct {
+ uint16_t check;
+ uint16_t cmd;
+ uint32_t data_size;
+} __attribute__((packed)) bootloader_cmd_t;
+
+/*
+ * Bootloader protocol
+ */
+static 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;
+}
+
+static int reboot_modem_i9250(fwloader_context *ctx, bool hard) {
+ int ret;
+
+ if (!hard) {
+ return 0;
+ }
+
+ /*
+ * Disable the hardware to ensure consistent state
+ */
+ if ((ret = modemctl_modem_power(ctx, false)) < 0) {
+ _e("failed to disable modem power");
+ goto fail;
+ }
+ else {
+ _d("disabled modem power");
+ }
+
+ if ((ret = modemctl_modem_boot_power(ctx, false)) < 0) {
+ _e("failed to disable modem boot power");
+ goto fail;
+ }
+ else {
+ _d("disabled modem boot power");
+ }
+
+ /*
+ * Now, initialize the hardware
+ */
+ if ((ret = modemctl_modem_boot_power(ctx, true)) < 0) {
+ _e("failed to enable modem boot power");
+ goto fail;
+ }
+ else {
+ _d("enabled modem boot power");
+ }
+
+ if ((ret = modemctl_modem_power(ctx, true)) < 0) {
+ _e("failed to enable modem power");
+ goto fail;
+ }
+ else {
+ _d("enabled modem power");
+ }
+
+fail:
+ return ret;
+}
+
+/*
+ * i9250 (Galaxy Nexus) board-specific code
+ */
+
+#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)
+
+/* same for i9100 and i9250? */
+
+#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
+
+#define POST_BOOT_TIMEOUT_US (1000 * 1000)
+
+
+static int send_image_i9250(fwloader_context *ctx, enum xmm6260_image type) {
+ int ret;
+
+ if (type >= ARRAY_SIZE(i9250_radio_parts)) {
+ _e("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(ctx->radio_data, offset, length);
+
+ //dump some image bytes
+ _d("image start");
+ hexdump(ctx->radio_data + start, length);
+
+ 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(ctx->boot_fd, ctx->radio_data + start, curr_chunk);
+ if (ret < 0) {
+ _e("failed to write image chunk");
+ goto fail;
+ }
+ start += ret;
+ }
+ _d("sent image type=%d", type);
+
+ if (type == EBL) {
+ if ((ret = write(ctx->boot_fd, &crc, 1)) < 1) {
+ _e("failed to write EBL CRC");
+ goto fail;
+ }
+ else {
+ _d("wrote EBL CRC %02x", crc);
+ }
+ goto done;
+ }
+
+ uint32_t crc32 = (crc << 24) | 0xffffff;
+ if ((ret = write(ctx->boot_fd, &crc32, 4)) != 4) {
+ _e("failed to write CRC");
+ goto fail;
+ }
+ else {
+ _d("wrote CRC %x", crc);
+ }
+
+done:
+ ret = 0;
+
+fail:
+ return ret;
+}
+
+static int send_PSI_i9250(fwloader_context *ctx) {
+ int ret = -1;
+
+ if ((ret = write(ctx->boot_fd, I9250_PSI_START_MAGIC, 4)) < 0) {
+ _d("%s: failed to write header, ret %d", __func__, ret);
+ goto fail;
+ }
+
+ if ((ret = send_image_i9250(ctx, PSI)) < 0) {
+ _e("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",
+ };
+
+ int i;
+ for (i = 0; i < ARRAY_SIZE(expected_acks); i++) {
+ ret = expect_data(ctx->boot_fd, expected_acks[i], 4);
+ if (ret < 0) {
+ _d("failed to wait for ack %d", i);
+ goto fail;
+ }
+ }
+ _d("received PSI ACK");
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int send_EBL_i9250(fwloader_context *ctx) {
+ int ret;
+ int fd = ctx->boot_fd;
+ unsigned length = i9250_radio_parts[EBL].length;
+
+ if ((ret = write(fd, "\x04\x00\x00\x00", 4)) != 4) {
+ _e("failed to write length of EBL length ('4') ");
+ goto fail;
+ }
+
+ if ((ret = write(fd, &length, sizeof(length))) != sizeof(length)) {
+ _e("failed to write EBL length");
+ goto fail;
+ }
+
+ if ((ret = expect_data(fd, I9250_GENERAL_ACK, 4)) < 0) {
+ _e("failed to wait for EBL length ACK");
+ goto fail;
+ }
+
+ if ((ret = expect_data(fd, I9250_EBL_HDR_ACK_MAGIC, 4)) < 0) {
+ _e("failed to wait for EBL header ACK");
+ goto fail;
+ }
+
+ length++;
+ if ((ret = write(fd, &length, sizeof(length))) != sizeof(length)) {
+ _e("failed to write EBL length + 1");
+ goto fail;
+ }
+
+ if ((ret = send_image_i9250(ctx, EBL)) < 0) {
+ _e("failed to send EBL image");
+ goto fail;
+ }
+ else {
+ _d("sent EBL image, waiting for ACK");
+ }
+
+ if ((ret = expect_data(fd, I9250_GENERAL_ACK, 4)) < 0) {
+ _e("failed to wait for EBL image general ACK");
+ goto fail;
+ }
+
+ if ((ret = expect_data(fd, I9250_EBL_IMG_ACK_MAGIC, 4)) < 0) {
+ _e("failed to wait for EBL image ACK");
+ goto fail;
+ }
+ else {
+ _d("got EBL ACK");
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+typedef struct {
+ uint32_t total_size;
+ uint16_t hdr_magic;
+ uint16_t cmd;
+ uint16_t data_size;
+} __attribute__((packed)) bootloader_cmd_hdr_t;
+
+#define DECLARE_BOOT_CMD_HEADER(name, code, size) \
+bootloader_cmd_hdr_t name = {\
+ .total_size = size + 10,\
+ .hdr_magic = 2,\
+ .cmd = code,\
+ .data_size = size,\
+}
+
+typedef struct {
+ uint16_t checksum;
+ uint16_t tail_magic;
+ uint8_t unknown[2];
+} __attribute__((packed)) bootloader_cmd_tail_t;
+
+#define DECLARE_BOOT_TAIL_HEADER(name, checksum) \
+bootloader_cmd_tail_t name = {\
+ .checksum = checksum,\
+ .tail_magic = 3,\
+ .unknown = "\xea\xea",\
+}
+
+static int bootloader_cmd(fwloader_context *ctx,
+ enum xmm6260_boot_cmd cmd, void *data, size_t data_size)
+{
+ int ret = 0;
+ if (cmd >= ARRAY_SIZE(i9250_boot_cmd_desc)) {
+ _e("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 c;
+ for (c = 0; c < data_size; c++) {
+ checksum += ptr[c];
+ }
+
+ 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;
+ _d("data_size %d [%d] checksum 0x%x", data_size, cmd_buffer_size, checksum);
+
+ char *cmd_data = (char*)malloc(cmd_buffer_size);
+ if (!cmd_data) {
+ _e("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);
+
+ _d("bootloader cmd packet");
+ hexdump(cmd_data, cmd_buffer_size);
+ hexdump(cmd_data + cmd_buffer_size - 16, 16);
+
+ if ((ret = write(ctx->boot_fd, cmd_data, cmd_buffer_size)) < 0) {
+ _e("failed to write command to socket");
+ goto done_or_fail;
+ }
+
+ if (ret < cmd_buffer_size) {
+ _e("written %d bytes of %d", ret, cmd_buffer_size);
+ ret = -EINVAL;
+ goto done_or_fail;
+ }
+
+ _d("sent command %x", header.cmd);
+ if (i9250_boot_cmd_desc[cmd].no_ack) {
+ _i("not waiting for ACK");
+ goto done_or_fail;
+ }
+
+ uint32_t ack_length;
+ if ((ret = receive(ctx->boot_fd, &ack_length, 4)) < 0) {
+ _e("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) {
+ _e("failed to allocate the buffer for ack data");
+ goto done_or_fail;
+ }
+ }
+ memset(cmd_data, 0, ack_length);
+ memcpy(cmd_data, &ack_length, 4);
+
+ int i;
+ for (i = 0; i < (ack_length + 3) / 4; i++) {
+ if ((ret = receive(ctx->boot_fd, cmd_data + ((i + 1) << 2), 4)) < 0) {
+ _e("failed to receive ack chunk");
+ goto done_or_fail;
+ }
+ }
+
+ _d("received ack");
+ hexdump(cmd_data, ack_length + 4);
+
+ bootloader_cmd_hdr_t *ack_hdr = (bootloader_cmd_hdr_t*)cmd_data;
+ bootloader_cmd_tail_t *ack_tail = (bootloader_cmd_tail_t*)
+ (cmd_data + ack_length + 4 - sizeof(bootloader_cmd_tail_t));
+
+ _d("ack code 0x%x checksum 0x%x", ack_hdr->cmd, ack_tail->checksum);
+ if (ack_hdr->cmd != header.cmd) {
+ _e("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 ack_BootInfo_i9250(fwloader_context *ctx) {
+ int ret = -1;
+ uint32_t boot_info_length;
+ char *boot_info = 0;
+
+
+ if ((ret = receive(ctx->boot_fd, &boot_info_length, 4)) < 0) {
+ _e("failed to receive boot info length");
+ goto fail;
+ }
+
+ _d("Boot Info length=0x%x", boot_info_length);
+
+ boot_info = (char*)malloc(boot_info_length);
+ if (!boot_info) {
+ _e("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;
+
+ int i;
+ for (i = 0; i < boot_chunk_count; i++) {
+ ret = receive(ctx->boot_fd, boot_info + (i * boot_chunk), boot_chunk);
+ if (ret < 0) {
+ _e("failed to receive Boot Info chunk %i ret=%d", i, ret);
+ goto fail;
+ }
+ }
+
+ _d("received Boot Info");
+ hexdump(boot_info, boot_info_length);
+
+ ret = bootloader_cmd(ctx, SetPortConf, boot_info, boot_info_length);
+ if (ret < 0) {
+ _e("failed to send SetPortConf command");
+ goto fail;
+ }
+ else {
+ _d("sent SetPortConf command");
+ }
+
+ ret = 0;
+
+fail:
+ if (boot_info) {
+ free(boot_info);
+ }
+
+ return ret;
+}
+
+static int send_secure_image(fwloader_context *ctx, uint32_t addr,
+ enum xmm6260_image type)
+{
+ int ret = 0;
+ if ((ret = bootloader_cmd(ctx, ReqFlashSetAddress, &addr, 4)) < 0) {
+ _e("failed to send ReqFlashSetAddress");
+ goto fail;
+ }
+ else {
+ _d("sent ReqFlashSetAddress");
+ }
+
+ uint32_t offset = i9250_radio_parts[type].offset;
+ uint32_t length = i9250_radio_parts[type].length;
+
+ char *start = ctx->radio_data + offset;
+ char *end = start + length;
+
+ while (start < end) {
+ unsigned rest = end - start;
+ unsigned chunk = rest < SEC_DOWNLOAD_CHUNK ? rest : SEC_DOWNLOAD_CHUNK;
+
+ ret = bootloader_cmd(ctx, ReqFlashWriteBlock, start, chunk);
+ if (ret < 0) {
+ _e("failed to send data chunk");
+ goto fail;
+ }
+
+ start += chunk;
+ }
+
+ usleep(SEC_DOWNLOAD_DELAY_US);
+
+fail:
+ return ret;
+}
+
+static int send_mps_data(fwloader_context *ctx) {
+ 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) {
+ _e("failed to open MPS data");
+ }
+ else {
+ read(mps_fd, mps_data, I9250_MPS_LENGTH);
+ }
+
+ if ((ret = bootloader_cmd(ctx, ReqFlashSetAddress, &addr, 4)) < 0) {
+ _e("failed to send ReqFlashSetAddress");
+ goto fail;
+ }
+ else {
+ _d("sent ReqFlashSetAddress");
+ }
+
+ if ((ret = bootloader_cmd(ctx, ReqFlashWriteBlock,
+ mps_data, I9250_MPS_LENGTH)) < 0) {
+ _e("failed to write MPS data to modem");
+ goto fail;
+ }
+
+
+fail:
+ if (mps_fd >= 0) {
+ close(mps_fd);
+ }
+
+ return ret;
+}
+
+static int send_SecureImage_i9250(fwloader_context *ctx) {
+ 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 = ctx->radio_data + sec_off;
+
+ if ((ret = bootloader_cmd(ctx, ReqSecStart, sec_img, sec_len)) < 0) {
+ _e("failed to write ReqSecStart");
+ goto fail;
+ }
+ else {
+ _d("sent ReqSecStart");
+ }
+
+ if ((ret = send_secure_image(ctx, FW_LOAD_ADDR, FIRMWARE)) < 0) {
+ _e("failed to send FIRMWARE image");
+ goto fail;
+ }
+ else {
+ _d("sent FIRMWARE image");
+ }
+
+ if ((ret = send_secure_image(ctx, NVDATA_LOAD_ADDR, NVDATA)) < 0) {
+ _e("failed to send NVDATA image");
+ goto fail;
+ }
+ else {
+ _d("sent NVDATA image");
+ }
+
+ if ((ret = send_mps_data(ctx)) < 0) {
+ _e("failed to send MPS data");
+ goto fail;
+ }
+ else {
+ _d("sent MPS data");
+ }
+
+ if ((ret = bootloader_cmd(ctx, ReqSecEnd,
+ BL_END_MAGIC, BL_END_MAGIC_LEN)) < 0)
+ {
+ _e("failed to write ReqSecEnd");
+ goto fail;
+ }
+ else {
+ _d("sent ReqSecEnd");
+ }
+
+ ret = bootloader_cmd(ctx, ReqForceHwReset,
+ BL_RESET_MAGIC, BL_RESET_MAGIC_LEN);
+ if (ret < 0) {
+ _e("failed to write ReqForceHwReset");
+ goto fail;
+ }
+ else {
+ _d("sent ReqForceHwReset");
+ }
+
+fail:
+ return ret;
+}
+
+int boot_modem_i9250(void) {
+ int ret;
+ fwloader_context ctx;
+ memset(&ctx, 0, sizeof(ctx));
+
+ ctx.radio_fd = open(I9250_RADIO_IMAGE, O_RDONLY);
+ if (ctx.radio_fd < 0) {
+ _e("failed to open radio firmware");
+ goto fail;
+ }
+ else {
+ _d("opened radio image %s, fd=%d", I9250_RADIO_IMAGE, ctx.radio_fd);
+ }
+
+ if (fstat(ctx.radio_fd, &ctx.radio_stat) < 0) {
+ _e("failed to stat radio image, error %s", strerror(errno));
+ goto fail;
+ }
+
+ ctx.radio_data = mmap(0, RADIO_MAP_SIZE, PROT_READ, MAP_SHARED,
+ ctx.radio_fd, 0);
+ if (ctx.radio_data == MAP_FAILED) {
+ _e("failed to mmap radio image, error %s", strerror(errno));
+ goto fail;
+ }
+
+ ctx.boot_fd = open(BOOT_DEV, O_RDWR);
+ if (ctx.boot_fd < 0) {
+ _e("failed to open boot device");
+ goto fail;
+ }
+ else {
+ _d("opened boot device %s, fd=%d", BOOT_DEV, ctx.boot_fd);
+ }
+
+ if (reboot_modem_i9250(&ctx, true) < 0) {
+ _e("failed to hard reset modem");
+ goto fail;
+ }
+ else {
+ _d("modem hard reset done");
+ }
+
+ /*
+ * Now, actually load the firmware
+ */
+ int i;
+ for (i = 0; i < 2; i++) {
+ if (write(ctx.boot_fd, "ATAT", 4) != 4) {
+ _e("failed to write ATAT to boot socket");
+ goto fail;
+ }
+ else {
+ _d("written ATAT to boot socket, waiting for ACK");
+ }
+
+ if (read_select(ctx.boot_fd, 100) < 0) {
+ _d("failed to select before next ACK, ignoring");
+ }
+ }
+
+ //FIXME: make sure it does not timeout or add the retry in the ril library
+
+ if ((ret = read_select(ctx.boot_fd, 100)) < 0) {
+ _e("failed to wait for bootloader ready state");
+ goto fail;
+ }
+ else {
+ _d("ready for PSI upload");
+ }
+
+ ret = -ETIMEDOUT;
+ for (i = 0; i < I9250_BOOT_REPLY_MAX; i++) {
+ uint32_t id_buf;
+ if ((ret = receive(ctx.boot_fd, (void*)&id_buf, 4)) != 4) {
+ _e("failed receiving bootloader reply");
+ goto fail;
+ }
+ _d("got bootloader reply %08x", id_buf);
+ if (id_buf == I9250_BOOT_LAST_MARKER) {
+ ret = 0;
+ break;
+ }
+ }
+
+ if (ret < 0) {
+ _e("bootloader id marker not received");
+ goto fail;
+ }
+ else {
+ _d("got bootloader id marker");
+ }
+
+ if ((ret = send_PSI_i9250(&ctx)) < 0) {
+ _e("failed to upload PSI");
+ goto fail;
+ }
+ else {
+ _d("PSI download complete");
+ }
+
+ close(ctx.boot_fd);
+ ctx.boot_fd = open(I9250_SECOND_BOOT_DEV, O_RDWR);
+ if (ctx.boot_fd < 0) {
+ _e("failed to open " I9250_SECOND_BOOT_DEV " control device");
+ goto fail;
+ }
+ else {
+ _d("opened second boot device %s, fd=%d", I9250_SECOND_BOOT_DEV, ctx.boot_fd);
+ }
+
+ //RpsiCmdLoadAndExecute
+ if ((ret = write(ctx.boot_fd, I9250_PSI_CMD_EXEC, 4)) < 0) {
+ _e("failed writing cmd_load_exe_EBL");
+ goto fail;
+ }
+ if ((ret = write(ctx.boot_fd, I9250_PSI_EXEC_DATA, 8)) < 0) {
+ _e("failed writing 8 bytes to boot1");
+ goto fail;
+ }
+
+ if ((ret = expect_data(ctx.boot_fd, I9250_GENERAL_ACK, 4)) < 0) {
+ _e("failed to receive cmd_load_exe_EBL ack");
+ goto fail;
+ }
+
+ if ((ret = expect_data(ctx.boot_fd, I9250_PSI_READY_ACK, 4)) < 0) {
+ _e("failed to receive PSI ready ack");
+ goto fail;
+ }
+
+ if ((ret = send_EBL_i9250(&ctx)) < 0) {
+ _e("failed to upload EBL");
+ goto fail;
+ }
+ else {
+ _d("EBL download complete");
+ }
+
+ if ((ret = ack_BootInfo_i9250(&ctx)) < 0) {
+ _e("failed to receive Boot Info");
+ goto fail;
+ }
+ else {
+ _d("Boot Info ACK done");
+ }
+
+ if ((ret = send_SecureImage_i9250(&ctx)) < 0) {
+ _e("failed to upload Secure Image");
+ goto fail;
+ }
+ else {
+ _d("Secure Image download complete");
+ }
+
+ usleep(POST_BOOT_TIMEOUT_US);
+
+ if ((ret = reboot_modem_i9250(&ctx, false))) {
+ _e("failed to soft reset modem");
+ goto fail;
+ }
+ else {
+ _d("modem soft reset done");
+ }
+
+ _i("online");
+
+fail:
+ if (ctx.radio_data != MAP_FAILED) {
+ munmap(ctx.radio_data, RADIO_MAP_SIZE);
+ }
+
+ if (ctx.radio_fd >= 0) {
+ close(ctx.radio_fd);
+ }
+
+ if (ctx.boot_fd >= 0) {
+ close(ctx.boot_fd);
+ }
+
+ return ret;
+}
diff --git a/samsung-ipc/device/xmm6260/io_helpers.c b/samsung-ipc/device/xmm6260/io_helpers.c
new file mode 100644
index 0000000..9ecdb43
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/io_helpers.c
@@ -0,0 +1,89 @@
+/*
+ * io_helpers.c - I/O helper functions for the firmware loader
+ * This file is part of:
+ *
+ * Firmware loader for Samsung I9100 and I9250
+ * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com>
+ *
+ * based on the incomplete C++ implementation which is
+ * Copyright (C) 2012 Sergey Gridasov <grindars@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "io_helpers.h"
+#include "log.h"
+
+#define DEFAULT_TIMEOUT 50
+
+int c_ioctl(int fd, unsigned long code, void* data) {
+ int ret;
+
+ if (!data) {
+ ret = ioctl(fd, code);
+ }
+ else {
+ ret = ioctl(fd, code, data);
+ }
+
+ if (ret < 0) {
+ _e("ioctl fd=%d code=%lx failed: %s", fd, code, strerror(errno));
+ }
+ else {
+ _d("ioctl fd=%d code=%lx OK", fd, code);
+ }
+
+ return ret;
+}
+
+int read_select(int fd, unsigned timeout) {
+ struct timeval tv = {
+ tv.tv_sec = timeout / 1000,
+ tv.tv_usec = 1000 * (timeout % 1000),
+ };
+
+ fd_set read_set;
+ FD_ZERO(&read_set);
+ FD_SET(fd, &read_set);
+
+ return select(fd + 1, &read_set, 0, 0, &tv);
+}
+
+int receive(int fd, void *buf, size_t size) {
+ int ret;
+ if ((ret = read_select(fd, DEFAULT_TIMEOUT)) < 1) {
+ _e("%s: failed to select the fd %d", __func__, fd);
+ return ret;
+ }
+ else {
+ _d("%s: selected %d fds for fd=%d", __func__, ret, fd);
+ }
+
+ return read(fd, buf, size);
+}
+
+int expect_data(int fd, void *data, size_t size) {
+ int ret;
+ char buf[size];
+ if ((ret = receive(fd, buf, size)) != size) {
+ ret = -1;
+ _e("failed to receive data");
+ return ret;
+ }
+ ret = memcmp(buf, data, size);
+ hexdump(buf, size);
+
+ return ret;
+}
+
diff --git a/samsung-ipc/device/xmm6260/io_helpers.h b/samsung-ipc/device/xmm6260/io_helpers.h
new file mode 100644
index 0000000..df3bf20
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/io_helpers.h
@@ -0,0 +1,70 @@
+/*
+ * Firmware loader for Samsung I9100 and I9250
+ * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com>
+ *
+ * based on the incomplete C++ implementation which is
+ * Copyright (C) 2012 Sergey Gridasov <grindars@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __IO_HELPERS_H__
+#define __IO_HELPERS_H__
+
+#include "common.h"
+
+/*
+ * @brief A wrapper around ioctl that prints the error to the log
+ *
+ * @param fd [in] File descriptor of the socket
+ * @param code [in] ioctl code
+ * @param data argument to the ioctl
+ * @return Negative value indicating error code
+ * @return ioctl call result
+ */
+int c_ioctl(int fd, unsigned long code, void* data);
+
+/*
+ * @brief Waits for fd to become available for reading
+ *
+ * @param fd [in] File descriptor of the socket
+ * @param timeout [in] Timeout in milliseconds
+ * @return Negative value indicating error code
+ * @return Available socket number - 1, as select()
+ */
+int read_select(int fd, unsigned timeout);
+
+/*
+ * @brief Waits for data available and reads it to the buffer
+ *
+ * @param fd [in] File descriptor of the socket
+ * @param buf Buffer to hold data
+ * @param size [in] The number of bytes to read
+ * @return Negative value indicating error code
+ * @return The size of data received
+ */
+int receive(int fd, void *buf, size_t size);
+
+/*
+ * @brief Receives data and compares with the pattern in memory
+ *
+ * @param fd [in] File descriptor of the socket
+ * @param data [in] The pattern to compare to
+ * @param size [in] The length of data to read in bytes
+ * @return Negative value indicating error code
+ * @return Available socket number - 1, as select()
+ */
+int expect_data(int fd, void *data, size_t size);
+
+#endif //__IO_HELPERS_H__
diff --git a/samsung-ipc/device/xmm6260/log.c b/samsung-ipc/device/xmm6260/log.c
new file mode 100644
index 0000000..e5d14e0
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/log.c
@@ -0,0 +1,44 @@
+/*
+ * log.c: Log debug macros for the Firmware Loader
+ * This file is part of:
+ *
+ * Firmware loader for Samsung I9100 and I9250
+ * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com>
+ *
+ * based on the incomplete C++ implementation which is
+ * Copyright (C) 2012 Sergey Gridasov <grindars@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "log.h"
+
+void hexdump(void* data, size_t size) {
+ if (size < 1) {
+ return;
+ }
+ char *_data = (char*)data;
+ char __hd_buf[DUMP_SIZE * 3 + 1];
+
+ size_t len = size < DUMP_SIZE ? size : DUMP_SIZE;
+ memset(__hd_buf, 0, sizeof(__hd_buf));
+
+ int i;
+ for (i = 0; i < len; i++) {
+ snprintf(__hd_buf + i * 3, 4, "%02x ", _data[i]);
+ }
+
+ __hd_buf[sizeof(__hd_buf) - 1] = '\0';
+ _d("%s", __hd_buf);
+}
diff --git a/samsung-ipc/device/xmm6260/log.h b/samsung-ipc/device/xmm6260/log.h
new file mode 100644
index 0000000..bfcc65d
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/log.h
@@ -0,0 +1,54 @@
+/*
+ * log.h: Log debug macros for the Firmware Loader
+ * This file is part of:
+ *
+ * Firmware loader for Samsung I9100 and I9250
+ * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com>
+ *
+ * based on the incomplete C++ implementation which is
+ * Copyright (C) 2012 Sergey Gridasov <grindars@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#include "common.h"
+
+#ifndef SILENT
+ #define LOG_TAG "xmm6260-sec"
+ #define _p(fmt, x...) \
+ do {\
+ printf("[" LOG_TAG "]: " fmt " at %s:%s:%d\n", \
+ ##x, __FILE__, __func__, __LINE__); \
+ } while (0)
+#else
+ #define _p(fmt, x...) do {} while (0)
+#endif
+
+#ifdef DEBUG
+ #define _d(fmt, x...) _p("D/" fmt, ##x)
+#else
+ #define _d(fmt, x...) do {} while (0)
+#endif
+
+#define _e(fmt, x...) _p("E/" fmt, ##x)
+#define _i(fmt, x...) _p("I/" fmt, ##x)
+
+#define DUMP_SIZE 16
+
+void hexdump(void* data, size_t size);
+
+#endif //__LOG_H__
diff --git a/samsung-ipc/device/xmm6260/modem_prj.h b/samsung-ipc/device/xmm6260/modem_prj.h
new file mode 100644
index 0000000..df4c37d
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/modem_prj.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __MODEM_PRJ_H__
+#define __MODEM_PRJ_H__
+
+#include <stdint.h>
+
+#define MAX_LINK_DEVTYPE 3
+#define MAX_RAW_DEVS 32
+#define MAX_NUM_IO_DEV (MAX_RAW_DEVS + 4)
+
+#define IOCTL_MODEM_ON _IO('o', 0x19)
+#define IOCTL_MODEM_OFF _IO('o', 0x20)
+#define IOCTL_MODEM_RESET _IO('o', 0x21)
+#define IOCTL_MODEM_BOOT_ON _IO('o', 0x22)
+#define IOCTL_MODEM_BOOT_OFF _IO('o', 0x23)
+#define IOCTL_MODEM_START _IO('o', 0x24)
+
+#define IOCTL_MODEM_PROTOCOL_SUSPEND _IO('o', 0x25)
+#define IOCTL_MODEM_PROTOCOL_RESUME _IO('o', 0x26)
+
+#define IOCTL_MODEM_STATUS _IO('o', 0x27)
+#define IOCTL_MODEM_GOTA_START _IO('o', 0x28)
+#define IOCTL_MODEM_FW_UPDATE _IO('o', 0x29)
+
+#define IOCTL_MODEM_NET_SUSPEND _IO('o', 0x30)
+#define IOCTL_MODEM_NET_RESUME _IO('o', 0x31)
+
+#define IOCTL_MODEM_DUMP_START _IO('o', 0x32)
+#define IOCTL_MODEM_DUMP_UPDATE _IO('o', 0x33)
+#define IOCTL_MODEM_FORCE_CRASH_EXIT _IO('o', 0x34)
+#define IOCTL_MODEM_CP_UPLOAD _IO('o', 0x35)
+#define IOCTL_MODEM_DUMP_RESET _IO('o', 0x36)
+
+#define IOCTL_DPRAM_SEND_BOOT _IO('o', 0x40)
+#define IOCTL_DPRAM_INIT_STATUS _IO('o', 0x43)
+
+/* ioctl command definitions. */
+#define IOCTL_DPRAM_PHONE_POWON _IO('o', 0xd0)
+#define IOCTL_DPRAM_PHONEIMG_LOAD _IO('o', 0xd1)
+#define IOCTL_DPRAM_NVDATA_LOAD _IO('o', 0xd2)
+#define IOCTL_DPRAM_PHONE_BOOTSTART _IO('o', 0xd3)
+
+#define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xde)
+#define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xdf)
+
+/* modem status */
+#define MODEM_OFF 0
+#define MODEM_CRASHED 1
+#define MODEM_RAMDUMP 2
+#define MODEM_POWER_ON 3
+#define MODEM_BOOTING_NORMAL 4
+#define MODEM_BOOTING_RAMDUMP 5
+#define MODEM_DUMPING 6
+#define MODEM_RUNNING 7
+
+#define HDLC_HEADER_MAX_SIZE 6 /* fmt 3, raw 6, rfs 6 */
+
+#define PSD_DATA_CHID_BEGIN 0x2A
+#define PSD_DATA_CHID_END 0x38
+
+#define IP6VERSION 6
+
+#define SOURCE_MAC_ADDR {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}
+
+/* Does modem ctl structure will use state ? or status defined below ?*/
+enum modem_state {
+ STATE_OFFLINE,
+ STATE_CRASH_RESET, /* silent reset */
+ STATE_CRASH_EXIT, /* cp ramdump */
+ STATE_BOOTING,
+ STATE_ONLINE,
+ STATE_NV_REBUILDING, /* <= rebuilding start */
+ STATE_LOADER_DONE,
+};
+
+enum com_state {
+ COM_NONE,
+ COM_ONLINE,
+ COM_HANDSHAKE,
+ COM_BOOT,
+ COM_CRASH,
+};
+
+struct header_data {
+ char hdr[HDLC_HEADER_MAX_SIZE];
+ unsigned len;
+ unsigned frag_len;
+ char start; /*hdlc start header 0x7F*/
+};
+
+struct sipc4_hdlc_fmt_hdr {
+ uint16_t len;
+ uint8_t control;
+} __attribute__((packed));
+
+struct sipc4_fmt_hdr {
+ uint16_t len;
+ uint8_t msg_seq;
+ uint8_t ack_seq;
+ uint8_t main_cmd;
+ uint8_t sub_cmd;
+ uint8_t cmd_type;
+} __attribute__((packed));
+
+//Link control
+
+#define IOCTL_LINK_CONTROL_ENABLE _IO('o', 0x30)
+#define IOCTL_LINK_CONTROL_ACTIVE _IO('o', 0x31)
+#define IOCTL_LINK_GET_HOSTWAKE _IO('o', 0x32)
+#define IOCTL_LINK_CONNECTED _IO('o', 0x33)
+#define IOCTL_LINK_SET_BIAS_CLEAR _IO('o', 0x34)
+
+#endif //__MODEM_PRJ_H__
diff --git a/samsung-ipc/device/xmm6260/modemctl_common.c b/samsung-ipc/device/xmm6260/modemctl_common.c
new file mode 100644
index 0000000..90fd5fe
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/modemctl_common.c
@@ -0,0 +1,116 @@
+/*
+ * Firmware loader for Samsung I9100 and I9250
+ * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com>
+ *
+ * based on the incomplete C++ implementation which is
+ * Copyright (C) 2012 Sergey Gridasov <grindars@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//modemctl shared code
+#include "modemctl_common.h"
+
+/*
+ * modemctl generic functions
+ */
+int modemctl_link_set_active(fwloader_context *ctx, bool enabled) {
+ unsigned status = enabled;
+ int ret;
+ unsigned long ioctl_code;
+
+ ioctl_code = IOCTL_LINK_CONTROL_ACTIVE;
+ ret = c_ioctl(ctx->link_fd, ioctl_code, &status);
+
+ if (ret < 0) {
+ _d("failed to set link active to %d", enabled);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return ret;
+}
+
+int modemctl_link_set_enabled(fwloader_context *ctx, bool enabled) {
+ unsigned status = enabled;
+ int ret;
+ unsigned long ioctl_code;
+
+ ioctl_code = IOCTL_LINK_CONTROL_ENABLE;
+ ret = c_ioctl(ctx->link_fd, ioctl_code, &status);
+
+ if (ret < 0) {
+ _d("failed to set link state to %d", enabled);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return ret;
+}
+
+int modemctl_wait_link_ready(fwloader_context *ctx) {
+ int ret;
+
+ struct timeval tv_start = {};
+ struct timeval tv_end = {};
+
+ gettimeofday(&tv_start, 0);;
+
+ //link wakeup timeout in milliseconds
+ long diff = 0;
+
+ do {
+ ret = c_ioctl(ctx->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_modem_power(fwloader_context *ctx, bool enabled) {
+ if (enabled) {
+ return c_ioctl(ctx->boot_fd, IOCTL_MODEM_ON, 0);
+ }
+ else {
+ return c_ioctl(ctx->boot_fd, IOCTL_MODEM_OFF, 0);
+ }
+ return -1;
+}
+
+int modemctl_modem_boot_power(fwloader_context *ctx, bool enabled) {
+ if (enabled) {
+ return c_ioctl(ctx->boot_fd, IOCTL_MODEM_BOOT_ON, 0);
+ }
+ else {
+ return c_ioctl(ctx->boot_fd, IOCTL_MODEM_BOOT_OFF, 0);
+ }
+ return -1;
+}
diff --git a/samsung-ipc/device/xmm6260/modemctl_common.h b/samsung-ipc/device/xmm6260/modemctl_common.h
new file mode 100644
index 0000000..8817d94
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/modemctl_common.h
@@ -0,0 +1,148 @@
+/*
+ * Firmware loader for Samsung I9100 and I9250
+ * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com>
+ *
+ * based on the incomplete C++ implementation which is
+ * Copyright (C) 2012 Sergey Gridasov <grindars@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __MODEMCTL_COMMON_H__
+#define __MODEMCTL_COMMON_H__
+
+#include "common.h"
+#include "log.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)
+
+typedef struct {
+ int link_fd;
+ int boot_fd;
+
+ int radio_fd;
+ char *radio_data;
+ struct stat radio_stat;
+} fwloader_context;
+
+/*
+ * 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,
+};
+
+/*
+ * Function prototypes
+ */
+
+/*
+ * @brief Activates the modem <-> cpu link data transfer
+ *
+ * @param ctx [in] firmware loader context
+ * @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(fwloader_context *ctx, bool enabled);
+
+/*
+ * @brief Activates the modem <-> cpu link connection
+ *
+ * @param ctx [in] firmware loader context
+ * @param enabled [in] the state to set link to
+ * @return Negative value indicating error code
+ * @return ioctl call result
+ */
+int modemctl_link_set_enabled(fwloader_context *ctx, bool enabled);
+
+/*
+ * @brief Poll the link until it gets ready or times out
+ *
+ * @param ctx [in] firmware loader context
+ * @return Negative value indicating error code
+ * @return ioctl call result
+ */
+int modemctl_wait_link_ready(fwloader_context *ctx);
+
+/*
+ * @brief Sets the modem power
+ *
+ * @param ctx [in] firmware loader context
+ * @param enabled [in] whether to enable or disable modem power
+ * @return Negative value indicating error code
+ * @return ioctl call result
+ */
+int modemctl_modem_power(fwloader_context *ctx, bool enabled);
+
+/*
+ * @brief Sets the modem bootloader power/UART configuration
+ *
+ * @param ctx [in] firmware loader context
+ * @param enabled [in] whether to enable or disable power
+ * @return Negative value indicating error code
+ * @return ioctl call result
+ */
+int modemctl_modem_boot_power(fwloader_context *ctx, bool enabled);
+
+/*
+ * @brief Boots the modem on the I9100 (Galaxy S2) board
+ *
+ * @return Negative value indicating error code
+ * @return zero on success
+ */
+int boot_modem_i9100(void);
+
+/*
+ * @brief Boots the modem on the I9250 (Galaxy Nexus) board
+ *
+ * @return Negative value indicating error code
+ * @return zero on success
+ */
+int boot_modem_i9250(void);
+
+#endif //__MODEMCTL_COMMON_H__
diff --git a/samsung-ipc/device/xmm6260/xmm6260_ipc.c b/samsung-ipc/device/xmm6260/xmm6260_ipc.c
new file mode 100644
index 0000000..4c33001
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/xmm6260_ipc.c
@@ -0,0 +1,362 @@
+/**
+ * This file is part of libsamsung-ipc.
+ *
+ * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com>
+ * based on xmm6260 IPC code which is:
+ *
+ * Copyright (C) 2011 Paul Kocialkowski <contact@paulk.fr>
+ * Joerie de Gram <j.de.gram@gmail.com>
+ * Simon Busch <morphis@gravedo.de>
+ *
+ * libsamsung-ipc 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.
+ *
+ * libsamsung-ipc 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 libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <mtd/mtd-abi.h>
+#include <assert.h>
+
+#include <radio.h>
+
+#include "xmm6260_ipc.h"
+#include "ipc_private.h"
+
+#include "modemctl_common.h"
+
+extern int boot_modem(void);
+
+int i9100_modem_bootstrap(struct ipc_client *client)
+{
+ return boot_modem_i9100();
+}
+
+int i9250_modem_bootstrap(struct ipc_client *client)
+{
+ return boot_modem_i9250();
+}
+
+int xmm6260_ipc_send(struct ipc_client *client, struct ipc_message_info *request)
+{
+ struct ipc_header *hdr;
+ unsigned char *frame;
+ unsigned char *payload;
+ size_t frame_length;
+
+ /* Frame IPC header + payload length */
+ frame_length = (sizeof(*hdr) + request->length);
+
+ frame = (unsigned char*)malloc(frame_length);
+ hdr = (struct ipc_header*)frame;
+
+ /* IPC header */
+ hdr->length = frame_length;
+ hdr->mseq = request->mseq;
+ hdr->aseq = request->aseq;
+ hdr->group = request->group;
+ hdr->index = request->index;
+ hdr->type = request->type;
+
+ /* IPC payload */
+ payload = (frame + sizeof(*hdr));
+ memcpy(payload, request->data, request->length);
+
+ ipc_client_log(client, "sending %s %s\n",
+ ipc_command_to_str(IPC_COMMAND(request)),
+ ipc_response_type_to_str(request->type));
+
+ ipc_hex_dump(client, frame, frame_length);
+
+ client->handlers->write(frame, frame_length, client->handlers->write_data);
+
+ free(frame);
+
+ return 0;
+}
+
+//HACK
+#define IPC_BS 1024
+
+int xmm6260_ipc_recv(struct ipc_client *client, struct ipc_message_info *response)
+{
+ unsigned char buf[IPC_BS] = {};
+ unsigned char *data;
+ unsigned short *frame_length;
+
+ struct ipc_header ipc = {};
+ int num_read = 0;
+ int left = 0;
+
+ num_read = client->handlers->read(buf, IPC_BS,// sizeof(ipc),
+ client->handlers->read_data);
+
+ ipc_client_log(client, "read %d", num_read);
+
+ if (num_read < 0) {
+ ipc_client_log(client, "read failed to read ipc length: %d", num_read);
+
+ response->data = 0;
+ response->length = 0;
+ return 0;
+ }
+ else {
+ ipc_client_log(client, "we've read %d", num_read);
+ }
+
+ memcpy(&ipc, buf, sizeof(ipc));
+ ipc_client_log(client, "read %d bytes for header", num_read);
+
+ ipc_client_log(client, "ipc: %d bytes in header", ipc.length);
+
+ left = ipc.length - num_read;
+ ipc_client_log(client, "left %d bytes of data", left);
+
+ if (left > 0) {
+ num_read = client->handlers->read(buf + num_read, left,
+ client->handlers->read_data);
+ ipc_client_log(client, "read %d bytes of %d left",
+ num_read, left);
+ }
+
+ memcpy(&ipc, buf, sizeof(ipc));
+
+ response->mseq = ipc.mseq;
+ response->aseq = ipc.aseq;
+ response->group = ipc.group;
+ response->index = ipc.index;
+ response->type = ipc.type;
+ response->length = ipc.length - sizeof(ipc);
+
+ response->data = (unsigned char*)malloc(response->length);
+ memcpy(response->data, buf + sizeof(ipc), response->length);
+
+ ipc_client_log(client, "received %s %s\n",
+ ipc_command_to_str(IPC_COMMAND(response)),
+
+ ipc_response_type_to_str(response->type));
+ ipc_hex_dump(client, response->data, left);
+
+ return 0;
+}
+
+int xmm6260_rfs_recv(struct ipc_client *client, struct ipc_message_info *response)
+{
+ printf("+%s\n", __func__);
+ return 0;
+}
+int xmm6260_rfs_send(struct ipc_client *client, struct ipc_message_info *request)
+{
+ printf("+%s\n", __func__);
+ return 0;
+}
+
+int xmm6260_ipc_open(void *data, unsigned int size, void *io_data)
+{
+ int type = *((int *) data);
+ int fd = -1;
+
+ switch(type)
+ {
+ case IPC_CLIENT_TYPE_FMT:
+ fd = open("/dev/umts_ipc0", O_RDWR | O_NOCTTY | O_NONBLOCK);
+ break;
+ case IPC_CLIENT_TYPE_RFS:
+ fd = open("/dev/umts_rfs0", O_RDWR | O_NOCTTY | O_NONBLOCK);
+ break;
+ default:
+ break;
+ }
+
+ if(fd < 0)
+ return -1;
+
+ if(io_data == NULL)
+ return -1;
+
+ memcpy(io_data, &fd, sizeof(int));
+
+ return 0;
+}
+
+int xmm6260_ipc_close(void *data, unsigned int size, void *io_data)
+{
+ int fd = -1;
+
+ if(io_data == NULL)
+ return -1;
+
+ fd = *((int *) io_data);
+
+ if(fd < 0)
+ return -1;
+
+ close(fd);
+
+ return 0;
+}
+
+int xmm6260_ipc_read(void *data, unsigned int size, void *io_data)
+{
+ int fd = -1;
+ int rc;
+
+ if(io_data == NULL)
+ return -1;
+
+ if(data == NULL)
+ return -1;
+
+ fd = *((int *) io_data);
+
+ if(fd < 0)
+ return -1;
+
+ printf("%s: wanting %d bytes\n", __func__, size);
+
+ //hack
+ read_select(fd, 100);
+
+ rc = read(fd, data, size);
+ printf("%s: read %d bytes\n", __func__, rc);
+
+ if(rc < 0)
+ return -1;
+
+ return rc;
+}
+
+int xmm6260_ipc_write(void *data, unsigned int size, void *io_data)
+{
+ int fd = -1;
+ int rc;
+
+ if(io_data == NULL)
+ return -1;
+
+ fd = *((int *) io_data);
+
+ if(fd < 0)
+ return -1;
+
+ rc = write(fd, data, size);
+
+ if(rc < 0)
+ return -1;
+
+ return rc;
+}
+
+int xmm6260_ipc_power_on(void *io_data)
+{
+ return 0;
+}
+
+int xmm6260_ipc_power_off(void *io_data)
+{
+ return 0;
+}
+
+void *xmm6260_ipc_common_data_create(void)
+{
+ void *io_data;
+ int io_data_len;
+
+ io_data_len = sizeof(int);
+ io_data = malloc(io_data_len);
+
+ if(io_data == NULL)
+ return NULL;
+
+ memset(io_data, 0, io_data_len);
+
+ return io_data;
+}
+
+int xmm6260_ipc_common_data_destroy(void *io_data)
+{
+ // This was already done, not an error but we need to return
+ if(io_data == NULL)
+ return 0;
+
+ free(io_data);
+
+ return 0;
+}
+
+int xmm6260_ipc_common_data_set_fd(void *io_data, int fd)
+{
+ int *common_data;
+
+ if(io_data == NULL)
+ return -1;
+
+ common_data = (int *) io_data;
+ common_data = &fd;
+
+ return 0;
+}
+
+int xmm6260_ipc_common_data_get_fd(void *io_data)
+{
+ int *common_data;
+
+ if(io_data == NULL)
+ return -1;
+
+ common_data = (int *) io_data;
+
+ return (int) *(common_data);
+}
+
+struct ipc_handlers xmm6260_default_handlers = {
+ .read = xmm6260_ipc_read,
+ .write = xmm6260_ipc_write,
+ .open = xmm6260_ipc_open,
+ .close = xmm6260_ipc_close,
+ .power_on = xmm6260_ipc_power_on,
+ .power_off = xmm6260_ipc_power_off,
+ .common_data = NULL,
+ .common_data_create = xmm6260_ipc_common_data_create,
+ .common_data_destroy = xmm6260_ipc_common_data_destroy,
+ .common_data_set_fd = xmm6260_ipc_common_data_set_fd,
+ .common_data_get_fd = xmm6260_ipc_common_data_get_fd,
+};
+
+struct ipc_ops xmm6260_i9100_fmt_ops = {
+ .send = xmm6260_ipc_send,
+ .recv = xmm6260_ipc_recv,
+ .bootstrap = i9100_modem_bootstrap,
+};
+
+struct ipc_ops xmm6260_i9250_fmt_ops = {
+ .send = xmm6260_ipc_send,
+ .recv = xmm6260_ipc_recv,
+ .bootstrap = i9250_modem_bootstrap,
+};
+
+struct ipc_ops xmm6260_rfs_ops = {
+ .send = xmm6260_rfs_send,
+ .recv = xmm6260_rfs_recv,
+ .bootstrap = NULL,
+};
+
+// vim:ts=4:sw=4:expandtab
diff --git a/samsung-ipc/device/xmm6260/xmm6260_ipc.h b/samsung-ipc/device/xmm6260/xmm6260_ipc.h
new file mode 100644
index 0000000..4529dd6
--- /dev/null
+++ b/samsung-ipc/device/xmm6260/xmm6260_ipc.h
@@ -0,0 +1,31 @@
+/**
+ * This file is part of libsamsung-ipc.
+ *
+ * Copyright (C) 2012 Alexander Tarasikov <alexander.tarasikov@gmail.com>
+ * based on crespo IPC code which is:
+ *
+ * Copyright (C) 2011 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * libsamsung-ipc 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.
+ *
+ * libsamsung-ipc 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 libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __XMM6260_IPC_H__
+#define __XMM6260_IPC_H__
+
+extern struct ipc_handlers xmm6260_ipc_default_handlers;
+
+#endif
+
+// vim:ts=4:sw=4:expandtab
diff --git a/samsung-ipc/ipc_devices.c b/samsung-ipc/ipc_devices.c
index f3f2b17..cd38ff6 100644
--- a/samsung-ipc/ipc_devices.c
+++ b/samsung-ipc/ipc_devices.c
@@ -23,6 +23,13 @@
#include "ipc_devices.h"
struct ipc_device_desc ipc_devices[] = {
+#if 0
+ { "xmm6260", "i9100", &xmm6260_default_handlers, &xmm6260_i9100_fmt_ops, &xmm6260_rfs_ops },
+ { "xmm6260", "SMDK4210", &xmm6260_default_handlers, &xmm6260_i9100_fmt_ops, &xmm6260_rfs_ops },
+
+ { "xmm6260", "maguro", &xmm6260_default_handlers, &xmm6260_i9250_fmt_ops, &xmm6260_rfs_ops },
+#endif
+ { "xmm6260", "Tuna", &xmm6260_default_handlers, &xmm6260_i9250_fmt_ops, &xmm6260_rfs_ops },
{ "crespo", "herring", &crespo_default_handlers, &crespo_fmt_ops, &crespo_rfs_ops },
{ "aries", "aries", &aries_default_handlers, &aries_fmt_ops, &aries_rfs_ops },
{ "aries", "gt-p1000", &aries_default_handlers, &aries_fmt_ops, &aries_rfs_ops }
diff --git a/samsung-ipc/ipc_devices.h b/samsung-ipc/ipc_devices.h
index baddb76..c854d8b 100644
--- a/samsung-ipc/ipc_devices.h
+++ b/samsung-ipc/ipc_devices.h
@@ -35,6 +35,14 @@ struct ipc_device_desc {
extern struct ipc_device_desc ipc_devices[];
extern int ipc_devices_count;
+// xmm6260
+
+extern struct ipc_handlers xmm6260_default_handlers;
+extern struct ipc_ops xmm6260_i9100_fmt_ops;
+extern struct ipc_ops xmm6260_i9250_fmt_ops;
+extern struct ipc_ops xmm6260_rfs_ops;
+
+
// h1
extern struct ipc_handlers h1_default_handlers;