/* $Id: osmocom.c,v 1.1 2012/07/05 22:17:20 khorben Exp $ */ /* Copyright (c) 2012 Pierre Pronchery */ /* This file is part of DeforaOS Desktop Phone */ /* 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, version 3 of the License. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "../../config.h" /* Osmocom */ /* private */ /* types */ struct tool_server { struct osmo_fd bfd; uint8_t dlci; struct llist_head connections; }; enum dnload_state { WAITING_PROMPT1, WAITING_PROMPT2, DOWNLOADING }; enum romload_state { WAITING_IDENTIFICATION, WAITING_PARAM_ACK, SENDING_BLOCKS, SENDING_LAST_BLOCK, LAST_BLOCK_SENT, WAITING_BLOCK_ACK, WAITING_CHECKSUM_ACK, WAITING_BRANCH_ACK, FINISHED }; enum mtk_state { MTK_INIT_1, MTK_INIT_2, MTK_INIT_3, MTK_INIT_4, MTK_WAIT_WRITE_ACK, MTK_WAIT_ADDR_ACK, MTK_WAIT_SIZE_ACK, MTK_SENDING_BLOCKS, MTK_WAIT_BRANCH_CMD_ACK, MTK_WAIT_BRANCH_ADDR_ACK, MTK_FINISHED }; enum dnload_mode { MODE_C123, MODE_C123xor, MODE_C140, MODE_C140xor, MODE_C155, MODE_ROMLOAD, MODE_MTK, MODE_INVALID }; typedef struct _OsmocomDnload { enum dnload_state state; enum romload_state romload_state; enum mtk_state mtk_state; enum dnload_mode mode, previous_mode; struct osmo_fd serial_fd; char *filename, *previous_filename; char *chainload_filename; int expect_hdlc; int dump_rx; int dump_tx; int beacon_interval; /* data to be downloaded */ uint8_t *data; int data_len; uint8_t *write_ptr; /* romload: block to be downloaded */ uint8_t *block; int block_len; uint8_t block_number; uint16_t block_payload_size; int romload_dl_checksum; uint8_t *block_ptr; uint8_t load_address[4]; uint8_t mtk_send_size[4]; int block_count; int echo_bytecount; struct tool_server layer2_server; struct tool_server loader_server; } OsmocomDnload; typedef struct _ModemPlugin { ModemPluginHelper * helper; guint reset; /* modem */ struct osmo_fd fd; guint source; OsmocomDnload dnload; struct osmo_timer_list tick_timer; } Osmocom; /* constants */ #define ROMLOAD_INIT_BAUDRATE B19200 #define ROMLOAD_DL_BAUDRATE B115200 #define ROMLOAD_BLOCK_HDR_LEN 10 #define ROMLOAD_ADDRESS 0x820000 #define MTK_INIT_BAUDRATE B19200 #define MTK_ADDRESS 0x40001400 #define MTK_BLOCK_SIZE 1024 static const uint8_t romload_ident_cmd[] = { 0x3c, 0x69 }; /* helper = helper; return osmocom; } /* osmocom_destroy */ static void _osmocom_destroy(ModemPlugin * modem) { _osmocom_stop(modem); object_delete(osmocom); osmocom = NULL; } /* osmocom_reset */ static int _reset_open(ModemPlugin * modem); static unsigned int _reset_baudrate(ModemPlugin * modem, unsigned int baudrate); static int _osmocom_reset(ModemPlugin * modem, unsigned int retry) { Osmocom * osmocom = modem; _osmocom_stop(modem); if(_reset_open(modem) != 0) { if(retry > 0) osmocom->reset = g_timeout_add(retry, _osmocom_on_reset, modem); return -1; } #if 0 osmocom->channel = g_io_channel_unix_new(fd); if(g_io_channel_set_encoding(osmocom->channel, NULL, &error) != G_IO_STATUS_NORMAL) { modem->helper->error(modem->helper->modem, error->message, 1); g_error_free(error); } g_io_channel_set_buffered(osmocom->channel, FALSE); osmocom-> #else osmocom->source = g_idle_add(_osmocom_on_idle, modem); #endif return 0; } static int _reset_open(ModemPlugin * modem) { Osmocom * osmocom = modem; ModemPluginHelper * helper = osmocom->helper; char const * device; unsigned int baudrate; int flags; uint32_t tmpaddr = ROMLOAD_ADDRESS; char const * p; if((device = helper->config_get(helper->modem, "device")) == NULL) device = "/dev/modem"; if((p = helper->config_get(helper->modem, "baudrate")) == NULL || (baudrate = strtoul(p, NULL, 10)) == 0) baudrate = 115200; baudrate = _reset_baudrate(modem, baudrate); if((osmocom->fd.fd = osmo_serial_init(device, baudrate)) < 0) { /* XXX report error */ #ifdef DEBUG fprintf(stderr, "DEBUG: %s: %s\n", device, strerror(errno)); #endif return -1; } if(osmo_fd_register(&osmocom->fd) != 0) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s: %s\n", device, strerror(errno)); #endif /* XXX report error */ return -1; } /* Set serial socket to non-blocking mode of operation */ if((flags = fcntl(osmocom->fd.fd, F_GETFL)) != -1) { flags |= O_NONBLOCK; fcntl(osmocom->fd.fd, F_SETFL, flags); } osmocom->dnload.serial_fd.when = BSC_FD_READ; osmocom->dnload.serial_fd.cb = _osmocom_on_serial_read; if(osmocom->dnload.mode == MODE_ROMLOAD) { tmpaddr = ROMLOAD_ADDRESS; osmo_serial_set_baudrate(osmocom->fd.fd, ROMLOAD_INIT_BAUDRATE); osmocom->tick_timer.cb = &_osmocom_on_beacon_timer; osmocom->tick_timer.data = modem; osmo_timer_schedule(&osmocom->tick_timer, 0, osmocom->dnload.beacon_interval); } else { tmpaddr = MTK_ADDRESS; osmo_serial_set_baudrate(osmocom->fd.fd, MTK_INIT_BAUDRATE); osmocom->tick_timer.cb = &_osmocom_on_mtk_timer; osmocom->tick_timer.data = modem; osmo_timer_schedule(&osmocom->tick_timer, 0, osmocom->dnload.beacon_interval); } /* FIXME not endian proof */ osmocom->dnload.load_address[0] = (tmpaddr >> 24) & 0xff; osmocom->dnload.load_address[1] = (tmpaddr >> 16) & 0xff; osmocom->dnload.load_address[2] = (tmpaddr >> 8) & 0xff; osmocom->dnload.load_address[3] = tmpaddr & 0xff; return 0; } static unsigned int _reset_baudrate(ModemPlugin * modem, unsigned int baudrate) { switch(baudrate) { case 1200: return B1200; case 2400: return B2400; case 4800: return B4800; case 9600: return B9600; case 19200: return B19200; case 38400: return B38400; #ifdef B76800 case 76800: return B76800; #endif #ifdef B14400 case 14400: return B14400; #endif #ifdef B28800 case 28800: return B28800; #endif case 57600: return B57600; case 115200: return B115200; case 230400: return B230400; case 460800: return B460800; case 921600: return B921600; default: error_set("%u%s", baudrate, "Unsupported baudrate (using 115200)"); modem->helper->error(NULL, error_get(), 1); return B115200; } } /* osmocom_start */ static int _osmocom_start(ModemPlugin * modem, unsigned int retry) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif _osmocom_reset(modem, retry); return 0; } /* osmocom_stop */ static int _osmocom_stop(ModemPlugin * modem) { Osmocom * osmocom = modem; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(osmocom->source != 0) { g_source_remove(osmocom->source); osmocom->source = 0; } return 0; } /* osmocom_request */ static int _request_call(ModemPlugin * modem, ModemRequest * request); static int _request_message_send(ModemPlugin * modem, ModemRequest * request); static int _osmocom_request(ModemPlugin * modem, ModemRequest * request) { switch(request->type) { case MODEM_REQUEST_CALL: return _request_call(modem, request); case MODEM_REQUEST_MESSAGE_SEND: return _request_message_send(modem, request); #ifdef DEBUG default: break; #endif } return 0; } static int _request_call(ModemPlugin * modem, ModemRequest * request) { Osmocom * osmocom = modem; /* FIXME implement */ return -1; } static int _request_message_send(ModemPlugin * modem, ModemRequest * request) { Osmocom * osmocom = modem; /* FIXME implement */ return -1; } /* callbacks */ /* osmocom_on_idle */ static gboolean _osmocom_on_idle(gpointer data) { ModemPlugin * modem = data; Osmocom * osmocom = modem; if(osmo_select_main(0) < 0) { osmocom->source = 0; return FALSE; } return TRUE; } /* osmocom_on_reset */ static gboolean _osmocom_on_reset(gpointer data) { ModemPlugin * modem = data; Osmocom * osmocom = modem; if(_osmocom_reset(modem, 0) == 0) { osmocom->reset = 0; return FALSE; } return TRUE; } /* osmocom_on_beacon_timer */ static void _osmocom_on_beacon_timer(void * data) { ModemPlugin * modem = data; Osmocom * osmocom = modem; int rc; if(osmocom->dnload.romload_state == WAITING_IDENTIFICATION) { printf("Sending Calypso romloader beacon...\n"); rc = write(osmocom->dnload.serial_fd.fd, romload_ident_cmd, sizeof(romload_ident_cmd)); if(rc != sizeof(romload_ident_cmd)) printf("Error sending identification beacon\n"); osmo_timer_schedule(&osmocom->tick_timer, 0, osmocom->dnload.beacon_interval); } } /* osmocom_on_mtk_timer */ static void _osmocom_on_mtk_timer(void * data) { ModemPlugin * modem = data; Osmocom * osmocom = modem; int rc; if(osmocom->dnload.mtk_state == MTK_INIT_1) { printf("Sending MTK romloader beacon...\n"); rc = write(osmocom->dnload.serial_fd.fd, &mtk_init_cmd[0], 1); if(rc != 1) printf("Error sending identification beacon\n"); osmo_timer_schedule(&osmocom->tick_timer, 0, osmocom->dnload.beacon_interval); } } /* osmocom_on_serial_read */ static int _read_handle_romload(); static int _read_handle(); static int _read_handle_write(); static int _osmocom_on_serial_read(struct osmo_fd * fd, unsigned int flags) { int rc; /* FIXME really implement */ if(flags & BSC_FD_READ) { #if 0 switch(osmocom->mode) { case MODE_ROMLOAD: rc = _read_handle_romload(); break; default: rc = _read_handle(); break; } if(rc == 0) /* XXX wtf? */ exit(2); #endif } if(flags & BSC_FD_WRITE) { rc = _read_handle_write(); if(rc != 0) osmocom->dnload.state = WAITING_PROMPT1; } return 0; } static int _read_handle_romload() { return -1; } static int _read_handle() { return -1; } static int _read_handle_write() { return -1; }