~awe/phablet-extras/ofono-lp1204683

« back to all changes in this revision

Viewing changes to drivers/atmodem/session.c

  • Committer: Bazaar Package Importer
  • Author(s): Andres Salomon
  • Date: 2009-08-15 15:55:11 UTC
  • Revision ID: james.westby@ubuntu.com-20090815155511-frst06dijguhyfi4
Tags: upstream-0.3
ImportĀ upstreamĀ versionĀ 0.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *
 
3
 *  oFono - Open Source Telephony
 
4
 *
 
5
 *  Copyright (C) 2008-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 <fcntl.h>
 
28
#include <errno.h>
 
29
#include <unistd.h>
 
30
#include <termios.h>
 
31
#include <sys/socket.h>
 
32
#include <sys/un.h>
 
33
#include <netinet/in.h>
 
34
#include <netinet/tcp.h>
 
35
#include <arpa/inet.h>
 
36
#include <errno.h>
 
37
#include <stdlib.h>
 
38
 
 
39
#include <glib.h>
 
40
 
 
41
#include <ofono/log.h>
 
42
 
 
43
#include "session.h"
 
44
 
 
45
struct modem_session_callback {
 
46
        modem_session_callback_t callback;
 
47
        gpointer user_data;
 
48
        GDestroyNotify notify;
 
49
        guint timeout_watcher;
 
50
        GIOChannel *io;
 
51
};
 
52
 
 
53
static void connect_destroy(gpointer user)
 
54
{
 
55
        struct modem_session_callback *callback = user;
 
56
 
 
57
        if (callback->notify)
 
58
                callback->notify(callback->user_data);
 
59
 
 
60
        if (callback->timeout_watcher != 0)
 
61
                g_source_remove(callback->timeout_watcher);
 
62
 
 
63
        g_free(callback);
 
64
}
 
65
 
 
66
static gboolean connect_cb(GIOChannel *io, GIOCondition cond, gpointer user)
 
67
{
 
68
        struct modem_session_callback *callback = user;
 
69
        int err = 0;
 
70
        gboolean success;
 
71
 
 
72
        if (cond & G_IO_NVAL)
 
73
                return FALSE;
 
74
 
 
75
        if (cond & G_IO_OUT) {
 
76
                int sock = g_io_channel_unix_get_fd(io);
 
77
                socklen_t len = sizeof(err);
 
78
 
 
79
                if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
 
80
                        err = errno == ENOTSOCK ? 0 : errno;
 
81
        } else if (cond & (G_IO_HUP | G_IO_ERR))
 
82
                err = ECONNRESET;
 
83
 
 
84
        success = !err;
 
85
 
 
86
        callback->callback(io, success, callback->user_data);
 
87
 
 
88
        return FALSE;
 
89
}
 
90
 
 
91
static gboolean connect_timeout(gpointer user)
 
92
{
 
93
        struct modem_session_callback *callback = user;
 
94
 
 
95
        callback->callback(callback->io, FALSE, callback->user_data);
 
96
 
 
97
        callback->timeout_watcher = 0;
 
98
 
 
99
        g_io_channel_unref(callback->io);
 
100
 
 
101
        return FALSE;
 
102
}
 
103
 
 
104
static GIOChannel *tty_connect(const char *tty)
 
105
{
 
106
        GIOChannel *io;
 
107
        int sk;
 
108
        struct termios newtio;
 
109
 
 
110
        sk = open(tty, O_RDWR | O_NOCTTY);
 
111
 
 
112
        if (sk < 0) {
 
113
                ofono_error("Can't open TTY %s: %s(%d)",
 
114
                                tty, strerror(errno), errno);
 
115
                return NULL;
 
116
        }
 
117
 
 
118
        newtio.c_cflag = B115200 | CRTSCTS | CLOCAL | CREAD;
 
119
        newtio.c_iflag = IGNPAR;
 
120
        newtio.c_oflag = 0;
 
121
        newtio.c_lflag = 0;
 
122
 
 
123
        newtio.c_cc[VTIME] = 1;
 
124
        newtio.c_cc[VMIN] = 5;
 
125
 
 
126
        tcflush(sk, TCIFLUSH);
 
127
        if (tcsetattr(sk, TCSANOW, &newtio) < 0) {
 
128
                ofono_error("Can't change serial settings: %s(%d)",
 
129
                                strerror(errno), errno);
 
130
                close(sk);
 
131
                return NULL;
 
132
        }
 
133
 
 
134
        io = g_io_channel_unix_new(sk);
 
135
        g_io_channel_set_close_on_unref(io, TRUE);
 
136
 
 
137
        if (g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK,
 
138
                                        NULL) != G_IO_STATUS_NORMAL) {
 
139
                g_io_channel_unref(io);
 
140
                return NULL;
 
141
        }
 
142
 
 
143
        return io;
 
144
}
 
145
 
 
146
static GIOChannel *socket_common(int sk, struct sockaddr *addr,
 
147
                                        socklen_t addrlen)
 
148
{
 
149
        GIOChannel *io = g_io_channel_unix_new(sk);
 
150
 
 
151
        if (io == NULL) {
 
152
                close(sk);
 
153
                return NULL;
 
154
        }
 
155
 
 
156
        g_io_channel_set_close_on_unref(io, TRUE);
 
157
 
 
158
        if (g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK,
 
159
                                        NULL) != G_IO_STATUS_NORMAL) {
 
160
                g_io_channel_unref(io);
 
161
                return NULL;
 
162
        }
 
163
 
 
164
        if (connect(sk, addr, addrlen) < 0) {
 
165
                if (errno != EAGAIN && errno != EINPROGRESS) {
 
166
                        g_io_channel_unref(io);
 
167
                        return NULL;
 
168
                }
 
169
        }
 
170
 
 
171
        return io;
 
172
}
 
173
 
 
174
static GIOChannel *unix_connect(const char *address)
 
175
{
 
176
        struct sockaddr_un addr;
 
177
        int sk;
 
178
 
 
179
        memset(&addr, 0, sizeof(addr));
 
180
        addr.sun_family = PF_UNIX;
 
181
 
 
182
        if (strncmp("x00", address, 3) == 0)
 
183
                strcpy(addr.sun_path + 1, address + 3);
 
184
        else
 
185
                strcpy(addr.sun_path, address);
 
186
 
 
187
        sk = socket(AF_UNIX, SOCK_STREAM, 0);
 
188
 
 
189
        if (sk < 0)
 
190
                return NULL;
 
191
 
 
192
        return socket_common(sk, (struct sockaddr *)&addr, sizeof(addr));
 
193
}
 
194
 
 
195
static GIOChannel *tcp_connect(const char *address)
 
196
{
 
197
        struct sockaddr_in addr;
 
198
        int sk;
 
199
        unsigned short port;
 
200
        in_addr_t inetaddr;
 
201
        char *portstr;
 
202
        char addrstr[16];
 
203
 
 
204
        memset(&addr, 0, sizeof(addr));
 
205
 
 
206
        portstr = strchr(address, ':');
 
207
 
 
208
        if (!portstr || (unsigned int)(portstr-address) > (sizeof(addrstr) - 1))
 
209
                return NULL;
 
210
 
 
211
        strncpy(addrstr, address, portstr-address);
 
212
        addrstr[portstr-address] = '\0';
 
213
 
 
214
        portstr += 1;
 
215
 
 
216
        port = atoi(portstr);
 
217
 
 
218
        if (port == 0)
 
219
                return NULL;
 
220
 
 
221
        inetaddr = inet_addr(addrstr);
 
222
 
 
223
        if (inetaddr == INADDR_NONE)
 
224
                return NULL;
 
225
 
 
226
        sk = socket(PF_INET, SOCK_STREAM, 0);
 
227
 
 
228
        if (sk < 0)
 
229
                return NULL;
 
230
 
 
231
        addr.sin_family = AF_INET;
 
232
        addr.sin_addr.s_addr = inetaddr;
 
233
        addr.sin_port = htons(port);
 
234
 
 
235
        return socket_common(sk, (struct sockaddr *) &addr, sizeof(addr));
 
236
}
 
237
 
 
238
GIOChannel *modem_session_create(const char *target,
 
239
                                        modem_session_callback_t func,
 
240
                                        gpointer user_data,
 
241
                                        GDestroyNotify notify)
 
242
{
 
243
        struct modem_session_callback *callback;
 
244
        GIOChannel *io = NULL;
 
245
        GIOCondition cond;
 
246
 
 
247
        if (target == NULL || func == NULL)
 
248
                return NULL;
 
249
 
 
250
        if (!strncasecmp(target, "tcp:", 4))
 
251
                io = tcp_connect(target+4);
 
252
        else if (!strncasecmp(target, "unix:", 5))
 
253
                io = unix_connect(target+5);
 
254
        else if (!strncasecmp(target, "dev:", 4))
 
255
                io = tty_connect(target+4);
 
256
 
 
257
        if (io == NULL)
 
258
                return NULL;
 
259
 
 
260
        callback = g_new0(struct modem_session_callback, 1);
 
261
 
 
262
        callback->callback = func;
 
263
        callback->user_data = user_data;
 
264
        callback->notify = notify;
 
265
        callback->io = io;
 
266
        callback->timeout_watcher = g_timeout_add_seconds(20, connect_timeout,
 
267
                                                                callback);
 
268
 
 
269
        cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
 
270
        g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb,
 
271
                                callback, connect_destroy);
 
272
 
 
273
        g_io_channel_unref(io);
 
274
 
 
275
        return io;
 
276
}