From 3ee91dd1a20083d571718e2ee07570ac2c8cfe7c Mon Sep 17 00:00:00 2001 From: Alexander Tarasikov Date: Sat, 30 Jun 2012 02:39:43 +0400 Subject: Add the dirty, hackish and barely working xmm6260 modem support for galaxy nexus (tuna/maguro) --- Android.mk | 18 +- samsung-ipc/device/xmm6260/common.h | 50 ++ samsung-ipc/device/xmm6260/fwloader_i9100.c | 776 +++++++++++++++++++++++ samsung-ipc/device/xmm6260/fwloader_i9250.c | 883 +++++++++++++++++++++++++++ samsung-ipc/device/xmm6260/io_helpers.c | 89 +++ samsung-ipc/device/xmm6260/io_helpers.h | 70 +++ samsung-ipc/device/xmm6260/log.c | 44 ++ samsung-ipc/device/xmm6260/log.h | 54 ++ samsung-ipc/device/xmm6260/modem_prj.h | 127 ++++ samsung-ipc/device/xmm6260/modemctl_common.c | 116 ++++ samsung-ipc/device/xmm6260/modemctl_common.h | 148 +++++ samsung-ipc/device/xmm6260/xmm6260_ipc.c | 362 +++++++++++ samsung-ipc/device/xmm6260/xmm6260_ipc.h | 31 + samsung-ipc/ipc_devices.c | 7 + samsung-ipc/ipc_devices.h | 8 + 15 files changed, 2782 insertions(+), 1 deletion(-) create mode 100644 samsung-ipc/device/xmm6260/common.h create mode 100644 samsung-ipc/device/xmm6260/fwloader_i9100.c create mode 100644 samsung-ipc/device/xmm6260/fwloader_i9250.c create mode 100644 samsung-ipc/device/xmm6260/io_helpers.c create mode 100644 samsung-ipc/device/xmm6260/io_helpers.h create mode 100644 samsung-ipc/device/xmm6260/log.c create mode 100644 samsung-ipc/device/xmm6260/log.h create mode 100644 samsung-ipc/device/xmm6260/modem_prj.h create mode 100644 samsung-ipc/device/xmm6260/modemctl_common.c create mode 100644 samsung-ipc/device/xmm6260/modemctl_common.h create mode 100644 samsung-ipc/device/xmm6260/xmm6260_ipc.c create mode 100644 samsung-ipc/device/xmm6260/xmm6260_ipc.h 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 + * + * 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 __COMMON_H__ +#define __COMMON_H__ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +//for timeval +#include + +//for mmap +#include +#include + +#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 + * + * 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" + +/* + * 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 + * + * 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" + +/* + * 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 + * + * 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 "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 + * + * 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 __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 + * + * 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 "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 + * + * 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 __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 + +#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 + * + * 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(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 + * + * 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 "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 + * based on xmm6260 IPC code which is: + * + * Copyright (C) 2011 Paul Kocialkowski + * Joerie de Gram + * Simon Busch + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 + * based on crespo IPC code which is: + * + * Copyright (C) 2011 Paul Kocialkowski + * + * 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 . + * + */ + +#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; -- cgit v1.1