1/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10** GNU General Public License for more details.
11*/
12/* implement the modem character device for Android within the QEMU event loop.
13 * it communicates through a serial port with "rild" (Radio Interface Layer Daemon)
14 * on the emulated device.
15 */
16#include "modem_driver.h"
17#include "qemu-char.h"
18
19#define  xxDEBUG
20
21#ifdef DEBUG
22#  include <stdio.h>
23#  define  D(...)   ( fprintf( stderr, __VA_ARGS__ ) )
24#else
25#  define  D(...)   ((void)0)
26#endif
27
28AModem            android_modem;
29CharDriverState*  android_modem_cs;
30
31typedef struct {
32    CharDriverState*  cs;
33    AModem            modem;
34    char              in_buff[ 1024 ];
35    int               in_pos;
36    int               in_sms;
37} ModemDriver;
38
39/* send unsollicited messages to the device */
40static void
41modem_driver_unsol( void*  _md, const char*  message)
42{
43    ModemDriver*      md = _md;
44    int               len = strlen(message);
45
46    qemu_chr_write(md->cs, (const uint8_t*)message, len);
47}
48
49static int
50modem_driver_can_read( void*  _md )
51{
52    ModemDriver*  md  = _md;
53    int           ret = sizeof(md->in_buff) - md->in_pos;
54
55    return ret;
56}
57
58/* despite its name, this function is called when the device writes to the modem */
59static void
60modem_driver_read( void*  _md, const uint8_t*  src, int  len )
61{
62    ModemDriver*      md  = _md;
63    const uint8_t*    end = src + len;
64    int               nn;
65
66    D( "%s: reading %d from %p bytes:", __FUNCTION__, len, src );
67    for (nn = 0; nn < len; nn++) {
68        int  c = src[nn];
69        if (c >= 32 && c < 127)
70            D( "%c", c );
71        else if (c == '\n')
72            D( "<LF>" );
73        else if (c == '\r')
74            D( "<CR>" );
75        else
76            D( "\\x%02x", c );
77    }
78    D( "\n" );
79
80    for ( ; src < end; src++ ) {
81        char  c = src[0];
82
83        if (md->in_sms) {
84            if (c != 26)
85                goto AppendChar;
86
87            md->in_buff[ md->in_pos ] = c;
88            md->in_pos++;
89            md->in_sms = 0;
90            c = '\n';
91        }
92
93        if (c == '\n' || c == '\r') {
94            const char*  answer;
95
96            if (md->in_pos == 0)  /* skip empty lines */
97                continue;
98
99            md->in_buff[ md->in_pos ] = 0;
100            md->in_pos                = 0;
101
102            D( "%s: << %s\n", __FUNCTION__, md->in_buff );
103            answer = amodem_send(android_modem, md->in_buff);
104            if (answer != NULL) {
105                D( "%s: >> %s\n", __FUNCTION__, answer );
106                len = strlen(answer);
107                if (len == 2 && answer[0] == '>' && answer[1] == ' ')
108                    md->in_sms = 1;
109
110                qemu_chr_write(md->cs, (const uint8_t*)answer, len);
111                qemu_chr_write(md->cs, (const uint8_t*)"\r", 1);
112            } else
113                D( "%s: -- NO ANSWER\n", __FUNCTION__ );
114
115            continue;
116        }
117    AppendChar:
118        md->in_buff[ md->in_pos++ ] = c;
119        if (md->in_pos == sizeof(md->in_buff)) {
120            /* input is too long !! */
121            md->in_pos = 0;
122        }
123    }
124    D( "%s: done\n", __FUNCTION__ );
125}
126
127
128static void
129modem_driver_init( int  base_port, ModemDriver*  dm, CharDriverState*  cs )
130{
131    dm->cs     = cs;
132    dm->in_pos = 0;
133    dm->in_sms = 0;
134    dm->modem  = amodem_create( base_port, modem_driver_unsol, dm );
135
136    qemu_chr_add_handlers( cs, modem_driver_can_read, modem_driver_read, NULL, dm );
137}
138
139
140void android_modem_init( int  base_port )
141{
142    static ModemDriver  modem_driver[1];
143
144    if (android_modem_cs != NULL) {
145        modem_driver_init( base_port, modem_driver, android_modem_cs );
146        android_modem = modem_driver->modem;
147    }
148}
149