osmocon: make beacon interval configurable via cmdline
[osmocom-bb.git] / src / host / osmocon / osmocon.c
index 557181f..6f6f566 100644 (file)
@@ -22,6 +22,7 @@
  *
  */
 
+#include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <termios.h>
-#include <signal.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/un.h>
-#include <sys/time.h>
 
 #include <sercomm.h>
 
 #include <osmocore/linuxlist.h>
 #include <osmocore/select.h>
 #include <osmocore/talloc.h>
+#include <osmocore/timer.h>
 
 #include <arpa/inet.h>
 
 #define MAX_HDR_SIZE           128
 #define MAGIC_OFFSET           0x3be2
 
+#define DEFAULT_BEACON_INTERVAL        50000
 #define ROMLOAD_INIT_BAUDRATE  B19200
 #define ROMLOAD_DL_BAUDRATE    B115200
-#define ROMLOAD_BEACON_INTERVAL        50000
 #define ROMLOAD_BLOCK_HDR_LEN  10
-#define ROMLOAD_BLOCKSIZE      0x200
 #define ROMLOAD_ADDRESS                0x820000
 
+#define MTK_INIT_BAUDRATE      B19200
+#define MTK_ADDRESS            0x40001400
+#define MTK_BLOCK_SIZE         1024
+
 struct tool_server *tool_server_for_dlci[256];
 
 /**
@@ -97,6 +100,20 @@ enum romload_state {
        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,
@@ -104,16 +121,23 @@ enum dnload_mode {
        MODE_C140xor,
        MODE_C155,
        MODE_ROMLOAD,
+       MODE_MTK,
 };
 
 struct dnload {
        enum dnload_state state;
        enum romload_state romload_state;
+       enum mtk_state mtk_state;
        enum dnload_mode mode;
        struct bsc_fd serial_fd;
        char *filename;
+       char *chainload_filename;
+
+       int expect_hdlc;
 
-       int print_hdlc;
+       int dump_rx;
+       int dump_tx;
+       int beacon_interval;
 
        /* data to be downloaded */
        uint8_t *data;
@@ -128,6 +152,11 @@ struct dnload {
        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;
@@ -135,6 +164,7 @@ struct dnload {
 
 
 static struct dnload dnload;
+static struct timer_list tick_timer;
 
 /* Compal ramloader specific */
 static const uint8_t phone_prompt1[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x01, 0x40 };
@@ -146,7 +176,19 @@ static const uint8_t phone_nack[]    = { 0x1b, 0xf6, 0x02, 0x00, 0x45, 0x53, 0x1
 static const uint8_t ftmtool[] = { 0x66, 0x74, 0x6d, 0x74, 0x6f, 0x6f, 0x6c };
 static const uint8_t phone_magic[] = { 0x31, 0x30, 0x30, 0x33 }; /* "1003" */
 
-/* romloader specific */
+/* The C123 has a hard-coded check inside the ramloader that requires the
+ * following bytes to be always the first four bytes of the image */
+static const uint8_t data_hdr_c123[]    = { 0xee, 0x4c, 0x9f, 0x63 };
+
+/* The C155 doesn't have some strange restriction on what the first four bytes
+ *  have to be, but it starts the ramloader in THUMB mode. We use the following
+ * four bytes to switch back to ARM mode:
+  800100:       4778            bx      pc
+  800102:       46c0            nop                     ; (mov r8, r8)
+ */
+static const uint8_t data_hdr_c155[]    = { 0x78, 0x47, 0xc0, 0x46 };
+
+/* Calypso romloader specific */
 static const uint8_t romload_ident_cmd[] =     { 0x3c, 0x69 }; /* <i */
 static const uint8_t romload_abort_cmd[] =     { 0x3c, 0x61 }; /* <a */
 static const uint8_t romload_write_cmd[] =     { 0x3c, 0x77 }; /* <w */
@@ -163,22 +205,15 @@ static const uint8_t romload_branch_ack[] =       { 0x3e, 0x62 }; /* >b */
 static const uint8_t romload_branch_nack[] =   { 0x3e, 0x42 }; /* >B */
 
 /* romload_param: {"<p", uint8_t baudrate, uint8_t dpll, uint16_t memory_config,
-* uint8_t strobe_af, uint32_t uart_timeout} */
+ * uint8_t strobe_af, uint32_t uart_timeout} */
 
 static const uint8_t romload_param[] = { 0x3c, 0x70, 0x00, 0x00, 0x00, 0x04,
                                         0x00, 0x00, 0x00, 0x00, 0x00 };
 
-/* The C123 has a hard-coded check inside the ramloder that requires the following
- * bytes to be always the first four bytes of the image */
-static const uint8_t data_hdr_c123[]    = { 0xee, 0x4c, 0x9f, 0x63 };
-
-/* The C155 doesn't have some strange restriction on what the first four bytes have
- * to be, but it starts the ramloader in THUMB mode.  We use the following four bytes
- * to switch back to ARM mode:
-  800100:       4778            bx      pc
-  800102:       46c0            nop                     ; (mov r8, r8)
- */
-static const uint8_t data_hdr_c155[]    = { 0x78, 0x47, 0xc0, 0x46 };
+/* MTK romloader specific */
+static const uint8_t mtk_init_cmd[] =  { 0xa0, 0x0a, 0x50, 0x05 };
+static const uint8_t mtk_init_resp[] = { 0x5f, 0xf5, 0xaf, 0xfa };
+static const uint8_t mtk_command[] =   { 0xa1, 0xa2, 0xa4, 0xa8 };
 
 /* FIXME: this routine is more or less what openbsc/src/rs232:rs232_setup()
  * does, we should move it to libosmocore at some point */
@@ -203,8 +238,8 @@ static int serial_init(const char *serial_port)
        }
        cfsetispeed(&tio, MODEM_BAUDRATE);
        cfsetospeed(&tio, MODEM_BAUDRATE);
-       tio.c_cflag |=  (CREAD | CLOCAL | CS8);
        tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
+       tio.c_cflag |=  (CREAD | CLOCAL | CS8);
        tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
        tio.c_iflag |=  (INPCK | ISTRIP);
        tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
@@ -231,6 +266,11 @@ static int serial_set_baudrate(speed_t baudrate)
        int rc;
        struct termios tio;
 
+       rc = tcgetattr(dnload.serial_fd.fd, &tio);
+       if (rc < 0) {
+               perror("tcgetattr()");
+               return rc;
+       }
        cfsetispeed(&tio, baudrate);
        cfsetospeed(&tio, baudrate);
 
@@ -238,45 +278,35 @@ static int serial_set_baudrate(speed_t baudrate)
        return rc;
 }
 
-static void reload_beacon_timer(void)
-{
-       struct itimerval t_val;
-       int rc;
-
-       /* load timer to our <i beacon interval */
-       bzero(&t_val, sizeof(t_val));
-       t_val.it_value.tv_usec = ROMLOAD_BEACON_INTERVAL;
-
-       if ((rc = setitimer(ITIMER_REAL, &t_val, NULL)) < 0) {
-               fprintf(stderr, "Error in setitimer()\n");
-               exit(1);
-       }
-}
-
-static void beacon_timer_cb(int signr)
+static void beacon_timer_cb(void *p)
 {
        int rc;
 
        if (dnload.romload_state == WAITING_IDENTIFICATION) {
-               printf("Sending beacon...\n");
+               printf("Sending Calypso romloader beacon...\n");
                rc = write(dnload.serial_fd.fd, romload_ident_cmd,
                           sizeof(romload_ident_cmd));
 
                if (!(rc == sizeof(romload_ident_cmd)))
                        printf("Error sending identification beacon\n");
 
-               reload_beacon_timer();
+               bsc_schedule_timer(p, 0, dnload.beacon_interval);
        }
 }
 
-static void start_beacon_timer(void)
+static void mtk_timer_cb(void *p)
 {
-       if (signal(SIGALRM, beacon_timer_cb) == SIG_ERR) {
-               fprintf(stderr, "Cannot register signal handler\n");
-               exit(1);
-       }
+       int rc;
+
+       if (dnload.mtk_state == MTK_INIT_1) {
+               printf("Sending MTK romloader beacon...\n");
+               rc = write(dnload.serial_fd.fd, &mtk_init_cmd[0], 1);
+
+               if (!(rc == 1))
+                       printf("Error sending identification beacon\n");
 
-       reload_beacon_timer();
+               bsc_schedule_timer(p, 0, dnload.beacon_interval);
+       }
 }
 
 /* Read the to-be-downloaded file, prepend header and length, append XOR sum */
@@ -419,17 +449,22 @@ int read_file(const char *filename)
 
 static void hexdump(const uint8_t *data, unsigned int len)
 {
-       const uint8_t *bufptr = data;
        int n;
 
-       for (n=0; n < len; n++, bufptr++)
-               printf("%02x ", *bufptr);
+       for (n=0; n < len; n++)
+               printf("%02x ", data[n]);
+       printf(" ");
+       for (n=0; n < len; n++)
+               if (isprint(data[n]))
+                       putchar(data[n]);
+               else
+                       putchar('.');
        printf("\n");
 }
 
 static int romload_prepare_block(void)
 {
-       int rc, i;
+       int i;
 
        int block_checksum = 5;
        int remaining_bytes;
@@ -440,7 +475,7 @@ static int romload_prepare_block(void)
        dnload.block_len = ROMLOAD_BLOCK_HDR_LEN + dnload.block_payload_size;
 
        /* if first block, allocate memory */
-       if (!dnload.block_number){
+       if (!dnload.block_number) {
                dnload.block = malloc(dnload.block_len);
                if (!dnload.block) {
                        fprintf(stderr, "No memory\n");
@@ -470,7 +505,7 @@ static int romload_prepare_block(void)
        dnload.write_ptr = dnload.data + 2 +
                        (dnload.block_payload_size * dnload.block_number);
 
-       remaining_bytes = dnload.data_len-3 -
+       remaining_bytes = dnload.data_len - 3 -
                        (dnload.block_payload_size * dnload.block_number);
 
        memcpy(block_data, dnload.write_ptr, dnload.block_payload_size);
@@ -486,11 +521,12 @@ static int romload_prepare_block(void)
                        printf("Preparing block %i,", dnload.block_number+1);
        }
 
-       /* block checksum is lsb of !(5 + block_size_lsb + block_address + data) */
+       /* block checksum is lsb of ~(5 + block_size_lsb +  all bytes of
+        * block_address + all data bytes) */
        for (i = 5; i < ROMLOAD_BLOCK_HDR_LEN + dnload.block_payload_size; i++)
                block_checksum += dnload.block[i];
 
-       /* checksum is lsb of !(sum of LSBs of all block checksums) */
+       /* checksum is lsb of ~(sum of LSBs of all block checksums) */
        printf(" block checksum is 0x%02x \n", ~(block_checksum) & 0xff);
        dnload.romload_dl_checksum += ~(block_checksum) & 0xff;
 
@@ -499,6 +535,79 @@ static int romload_prepare_block(void)
 
        dnload.block_number++;
        dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE;
+       return 0;
+}
+
+static int mtk_prepare_block(void)
+{
+       int rc, i;
+
+       int remaining_bytes;
+       int fill_bytes;
+       uint8_t *block_data;
+       uint8_t tmp_byteswap;
+       uint32_t tmp_size;
+
+       dnload.block_len = MTK_BLOCK_SIZE;
+       dnload.echo_bytecount = 0;
+
+       /* if first block, allocate memory */
+       if (!dnload.block_number) {
+               dnload.block = malloc(dnload.block_len);
+               if (!dnload.block) {
+                       fprintf(stderr, "No memory\n");
+                       return -ENOMEM;
+               }
+
+               /* calculate the number of blocks we need to send */
+               dnload.block_count = (dnload.data_len-3) / MTK_BLOCK_SIZE;
+               /* add one more block if no multiple of blocksize */
+               if((dnload.data_len-3) % MTK_BLOCK_SIZE)
+                       dnload.block_count++;
+
+               /* divide by 2, since we have to tell the mtk loader the size
+                * as count of uint16 (odd transfer sizes are not possible) */
+               tmp_size = (dnload.block_count * MTK_BLOCK_SIZE)/2;
+               dnload.mtk_send_size[0] = (tmp_size >> 24) & 0xff;
+               dnload.mtk_send_size[1] = (tmp_size >> 16) & 0xff;
+               dnload.mtk_send_size[2] = (tmp_size >> 8) & 0xff;
+               dnload.mtk_send_size[3] = tmp_size & 0xff;
+
+               /* initialize write pointer to start of data */
+               dnload.write_ptr = dnload.data;
+       }
+
+       block_data = dnload.block;
+       dnload.write_ptr = dnload.data + 2 +
+                       (dnload.block_len * dnload.block_number);
+
+       remaining_bytes = dnload.data_len - 3 -
+                       (dnload.block_len * dnload.block_number);
+
+       memcpy(block_data, dnload.write_ptr, MTK_BLOCK_SIZE);
+
+       if (remaining_bytes <= MTK_BLOCK_SIZE) {
+               fill_bytes = (MTK_BLOCK_SIZE - remaining_bytes);
+               printf("Preparing the last block, filling %i bytes\n",
+                       fill_bytes);
+               memset(block_data + remaining_bytes, 0x00, fill_bytes);
+               dnload.romload_state = SENDING_LAST_BLOCK;
+       } else {
+               dnload.romload_state = SENDING_BLOCKS;
+               printf("Preparing block %i\n", dnload.block_number+1);
+       }
+
+       /* for the mtk romloader we need to swap MSB <-> LSB */
+       for (i = 0; i < dnload.block_len; i += 2) {
+               tmp_byteswap = dnload.block[i];
+               dnload.block[i] = dnload.block[i+1];
+               dnload.block[i+1] = tmp_byteswap;
+       }
+
+       /* initialize block pointer to start of block */
+       dnload.block_ptr = dnload.block;
+
+       dnload.block_number++;
        return rc;
 }
 
@@ -508,14 +617,14 @@ static int handle_write_block(void)
 
        printf("handle_write_block(): ");
 
-       if (dnload.block_ptr >= dnload.block + dnload.block_len) { //FIXME
-               printf("Block %i finished\n", dnload.block_number-1);
+       if (dnload.block_ptr >= dnload.block + dnload.block_len) {
+               printf("Block %i finished\n", dnload.block_number);
                dnload.write_ptr = dnload.data;
                dnload.serial_fd.when &= ~BSC_FD_WRITE;
                if (dnload.romload_state == SENDING_LAST_BLOCK) {
                        dnload.romload_state = LAST_BLOCK_SENT;
                        printf("Finished, sent %i blocks in total\n",
-                               dnload.block_number-1);
+                               dnload.block_number);
                } else {
                        dnload.romload_state = WAITING_BLOCK_ACK;
                }
@@ -548,16 +657,20 @@ static int handle_write_block(void)
 static int handle_write_dnload(void)
 {
        int bytes_left, write_len, rc;
+       uint8_t xor_init = 0x02;
 
        printf("handle_write(): ");
        if (dnload.write_ptr == dnload.data) {
                /* no bytes have been transferred yet */
-               if (dnload.mode == MODE_C155 ||
-                   dnload.mode == MODE_C123xor) {
-                       uint8_t xor_init = 0x02;
+               switch (dnload.mode) {
+               case MODE_C155:
+               case MODE_C140xor:
+               case MODE_C123xor:
                        rc = write(dnload.serial_fd.fd, &xor_init, 1);
-               } else
-                       usleep(1);
+                       break;
+               default:
+                       break;
+               }
        } else if (dnload.write_ptr >= dnload.data + dnload.data_len) { 
                printf("finished\n");
                dnload.write_ptr = dnload.data;
@@ -600,7 +713,9 @@ static int handle_sercomm_write(void)
 
 static int handle_write(void)
 {
-       if (dnload.mode == MODE_ROMLOAD) {
+       /* TODO: simplify this again (global state: downloading, sercomm) */
+       switch (dnload.mode) {
+       case MODE_ROMLOAD:
                switch (dnload.romload_state) {
                case SENDING_BLOCKS:
                case SENDING_LAST_BLOCK:
@@ -608,7 +723,16 @@ static int handle_write(void)
                default:
                        return handle_sercomm_write();
                }
-       } else {
+               break;
+       case MODE_MTK:
+               switch (dnload.mtk_state) {
+               case MTK_SENDING_BLOCKS:
+                       return handle_write_block();
+               default:
+                       return handle_sercomm_write();
+               }
+               break;
+       default:
                switch (dnload.state) {
                case DOWNLOADING:
                        return handle_write_dnload();
@@ -628,8 +752,10 @@ static void hdlc_send_to_phone(uint8_t dlci, uint8_t *data, int len)
        struct msgb *msg;
        uint8_t *dest;
 
-       printf("hdlc_send_to_phone(dlci=%u): ", dlci);
-       hexdump(data, len);
+       if(dnload.dump_tx) {
+               printf("hdlc_send(dlci=%u): ", dlci);
+               hexdump(data, len);
+       }
 
        if (len > 512) {
                fprintf(stderr, "Too much data to send. %u\n", len);
@@ -664,11 +790,16 @@ static void hdlc_tool_cb(uint8_t dlci, struct msgb *msg)
 {
        struct tool_server *srv = tool_server_for_dlci[dlci];
 
+       if(dnload.dump_rx) {
+               printf("hdlc_recv(dlci=%u): ", dlci);
+               hexdump(msg->data, msg->len);
+       }
+
        if(srv) {
                struct tool_connection *con;
-               u_int16_t *len;
+               uint16_t *len;
 
-               len = (u_int16_t *) msgb_push(msg, 2);
+               len = (uint16_t *) msgb_push(msg, 2);
                *len = htons(msg->len - sizeof(*len));
 
                llist_for_each_entry(con, &srv->connections, entry) {
@@ -683,18 +814,9 @@ static void hdlc_tool_cb(uint8_t dlci, struct msgb *msg)
        msgb_free(msg);
 }
 
-static void print_hdlc(uint8_t *buffer, int length)
-{
-       int i;
-
-       for (i = 0; i < length; ++i)
-               if (sercomm_drv_rx_char(buffer[i]) == 0)
-                       printf("Dropping sample '%c'\n", buffer[i]);
-}
-
 static int handle_buffer(int buf_used_len)
 {
-       int nbytes, buf_left;
+       int nbytes, buf_left, i;
 
        buf_left = buf_used_len - (bufptr - buffer);
        if (buf_left <= 0) {
@@ -707,12 +829,14 @@ static int handle_buffer(int buf_used_len)
        if (nbytes <= 0)
                return nbytes;
 
-       if (!dnload.print_hdlc) {
+       if (!dnload.expect_hdlc) {
                printf("got %i bytes from modem, ", nbytes);
                printf("data looks like: ");
                hexdump(bufptr, nbytes);
        } else {
-               print_hdlc(bufptr, nbytes);
+               for (i = 0; i < nbytes; ++i)
+                       if (sercomm_drv_rx_char(bufptr[i]) == 0)
+                               printf("Dropping sample '%c'\n", bufptr[i]);
        }
 
        return nbytes;
@@ -729,34 +853,53 @@ static int handle_read(void)
 
        if (!memcmp(buffer, phone_prompt1, sizeof(phone_prompt1))) {
                printf("Received PROMPT1 from phone, responding with CMD\n");
-               dnload.print_hdlc = 0;
+               dnload.expect_hdlc = 0;
                dnload.state = WAITING_PROMPT2;
-               rc = write(dnload.serial_fd.fd, dnload_cmd, sizeof(dnload_cmd));
-
-               /* re-read file */
-               rc = read_file(dnload.filename);
-               if (rc < 0) {
-                       fprintf(stderr, "read_file(%s) failed with %d\n",
-                               dnload.filename, rc);
-                       exit(1);
+               if(dnload.filename) {
+                       rc = write(dnload.serial_fd.fd, dnload_cmd, sizeof(dnload_cmd));
+
+                       /* re-read file */
+                       rc = read_file(dnload.filename);
+                       if (rc < 0) {
+                               fprintf(stderr, "read_file(%s) failed with %d\n",
+                                               dnload.filename, rc);
+                               exit(1);
+                       }
                }
        } else if (!memcmp(buffer, phone_prompt2, sizeof(phone_prompt2))) {
                printf("Received PROMPT2 from phone, starting download\n");
                dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE;
                dnload.state = DOWNLOADING;
        } else if (!memcmp(buffer, phone_ack, sizeof(phone_ack))) {
-               printf("Received DOWNLOAD ACK from phone, your code is running now!\n");
+               printf("Received DOWNLOAD ACK from phone, your code is"
+                       " running now!\n");
                dnload.serial_fd.when = BSC_FD_READ;
                dnload.state = WAITING_PROMPT1;
                dnload.write_ptr = dnload.data;
-               dnload.print_hdlc = 1;
+               dnload.expect_hdlc = 1;
+
+               /* check for romloader chainloading mode used as a workaround
+                * for the magic on the C139/C140 and J100i */
+               if (dnload.chainload_filename != NULL) {
+                       printf("Enabled Compal ramloader -> Calypso romloader"
+                               " chainloading mode\n");
+                       bufptr = buffer;
+                       dnload.filename = dnload.chainload_filename;
+                       dnload.mode = MODE_ROMLOAD;
+                       serial_set_baudrate(ROMLOAD_INIT_BAUDRATE);
+                       tick_timer.cb = &beacon_timer_cb;
+                       tick_timer.data = &tick_timer;
+                       bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval);
+               }
        } else if (!memcmp(buffer, phone_nack, sizeof(phone_nack))) {
-               printf("Received DOWNLOAD NACK from phone, something went wrong :(\n");
+               printf("Received DOWNLOAD NACK from phone, something went"
+                       " wrong :(\n");
                dnload.serial_fd.when = BSC_FD_READ;
                dnload.state = WAITING_PROMPT1;
                dnload.write_ptr = dnload.data;
        } else if (!memcmp(buffer, phone_nack_magic, sizeof(phone_nack_magic))) {
-               printf("Received MAGIC NACK from phone, you need to have \"1003\" at 0x803ce0\n");
+               printf("Received MAGIC NACK from phone, you need to"
+                       " have \"1003\" at 0x803ce0\n");
                dnload.serial_fd.when = BSC_FD_READ;
                dnload.state = WAITING_PROMPT1;
                dnload.write_ptr = dnload.data;
@@ -805,7 +948,7 @@ static int handle_read_romload(void)
 
                printf("Received ident ack from phone, sending "
                        "parameter sequence\n");
-               dnload.print_hdlc = 1;
+               dnload.expect_hdlc = 1;
                dnload.romload_state = WAITING_PARAM_ACK;
                rc = write(dnload.serial_fd.fd, romload_param,
                           sizeof(romload_param));
@@ -825,18 +968,16 @@ static int handle_read_romload(void)
                printf("Received parameter ack from phone, "
                        "starting download\n");
                serial_set_baudrate(ROMLOAD_DL_BAUDRATE);
-               /* let the target's UART settle after changing baud*/
-               usleep(ROMLOAD_BEACON_INTERVAL*2);
 
                /* using the max blocksize the phone tells us */
                dnload.block_payload_size = ((buffer[3] << 8) + buffer[2]);
-               printf("Used blocksize for download is %i bytes \n",
+               printf("Used blocksize for download is %i bytes\n",
                        dnload.block_payload_size);
                dnload.block_payload_size -= ROMLOAD_BLOCK_HDR_LEN;
                dnload.romload_state = SENDING_BLOCKS;
                dnload.block_number = 0;
                romload_prepare_block();
-               bufptr = (bufptr - 2);
+               bufptr -= 2;
                break;
        case WAITING_BLOCK_ACK:
        case LAST_BLOCK_SENT:
@@ -863,8 +1004,7 @@ static int handle_read_romload(void)
                                "something went wrong, aborting\n");
                        serial_set_baudrate(ROMLOAD_INIT_BAUDRATE);
                        dnload.romload_state = WAITING_IDENTIFICATION;
-                       usleep(ROMLOAD_BEACON_INTERVAL*2);
-                       reload_beacon_timer();
+                       bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval);
                }
                break;
        case WAITING_CHECKSUM_ACK:
@@ -872,29 +1012,22 @@ static int handle_read_romload(void)
                            sizeof(romload_checksum_ack))) {
                        printf("Checksum on phone side matches, "
                                "let's branch to your code\n");
-
-                       uint32_t branch_address_32 = ROMLOAD_ADDRESS;
-                       uint8_t branch_address[4];
-                       branch_address[0] = (branch_address_32 >> 24) & 0xff;
-                       branch_address[1] = (branch_address_32 >> 16) & 0xff;
-                       branch_address[2] = (branch_address_32 >> 8) & 0xff;
-                       branch_address[3] = branch_address_32 & 0xff;
-                       printf("Branching to 0x%08x\n", branch_address_32);
+                       printf("Branching to 0x%08x\n", ROMLOAD_ADDRESS);
 
                        rc = write(dnload.serial_fd.fd, romload_branch_cmd,
                                   sizeof(romload_branch_cmd));
-                       rc = write(dnload.serial_fd.fd, &branch_address, 4);
+                       rc = write(dnload.serial_fd.fd, &dnload.load_address,
+                                  sizeof(dnload.load_address));
                        dnload.romload_state = WAITING_BRANCH_ACK;
-                       bufptr = (bufptr - 1);
+                       bufptr -= 1;
                } else if (!memcmp(buffer, romload_checksum_nack,
                                   sizeof(romload_checksum_nack))) {
                        printf("Checksum on phone side (0x%02x) doesn't "
-                               "match ours, aborting\n", buffer[2]);
+                               "match ours, aborting\n", ~buffer[2]);
                        serial_set_baudrate(ROMLOAD_INIT_BAUDRATE);
                        dnload.romload_state = WAITING_IDENTIFICATION;
-                       usleep(ROMLOAD_BEACON_INTERVAL*2);
-                       reload_beacon_timer();
-                       bufptr = (bufptr - 1);
+                       bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval);
+                       bufptr -= 1;
                }
                break;
        case WAITING_BRANCH_ACK:
@@ -904,16 +1037,155 @@ static int handle_read_romload(void)
                        dnload.serial_fd.when = BSC_FD_READ;
                        dnload.romload_state = FINISHED;
                        dnload.write_ptr = dnload.data;
-                       dnload.print_hdlc = 1;
+                       dnload.expect_hdlc = 1;
                } else if (!memcmp(buffer, romload_branch_nack,
                           sizeof(romload_branch_nack))) {
                        printf("Received branch nack, aborting\n");
                        serial_set_baudrate(ROMLOAD_INIT_BAUDRATE);
                        dnload.romload_state = WAITING_IDENTIFICATION;
-                       usleep(ROMLOAD_BEACON_INTERVAL*2);
-                       reload_beacon_timer();
+                       bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval);
                }
                break;
+       default:
+               break;
+       }
+
+       bufptr += nbytes;
+       return nbytes;
+}
+
+/* MTK romloader */
+static int handle_read_mtk(void)
+{
+       int rc, nbytes, buf_used_len;
+
+       switch (dnload.mtk_state) {
+       case MTK_WAIT_ADDR_ACK:
+       case MTK_WAIT_SIZE_ACK:
+       case MTK_WAIT_BRANCH_ADDR_ACK:
+               buf_used_len = 4;
+               break;
+       case MTK_FINISHED:
+               buf_used_len = sizeof(buffer);
+               break;
+       default:
+               buf_used_len = 1;
+       }
+
+       nbytes = handle_buffer(buf_used_len);
+       if (nbytes <= 0)
+               return nbytes;
+
+       switch (dnload.mtk_state) {
+       case MTK_INIT_1:
+               if (!(buffer[0] == mtk_init_resp[0]))
+                       break;
+               dnload.mtk_state = MTK_INIT_2;
+               printf("Received init magic byte 1\n");
+               rc = write(dnload.serial_fd.fd, &mtk_init_cmd[1], 1);
+               break;
+       case MTK_INIT_2:
+               if (!(buffer[0] == mtk_init_resp[1]))
+                       break;
+               dnload.mtk_state = MTK_INIT_3;
+               printf("Received init magic byte 2\n");
+               rc = write(dnload.serial_fd.fd, &mtk_init_cmd[2], 1);
+               break;
+       case MTK_INIT_3:
+               if (!(buffer[0] == mtk_init_resp[2]))
+                       break;
+               dnload.mtk_state = MTK_INIT_4;
+               printf("Received init magic byte 3\n");
+               rc = write(dnload.serial_fd.fd, &mtk_init_cmd[3], 1);
+               break;
+       case MTK_INIT_4:
+               if (!(buffer[0] == mtk_init_resp[3]))
+                       break;
+               dnload.mtk_state = MTK_WAIT_WRITE_ACK;
+               printf("Received init magic byte 4, requesting write\n");
+               rc = write(dnload.serial_fd.fd, &mtk_command[0], 1);
+               break;
+       case MTK_WAIT_WRITE_ACK:
+               if (!(buffer[0] == mtk_command[0]))
+                       break;
+               dnload.mtk_state = MTK_WAIT_ADDR_ACK;
+               printf("Received write ack, sending load address\n");
+
+               rc = write(dnload.serial_fd.fd, &dnload.load_address,
+                          sizeof(dnload.load_address));
+               break;
+       case MTK_WAIT_ADDR_ACK:
+               if (memcmp(buffer, dnload.load_address,
+                           sizeof(dnload.load_address)))
+                       break;
+               printf("Received address ack from phone, sending loadsize\n");
+               /* re-read file */
+               rc = read_file(dnload.filename);
+               if (rc < 0) {
+                       fprintf(stderr, "read_file(%s) failed with %d\n",
+                               dnload.filename, rc);
+                       exit(1);
+               }
+               dnload.block_number = 0;
+               mtk_prepare_block();
+               dnload.mtk_state = MTK_WAIT_SIZE_ACK;
+               rc = write(dnload.serial_fd.fd, &dnload.mtk_send_size,
+                          sizeof(dnload.mtk_send_size));
+               break;
+       case MTK_WAIT_SIZE_ACK:
+               if (memcmp(buffer, dnload.mtk_send_size,
+                           sizeof(dnload.mtk_send_size)))
+                       break;
+               printf("Received size ack\n");
+               dnload.expect_hdlc = 1;
+               dnload.mtk_state = MTK_SENDING_BLOCKS;
+               dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE;
+               bufptr -= 3;
+               break;
+       case MTK_SENDING_BLOCKS:
+               if (!(buffer[0] == dnload.block[dnload.echo_bytecount]))
+                       printf("Warning: Byte %i of Block %i doesn't match,"
+                               " check your serial connection!\n",
+                               dnload.echo_bytecount, dnload.block_number);
+               dnload.echo_bytecount++;
+
+               if ((dnload.echo_bytecount+1) > MTK_BLOCK_SIZE) {
+                       if ( dnload.block_number == dnload.block_count) {
+                               rc = write(dnload.serial_fd.fd,
+                                          &mtk_command[3], 1);
+                               printf("Sending branch command\n");
+                               dnload.expect_hdlc = 0;
+                               dnload.mtk_state = MTK_WAIT_BRANCH_CMD_ACK;
+                               break;
+                       }
+                       printf("Received Block %i preparing next block\n",
+                               dnload.block_number);
+                       mtk_prepare_block();
+                       dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE;
+               }
+               break;
+       case MTK_WAIT_BRANCH_CMD_ACK:
+               if (!(buffer[0] == mtk_command[3]))
+                       break;
+               dnload.mtk_state = MTK_WAIT_BRANCH_ADDR_ACK;
+               printf("Received branch command ack, sending address\n");
+
+               rc = write(dnload.serial_fd.fd, &dnload.load_address,
+                          sizeof(dnload.load_address));
+               break;
+       case MTK_WAIT_BRANCH_ADDR_ACK:
+               if (memcmp(buffer, dnload.load_address,
+                           sizeof(dnload.load_address)))
+                       break;
+               printf("Received branch address ack, code should run now\n");
+               serial_set_baudrate(MODEM_BAUDRATE);
+               dnload.serial_fd.when = BSC_FD_READ;
+               dnload.mtk_state = MTK_FINISHED;
+               dnload.write_ptr = dnload.data;
+               dnload.expect_hdlc = 1;
+               break;
+       default:
+               break;
        }
 
        bufptr += nbytes;
@@ -924,11 +1196,17 @@ static int serial_read(struct bsc_fd *fd, unsigned int flags)
 {
        int rc;
        if (flags & BSC_FD_READ) {
-               if (dnload.mode == MODE_ROMLOAD)
-                       rc = handle_read_romload();
-               else
-                       rc = handle_read();
-
+               switch (dnload.mode) {
+                       case MODE_ROMLOAD:
+                               rc = handle_read_romload();
+                               break;
+                       case MODE_MTK:
+                               rc = handle_read_mtk();
+                               break;
+                       default:
+                               rc = handle_read();
+                               break;
+               }
                if (rc == 0)
                        exit(2);
        }
@@ -955,15 +1233,20 @@ static int parse_mode(const char *arg)
                return MODE_C155;
        else if (!strcasecmp(arg, "romload"))
                return MODE_ROMLOAD;
+       else if (!strcasecmp(arg, "mtk"))
+               return MODE_MTK;
 
        return -1;
 }
 
 #define HELP_TEXT \
-       "[ -v | -h ] [ -p /dev/ttyXXXX ] [ -s /tmp/osmocom_l2 ]\n" \
-       "\t\t[ -l /tmp/osmocom_loader ]\n" \
-       "\t\t[ -m {c123,c123xor,c140,c140xor,c155,romload} ]\n" \
-       "\t\t file.bin\n\n" \
+       "[ -v | -h ] [ -d [t][r] ] [ -p /dev/ttyXXXX ]\n" \
+       "\t\t [ -s /tmp/osmocom_l2 ]\n" \
+       "\t\t [ -l /tmp/osmocom_loader ]\n" \
+       "\t\t [ -m {c123,c123xor,c140,c140xor,c155,romload,mtk} ]\n" \
+       "\t\t [ -c /to-be-chainloaded-file.bin ]\n" \
+       "\t\t [ -i beacon-interval (mS) ]\n" \
+       "\t\t  file.bin\n\n" \
        "* Open serial port /dev/ttyXXXX (connected to your phone)\n" \
        "* Perform handshaking with the ramloader in the phone\n" \
        "* Download file.bin to the attached phone (base address 0x00800100)\n"
@@ -984,8 +1267,8 @@ static int version(const char *name)
 static int un_tool_read(struct bsc_fd *fd, unsigned int flags)
 {
        int rc, c;
-       u_int16_t length = 0xffff;
-       u_int8_t buf[4096];
+       uint16_t length = 0xffff;
+       uint8_t buf[4096];
        struct tool_connection *con = (struct tool_connection *)fd->data;
 
        c = 0;
@@ -1005,7 +1288,7 @@ static int un_tool_read(struct bsc_fd *fd, unsigned int flags)
                c += rc;
        }
 
-       length = ntohs(*(u_int16_t*)buf);
+       length = ntohs(*(uint16_t*)buf);
 
        c = 0;
        while(c < length) {
@@ -1077,8 +1360,8 @@ static int tool_accept(struct bsc_fd *fd, unsigned int flags)
  * Register and start a tool server
  */
 static int register_tool_server(struct tool_server *ts,
-                                                               const char *path,
-                                                               uint8_t dlci)
+                               const char *path,
+                               uint8_t dlci)
 {
        struct bsc_fd *bfd = &ts->bfd;
        struct sockaddr_un local;
@@ -1142,16 +1425,38 @@ static int register_tool_server(struct tool_server *ts,
 
 extern void hdlc_tpudbg_cb(uint8_t dlci, struct msgb *msg);
 
+void parse_debug(const char *str)
+{
+       while(*str) {
+               switch(*str) {
+               case 't':
+                       dnload.dump_tx = 1;
+                       break;
+               case 'r':
+                       dnload.dump_rx = 1;
+                       break;
+               default:
+                       printf("Unknown debug flag %c\n", *str);
+                       abort();
+                       break;
+               }
+               str++;
+       }
+}
+
 int main(int argc, char **argv)
 {
        int opt, flags;
+       uint32_t tmp_load_address = ROMLOAD_ADDRESS;
        const char *serial_dev = "/dev/ttyUSB1";
        const char *layer2_un_path = "/tmp/osmocom_l2";
        const char *loader_un_path = "/tmp/osmocom_loader";
 
        dnload.mode = MODE_C123;
+       dnload.chainload_filename = NULL;
+       dnload.beacon_interval = DEFAULT_BEACON_INTERVAL;
 
-       while ((opt = getopt(argc, argv, "hl:p:m:s:v")) != -1) {
+       while ((opt = getopt(argc, argv, "d:hl:p:m:c:s:i:v")) != -1) {
                switch (opt) {
                case 'p':
                        serial_dev = optarg;
@@ -1170,6 +1475,15 @@ int main(int argc, char **argv)
                case 'v':
                        version(argv[0]);
                        break;
+               case 'd':
+                       parse_debug(optarg);
+                       break;
+               case 'c':
+                       dnload.chainload_filename = optarg;
+                       break;
+               case 'i':
+                       dnload.beacon_interval = atoi(optarg) * 1000;
+                       break;
                case 'h':
                default:
                        usage(argv[0]);
@@ -1178,12 +1492,11 @@ int main(int argc, char **argv)
        }
 
        if (argc <= optind) {
-               fprintf(stderr, "You have to specify the filename\n");
-               usage(argv[0]);
+               dnload.filename = NULL;
+       } else {
+               dnload.filename = argv[optind];
        }
 
-       dnload.filename = argv[optind];
-
        dnload.serial_fd.fd = serial_init(serial_dev);
        if (dnload.serial_fd.fd < 0) {
                fprintf(stderr, "Cannot open serial device %s\n", serial_dev);
@@ -1219,10 +1532,25 @@ int main(int argc, char **argv)
 
        /* if in romload mode, start our beacon timer */
        if (dnload.mode == MODE_ROMLOAD) {
+               tmp_load_address = ROMLOAD_ADDRESS;
                serial_set_baudrate(ROMLOAD_INIT_BAUDRATE);
-               start_beacon_timer();
+               tick_timer.cb = &beacon_timer_cb;
+               tick_timer.data = &tick_timer;
+               bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval);
+       }
+       else if (dnload.mode == MODE_MTK) {
+               tmp_load_address = MTK_ADDRESS;
+               serial_set_baudrate(MTK_INIT_BAUDRATE);
+               tick_timer.cb = &mtk_timer_cb;
+               tick_timer.data = &tick_timer;
+               bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval);
        }
 
+       dnload.load_address[0] = (tmp_load_address >> 24) & 0xff;
+       dnload.load_address[1] = (tmp_load_address >> 16) & 0xff;
+       dnload.load_address[2] = (tmp_load_address >> 8) & 0xff;
+       dnload.load_address[3] = tmp_load_address & 0xff;
+
        while (1)
                bsc_select_main(0);