GTA04: Retry multiple times to find and open modem nodes, with delay
[hayes-ril.git] / device / gta04 / gta04.c
1 /*
2  * This file is part of hayes-ril.
3  *
4  * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdlib.h>
20 #include <termios.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <sys/stat.h>
24
25 #define LOG_TAG "RIL-DEV"
26 #include <utils/Log.h>
27
28 #include "gta04.h"
29 #include <hayes-ril.h>
30
31 int gta04_power_count_nodes(void)
32 {
33         struct stat tty_node_stat;
34         char *tty_node = NULL;
35         int tty_nodes_count = 0;
36         int rc;
37         int i;
38
39         // Count how many nodes are available
40         for(i=0 ; i < TTY_NODE_MAX ; i++) {
41                 asprintf(&tty_node, "%s/%s%d", TTY_DEV_BASE, TTY_NODE_BASE, i);
42
43                 rc = stat(tty_node, &tty_node_stat);
44                 if(rc == 0)
45                         tty_nodes_count++;
46
47                 free(tty_node);
48         }
49
50         return tty_nodes_count;
51 }
52
53 int gta04_power_on(void *sdata)
54 {
55         char gpio_sysfs_value[] = "1\n";
56         int tty_nodes_count;
57         int fd;
58
59         tty_nodes_count = gta04_power_count_nodes();
60         if(tty_nodes_count < 2) {
61                 LOGD("Powering modem on");
62
63                 fd = open(GPIO_SYSFS, O_RDWR);
64                 if(fd < 0) {
65                         LOGE("Unable to open GPIO SYSFS node, modem will stay off");
66                         return -1;
67                 }
68
69                 write(fd, gpio_sysfs_value, strlen(gpio_sysfs_value));
70                 sleep(1);
71
72                 return 0;
73         }
74
75         LOGD("Modem is already on");
76         return 0;
77 }
78
79 int gta04_power_off(void *sdata)
80 {
81         char gpio_sysfs_value_0[] = "0\n";
82         char gpio_sysfs_value_1[] = "1\n";
83         int tty_nodes_count;
84         int fd;
85
86         tty_nodes_count = gta04_power_count_nodes();
87         if(tty_nodes_count > 0) {
88                 LOGD("Powering modem off");
89
90                 fd = open(GPIO_SYSFS, O_RDWR);
91                 if(fd < 0) {
92                         LOGE("Unable to open GPIO SYSFS node, modem will stay on");
93                         return -1;
94                 }
95
96                 write(fd, gpio_sysfs_value_0, strlen(gpio_sysfs_value_0));
97                 usleep(500);
98                 write(fd, gpio_sysfs_value_1, strlen(gpio_sysfs_value_1));
99                 usleep(500);
100                 write(fd, gpio_sysfs_value_0, strlen(gpio_sysfs_value_0));
101                 sleep(1);
102
103                 return 0;
104         }
105
106         LOGD("Modem is already off");
107         return 0;
108 }
109
110 int gta04_power_boot(void *sdata)
111 {
112         int tty_nodes_count;
113
114         tty_nodes_count = gta04_power_count_nodes();
115         if(tty_nodes_count < 2) {
116                 // We need at least Modem and Application
117                 LOGE("Not enough modem nodes available!");
118                 return -1;
119         }
120
121         return 0;
122 }
123
124 int gta04_transport_sdata_create(void **sdata)
125 {
126         struct gta04_transport_data *transport_data = NULL;
127
128         transport_data = malloc(sizeof(struct gta04_transport_data));
129         memset(transport_data, 0, sizeof(struct gta04_transport_data));
130
131         *sdata = (void *) transport_data;
132
133         return 0;
134 }
135
136 int gta04_transport_sdata_destroy(void *sdata)
137 {
138         if(sdata != NULL)
139                 free(sdata);
140
141         return 0;
142 }
143
144 int gta04_transport_find_node(char **tty_node, char *name)
145 {
146         char *tty_sysfs_node = NULL;
147         char *buf = NULL;
148         int name_length;
149         int retries = 10;
150         int fd = -1;
151         int i;
152
153         if(tty_node == NULL || name == NULL)
154                 return -1;
155
156         name_length = strlen(name);
157         buf = calloc(1, name_length);
158
159         while(retries) {
160                 for(i=0 ; i < TTY_NODE_MAX ; i++) {
161                         asprintf(&tty_sysfs_node, "%s/%s%d/%s",
162                                 TTY_SYSFS_BASE, TTY_NODE_BASE, i, TTY_HSOTYPE);
163
164                         fd = open(tty_sysfs_node, O_RDONLY);
165                         if(fd < 0) {
166                                 free(tty_sysfs_node);
167                                 continue;
168                         }
169
170                         read(fd, buf, name_length);
171                         if(strncmp(name, buf, name_length) == 0) {
172                                 asprintf(tty_node, "%s/%s%d", TTY_DEV_BASE, TTY_NODE_BASE, i);
173
174                                 free(tty_sysfs_node);
175                                 return 0;
176                         } else {
177                                 free(tty_sysfs_node);
178                         }
179                 }
180
181                 retries--;
182                 usleep(750000);
183         }
184
185         *tty_node = NULL;
186
187         return -1;
188 }
189
190 int gta04_transport_open_node(char *dev_node)
191 {
192         struct termios term;
193         int retries = 10;
194         int fd = -1;
195         int rc = -1;
196
197         while(retries) {
198                 fd = open(dev_node, O_RDWR | O_NOCTTY | O_NDELAY);
199                 if(fd < 0)
200                         goto failure;
201
202                 rc = tcgetattr(fd, &term);
203                 if(rc < 0)
204                         goto failure;
205
206                 term.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
207                 cfsetispeed(&term, B115200);
208                 cfsetospeed(&term, B115200);
209
210                 rc = tcsetattr(fd, TCSANOW, &term);
211                 if(rc < 0)
212                         goto failure;
213
214                 return fd;
215
216 failure:
217                 retries--;
218                 usleep(750000);
219         }
220
221         return -1;
222 }
223
224 int gta04_transport_open(void *sdata)
225 {
226         struct gta04_transport_data *transport_data = NULL;
227         char *dev_node = NULL;
228         int fd = -1;
229         int rc = -1;
230
231         if(sdata == NULL)
232                 return -1;
233
234         transport_data = (struct gta04_transport_data *) sdata;
235
236         // Open Modem node
237         if(transport_data->modem_fd <= 0) {
238                 rc = gta04_transport_find_node(&dev_node, "Modem");
239                 if(rc < 0 || dev_node == NULL) {
240                         LOGE("Unable to find Modem node, aborting!");
241                         goto failure;
242                 }
243
244                 fd = gta04_transport_open_node(dev_node);
245                 if(fd < 0) {
246                         LOGE("Unable to open Modem node, aborting!");
247                         goto failure;
248                 }
249
250                 LOGD("Opened Modem node");
251                 transport_data->modem_fd = fd;
252         }
253
254         // Open Application node
255         if(transport_data->application_fd <= 0) {
256                 rc = gta04_transport_find_node(&dev_node, "Application");
257                 if(rc < 0) {
258                         LOGE("Unable to find Application node, aborting!");
259                         goto failure;
260                 }
261
262                 fd = gta04_transport_open_node(dev_node);
263                 if(fd < 0) {
264                         LOGE("Unable to open Application node, aborting!");
265                         goto failure;
266                 }
267
268                 LOGD("Opened Application node");
269                 transport_data->application_fd = fd;
270         }
271
272         return 0;
273
274 failure:
275         if(dev_node != NULL)
276                 free(dev_node);
277
278         return -1;
279 }
280
281 int gta04_transport_close(void *sdata)
282 {
283         struct gta04_transport_data *transport_data = NULL;
284
285         if(sdata == NULL)
286                 return -1;
287
288         transport_data = (struct gta04_transport_data *) sdata;
289
290         if(transport_data->modem_fd > 0)
291                 close(transport_data->modem_fd);
292         transport_data->modem_fd = -1;
293
294         if(transport_data->application_fd > 0)
295                 close(transport_data->application_fd);
296         transport_data->application_fd = -1;
297
298         return 0;
299 }
300
301 int gta04_transport_send(void *sdata, void *data, int length)
302 {
303         struct gta04_transport_data *transport_data = NULL;
304
305         // Written data count
306         int wc;
307         // Min count
308         int mc;
309         // Total written data count
310         int tc = 0;
311
312         if(sdata == NULL || data == NULL || length <= 0)
313                 return -1;
314
315         transport_data = (struct gta04_transport_data *) sdata;
316
317         if(transport_data->modem_fd < 0)
318                 return -1;
319
320         mc = length;
321         while(mc > 0) {
322                 // Outgoing AT data must be written to the Modem node apparently
323                 wc = write(transport_data->modem_fd, data + (length - mc), mc);
324                 if(wc < 0)
325                         return -1;
326
327                 tc += wc;
328                 mc -= wc;
329         }
330
331         return tc;
332 }
333
334 int gta04_transport_recv(void *sdata, void **data, int length)
335 {
336         struct gta04_transport_data *transport_data = NULL;
337         char *buffer;
338         int fd;
339
340         // Read data count
341         int rc;
342         // Min count
343         int mc;
344         // Total read data count
345         int tc = 0;
346
347         if(sdata == NULL)
348                 return -1;
349
350         transport_data = (struct gta04_transport_data *) sdata;
351
352         if(transport_data->modem_fd < 0)
353                 return -1;
354         if(transport_data->application_fd < 0)
355                 return -1;
356
357         if(transport_data->modem_ac > 0) {
358                 fd = transport_data->modem_fd;
359                 mc = transport_data->modem_ac;
360         } else if(transport_data->application_ac > 0) {
361                 fd = transport_data->application_fd;
362                 mc = transport_data->application_ac;
363         }
364
365         if(fd < 0)
366                 return -1;
367
368         if(length == 1 && mc == 1) {
369                 // Read an unknown number of bytes
370
371                 buffer = (char *) calloc(1, RECV_BYTES_MAX);
372
373                 rc = read(fd, (void *) buffer, RECV_BYTES_MAX);
374                 if(rc < 0) {
375                         free(buffer);
376                         return -1;
377                 }
378
379                 tc = rc;
380         } else {
381                 // Read the exact number of bytes
382
383                 buffer = (char *) calloc(1, mc);
384
385                 while(mc > 0) {
386                         rc = read(fd, (void *) (buffer + tc), mc - tc);
387                         if(rc < 0) {
388                                 free(buffer);
389                                 return -1;
390                         }
391
392                         tc += rc;
393                         mc -= rc;
394                 }
395
396                 if(tc > length)
397                         tc = length;
398         }
399
400         *data = (void *) buffer;
401
402         if(transport_data->modem_ac > 0)
403                 transport_data->modem_ac = 0;
404         else if(transport_data->application_ac > 0)
405                 transport_data->application_ac = 0;
406
407         return tc;
408 }
409
410 int gta04_transport_recv_poll(void *sdata)
411 {
412         struct gta04_transport_data *transport_data = NULL;
413         fd_set fds;
414
415         if(sdata == NULL)
416                 return -1;
417
418         transport_data = (struct gta04_transport_data *) sdata;
419
420         if(transport_data->modem_fd < 0)
421                 return -1;
422         if(transport_data->application_fd < 0)
423                 return -1;
424
425         FD_ZERO(&fds);
426         FD_SET(transport_data->modem_fd, &fds);
427         FD_SET(transport_data->application_fd, &fds);
428
429         select(FD_SETSIZE, &fds, NULL, NULL, NULL);
430
431         // Process one at a time
432         if(FD_ISSET(transport_data->modem_fd, &fds)) {
433                 transport_data->modem_ac = 1;
434         } else if(FD_ISSET(transport_data->application_fd, &fds)) {
435                 transport_data->application_ac = 1;
436         }
437
438         return 1;
439 }
440
441 int gta04_dummy(void *sdata)
442 {
443         return 0;
444 }
445
446 struct ril_device_power_handlers gta04_power_handlers = {
447         .sdata = NULL,
448         .sdata_create = gta04_dummy,
449         .sdata_destroy = gta04_dummy,
450         .power_on = gta04_power_on,
451         .power_off = gta04_power_off,
452         .suspend = gta04_dummy,
453         .resume = gta04_dummy,
454         .boot = gta04_power_boot,
455 };
456
457 struct ril_device_transport_handlers gta04_transport_handlers = {
458         .sdata = NULL,
459         .sdata_create = gta04_transport_sdata_create,
460         .sdata_destroy = gta04_transport_sdata_destroy,
461         .open = gta04_transport_open,
462         .close = gta04_transport_close,
463         .send = gta04_transport_send,
464         .recv = gta04_transport_recv,
465         .recv_poll = gta04_transport_recv_poll,
466 };
467
468 struct ril_device_handlers gta04_handlers = {
469         .power = &gta04_power_handlers,
470         .transport = &gta04_transport_handlers,
471 };
472
473 struct ril_device gta04_device = {
474         .name = "Goldelico GTA04",
475         .tag = "GTA04",
476         .type = DEV_GSM,
477         .sdata = NULL,
478         .handlers = &gta04_handlers,
479 };
480
481 void ril_device_register(struct ril_device **ril_device_p)
482 {
483         *ril_device_p = &gta04_device;
484 }