1 /* $Id$ */
2 /* Copyright (c) 2012 Pierre Pronchery <khorben@defora.org> */
3 /* This file is part of DeforaOS Desktop Phone */
4 /* This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */
15
16
17
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <termios.h>
23 #include <errno.h>
24 #include <osmocom/core/select.h>
25 #include <osmocom/core/serial.h>
26 #include <osmocom/core/timer.h>
27 #include <glib.h>
28 #include <System.h>
29 #include <Phone/modem.h>
30 #include "../../config.h"
31
32
33 /* Osmocom */
34 /* private */
35 /* types */
36 struct tool_server
37 {
38 struct osmo_fd bfd;
39 uint8_t dlci;
40 struct llist_head connections;
41 };
42
43 enum dnload_state
44 {
45 WAITING_PROMPT1,
46 WAITING_PROMPT2,
47 DOWNLOADING
48 };
49
50 enum romload_state
51 {
52 WAITING_IDENTIFICATION,
53 WAITING_PARAM_ACK,
54 SENDING_BLOCKS,
55 SENDING_LAST_BLOCK,
56 LAST_BLOCK_SENT,
57 WAITING_BLOCK_ACK,
58 WAITING_CHECKSUM_ACK,
59 WAITING_BRANCH_ACK,
60 FINISHED
61 };
62
63 enum mtk_state
64 {
65 MTK_INIT_1,
66 MTK_INIT_2,
67 MTK_INIT_3,
68 MTK_INIT_4,
69 MTK_WAIT_WRITE_ACK,
70 MTK_WAIT_ADDR_ACK,
71 MTK_WAIT_SIZE_ACK,
72 MTK_SENDING_BLOCKS,
73 MTK_WAIT_BRANCH_CMD_ACK,
74 MTK_WAIT_BRANCH_ADDR_ACK,
75 MTK_FINISHED
76 };
77
78 enum dnload_mode
79 {
80 MODE_C123,
81 MODE_C123xor,
82 MODE_C140,
83 MODE_C140xor,
84 MODE_C155,
85 MODE_ROMLOAD,
86 MODE_MTK,
87 MODE_INVALID
88 };
89
90 typedef struct _OsmocomDnload
91 {
92 enum dnload_state state;
93 enum romload_state romload_state;
94 enum mtk_state mtk_state;
95 enum dnload_mode mode, previous_mode;
96 struct osmo_fd serial_fd;
97 char *filename, *previous_filename;
98 char *chainload_filename;
99
100 int expect_hdlc;
101
102 int dump_rx;
103 int dump_tx;
104 int beacon_interval;
105
106 /* data to be downloaded */
107 uint8_t *data;
108 int data_len;
109
110 uint8_t *write_ptr;
111
112 /* romload: block to be downloaded */
113 uint8_t *block;
114 int block_len;
115 uint8_t block_number;
116 uint16_t block_payload_size;
117 int romload_dl_checksum;
118 uint8_t *block_ptr;
119 uint8_t load_address[4];
120
121 uint8_t mtk_send_size[4];
122 int block_count;
123 int echo_bytecount;
124
125 struct tool_server layer2_server;
126 struct tool_server loader_server;
127 } OsmocomDnload;
128
129 typedef struct _ModemPlugin
130 {
131 ModemPluginHelper * helper;
132
133 guint reset;
134
135 /* modem */
136 struct osmo_fd fd;
137 guint source;
138 OsmocomDnload dnload;
139 struct osmo_timer_list tick_timer;
140 } Osmocom;
141
142
143 /* constants */
144 #define ROMLOAD_INIT_BAUDRATE B19200
145 #define ROMLOAD_DL_BAUDRATE B115200
146 #define ROMLOAD_BLOCK_HDR_LEN 10
147 #define ROMLOAD_ADDRESS 0x820000
148
149 #define MTK_INIT_BAUDRATE B19200
150 #define MTK_ADDRESS 0x40001400
151 #define MTK_BLOCK_SIZE 1024
152
153 static const uint8_t romload_ident_cmd[] = { 0x3c, 0x69 }; /* <i */
154
155 /* MTK romloader specific */
156 static const uint8_t mtk_init_cmd[] = { 0xa0, 0x0a, 0x50, 0x05 };
157 static const uint8_t mtk_init_resp[] = { 0x5f, 0xf5, 0xaf, 0xfa };
158 static const uint8_t mtk_command[] = { 0xa1, 0xa2, 0xa4, 0xa8 };
159
160
161 /* variables */
162 /* FIXME ugly hack to circumvent API limitations in Osmocom */
163 Osmocom * osmocom;
164
165 static ModemConfig _osmocom_config[] =
166 {
167 { "device", "Device", MCT_FILENAME },
168 { "baudrate", "Baudrate", MCT_UINT32 },
169 { "hwflow", "Hardware flow control",MCT_BOOLEAN },
170 { NULL, NULL, MCT_NONE }
171 };
172
173
174 /* prototypes */
175 static ModemPlugin * _osmocom_init(ModemPluginHelper * helper);
176 static void _osmocom_destroy(ModemPlugin * modem);
177 static int _osmocom_start(ModemPlugin * modem, unsigned int retry);
178 static int _osmocom_stop(ModemPlugin * modem);
179 static int _osmocom_request(ModemPlugin * modem, ModemRequest * request);
180
181 /* callbacks */
182 static gboolean _osmocom_on_idle(gpointer data);
183 static gboolean _osmocom_on_reset(gpointer data);
184
185 /* XXX re-write both to be non-blocking instead */
186 static void _osmocom_on_beacon_timer(void * data);
187 static void _osmocom_on_mtk_timer(void * data);
188
189 static int _osmocom_on_serial_read(struct osmo_fd * fd, unsigned int flags);
190
191
192 /* public */
193 /* variables */
194 ModemPluginDefinition plugin =
195 {
196 "Osmocom",
197 NULL,
198 _osmocom_config,
199 _osmocom_init,
200 _osmocom_destroy,
201 _osmocom_start,
202 _osmocom_stop,
203 _osmocom_request,
204 NULL
205 };
206
207
208 /* private */
209 /* functions */
210 /* osmocom_init */
_osmocom_init(ModemPluginHelper * helper)211 static ModemPlugin * _osmocom_init(ModemPluginHelper * helper)
212 {
213 if((osmocom = object_new(sizeof(*osmocom))) == NULL)
214 return NULL;
215 memset(osmocom, 0, sizeof(*osmocom));
216 osmocom->helper = helper;
217 return osmocom;
218 }
219
220
221 /* osmocom_destroy */
_osmocom_destroy(ModemPlugin * modem)222 static void _osmocom_destroy(ModemPlugin * modem)
223 {
224 _osmocom_stop(modem);
225 object_delete(osmocom);
226 osmocom = NULL;
227 }
228
229
230 /* osmocom_reset */
231 static int _reset_open(ModemPlugin * modem);
232 static unsigned int _reset_baudrate(ModemPlugin * modem, unsigned int baudrate);
233
_osmocom_reset(ModemPlugin * modem,unsigned int retry)234 static int _osmocom_reset(ModemPlugin * modem, unsigned int retry)
235 {
236 Osmocom * osmocom = modem;
237
238 _osmocom_stop(modem);
239 if(_reset_open(modem) != 0)
240 {
241 if(retry > 0)
242 osmocom->reset = g_timeout_add(retry,
243 _osmocom_on_reset, modem);
244 return -1;
245 }
246 #if 0
247 osmocom->channel = g_io_channel_unix_new(fd);
248 if(g_io_channel_set_encoding(osmocom->channel, NULL, &error)
249 != G_IO_STATUS_NORMAL)
250 {
251 modem->helper->error(modem->helper->modem, error->message, 1);
252 g_error_free(error);
253 }
254 g_io_channel_set_buffered(osmocom->channel, FALSE);
255 osmocom->
256 #else
257 osmocom->source = g_idle_add(_osmocom_on_idle, modem);
258 #endif
259 return 0;
260 }
261
_reset_open(ModemPlugin * modem)262 static int _reset_open(ModemPlugin * modem)
263 {
264 Osmocom * osmocom = modem;
265 ModemPluginHelper * helper = osmocom->helper;
266 char const * device;
267 unsigned int baudrate;
268 int flags;
269 uint32_t tmpaddr = ROMLOAD_ADDRESS;
270 char const * p;
271
272 if((device = helper->config_get(helper->modem, "device")) == NULL)
273 device = "/dev/modem";
274 if((p = helper->config_get(helper->modem, "baudrate")) == NULL
275 || (baudrate = strtoul(p, NULL, 10)) == 0)
276 baudrate = 115200;
277 baudrate = _reset_baudrate(modem, baudrate);
278 if((osmocom->fd.fd = osmo_serial_init(device, baudrate)) < 0)
279 {
280 /* XXX report error */
281 #ifdef DEBUG
282 fprintf(stderr, "DEBUG: %s: %s\n", device, strerror(errno));
283 #endif
284 return -1;
285 }
286 if(osmo_fd_register(&osmocom->fd) != 0)
287 {
288 #ifdef DEBUG
289 fprintf(stderr, "DEBUG: %s: %s\n", device, strerror(errno));
290 #endif
291 /* XXX report error */
292 return -1;
293 }
294 /* Set serial socket to non-blocking mode of operation */
295 if((flags = fcntl(osmocom->fd.fd, F_GETFL)) != -1)
296 {
297 flags |= O_NONBLOCK;
298 fcntl(osmocom->fd.fd, F_SETFL, flags);
299 }
300 osmocom->dnload.serial_fd.when = BSC_FD_READ;
301 osmocom->dnload.serial_fd.cb = _osmocom_on_serial_read;
302 if(osmocom->dnload.mode == MODE_ROMLOAD)
303 {
304 tmpaddr = ROMLOAD_ADDRESS;
305 osmo_serial_set_baudrate(osmocom->fd.fd, ROMLOAD_INIT_BAUDRATE);
306 osmocom->tick_timer.cb = &_osmocom_on_beacon_timer;
307 osmocom->tick_timer.data = modem;
308 osmo_timer_schedule(&osmocom->tick_timer, 0,
309 osmocom->dnload.beacon_interval);
310 }
311 else
312 {
313 tmpaddr = MTK_ADDRESS;
314 osmo_serial_set_baudrate(osmocom->fd.fd, MTK_INIT_BAUDRATE);
315 osmocom->tick_timer.cb = &_osmocom_on_mtk_timer;
316 osmocom->tick_timer.data = modem;
317 osmo_timer_schedule(&osmocom->tick_timer, 0,
318 osmocom->dnload.beacon_interval);
319 }
320 /* FIXME not endian proof */
321 osmocom->dnload.load_address[0] = (tmpaddr >> 24) & 0xff;
322 osmocom->dnload.load_address[1] = (tmpaddr >> 16) & 0xff;
323 osmocom->dnload.load_address[2] = (tmpaddr >> 8) & 0xff;
324 osmocom->dnload.load_address[3] = tmpaddr & 0xff;
325 return 0;
326 }
327
_reset_baudrate(ModemPlugin * modem,unsigned int baudrate)328 static unsigned int _reset_baudrate(ModemPlugin * modem, unsigned int baudrate)
329 {
330 switch(baudrate)
331 {
332 case 1200:
333 return B1200;
334 case 2400:
335 return B2400;
336 case 4800:
337 return B4800;
338 case 9600:
339 return B9600;
340 case 19200:
341 return B19200;
342 case 38400:
343 return B38400;
344 #ifdef B76800
345 case 76800:
346 return B76800;
347 #endif
348 #ifdef B14400
349 case 14400:
350 return B14400;
351 #endif
352 #ifdef B28800
353 case 28800:
354 return B28800;
355 #endif
356 case 57600:
357 return B57600;
358 case 115200:
359 return B115200;
360 case 230400:
361 return B230400;
362 case 460800:
363 return B460800;
364 case 921600:
365 return B921600;
366 default:
367 error_set("%u%s", baudrate,
368 "Unsupported baudrate (using 115200)");
369 modem->helper->error(NULL, error_get(NULL), 1);
370 return B115200;
371 }
372 }
373
374
375 /* osmocom_start */
_osmocom_start(ModemPlugin * modem,unsigned int retry)376 static int _osmocom_start(ModemPlugin * modem, unsigned int retry)
377 {
378 #ifdef DEBUG
379 fprintf(stderr, "DEBUG: %s()\n", __func__);
380 #endif
381 _osmocom_reset(modem, retry);
382 return 0;
383 }
384
385
386 /* osmocom_stop */
_osmocom_stop(ModemPlugin * modem)387 static int _osmocom_stop(ModemPlugin * modem)
388 {
389 Osmocom * osmocom = modem;
390
391 #ifdef DEBUG
392 fprintf(stderr, "DEBUG: %s()\n", __func__);
393 #endif
394 if(osmocom->source != 0)
395 {
396 g_source_remove(osmocom->source);
397 osmocom->source = 0;
398 }
399 return 0;
400 }
401
402
403 /* osmocom_request */
404 static int _request_call(ModemPlugin * modem, ModemRequest * request);
405 static int _request_message_send(ModemPlugin * modem, ModemRequest * request);
406
_osmocom_request(ModemPlugin * modem,ModemRequest * request)407 static int _osmocom_request(ModemPlugin * modem, ModemRequest * request)
408 {
409 switch(request->type)
410 {
411 case MODEM_REQUEST_CALL:
412 return _request_call(modem, request);
413 case MODEM_REQUEST_MESSAGE_SEND:
414 return _request_message_send(modem, request);
415 #ifdef DEBUG
416 default:
417 break;
418 #endif
419 }
420 return 0;
421 }
422
_request_call(ModemPlugin * modem,ModemRequest * request)423 static int _request_call(ModemPlugin * modem, ModemRequest * request)
424 {
425 Osmocom * osmocom = modem;
426
427 /* FIXME implement */
428 return -1;
429 }
430
_request_message_send(ModemPlugin * modem,ModemRequest * request)431 static int _request_message_send(ModemPlugin * modem, ModemRequest * request)
432 {
433 Osmocom * osmocom = modem;
434
435 /* FIXME implement */
436 return -1;
437 }
438
439
440 /* callbacks */
441 /* osmocom_on_idle */
_osmocom_on_idle(gpointer data)442 static gboolean _osmocom_on_idle(gpointer data)
443 {
444 ModemPlugin * modem = data;
445 Osmocom * osmocom = modem;
446
447 if(osmo_select_main(0) < 0)
448 {
449 osmocom->source = 0;
450 return FALSE;
451 }
452 return TRUE;
453 }
454
455
456 /* osmocom_on_reset */
_osmocom_on_reset(gpointer data)457 static gboolean _osmocom_on_reset(gpointer data)
458 {
459 ModemPlugin * modem = data;
460 Osmocom * osmocom = modem;
461
462 if(_osmocom_reset(modem, 0) == 0)
463 {
464 osmocom->reset = 0;
465 return FALSE;
466 }
467 return TRUE;
468 }
469
470
471 /* osmocom_on_beacon_timer */
_osmocom_on_beacon_timer(void * data)472 static void _osmocom_on_beacon_timer(void * data)
473 {
474 ModemPlugin * modem = data;
475 Osmocom * osmocom = modem;
476 int rc;
477
478 if(osmocom->dnload.romload_state == WAITING_IDENTIFICATION)
479 {
480 printf("Sending Calypso romloader beacon...\n");
481 rc = write(osmocom->dnload.serial_fd.fd, romload_ident_cmd,
482 sizeof(romload_ident_cmd));
483 if(rc != sizeof(romload_ident_cmd))
484 printf("Error sending identification beacon\n");
485 osmo_timer_schedule(&osmocom->tick_timer, 0,
486 osmocom->dnload.beacon_interval);
487 }
488 }
489
490
491 /* osmocom_on_mtk_timer */
_osmocom_on_mtk_timer(void * data)492 static void _osmocom_on_mtk_timer(void * data)
493 {
494 ModemPlugin * modem = data;
495 Osmocom * osmocom = modem;
496 int rc;
497
498 if(osmocom->dnload.mtk_state == MTK_INIT_1)
499 {
500 printf("Sending MTK romloader beacon...\n");
501 rc = write(osmocom->dnload.serial_fd.fd, &mtk_init_cmd[0], 1);
502 if(rc != 1)
503 printf("Error sending identification beacon\n");
504 osmo_timer_schedule(&osmocom->tick_timer, 0,
505 osmocom->dnload.beacon_interval);
506 }
507 }
508
509
510 /* osmocom_on_serial_read */
511 static int _read_handle_romload();
512 static int _read_handle();
513 static int _read_handle_write();
514
_osmocom_on_serial_read(struct osmo_fd * fd,unsigned int flags)515 static int _osmocom_on_serial_read(struct osmo_fd * fd, unsigned int flags)
516 {
517 int rc;
518
519 /* FIXME really implement */
520 if(flags & BSC_FD_READ)
521 {
522 #if 0
523 switch(osmocom->mode)
524 {
525 case MODE_ROMLOAD:
526 rc = _read_handle_romload();
527 break;
528 default:
529 rc = _read_handle();
530 break;
531 }
532 if(rc == 0)
533 /* XXX wtf? */
534 exit(2);
535 #endif
536 }
537 if(flags & BSC_FD_WRITE)
538 {
539 rc = _read_handle_write();
540 if(rc != 0)
541 osmocom->dnload.state = WAITING_PROMPT1;
542 }
543 return 0;
544 }
545
_read_handle_romload()546 static int _read_handle_romload()
547 {
548 return -1;
549 }
550
_read_handle()551 static int _read_handle()
552 {
553 return -1;
554 }
555
_read_handle_write()556 static int _read_handle_write()
557 {
558 return -1;
559 }
560