Add support for sending flight mode indication
[connman] / plugins / modem.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <termios.h>
33
34 #include <glib.h>
35
36 #include <connman/log.h>
37
38 #include "modem.h"
39
40 struct modem_data {
41         char *device;
42         GIOChannel *channel;
43         guint watch;
44         GSList *callbacks;
45         GSList *commands;
46         char buf[1024];
47         int offset;
48 };
49
50 struct modem_callback {
51         char *command;
52         modem_cb_t function;
53         void *user_data;
54 };
55
56 struct modem_cmd {
57         char *cmd;
58         char *arg;
59         modem_cb_t callback;
60         void *user_data;
61 };
62
63 static int send_command(struct modem_data *modem, struct modem_cmd *cmd)
64 {
65         char *buf;
66         int fd, err;
67
68         if (cmd->arg == NULL) {
69                 DBG("AT%s", cmd->cmd);
70                 buf = g_strdup_printf("AT%s\r\n", cmd->cmd);
71         } else {
72                 DBG("AT%s=%s", cmd->cmd, cmd->arg);
73                 buf = g_strdup_printf("AT%s=%s\r\n", cmd->cmd, cmd->arg);
74         }
75
76         fd = g_io_channel_unix_get_fd(modem->channel);
77         err = write(fd, buf, strlen(buf));
78
79         fsync(fd);
80
81         g_free(buf);
82
83         return err;
84 }
85
86 static int queue_command(struct modem_data *modem, struct modem_cmd *cmd)
87 {
88         modem->commands = g_slist_append(modem->commands, cmd);
89
90         if (g_slist_length(modem->commands) > 1)
91                 return 0;
92
93         return send_command(modem, cmd);
94 }
95
96 struct modem_data *modem_create(const char *device)
97 {
98         struct modem_data *modem;
99
100         DBG("device %s", device);
101
102         modem = g_try_new0(struct modem_data, 1);
103         if (modem == NULL)
104                 return NULL;
105
106         modem->device = g_strdup(device);
107
108         DBG("modem %p", modem);
109
110         return modem;
111 }
112
113 void modem_destroy(struct modem_data *modem)
114 {
115         DBG("modem %p", modem);
116
117         if (modem == NULL)
118                 return;
119
120         g_free(modem->device);
121         g_free(modem);
122 }
123
124 static gboolean modem_event(GIOChannel *channel,
125                                 GIOCondition condition, gpointer user_data)
126 {
127         struct modem_data *modem = user_data;
128         struct modem_cmd *cmd;
129         GSList *list;
130         gsize len;
131         GIOError err;
132
133         if (condition & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
134                 return FALSE;
135
136         err = g_io_channel_read(channel, modem->buf + modem->offset,
137                                 sizeof(modem->buf) - modem->offset, &len);
138         if (err) {
139                 if (err == G_IO_ERROR_AGAIN)
140                         return TRUE;
141                 return FALSE;
142         }
143
144         DBG("Read %d bytes (offset %d)", len, modem->offset);
145
146         if (g_str_has_suffix(modem->buf, "\r\n") == TRUE) {
147                 for (list = modem->callbacks; list; list = list->next) {
148                         struct modem_callback *callback = list->data;
149
150                         if (callback->function == NULL)
151                                 continue;
152
153                         if (g_strrstr(modem->buf, callback->command) != NULL)
154                                 callback->function(modem->buf,
155                                                         callback->user_data);
156                 }
157         }
158
159         if (g_strrstr(modem->buf, "\r\nERROR\r\n") == NULL &&
160                                 g_strrstr(modem->buf, "\r\nOK\r\n") == NULL) {
161                 modem->offset += len;
162                 return TRUE;
163         }
164
165         memset(modem->buf, 0, sizeof(modem->buf));
166         modem->offset = 0;
167
168         cmd = g_slist_nth_data(modem->commands, 0);
169         if (cmd == NULL)
170                 return TRUE;
171
172         modem->commands = g_slist_remove(modem->commands, cmd);
173
174         DBG("AT%s", cmd->cmd);
175
176         if (cmd->callback)
177                 cmd->callback(modem->buf, cmd->user_data);
178
179         g_free(cmd->arg);
180         g_free(cmd->cmd);
181         g_free(cmd);
182
183         cmd = g_slist_nth_data(modem->commands, 0);
184         if (cmd == NULL)
185                 return TRUE;
186
187         send_command(modem, cmd);
188
189         return TRUE;
190 }
191
192 static int open_device(const char *device)
193 {
194         struct termios ti;
195         int fd;
196
197         fd = open(device, O_RDWR | O_NOCTTY);
198         if (fd < 0)
199                 return -1;
200
201         tcflush(fd, TCIOFLUSH);
202
203         /* Switch TTY to raw mode */
204         memset(&ti, 0, sizeof(ti));
205         cfmakeraw(&ti);
206
207         tcsetattr(fd, TCSANOW, &ti);
208
209         return fd;
210 }
211
212 int modem_open(struct modem_data *modem)
213 {
214         int fd, try = 5;
215
216         DBG("modem %p", modem);
217
218         if (modem == NULL)
219                 return -ENOENT;
220
221         while (try-- > 0) {
222                 fd = open_device(modem->device);
223                 if (fd < 0) {
224                         sleep(1);
225                         continue;
226                 }
227                 try = 0;
228         }
229
230         if (fd < 0) {
231                 connman_error("Can't open %s device", modem->device);
232                 return -EIO;
233         }
234
235         modem->channel = g_io_channel_unix_new(fd);
236         g_io_channel_set_close_on_unref(modem->channel, TRUE);
237
238         modem->watch = g_io_add_watch(modem->channel,
239                                 G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
240                                                         modem_event, modem);
241
242         return 0;
243 }
244
245 int modem_close(struct modem_data *modem)
246 {
247         DBG("modem %p", modem);
248
249         if (modem == NULL)
250                 return -ENOENT;
251
252         g_source_remove(modem->watch);
253         modem->watch = 0;
254
255         g_io_channel_unref(modem->channel);
256         modem->channel = NULL;
257
258         return 0;
259 }
260
261 int modem_add_callback(struct modem_data *modem, const char *command,
262                                         modem_cb_t function, void *user_data)
263 {
264         struct modem_callback *callback;
265
266         callback = g_try_new0(struct modem_callback, 1);
267         if (callback == NULL)
268                 return -ENOMEM;
269
270         callback->command   = g_strdup(command);
271         callback->function  = function;
272         callback->user_data = user_data;
273
274         modem->callbacks = g_slist_append(modem->callbacks, callback);
275
276         return 0;
277 }
278
279 static int modem_command_valist(struct modem_data *modem, modem_cb_t callback,
280                                         void *user_data, const char *command,
281                                         const char *format, va_list var_args)
282 {
283         struct modem_cmd *cmd;
284
285         cmd = g_try_new0(struct modem_cmd, 1);
286         if (cmd == NULL)
287                 return -ENOMEM;
288
289         cmd->cmd = g_strdup(command);
290         if (format != NULL)
291                 cmd->arg = g_strdup_vprintf(format, var_args);
292
293         cmd->callback  = callback;
294         cmd->user_data = user_data;
295
296         return queue_command(modem, cmd);
297 }
298
299 int modem_command(struct modem_data *modem,
300                                 modem_cb_t callback, void *user_data,
301                                 const char *command, const char *format, ...)
302 {
303         va_list args;
304         int err;
305
306         DBG("modem %p", modem);
307
308         if (modem == NULL)
309                 return -ENOENT;
310
311         va_start(args, format);
312         err = modem_command_valist(modem, callback, user_data,
313                                                 command, format, args);
314         va_end(args);
315
316         return err;
317 }