External fds polling support
[libsamsung-ipc.git] / samsung-ipc / ipc.c
1 /*
2  * This file is part of libsamsung-ipc.
3  *
4  * Copyright (C) 2010-2011 Joerie de Gram <j.de.gram@gmail.com>
5  * Copyright (C) 2011 Simon Busch <morphis@gravedo.de>
6  * Copyright (C) 2013-2014 Paul Kocialkowsk <contact@paulk.fr>
7  *
8  * libsamsung-ipc is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * libsamsung-ipc is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with libsamsung-ipc.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdint.h>
26 #include <stdarg.h>
27 #include <unistd.h>
28 #include <stdbool.h>
29 #include <termios.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <sys/ioctl.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <asm/types.h>
37 #include <sys/utsname.h>
38
39 #include <samsung-ipc.h>
40
41 #include <ipc.h>
42 #include <ipc_devices.h>
43
44 int ipc_device_detect(void)
45 {
46     char buffer[4096] = { 0 };
47     struct utsname utsname;
48     char *name = NULL;
49     char *board_name = NULL;
50     char *kernel_version = NULL;
51     char *line, *p, *c;
52     int index = -1;
53     int fd = -1;
54     size_t length;
55     int rc;
56     int i;
57
58 #ifdef IPC_DEVICE_NAME
59     name = strdup(IPC_DEVICE_NAME);
60 #endif
61
62 #ifdef IPC_DEVICE_BOARD_NAME
63     board_name = strdup(IPC_DEVICE_BOARD_NAME);
64 #else
65     // Read board name from cpuinfo
66
67     fd = open("/proc/cpuinfo", O_RDONLY);
68     if (fd < 0)
69         goto error;
70
71     length = read(fd, &buffer, sizeof(buffer));
72
73     close(fd);
74     fd = -1;
75
76     line = strtok(buffer, "\n");
77     while (line != NULL) {
78         if (strncmp(line, "Hardware", 9) == 9) {
79             p = line + 11;
80             c = p;
81
82             while (*c != '\n' && *c != '\0') {
83                 *c = tolower(*c);
84                 c++;
85             }
86
87             *c = '\0';
88
89             board_name = strdup(p);
90             break;
91         }
92
93         line = strtok(NULL, "\n");
94     }
95 #endif
96
97 #ifdef IPC_DEVICE_KERNEL_VERSION
98     kernel_version = strdup(IPC_DEVICE_KERNEL_VERSION);
99 #else
100     memset(&utsname, 0, sizeof(utsname));
101
102     uname(&utsname);
103
104     kernel_version = strdup(utsname.release);
105 #endif
106
107     if (name == NULL && board_name == NULL)
108         goto error;
109
110     for (i = 0; i < (int) ipc_devices_count; i++) {
111         // Eliminate index if neither name nor board name can be checked
112         if (ipc_devices[i].name == NULL && ipc_devices[i].board_name == NULL)
113             continue;
114
115         // Eliminate index if the name doesn't match
116         if (name != NULL && ipc_devices[i].name != NULL && strcmp(name, ipc_devices[i].name) != 0)
117             continue;
118
119         // Eliminate index if the board name doesn't match
120         if (board_name != NULL && ipc_devices[i].board_name != NULL && strcmp(board_name, ipc_devices[i].board_name) != 0)
121             continue;
122
123         // Keep index but don't break yet since we may have a better match with kernel version
124         index = i;
125
126         if (kernel_version == NULL || ipc_devices[i].kernel_version == NULL)
127             continue;
128
129         if (kernel_version != NULL && ipc_devices[i].kernel_version != NULL && strcmp(kernel_version, ipc_devices[i].kernel_version) != 0)
130             continue;
131
132         // Everything matches this particular index
133         break;
134     }
135
136     goto complete;
137
138 error:
139     index = -1;
140
141 complete:
142     if (name != NULL)
143         free(name);
144
145     if (board_name != NULL)
146         free(board_name);
147
148     if (kernel_version != NULL)
149         free(kernel_version);
150
151     if (fd >= 0)
152         close(fd);
153
154     return index;
155 }
156
157 struct ipc_client *ipc_client_create(int type)
158 {
159     struct ipc_client *client = NULL;
160     unsigned int device_index;
161     int rc;
162
163     if (type < 0 || type > IPC_CLIENT_TYPE_RFS)
164         return NULL;
165
166     rc = ipc_device_detect();
167     if (rc < 0)
168         goto error;
169
170     device_index = (unsigned int) rc;
171     if (device_index > ipc_devices_count)
172         goto error;
173
174     client = (struct ipc_client *) calloc(1, sizeof(struct ipc_client));
175     client->type = type;
176
177     switch (type) {
178         case IPC_CLIENT_TYPE_RFS:
179             client->ops = ipc_devices[device_index].rfs_ops;
180             break;
181         case IPC_CLIENT_TYPE_FMT:
182             client->ops = ipc_devices[device_index].fmt_ops;
183             break;
184         default:
185             goto error;
186     }
187
188     client->gprs_specs = ipc_devices[device_index].gprs_specs;
189     client->nv_data_specs = ipc_devices[device_index].nv_data_specs;
190
191     // Handlers can be modified
192     client->handlers = (struct ipc_client_handlers *) calloc(1, sizeof(struct ipc_client_handlers));
193
194     if (ipc_devices[device_index].handlers != NULL)
195         memcpy(client->handlers, ipc_devices[device_index].handlers, sizeof(struct ipc_client_handlers));
196
197     goto complete;
198
199 error:
200     if (client != NULL) {
201         free(client);
202         client = NULL;
203     }
204
205 complete:
206     return client;
207 }
208
209 int ipc_client_destroy(struct ipc_client *client)
210 {
211     if (client == NULL)
212         return -1;
213
214     if (client->handlers != NULL)
215         free(client->handlers);
216
217     memset(client, 0, sizeof(struct ipc_client));
218     free(client);
219
220     return 0;
221 }
222
223 int ipc_client_transport_handlers_register(struct ipc_client *client,
224     int (*open)(void *transport_data, int type),
225     int (*close)(void *transport_data),
226     int (*read)(void *transport_data, void *data, size_t size),
227     int (*write)(void *transport_data, const void *data, size_t size),
228     int (*poll)(void *transport_data, struct ipc_poll_fds *fds, struct timeval *timeout),
229     void *transport_data)
230 {
231     if (client == NULL || client->handlers == NULL)
232         return -1;
233
234     if (read != NULL)
235         client->handlers->read = read;
236     if (write != NULL)
237         client->handlers->write = write;
238     if (poll != NULL)
239         client->handlers->poll = poll;
240     if (open != NULL)
241         client->handlers->open = open;
242     if (close != NULL)
243         client->handlers->close = close;
244     if (transport_data != NULL)
245         client->handlers->transport_data = transport_data;
246
247     return 0;
248 }
249
250 int ipc_client_power_handlers_register(struct ipc_client *client,
251     int (*power_on)(void *power_data), int (*power_off)(void *power_data),
252     void *power_data)
253 {
254     if (client == NULL || client->handlers == NULL)
255         return -1;
256
257     if (power_on != NULL)
258         client->handlers->power_on = power_on;
259     if (power_off != NULL)
260         client->handlers->power_off = power_off;
261     if (power_data != NULL)
262         client->handlers->power_data = power_data;
263
264     return 0;
265 }
266
267 int ipc_client_gprs_handlers_register(struct ipc_client *client,
268     int (*gprs_activate)(void *gprs_data, unsigned int cid),
269     int (*gprs_deactivate)(void *gprs_data, unsigned int cid), void *gprs_data)
270 {
271     if (client == NULL || client->handlers == NULL)
272         return -1;
273
274     if (gprs_activate != NULL)
275         client->handlers->gprs_activate = gprs_activate;
276     if (gprs_deactivate != NULL)
277         client->handlers->gprs_deactivate = gprs_deactivate;
278     if (gprs_data != NULL)
279         client->handlers->gprs_data = gprs_data;
280
281     return 0;
282 }
283
284 void ipc_client_log(struct ipc_client *client, const char *message, ...)
285 {
286     char buffer[4096];
287     va_list args;
288
289     if (client == NULL || client->log_callback == NULL || message == NULL)
290         return;
291
292     va_start(args, message);
293     vsnprintf((char *) &buffer, sizeof(buffer), message, args);
294     client->log_callback(client->log_data, buffer);
295     va_end(args);
296 }
297
298 int ipc_client_log_callback_register(struct ipc_client *client,
299     void (*log_callback)(void *log_data, const char *message), void *log_data)
300 {
301     if (client == NULL)
302         return -1;
303
304     client->log_callback = log_callback;
305     client->log_data = log_data;
306
307     return 0;
308 }
309
310 int ipc_client_boot(struct ipc_client *client)
311 {
312     if (client == NULL || client->ops == NULL || client->ops->boot == NULL)
313         return -1;
314
315     return client->ops->boot(client);
316 }
317
318 int ipc_client_send(struct ipc_client *client, unsigned char mseq,
319     unsigned short command, unsigned char type, const void *data, size_t size)
320 {
321     struct ipc_message message;
322
323     if (client == NULL || client->ops == NULL || client->ops->send == NULL)
324         return -1;
325
326     memset(&message, 0, sizeof(message));
327     message.mseq = mseq;
328     message.aseq = 0xff;
329     message.command = command;
330     message.type = type;
331     message.data = (void *) data;
332     message.size = size;
333
334     return client->ops->send(client, &message);
335 }
336
337 int ipc_client_recv(struct ipc_client *client, struct ipc_message *message)
338 {
339     if (client == NULL || client->ops == NULL || client->ops->recv == NULL || message == NULL)
340         return -1;
341
342     return client->ops->recv(client, message);
343 }
344
345 int ipc_client_open(struct ipc_client *client)
346 {
347     if (client == NULL || client->handlers == NULL || client->handlers->open == NULL)
348         return -1;
349
350     return client->handlers->open(client->handlers->transport_data, client->type);
351 }
352
353 int ipc_client_close(struct ipc_client *client)
354 {
355     if (client == NULL || client->handlers == NULL || client->handlers->close == NULL)
356         return -1;
357
358     return client->handlers->close(client->handlers->transport_data);
359 }
360
361 int ipc_client_poll(struct ipc_client *client, struct ipc_poll_fds *fds, struct timeval *timeout)
362 {
363     if (client == NULL || client->handlers == NULL || client->handlers->poll == NULL)
364         return -1;
365
366     return client->handlers->poll(client->handlers->transport_data, fds, timeout);
367 }
368
369 int ipc_client_power_on(struct ipc_client *client)
370 {
371     if (client == NULL || client->handlers == NULL || client->handlers->power_on == NULL)
372         return -1;
373
374     return client->handlers->power_on(client->handlers->power_data);
375 }
376
377 int ipc_client_power_off(struct ipc_client *client)
378 {
379     if (client == NULL || client->handlers == NULL || client->handlers->power_off == NULL)
380         return -1;
381
382     return client->handlers->power_off(client->handlers->power_data);
383 }
384
385 int ipc_client_gprs_activate(struct ipc_client *client, unsigned int cid)
386 {
387     if (client == NULL || client->handlers == NULL || client->handlers->gprs_activate == NULL)
388         return -1;
389
390     return client->handlers->gprs_activate(client->handlers->gprs_data, cid);
391 }
392
393 int ipc_client_gprs_deactivate(struct ipc_client *client, unsigned int cid)
394 {
395     if (client == NULL || client->handlers == NULL || client->handlers->gprs_deactivate == NULL)
396         return -1;
397
398     return client->handlers->gprs_deactivate(client->handlers->gprs_data, cid);
399 }
400
401 int ipc_client_data_create(struct ipc_client *client)
402 {
403     if (client == NULL || client->handlers == NULL || client->handlers->data_create == NULL)
404         return -1;
405
406     return client->handlers->data_create(&client->handlers->transport_data, &client->handlers->power_data, &client->handlers->power_data);
407 }
408
409 int ipc_client_data_destroy(struct ipc_client *client)
410 {
411     if (client == NULL || client->handlers == NULL || client->handlers->data_destroy == NULL)
412         return -1;
413
414     return client->handlers->data_destroy(client->handlers->transport_data, client->handlers->power_data, client->handlers->power_data);
415 }
416
417 char *ipc_client_gprs_get_iface(struct ipc_client *client, unsigned int cid)
418 {
419     if (client == NULL || client->gprs_specs == NULL || client->gprs_specs->gprs_get_iface == NULL)
420         return NULL;
421
422     return client->gprs_specs->gprs_get_iface(cid);
423 }
424
425 int ipc_client_gprs_get_capabilities(struct ipc_client *client,
426     struct ipc_client_gprs_capabilities *capabilities)
427 {
428     if (client == NULL || client->gprs_specs == NULL || client->gprs_specs->gprs_get_capabilities == NULL)
429         return -1;
430
431     return client->gprs_specs->gprs_get_capabilities(capabilities);
432 }
433
434 char *ipc_client_nv_data_path(struct ipc_client *client)
435 {
436     if (client == NULL || client->nv_data_specs == NULL || client->nv_data_specs->nv_data_path == NULL)
437         return NULL;
438
439     return client->nv_data_specs->nv_data_path;
440 }
441
442 char *ipc_client_nv_data_md5_path(struct ipc_client *client)
443 {
444     if (client == NULL || client->nv_data_specs == NULL || client->nv_data_specs->nv_data_md5_path == NULL)
445         return NULL;
446
447     return client->nv_data_specs->nv_data_md5_path;
448 }
449
450 char *ipc_client_nv_data_backup_path(struct ipc_client *client)
451 {
452     if (client == NULL || client->nv_data_specs == NULL || client->nv_data_specs->nv_data_backup_path == NULL)
453         return NULL;
454
455     return client->nv_data_specs->nv_data_backup_path;
456 }
457
458 char *ipc_client_nv_data_backup_md5_path(struct ipc_client *client)
459 {
460     if (client == NULL || client->nv_data_specs == NULL || client->nv_data_specs->nv_data_backup_md5_path == NULL)
461         return NULL;
462
463     return client->nv_data_specs->nv_data_backup_md5_path;
464 }
465
466 char *ipc_client_nv_data_secret(struct ipc_client *client)
467 {
468     if (client == NULL || client->nv_data_specs == NULL || client->nv_data_specs->nv_data_secret == NULL)
469         return NULL;
470
471     return client->nv_data_specs->nv_data_secret;
472 }
473
474 size_t ipc_client_nv_data_size(struct ipc_client *client)
475 {
476     if (client == NULL || client->nv_data_specs == NULL || client->nv_data_specs->nv_data_size == 0)
477         return 0;
478
479     return client->nv_data_specs->nv_data_size;
480 }
481
482 size_t ipc_client_nv_data_chunk_size(struct ipc_client *client)
483 {
484     if (client == NULL || client->nv_data_specs == NULL || client->nv_data_specs->nv_data_chunk_size == 0)
485         return 0;
486
487     return client->nv_data_specs->nv_data_chunk_size;
488 }
489
490 // vim:ts=4:sw=4:expandtab