aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk10
-rw-r--r--samsung-ipc/device/galaxysmtd/galaxysmtd_ipc.c609
-rw-r--r--samsung-ipc/device/galaxysmtd/galaxysmtd_ipc.h55
-rw-r--r--samsung-ipc/device/galaxysmtd/galaxysmtd_modem_ctl.h44
-rw-r--r--samsung-ipc/device/galaxysmtd/galaxysmtd_nv_data.c356
-rw-r--r--samsung-ipc/device/galaxysmtd/galaxysmtd_nv_data.h41
6 files changed, 1115 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
index 0525ce2..8e79c8f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -26,6 +26,13 @@ ifeq ($(TARGET_DEVICE),crespo)
LOCAL_CFLAGS += -DDEVICE_CRESPO
endif
+ifeq ($(TARGET_DEVICE),galaxysmtd)
+ device_files := samsung-ipc/device/$(TARGET_DEVICE)/$(TARGET_DEVICE)_nv_data.c
+ LOCAL_CFLAGS += -Iexternal/openssl/include
+ LOCAL_LDFLAGS += -lcrypto
+ LOCAL_CFLAGS += -DDEVICE_CRESPO
+endif
+
ifeq ($(TARGET_DEVICE),h1)
LOCAL_CFLAGS += -DDEVICE_H1
endif
@@ -55,6 +62,9 @@ modemctrl_files := tools/modemctrl.c
ifeq ($(TARGET_DEVICE),crespo)
LOCAL_CFLAGS += -DDEVICE_CRESPO
endif
+ifeq ($(TARGET_DEVICE),galaxysmtd)
+ LOCAL_CFLAGS += -DDEVICE_CRESPO
+endif
ifeq ($(TARGET_DEVICE),h1)
LOCAL_CFLAGS += -DDEVICE_H1
endif
diff --git a/samsung-ipc/device/galaxysmtd/galaxysmtd_ipc.c b/samsung-ipc/device/galaxysmtd/galaxysmtd_ipc.c
new file mode 100644
index 0000000..04d24c4
--- /dev/null
+++ b/samsung-ipc/device/galaxysmtd/galaxysmtd_ipc.c
@@ -0,0 +1,609 @@
+/**
+ * This file is part of libsamsung-ipc.
+ *
+ * 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/mman.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 "galaxysmtd_modem_ctl.h"
+#include "galaxysmtd_nv_data.h"
+#include "galaxysmtd_ipc.h"
+#include "ipc_private.h"
+
+int wake_lock_fd= -1;
+int wake_unlock_fd= -1;
+
+int galaxysmtd_modem_bootstrap(struct ipc_client *client)
+{
+ int s3c2410_serial3_fd= -1;
+ int modem_ctl_fd= -1;
+
+ /* Control variables. */
+ int boot_tries_count=0;
+ int rc=0;
+
+ /* Boot variables */
+ uint8_t *radio_img_p=NULL;
+ uint8_t bootcore_version=0;
+ uint8_t info_size=0;
+ uint8_t crc_byte=0;
+ int block_size=0;
+
+ /* s3c2410 serial setup variables. */
+ struct termios termios;
+ int serial;
+
+ /* fds maniplation variables */
+ struct timeval timeout;
+ fd_set fds;
+
+ /* nv_data variables */
+ void *nv_data_p;
+
+ /* General purpose variables. */
+ uint8_t data;
+ uint16_t data_16;
+ uint8_t *data_p;
+ unsigned int modem_ctl_data;
+ int i;
+ void *onedram_map=NULL;
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: enter");
+
+boot_loop_start:
+ if(boot_tries_count > 5)
+ {
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: boot has failed too many times.");
+ goto error;
+ }
+
+ //TODO try to disable svnet0 first?
+
+ /* Read the radio.img image. */
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: reading radio image");
+ radio_img_p = ipc_mtd_read(client, "/dev/block/bml12", RADIO_IMG_SIZE, 0x1000);
+ if (radio_img_p == NULL) {
+ goto error;
+ }
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: radio image read");
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: open modem_ctl");
+ modem_ctl_fd = open("/dev/onedram", O_RDWR);
+ if(modem_ctl_fd < 0)
+ goto error_loop;
+
+ /* Reset the modem before init to send the first part of modem.bin. */
+ ipc_client_power_off(client);
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: sent PHONE \"off\" command");
+ usleep(1000);
+ ipc_client_power_on(client);
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: sent PHONE \"on\" command");
+ usleep(200000);
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: open s3c2410_serial3");
+ s3c2410_serial3_fd = open("/dev/s3c2410_serial3", O_RDWR);
+ if(s3c2410_serial3_fd < 0)
+ goto error_loop;
+
+ /* Setup the s3c2410 serial. */
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: setup s3c2410_serial3");
+
+ ioctl(s3c2410_serial3_fd, TIOCMGET, &serial); //FIXME
+ ioctl(s3c2410_serial3_fd, TIOCMSET, &serial); //FIXME
+
+ tcgetattr(s3c2410_serial3_fd, &termios); //FIXME
+ tcsetattr(s3c2410_serial3_fd, TCSANOW, &termios); //FIXME
+
+ tcgetattr(s3c2410_serial3_fd, &termios);
+
+ cfmakeraw(&termios);
+ cfsetispeed(&termios, B115200);
+ cfsetospeed(&termios, B115200);
+
+ tcsetattr(s3c2410_serial3_fd, TCSANOW, &termios);
+
+ /* Send 'AT' in ASCII. */
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: sending AT in ASCII");
+ for(i=0 ; i < 20 ; i++)
+ {
+ rc = write(s3c2410_serial3_fd, "AT", 2);
+ usleep(50000);
+ }
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: sending AT in ASCII done");
+
+ usleep(50000); //FIXME
+
+ /* Get and check bootcore version. */
+ read(s3c2410_serial3_fd, &bootcore_version, sizeof(bootcore_version));
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: got bootcore version: 0x%x", bootcore_version);
+
+ if(bootcore_version != BOOTCORE_VERSION)
+ goto error_loop;
+
+ /* Get info_size. */
+ read(s3c2410_serial3_fd, &info_size, sizeof(info_size));
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: got info_size: 0x%x", info_size);
+
+ /* Send PSI magic. */
+ data=PSI_MAGIC;
+ write(s3c2410_serial3_fd, &data, sizeof(data));
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: sent PSI_MAGIC (0x%x)", PSI_MAGIC);
+
+ /* Send PSI data len. */
+ data_16=PSI_DATA_LEN;
+ data_p=(uint8_t *)&data_16;
+
+ for(i=0 ; i < 2 ; i++)
+ {
+ write(s3c2410_serial3_fd, data_p, 1);
+ data_p++;
+ }
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: sent PSI_DATA_LEN (0x%x)", PSI_DATA_LEN);
+
+ /* Write the first part of modem.img. */
+ FD_ZERO(&fds);
+ FD_SET(s3c2410_serial3_fd, &fds);
+
+ timeout.tv_sec=5;
+ timeout.tv_usec=0;
+
+ data_p=radio_img_p;
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: sending the first part of modem.bin");
+
+ crc_byte=0;
+ for(i=0 ; i < PSI_DATA_LEN ; i++)
+ {
+ if(select(FD_SETSIZE, NULL, &fds, NULL, &timeout) == 0)
+ {
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: select timeout passed");
+ goto error_loop;
+ }
+
+ write(s3c2410_serial3_fd, data_p, 1);
+ crc_byte=crc_byte ^ *data_p;
+
+ data_p++;
+ }
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: first part of modem.bin sent; crc_byte is 0x%x", crc_byte);
+
+ if(select(FD_SETSIZE, NULL, &fds, NULL, &timeout) == 0)
+ {
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: select timeout passed");
+ goto error_loop;
+ }
+
+ write(s3c2410_serial3_fd, &crc_byte, sizeof(crc_byte));
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: crc_byte sent");
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: wait for ACK");
+ data = 0;
+ for(i = 0 ; data != 0x01 ; i++)
+ {
+ if(select(FD_SETSIZE, &fds, NULL, NULL, &timeout) == 0)
+ {
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: select timeout passed");
+ goto error_loop;
+ }
+
+ read(s3c2410_serial3_fd, &data, sizeof(data));
+
+ if(i > 50)
+ {
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: fairly too much attempts to get ACK");
+ goto error_loop;
+ }
+ }
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: got ACK");
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: close s3c2410_serial3");
+ close(s3c2410_serial3_fd);
+
+ FD_ZERO(&fds);
+ FD_SET(modem_ctl_fd, &fds);
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: wait for 0x12341234");
+ if(select(modem_ctl_fd+1, &fds, NULL, NULL, &timeout) == 0)
+ {
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: select timeout passed");
+ goto error_loop;
+ }
+
+ read(modem_ctl_fd, &modem_ctl_data, sizeof(modem_ctl_data));
+ if(modem_ctl_data != 0x12341234) {
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: wrong ACK flag from modem_ctl");
+ goto error_loop;
+ }
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: got 0x12341234");
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: writing the rest of modem.bin to modem_ctl.");
+
+ /* Pointer to the remaining part of radio.img. */
+ data_p=radio_img_p + PSI_DATA_LEN;
+
+ size_t map_length = 16773120; //magic buflen comes from proprietary ril trace
+ onedram_map = mmap(NULL, map_length, PROT_READ|PROT_WRITE, MAP_SHARED, modem_ctl_fd, 0);
+ if(onedram_map == -1) {
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: could not map onedram to memory");
+ goto error_loop;
+ }
+
+ memcpy(onedram_map, data_p, RADIO_IMG_SIZE - PSI_DATA_LEN);
+
+ free(radio_img_p);
+
+ /* nv_data part. */
+
+ /* Check if all the nv_data files are ok. */
+ nv_data_check(client);
+
+ /* Check if the MD5 is ok. */
+ nv_data_md5_check(client);
+
+ /* Write nv_data.bin to modem_ctl. */
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: write nv_data to modem_ctl");
+
+ nv_data_p = ipc_file_read(client, "/efs/nv_data.bin", NV_DATA_SIZE, 1024);
+ if (nv_data_p == NULL)
+ goto error;
+ data_p = nv_data_p;
+
+ //seems 0xd80000 is the absolute maximum for the radio_img, like crespo
+ //uses. it creates a gap though, since we only write 0x9fb000 bytes to /dev/onedram
+ memcpy(onedram_map + 0xd80000, data_p, NV_DATA_SIZE);
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: done writing nv_data");
+
+ free(nv_data_p);
+ munmap(onedram_map, map_length);
+
+ //TODO finalize bootstrap
+ ioctl(modem_ctl_fd, 0x6f22, 0); //magic request comes from proprietary ril trace
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: send 0x45674567");
+ modem_ctl_data = 0x45674567;
+ write(modem_ctl_fd, &modem_ctl_data, sizeof(modem_ctl_data));
+
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: wait for 0xabcdabcd");
+ for(i = 0 ; modem_ctl_data != 0xabcdabcd ; i++)
+ {
+ if(select(FD_SETSIZE, &fds, NULL, NULL, &timeout) == 0)
+ {
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: select timeout passed");
+ goto error_loop;
+ }
+
+ read(modem_ctl_fd, &modem_ctl_data, sizeof(modem_ctl_data));
+
+ if(i > 10)
+ {
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: fairly too much attempts to get 0xabcdabcd");
+ goto error_loop;
+ }
+ }
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: got 0xabcdabcd");
+
+ close(modem_ctl_fd);
+
+ rc = 0;
+ goto exit;
+
+error_loop:
+ ipc_client_log(client, "%s: something went wrong", __func__);
+ boot_tries_count++;
+ sleep(2);
+
+ goto boot_loop_start;
+
+error:
+ ipc_client_log(client, "%s: something went wrong", __func__);
+ rc = 1;
+exit:
+ ipc_client_log(client, "galaxysmtd_ipc_bootstrap: exit");
+ return rc;
+}
+
+int crespo_ipc_client_send(struct ipc_client *client, struct ipc_message_info *request)
+{
+ struct modem_io modem_data;
+ struct ipc_header reqhdr;
+ int rc = 0;
+
+ memset(&modem_data, 0, sizeof(struct modem_io));
+ modem_data.size = request->length + sizeof(struct ipc_header);
+
+ reqhdr.mseq = request->mseq;
+ reqhdr.aseq = request->aseq;
+ reqhdr.group = request->group;
+ reqhdr.index = request->index;
+ reqhdr.type = request->type;
+ reqhdr.length = (uint16_t) (request->length + sizeof(struct ipc_header));
+
+ modem_data.data = malloc(reqhdr.length);
+
+ memcpy(modem_data.data, &reqhdr, sizeof(struct ipc_header));
+ memcpy((unsigned char *) (modem_data.data + sizeof(struct ipc_header)), request->data, request->length);
+
+ assert(client->handlers->write != NULL);
+
+ ipc_client_log(client, "INFO: crespo_ipc_client_send: Modem SEND FMT (id=%d cmd=%d size=%d)!", modem_data.id, modem_data.cmd, modem_data.size);
+ ipc_client_log(client, "INFO: crespo_ipc_client_send: request: type = %d (%s), group = %d, index = %d (%s)",
+ request->type, ipc_request_type_to_str(request->type), request->group, request->index, ipc_command_type_to_str(IPC_COMMAND(request)));
+
+#ifdef DEBUG
+ if(request->length > 0)
+ {
+ ipc_client_log(client, "INFO: ==== DATA DUMP ====");
+ ipc_hex_dump(client, (void *) request->data, request->length);
+ }
+#endif
+
+ ipc_client_log(client, "");
+
+ rc = client->handlers->write((uint8_t*) &modem_data, sizeof(struct modem_io), client->handlers->write_data);
+ return rc;
+}
+
+int wake_lock(char *lock_name, int len)
+{
+ int rc = 0;
+
+ wake_lock_fd = open("/sys/power/wake_lock", O_RDWR);
+ rc = write(wake_lock_fd, lock_name, len);
+ close(wake_lock_fd);
+
+ return rc;
+}
+
+int wake_unlock(char *lock_name, int len)
+{
+ int rc = 0;
+
+ wake_lock_fd = open("/sys/power/wake_unlock", O_RDWR);
+ rc = write(wake_lock_fd, lock_name, len);
+ close(wake_unlock_fd);
+
+ return rc;
+}
+
+int crespo_ipc_client_recv(struct ipc_client *client, struct ipc_message_info *response)
+{
+ struct modem_io modem_data;
+ struct ipc_header *resphdr;
+ int bread = 0;
+
+ memset(&modem_data, 0, sizeof(struct modem_io));
+ modem_data.data = malloc(MAX_MODEM_DATA_SIZE);
+ modem_data.size = MAX_MODEM_DATA_SIZE;
+
+ memset(response, 0, sizeof(struct ipc_message_info));
+
+ wake_lock("secril_fmt-interface", 20);
+
+ assert(client->handlers->read != NULL);
+ bread = client->handlers->read((uint8_t*) &modem_data, sizeof(struct modem_io) + MAX_MODEM_DATA_SIZE, client->handlers->read_data);
+ if (bread < 0)
+ {
+ ipc_client_log(client, "ERROR: crespo_ipc_client_recv: can't receive enough bytes from modem to process incoming response!");
+ return 1;
+ }
+
+ ipc_client_log(client, "INFO: crespo_ipc_client_recv: Modem RECV FMT (id=%d cmd=%d size=%d)!", modem_data.id, modem_data.cmd, modem_data.size);
+
+ if(modem_data.size <= 0 || modem_data.size >= 0x1000 || modem_data.data == NULL)
+ {
+ ipc_client_log(client, "ERROR: crespo_ipc_client_recv: we retrieve less bytes from the modem than we exepected!");
+ return 1;
+ }
+
+ /* You MUST send back modem_data */
+
+ resphdr = (struct ipc_header *) modem_data.data;
+
+ response->mseq = resphdr->mseq;
+ response->aseq = resphdr->aseq;
+ response->group = resphdr->group;
+ response->index = resphdr->index;
+ response->type = resphdr->type;
+ response->length = modem_data.size - sizeof(struct ipc_header);
+ response->data = NULL;
+
+ ipc_client_log(client, "INFO: crespo_ipc_client_recv: response: type = %d (%s), group = %d, index = %d (%s)",
+ resphdr->type, ipc_response_type_to_str(resphdr->type), resphdr->group, resphdr->index, ipc_command_type_to_str(IPC_COMMAND(resphdr)));
+
+ if(response->length > 0)
+ {
+#ifdef DEBUG
+ ipc_client_log(client, "INFO: ==== DATA DUMP ====");
+ ipc_hex_dump(client, (void *) (modem_data.data + sizeof(struct ipc_header)), response->length);
+#endif
+ response->data = malloc(response->length);
+ memcpy(response->data, (uint8_t *) modem_data.data + sizeof(struct ipc_header), response->length);
+ }
+
+ free(modem_data.data);
+
+ ipc_client_log(client, "");
+
+ wake_unlock("secril_fmt-interface", 20);
+
+ return 0;
+}
+
+int crespo_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/modem_fmt", O_RDWR | O_NOCTTY | O_NONBLOCK);
+ break;
+ case IPC_CLIENT_TYPE_RFS:
+ fd = open("/dev/modem_rfs", 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 crespo_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 crespo_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;
+
+ rc = ioctl(fd, IOCTL_MODEM_RECV, data);
+
+ if(rc < 0)
+ return -1;
+
+ return 0;
+}
+
+int crespo_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 = ioctl(fd, IOCTL_MODEM_SEND, data);
+
+ if(rc < 0)
+ return -1;
+
+ return 0;
+}
+
+int galaxysmtd_ipc_power_on(void *data)
+{
+ int fd = open("/sys/class/modemctl/xmm/control", O_RDWR);
+ int rc;
+
+ if(fd < 0)
+ return -1;
+
+ rc = write(fd, "on", 2);
+ close(fd);
+
+ if(rc < 0)
+ return -1;
+
+ //TODO should check if nothing was written as well?
+
+ return 0;
+}
+
+int galaxysmtd_ipc_power_off(void *data)
+{
+ int fd = open("/sys/class/modemctl/xmm/control", O_RDWR);
+ int rc;
+
+ if(fd < 0)
+ return -1;
+
+ rc = write(fd, "off", 3);
+ close(fd);
+
+ if(rc < 0)
+ return -1;
+
+ //TODO should check if nothing was written as well?
+
+ return 0;
+}
+
+struct ipc_handlers ipc_default_handlers = {
+ .read = crespo_ipc_read,
+ .write = crespo_ipc_write,
+ .open = crespo_ipc_open,
+ .close = crespo_ipc_close,
+ .power_on = galaxysmtd_ipc_power_on,
+ .power_off = galaxysmtd_ipc_power_off,
+};
+
+struct ipc_ops ipc_ops = {
+ .send = crespo_ipc_client_send,
+ .recv = crespo_ipc_client_recv,
+ .bootstrap = galaxysmtd_modem_bootstrap,
+};
+
+// vim:ts=4:sw=4:expandtab
diff --git a/samsung-ipc/device/galaxysmtd/galaxysmtd_ipc.h b/samsung-ipc/device/galaxysmtd/galaxysmtd_ipc.h
new file mode 100644
index 0000000..05e0a5c
--- /dev/null
+++ b/samsung-ipc/device/galaxysmtd/galaxysmtd_ipc.h
@@ -0,0 +1,55 @@
+/**
+ * This file is part of libsamsung-ipc.
+ *
+ * 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 __GALAXYSMTD_IPC_H__
+#define __GALAXYSMTD_IPC_H__
+
+//from trace/logcat: nCoreVer = 0xf0
+#define BOOTCORE_VERSION 0xf0
+//from trace: write(14, "0", 1) before PSI_DATA_LEN
+#define PSI_MAGIC 0x30
+//from trace/logcat: Bootloader IMG Size: 0x00005000, sent after PSI_MAGIC, LSB first
+#define PSI_DATA_LEN 0x5000
+//from /radio/modem.bin size (0xc00000) - /efs/nv_data.bin size (0x2...)
+#define RADIO_IMG_SIZE 0xc00000-0x200000
+
+//should be the same from crespo
+#define MAX_MODEM_DATA_SIZE 0x1000
+
+struct samsung_rfs_msg
+{
+ uint32_t offset;
+ uint32_t size;
+};
+
+struct samsung_rfs_cfrm
+{
+ uint8_t confirmation;
+ struct samsung_rfs_msg msg;
+};
+
+int wake_lock(char *lock_name, int len);
+int wake_unlock(char *lock_name, int len);
+
+extern struct ipc_handlers galaxysmtd_ipc_default_handlers;
+
+#endif
+
+// vim:ts=4:sw=4:expandtab
diff --git a/samsung-ipc/device/galaxysmtd/galaxysmtd_modem_ctl.h b/samsung-ipc/device/galaxysmtd/galaxysmtd_modem_ctl.h
new file mode 100644
index 0000000..7c23165
--- /dev/null
+++ b/samsung-ipc/device/galaxysmtd/galaxysmtd_modem_ctl.h
@@ -0,0 +1,44 @@
+/*
+ * 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_CONTROL_H__
+#define __MODEM_CONTROL_H__
+
+#define IOCTL_MODEM_RAMDUMP _IO('o', 0x19)
+#define IOCTL_MODEM_RESET _IO('o', 0x20)
+#define IOCTL_MODEM_START _IO('o', 0x21)
+#define IOCTL_MODEM_OFF _IO('o', 0x22)
+
+#define IOCTL_MODEM_SEND _IO('o', 0x23)
+#define IOCTL_MODEM_RECV _IO('o', 0x24)
+
+struct modem_io {
+ uint32_t size;
+ uint32_t id;
+ uint32_t cmd;
+ void *data;
+};
+
+/* platform data */
+struct modemctl_data {
+ const char *name;
+ unsigned gpio_phone_active;
+ unsigned gpio_pda_active;
+ unsigned gpio_cp_reset;
+ unsigned gpio_phone_on;
+ bool is_cdma_modem; /* 1:CDMA Modem */
+};
+
+#endif
diff --git a/samsung-ipc/device/galaxysmtd/galaxysmtd_nv_data.c b/samsung-ipc/device/galaxysmtd/galaxysmtd_nv_data.c
new file mode 100644
index 0000000..f4c3967
--- /dev/null
+++ b/samsung-ipc/device/galaxysmtd/galaxysmtd_nv_data.c
@@ -0,0 +1,356 @@
+/**
+ * This file is part of libsamsung-ipc.
+ *
+ * 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/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <openssl/md5.h>
+
+#include <radio.h>
+#include "galaxysmtd_nv_data.h"
+#include "galaxysmtd_ipc.h"
+
+void md5hash2string(char *out, uint8_t *in)
+{
+ int i;
+
+ for(i=0 ; i < MD5_DIGEST_LENGTH ; i++)
+ {
+ /* After the first iteration, we override \0. */
+ if(*in < 0x10)
+ sprintf(out, "0%x", *in);
+ else
+ sprintf(out, "%x", *in);
+ in++;
+ out+=2;
+ }
+}
+
+void nv_data_generate(struct ipc_client *client)
+{
+ ipc_client_log(client, "This feature isn't present yet\n");
+
+// nv_data_backup_create();
+}
+
+void nv_data_md5_compute(void *data_p, int size, void *hash)
+{
+ MD5_CTX ctx;
+
+// MD5((unsigned char *)nv_data_p, nv_data_stat.st_size, nv_data_md5_hash);
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, data_p, size);
+ MD5_Update(&ctx, NV_DATA_MD5_SECRET, sizeof(NV_DATA_MD5_SECRET) - 1);
+ MD5_Final(hash, &ctx);
+}
+
+void nv_data_backup_create(struct ipc_client *client)
+{
+ uint8_t nv_data_md5_hash[MD5_DIGEST_LENGTH];
+ char *nv_data_md5_hash_string;
+
+ struct stat nv_stat;
+ void *nv_data_p;
+ uint8_t data;
+ uint8_t *data_p;
+
+ int fd;
+ int i;
+
+ ipc_client_log(client, "nv_data_backup_create: enter\n");
+
+ if(stat("/efs/nv_data.bin", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_check: nv_data.bin missing\n");
+ nv_data_generate(client);
+ }
+
+ /* Read the content of nv_data.bin. */
+ nv_data_p=ipc_file_read(client, "/efs/nv_data.bin", NV_DATA_SIZE, NV_DATA_SIZE / 10);
+
+ fd=open("/efs/.nv_data.bak", O_RDWR | O_CREAT, 0644);
+
+ data_p=nv_data_p;
+
+ /* Write the content of nv_data.bin in the backup file. */
+ for(i=0 ; i < NV_DATA_SIZE / 10 ; i++)
+ {
+ write(fd, data_p, NV_DATA_SIZE / 10);
+ data_p+=NV_DATA_SIZE / 10;
+ }
+
+ close(fd);
+ free(nv_data_p);
+
+ /* Alloc the memory for the md5 hash string. */
+ nv_data_md5_hash_string=malloc(MD5_STRING_SIZE);
+ memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
+
+ /* Read the newly-written .nv_data.bak. */
+ nv_data_p=ipc_file_read(client, "/efs/.nv_data.bak", NV_DATA_SIZE, NV_DATA_SIZE / 10);
+
+ /* Compute the MD5 hash for .nv_data.bak.. */
+ nv_data_md5_compute(nv_data_p, NV_DATA_SIZE, nv_data_md5_hash);
+ md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
+
+ ipc_client_log(client, "nv_data_backup_create: new MD5 hash is %s\n", nv_data_md5_hash_string);
+
+ free(nv_data_p);
+
+ /* Write the MD5 hash in nv_data.bin.md5. */
+ fd=open("/efs/.nv_data.bak.md5", O_RDWR | O_CREAT, 0644);
+
+ write(fd, nv_data_md5_hash_string, MD5_STRING_SIZE);
+
+ close(fd);
+ free(nv_data_md5_hash_string);
+
+ /* Write ASCII 1 on the state file. */
+ fd=open("/efs/.nv_state", O_RDWR | O_CREAT, 0644);
+
+ data='1';
+ write(fd, &data, sizeof(data));
+
+ close(fd);
+
+ ipc_client_log(client, "nv_data_backup_create: exit\n");
+}
+
+void nv_data_backup_restore(struct ipc_client *client)
+{
+ uint8_t nv_data_md5_hash[MD5_DIGEST_LENGTH];
+ char *nv_data_md5_hash_string;
+ char *nv_data_md5_hash_read;
+
+ struct stat nv_stat;
+ void *nv_data_p;
+ uint8_t data;
+ uint8_t *data_p;
+
+ int fd;
+ int i;
+
+ ipc_client_log(client, "nv_data_backup_restore: enter\n");
+
+ if(stat("/efs/.nv_data.bak", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_backup_restore: .nv_data.bak missing\n");
+ nv_data_generate(client);
+ nv_data_backup_create(client);
+ return;
+ }
+
+ if(nv_stat.st_size != NV_DATA_SIZE)
+ {
+ ipc_client_log(client, "nv_data_backup_restore: wrong .nv_data.bak size\n");
+ nv_data_generate(client);
+ nv_data_backup_create(client);
+ return;
+ }
+
+ /* Read the content of the backup file. */
+ nv_data_p=ipc_file_read(client, "/efs/.nv_data.bak", NV_DATA_SIZE, NV_DATA_SIZE / 10);
+
+ fd=open("/efs/nv_data.bin", O_RDWR | O_CREAT, 0644);
+
+ data_p=nv_data_p;
+
+ /* Write the content of the backup file in nv_data.bin. */
+ for(i=0 ; i < NV_DATA_SIZE / 10 ; i++)
+ {
+ write(fd, data_p, NV_DATA_SIZE / 10);
+ data_p+=NV_DATA_SIZE / 10;
+ }
+
+ close(fd);
+ free(nv_data_p);
+
+ /* Alloc the memory for the md5 hashes strings. */
+ nv_data_md5_hash_string=malloc(MD5_STRING_SIZE);
+ nv_data_md5_hash_read=malloc(MD5_STRING_SIZE);
+
+ memset(nv_data_md5_hash_read, 0, MD5_STRING_SIZE);
+ memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
+
+ /* Read the newly-written nv_data.bin. */
+ nv_data_p=ipc_file_read(client, "/efs/nv_data.bin", NV_DATA_SIZE, NV_DATA_SIZE / 10);
+
+ /* Compute the MD5 hash for nv_data.bin. */
+ nv_data_md5_compute(nv_data_p, NV_DATA_SIZE, nv_data_md5_hash);
+ md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
+
+ free(nv_data_p);
+
+ /* Open the backup file MD5 hash. */
+ fd=open("/efs/.nv_data.bak.md5", O_RDONLY);
+
+ /* Read the md5 stored in the file. */
+ read(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
+
+ /* Add 0x0 to end the string: not sure this is part of the file. */
+ nv_data_md5_hash_read[MD5_STRING_SIZE - 1]='\0';
+
+ ipc_client_log(client, "nv_data_backup_restore: computed MD5: %s read MD5: %s\n",
+ nv_data_md5_hash_string, nv_data_md5_hash_read);
+
+ /* Make sure both hashes are the same. */
+ if(strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
+ {
+ ipc_client_log(client, "nv_data_md5_check: MD5 hash mismatch\n");
+ nv_data_generate(client);
+ nv_data_backup_create(client);
+ return;
+ }
+
+ close(fd);
+
+ /* Write the MD5 hash in nv_data.bin.md5. */
+ fd=open("/efs/nv_data.bin.md5", O_RDWR | O_CREAT, 0644);
+
+ write(fd, nv_data_md5_hash_string, MD5_STRING_SIZE);
+
+ close(fd);
+
+ free(nv_data_md5_hash_string);
+ free(nv_data_md5_hash_read);
+
+ fd=open("/efs/.nv_state", O_RDWR | O_CREAT, 0644);
+
+ data='1';
+ write(fd, &data, sizeof(data));
+
+ close(fd);
+
+ ipc_client_log(client, "nv_data_backup_create: exit\n");
+}
+
+void nv_data_check(struct ipc_client *client)
+{
+ struct stat nv_stat;
+ int nv_state_fd=-1;
+ int nv_state=0;
+
+ ipc_client_log(client, "nv_data_check: enter\n");
+
+ if(stat("/efs/nv_data.bin", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_check: nv_data.bin missing\n");
+ nv_data_backup_restore(client);
+ stat("/efs/nv_data.bin", &nv_stat);
+ }
+
+ if(nv_stat.st_size != NV_DATA_SIZE)
+ {
+ ipc_client_log(client, "nv_data_check: wrong nv_data.bin size\n");
+ nv_data_backup_restore(client);
+ }
+
+ if(stat("/efs/.nv_data.bak", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_check: .nv_data.bak missing\n");
+ nv_data_backup_create(client);
+ }
+
+ if(stat("/efs/nv_data.bin.md5", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_check: nv_data.bin.md5 missing\n");
+ nv_data_backup_create(client);
+ }
+
+ nv_state_fd=open("/efs/.nv_state", O_RDONLY);
+
+ if(nv_state_fd < 0 || fstat(nv_state_fd, &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_check: .nv_state missing\n");
+ nv_data_backup_restore(client);
+ }
+
+ read(nv_state_fd, &nv_state, sizeof(nv_state));
+
+ close(nv_state_fd);
+
+ if(nv_state != '1')
+ {
+ ipc_client_log(client, "nv_data_check: bad nv_state\n");
+ nv_data_backup_restore(client);
+ }
+
+ ipc_client_log(client, "nv_data_check: everything should be alright\n");
+ ipc_client_log(client, "nv_data_check: exit\n");
+}
+
+void nv_data_md5_check(struct ipc_client *client)
+{
+ struct stat nv_stat;
+ uint8_t nv_data_md5_hash[MD5_DIGEST_LENGTH];
+ char *nv_data_md5_hash_string;
+ char *nv_data_md5_hash_read;
+ void *nv_data_p;
+
+ int fd;
+ uint8_t *data_p;
+
+ ipc_client_log(client, "nv_data_md5_check: enter\n");
+
+ nv_data_md5_hash_string=malloc(MD5_STRING_SIZE);
+ nv_data_md5_hash_read=malloc(MD5_STRING_SIZE);
+
+ memset(nv_data_md5_hash_read, 0, MD5_STRING_SIZE);
+ memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
+
+ nv_data_p=ipc_file_read(client, "/efs/nv_data.bin", NV_DATA_SIZE, 1024);
+ data_p=nv_data_p;
+
+ nv_data_md5_compute(data_p, NV_DATA_SIZE, nv_data_md5_hash);
+
+ md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
+
+ free(nv_data_p);
+
+ fd=open("/efs/nv_data.bin.md5", O_RDONLY);
+
+ /* Read the md5 stored in the file. */
+ read(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
+
+ /* Add 0x0 to end the string: not sure this is part of the file. */
+ nv_data_md5_hash_read[MD5_STRING_SIZE - 1]='\0';
+
+ ipc_client_log(client, "nv_data_md5_check: computed MD5: %s read MD5: %s\n",
+ nv_data_md5_hash_string, nv_data_md5_hash_read);
+
+ if(strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
+ {
+ ipc_client_log(client, "nv_data_md5_check: MD5 hash mismatch\n");
+ nv_data_backup_restore(client);
+ }
+
+ free(nv_data_md5_hash_string);
+ free(nv_data_md5_hash_read);
+
+ ipc_client_log(client, "nv_data_md5_check: exit\n");
+}
+
+// vim:ts=4:sw=4:expandtab
diff --git a/samsung-ipc/device/galaxysmtd/galaxysmtd_nv_data.h b/samsung-ipc/device/galaxysmtd/galaxysmtd_nv_data.h
new file mode 100644
index 0000000..e82f3da
--- /dev/null
+++ b/samsung-ipc/device/galaxysmtd/galaxysmtd_nv_data.h
@@ -0,0 +1,41 @@
+/**
+ * This file is part of libsamsung-ipc.
+ *
+ * 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 __GALAXYSMTD_NV_DATA_H__
+#define __GALAXYSMTD_NV_DATA__
+
+#include <radio.h>
+
+#define NV_DATA_MD5_SECRET "Samsung_Android_RIL"
+#define NV_DATA_SIZE 0x200000
+
+#define MD5_STRING_SIZE MD5_DIGEST_LENGTH * 2 + 1
+
+void md5hash2string(char *out, uint8_t *in);
+void nv_data_generate(struct ipc_client *client);
+void nv_data_md5_compute(void *data_p, int size, void *hash);
+void nv_data_backup_create(struct ipc_client *client);
+void nv_data_backup_restore(struct ipc_client *client);
+void nv_data_check(struct ipc_client *client);
+void nv_data_md5_check(struct ipc_client *client);
+
+#endif
+
+// vim:ts=4:sw=4:expandtab