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