diff options
Diffstat (limited to 'samsung-ipc/devices/xmm6160/xmm6160.c')
-rw-r--r-- | samsung-ipc/devices/xmm6160/xmm6160.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/samsung-ipc/devices/xmm6160/xmm6160.c b/samsung-ipc/devices/xmm6160/xmm6160.c new file mode 100644 index 0000000..8374541 --- /dev/null +++ b/samsung-ipc/devices/xmm6160/xmm6160.c @@ -0,0 +1,294 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2011-2013 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 2 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 <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <termios.h> +#include <sys/select.h> + +#include <samsung-ipc.h> +#include <util.h> + +#include "xmm6160.h" + +int xmm6160_psi_send(struct ipc_client *client, int serial_fd, + void *psi_data, unsigned short psi_size) +{ + char at[] = XMM6160_AT; + unsigned char version; + unsigned char info; + unsigned char psi_magic; + unsigned char psi_crc; + unsigned char psi_ack; + + struct termios termios; + struct timeval timeout; + fd_set fds; + + unsigned char *p; + int length; + int rc; + int i; + + if (client == NULL || serial_fd < 0 || psi_data == NULL || psi_size <= 0) + return -1; + + tcgetattr(serial_fd, &termios); + + cfmakeraw(&termios); + cfsetispeed(&termios, B115200); + cfsetospeed(&termios, B115200); + + tcsetattr(serial_fd, TCSANOW, &termios); + + length = strlen(at); + for (i = 0; i < XMM6160_AT_COUNT; i++) { + rc = write(serial_fd, at, length); + if (rc < length) { + ipc_client_log(client, "Writing AT in ASCII failed"); + goto error; + } + + usleep(50000); + } + ipc_client_log(client, "Wrote AT in ASCII"); + + usleep(50000); + + version = 0; + + rc = read(serial_fd, &version, sizeof(version)); + if (rc < (int) sizeof(version)) { + ipc_client_log(client, "Reading bootcore version failed"); + goto error; + } + + if (version != XMM6160_BOOTCORE_VERSION) { + ipc_client_log(client, "Read wrong bootcore version (0x%x)", version); + goto error; + } + + ipc_client_log(client, "Read bootcore version (0x%x)", version); + + rc = read(serial_fd, &info, sizeof(info)); + if (rc < (int) sizeof(info)) { + ipc_client_log(client, "Reading info size failed"); + goto error; + } + ipc_client_log(client, "Read info size (0x%x)", info); + + psi_magic = XMM6160_PSI_MAGIC; + + rc = write(serial_fd, &psi_magic, sizeof(psi_magic)); + if (rc < (int) sizeof(psi_magic)) { + ipc_client_log(client, "Writing PSI magic failed"); + goto error; + } + ipc_client_log(client, "Wrote PSI magic (0x%x)", psi_magic); + + rc = write(serial_fd, &psi_size, sizeof(psi_size)); + if (rc < (int) sizeof(psi_size)) { + ipc_client_log(client, "Writing PSI size failed"); + goto error; + } + ipc_client_log(client, "Wrote PSI size (0x%x)", psi_size); + + FD_ZERO(&fds); + FD_SET(serial_fd, &fds); + + timeout.tv_sec = 4; + timeout.tv_usec = 0; + + p = (unsigned char *) psi_data; + psi_crc = 0; + + for (i = 0; i < psi_size; i++) { + rc = select(serial_fd + 1, NULL, &fds, NULL, &timeout); + if (rc <= 0) { + ipc_client_log(client, "Writing PSI failed"); + goto error; + } + + rc = write(serial_fd, p, 1); + if (rc < 1) { + ipc_client_log(client, "Writing PSI failed"); + goto error; + } + + psi_crc ^= *p++; + } + ipc_client_log(client, "Wrote PSI, CRC is 0x%x", psi_crc); + + rc = select(serial_fd + 1, NULL, &fds, NULL, &timeout); + if (rc <= 0) { + ipc_client_log(client, "Writing PSI crc failed"); + goto error; + } + + rc = write(serial_fd, &psi_crc, sizeof(psi_crc)); + if (rc < (int) sizeof(psi_crc)) { + ipc_client_log(client, "Writing PSI crc failed"); + goto error; + } + ipc_client_log(client, "Wrote PSI CRC (0x%x)", psi_crc); + + timeout.tv_sec = 4; + timeout.tv_usec = 0; + + i = 0; + do { + rc = select(serial_fd + 1, &fds, NULL, NULL, &timeout); + if (rc <= 0) { + ipc_client_log(client, "Reading PSI ACK failed"); + goto error; + } + + rc = read(serial_fd, &psi_ack, sizeof(psi_ack)); + if (rc < (int) sizeof(psi_ack)) { + ipc_client_log(client, "Reading PSI ACK failed"); + goto error; + } + + if (i++ > 50) { + ipc_client_log(client, "Reading PSI ACK failed"); + goto error; + } + } while (psi_ack != XMM6160_PSI_ACK); + ipc_client_log(client, "Read PSI ACK (0x%x)", psi_ack); + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + return rc; +} + +int xmm6160_firmware_send(struct ipc_client *client, int device_fd, + void *device_address, void *firmware_data, int firmware_size) +{ + int wc; + + unsigned char *p; + int rc; + int i; + + if (client == NULL || (device_fd < 0 && device_address == NULL) || firmware_data == NULL || firmware_size <= 0) + return -1; + + p = (unsigned char *) firmware_data; + + if (device_address != NULL) { + memcpy(device_address, (void *) p, firmware_size); + } else { + wc = 0; + while (wc < firmware_size) { + rc = write(device_fd, (void *) p, firmware_size - wc); + if (rc < 0) { + ipc_client_log(client, "Writing firmware failed"); + goto error; + } + + p += rc; + wc += rc; + } + } + ipc_client_log(client, "Wrote firmware"); + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + return rc; +} + +int xmm6160_nv_data_send(struct ipc_client *client, int device_fd, + void *device_address) +{ + void *nv_data = NULL; + int nv_size; + int wc; + + unsigned char *p; + int rc; + + if (client == NULL || (device_fd < 0 && device_address == NULL)) + return -1; + + rc = nv_data_check(client); + if (rc < 0) { + ipc_client_log(client, "Checking nv_data failed"); + goto error; + } + ipc_client_log(client, "Checked nv_data"); + + rc = nv_data_md5_check(client); + if (rc < 0) { + ipc_client_log(client, "Checking nv_data md5 failed"); + goto error; + } + ipc_client_log(client, "Checked nv_data md5"); + + nv_data = file_data_read(nv_data_path(client), nv_data_size(client), nv_data_chunk_size(client)); + if (nv_data == NULL) { + ipc_client_log(client, "Reading nv_data failed"); + goto error; + } + ipc_client_log(client, "Read nv_data"); + + p = (unsigned char *) nv_data; + nv_size = nv_data_size(client); + + if (device_address != NULL) { + memcpy(device_address, p, nv_size); + } else { + wc = 0; + while (wc < nv_size) { + rc = write(device_fd, p, nv_size - wc); + if (rc < 0) { + ipc_client_log(client, "Writing modem image failed"); + goto error; + } + + p += rc; + wc += rc; + } + } + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (nv_data != NULL) + free(nv_data); + + return rc; +} + +// vim:ts=4:sw=4:expandtab |