Add support for async events callbacks
[connman] / plugins / modem.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2008  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         GSList *callbacks;
44         GSList *commands;
45         char buf[1024];
46         int offset;
47 };
48
49 struct modem_callback {
50         char *command;
51         modem_cb_t function;
52         void *user_data;
53 };
54
55 struct modem_cmd {
56         char *cmd;
57         char *arg;
58         modem_cb_t callback;
59         void *user_data;
60 };
61
62 static int send_command(struct modem_data *modem, struct modem_cmd *cmd)
63 {
64         char *buf;
65         int fd, err;
66
67         if (cmd->arg == NULL) {
68                 DBG("AT%s", cmd->cmd);
69                 buf = g_strdup_printf("AT%s\r\n", cmd->cmd);
70         } else {
71                 DBG("AT%s=%s", cmd->cmd, cmd->arg);
72                 buf = g_strdup_printf("AT%s=%s\r\n", cmd->cmd, cmd->arg);
73         }
74
75         fd = g_io_channel_unix_get_fd(modem->channel);
76         err = write(fd, buf, strlen(buf));
77
78         fsync(fd);
79
80         g_free(buf);
81
82         return err;
83 }
84
85 static int queue_command(struct modem_data *modem, struct modem_cmd *cmd)
86 {
87         modem->commands = g_slist_append(modem->commands, cmd);
88
89         if (g_slist_length(modem->commands) > 1)
90                 return 0;
91
92         return send_command(modem, cmd);
93 }
94
95 struct modem_data *modem_create(const char *device)
96 {
97         struct modem_data *modem;
98
99         DBG("device %s", device);
100
101         modem = g_try_new0(struct modem_data, 1);
102         if (modem == NULL)
103                 return NULL;
104
105         modem->device = g_strdup(device);
106
107         DBG("modem %p", modem);
108
109         return modem;
110 }
111
112 void modem_destroy(struct modem_data *modem)
113 {
114         DBG("modem %p", modem);
115
116         if (modem == NULL)
117                 return;
118
119         g_free(modem->device);
120         g_free(modem);
121 }
122
123 static gboolean modem_event(GIOChannel *channel,
124                                 GIOCondition condition, gpointer user_data)
125 {
126         struct modem_data *modem = user_data;
127         struct modem_cmd *cmd;
128         GSList *list;
129         gsize len;
130         GIOError err;
131
132         if (condition & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
133                 return FALSE;
134
135         err = g_io_channel_read(channel, modem->buf + modem->offset,
136                                 sizeof(modem->buf) - modem->offset, &len);
137         if (err) {
138                 if (err == G_IO_ERROR_AGAIN)
139                         return TRUE;
140                 return FALSE;
141         }
142
143         DBG("Read %d bytes (offset %d)", len, modem->offset);
144
145         if (g_str_has_suffix(modem->buf, "\r\n") == TRUE) {
146                 for (list = modem->callbacks; list; list = list->next) {
147                         struct modem_callback *callback = list->data;
148
149                         if (callback->function == NULL)
150                                 continue;
151
152                         if (g_strrstr(modem->buf, callback->command) != NULL)
153                                 callback->function(modem->buf,
154                                                         callback->user_data);
155                 }
156         }
157
158         if (g_strrstr(modem->buf, "\r\nERROR\r\n") == NULL &&
159                                 g_strrstr(modem->buf, "\r\nOK\r\n") == NULL) {
160                 modem->offset += len;
161                 return TRUE;
162         }
163
164         memset(modem->buf, 0, sizeof(modem->buf));
165         modem->offset = 0;
166
167         cmd = g_slist_nth_data(modem->commands, 0);
168         if (cmd == NULL)
169                 return TRUE;
170
171         modem->commands = g_slist_remove(modem->commands, cmd);
172
173         DBG("AT%s", cmd->cmd);
174
175         if (cmd->callback)
176                 cmd->callback(modem->buf, cmd->user_data);
177
178         g_free(cmd->arg);
179         g_free(cmd->cmd);
180         g_free(cmd);
181
182         cmd = g_slist_nth_data(modem->commands, 0);
183         if (cmd == NULL)
184                 return TRUE;
185
186         send_command(modem, cmd);
187
188         return TRUE;
189 }
190
191 static int open_device(const char *device)
192 {
193         struct termios ti;
194         int fd;
195
196         fd = open(device, O_RDWR | O_NOCTTY);
197         if (fd < 0)
198                 return -1;
199
200         tcflush(fd, TCIOFLUSH);
201
202         /* Switch TTY to raw mode */
203         cfmakeraw(&ti);
204         tcsetattr(fd, TCSANOW, &ti);
205
206         return fd;
207 }
208
209 int modem_open(struct modem_data *modem)
210 {
211         int fd, try = 5;
212
213         DBG("modem %p", modem);
214
215         if (modem == NULL)
216                 return -ENOENT;
217
218         while (try-- > 0) {
219                 fd = open_device(modem->device);
220                 if (fd < 0) {
221                         sleep(1);
222                         continue;
223                 }
224                 try = 0;
225         }
226
227         if (fd < 0) {
228                 connman_error("Can't open %s device", modem->device);
229                 return -EIO;
230         }
231
232         modem->channel = g_io_channel_unix_new(fd);
233         g_io_channel_set_close_on_unref(modem->channel, TRUE);
234
235         g_io_add_watch(modem->channel,
236                                 G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
237                                                         modem_event, modem);
238
239         return 0;
240 }
241
242 int modem_close(struct modem_data *modem)
243 {
244         DBG("modem %p", modem);
245
246         if (modem == NULL)
247                 return -ENOENT;
248
249         g_io_channel_shutdown(modem->channel, TRUE, NULL);
250         g_io_channel_unref(modem->channel);
251
252         modem->channel = NULL;
253
254         return 0;
255 }
256
257 int modem_add_callback(struct modem_data *modem, const char *command,
258                                         modem_cb_t function, void *user_data)
259 {
260         struct modem_callback *callback;
261
262         callback = g_try_new0(struct modem_callback, 1);
263         if (callback == NULL)
264                 return -ENOMEM;
265
266         callback->command   = g_strdup(command);
267         callback->function  = function;
268         callback->user_data = user_data;
269
270         modem->callbacks = g_slist_append(modem->callbacks, callback);
271
272         return 0;
273 }
274
275 static int modem_command_valist(struct modem_data *modem, modem_cb_t callback,
276                                         void *user_data, const char *command,
277                                         const char *format, va_list var_args)
278 {
279         struct modem_cmd *cmd;
280
281         cmd = g_try_new0(struct modem_cmd, 1);
282         if (cmd == NULL)
283                 return -ENOMEM;
284
285         cmd->cmd = g_strdup(command);
286         if (format != NULL)
287                 cmd->arg = g_strdup_vprintf(format, var_args);
288
289         cmd->callback  = callback;
290         cmd->user_data = user_data;
291
292         return queue_command(modem, cmd);
293 }
294
295 int modem_command(struct modem_data *modem,
296                                 modem_cb_t callback, void *user_data,
297                                 const char *command, const char *format, ...)
298 {
299         va_list args;
300         int err;
301
302         DBG("modem %p", modem);
303
304         if (modem == NULL)
305                 return -ENOENT;
306
307         va_start(args, format);
308         err = modem_command_valist(modem, callback, user_data,
309                                                 command, format, args);
310         va_end(args);
311
312         return err;
313 }