Add packaging directory
[platform/upstream/neard.git] / plugins / llcp-validation.c
1 /*
2  *
3  *  neard - Near Field Communication manager
4  *
5  *  Copyright (C) 2013  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 <stdint.h>
27 #include <errno.h>
28 #include <stdbool.h>
29 #include <string.h>
30 #include <sys/socket.h>
31
32 #include <near/nfc_copy.h>
33
34 #include <near/plugin.h>
35 #include <near/log.h>
36 #include <near/types.h>
37 #include <near/adapter.h>
38 #include <near/device.h>
39 #include <near/ndef.h>
40 #include <near/tlv.h>
41
42 #include "p2p.h"
43
44 #define ECHO_DELAY              2000    /* 2 seconds */
45
46 struct co_cl_client_data {
47         int fd;
48         uint8_t buf_count;
49         GList *sdu_list;
50
51         int miu_len;
52         uint8_t *miu_buffer;
53
54         int sock_type;
55         struct sockaddr_nfc_llcp cl_addr;
56 };
57
58 struct sdu {
59         int len;
60         uint8_t *data;
61 };
62
63 typedef bool (*near_incoming_cb) (struct co_cl_client_data *co_client);
64
65 static GHashTable *llcp_client_hash = NULL;
66
67 /* free one SDU */
68 static void free_one_sdu(gpointer data)
69 {
70         struct sdu *i_sdu = data;
71
72         if (!i_sdu)
73                 return;
74
75         g_free(i_sdu->data);
76         g_free(i_sdu);
77 }
78
79 /* Callback: free sdu data */
80 static void llcp_free_client(gpointer data)
81 {
82         struct co_cl_client_data *co_data = data;
83
84         DBG("");
85
86         if (co_data) {
87                 g_list_free_full(co_data->sdu_list, free_one_sdu);
88                 g_free(co_data->miu_buffer);
89         }
90
91         g_free(co_data);
92 }
93
94 static void llcp_send_data(gpointer data, gpointer user_data)
95 {
96         struct co_cl_client_data *clt = user_data;
97         struct sdu *i_sdu = data;
98         int err;
99
100         if (!i_sdu)
101                 return;
102
103         /* conn less or oriented ? */
104         if (clt->sock_type == SOCK_DGRAM)
105                 err = sendto(clt->fd, i_sdu->data, i_sdu->len, 0,
106                                 (struct sockaddr *) &clt->cl_addr,
107                                 sizeof(clt->cl_addr));
108         else
109                 err = send(clt->fd, i_sdu->data, i_sdu->len, 0);
110
111         if (err < 0)
112                 near_error("Could not send data to client %d", err);
113
114         /* free */
115         clt->sdu_list = g_list_remove(clt->sdu_list, i_sdu);
116         free_one_sdu(i_sdu);
117
118         return;
119 }
120
121 /* Connexion oriented code */
122 static gboolean llcp_common_delay_cb(gpointer user_data)
123 {
124
125         struct co_cl_client_data *clt = user_data;
126
127         DBG("");
128
129         /* process each sdu */
130         g_list_foreach(clt->sdu_list, llcp_send_data, user_data);
131
132         clt->buf_count = 0;
133
134         return FALSE;
135 }
136
137 /*
138  * Common function: add an incoming SDU to the glist.
139  * If this is the first SDU, we start a 2 secs timer, and be ready for
140  * another SDU
141  */
142 static bool llcp_add_incoming_sdu(struct co_cl_client_data *clt, int len)
143 {
144         struct sdu *i_sdu;
145
146         i_sdu = g_try_malloc0(sizeof(struct sdu));
147         if (!i_sdu)
148                 goto out_error;
149
150         i_sdu->len = len;
151         if (len > 0) {
152                 i_sdu->data = g_try_malloc0(len);
153                 if (!i_sdu->data)
154                         goto out_error;
155                 memcpy(i_sdu->data, clt->miu_buffer, len);
156         }
157
158         clt->sdu_list = g_list_append(clt->sdu_list, i_sdu);
159         clt->buf_count++;
160
161         /* on the first SDU, fire a 2 seconds timer */
162         if (clt->buf_count == 1)
163                 g_timeout_add(ECHO_DELAY, llcp_common_delay_cb, clt);
164
165         return true;
166
167 out_error:
168         g_free(i_sdu);
169         return false;
170 }
171
172 /*
173  * Connection-less mode. We get a SDU and add it to the the list. We cannot
174  * acceppt more than 2 SDUs, so we discard subsequent SDU.
175  *
176  * */
177 static bool llcp_cl_data_recv(struct co_cl_client_data *cl_client)
178 {
179         socklen_t addr_len;
180         int len;
181
182         DBG("");
183
184         /* retrieve sdu */
185         addr_len = sizeof(struct sockaddr_nfc_llcp);
186         len = recvfrom(cl_client->fd, cl_client->miu_buffer, cl_client->miu_len,
187                         0, (struct sockaddr *) &cl_client->cl_addr, &addr_len);
188
189         if (len < 0) {
190                 near_error("Could not read data %d %s", len, strerror(errno));
191                 return false;
192         }
193
194         /* Two SDUs max, reject the others */
195         if (cl_client->buf_count < 2)
196                 return llcp_add_incoming_sdu(cl_client, len);
197         else
198                 near_warn("No more than 2 SDU..ignored");
199
200         return true;
201 }
202
203 /*
204  * Connection oriented mode. We get the SDU and add it to the list.
205  */
206 static bool llcp_co_data_recv(struct co_cl_client_data *co_client)
207 {
208         int len;
209
210         DBG("");
211
212         len = recv(co_client->fd, co_client->miu_buffer, co_client->miu_len, 0);
213         if (len < 0) {
214                 near_error("Could not read data %d %s", len, strerror(errno));
215                 return false;
216         }
217         return llcp_add_incoming_sdu(co_client, len);
218
219 }
220
221 /* Common function to initialize client connection data */
222 static bool llcp_common_read(int client_fd, uint32_t adapter_idx,
223                                         uint32_t target_idx, near_tag_io_cb cb,
224                                         near_incoming_cb llcp_read_bytes,
225                                         const int sock_type)
226 {
227         struct co_cl_client_data *cx_client = NULL;
228         socklen_t len = sizeof(unsigned int);
229
230         /* Check if this is the 1st call for this client */
231         cx_client = g_hash_table_lookup(llcp_client_hash,
232                                                 GINT_TO_POINTER(client_fd));
233
234         if (!cx_client) {
235                 cx_client = g_try_malloc0(sizeof(struct co_cl_client_data));
236                 if (!cx_client)
237                         goto error;
238
239                 cx_client->fd = client_fd;
240                 cx_client->sock_type = sock_type;
241
242                 /* get MIU */
243                 if (getsockopt(client_fd, SOL_NFC, NFC_LLCP_MIUX,
244                                                 &cx_client->miu_len, &len) == 0)
245                         cx_client->miu_len = cx_client->miu_len +
246                                                         LLCP_DEFAULT_MIU;
247                 else
248                         cx_client->miu_len = LLCP_DEFAULT_MIU;
249
250                 cx_client->miu_buffer = g_try_malloc0(cx_client->miu_len);
251                 if (!cx_client->miu_buffer) {
252                         DBG("Cannot allocate MIU buffer (size: %d)",
253                                                         cx_client->miu_len);
254                         goto error;
255                 }
256
257                 /* Add to the client hash table */
258                 g_hash_table_insert(llcp_client_hash,
259                                 GINT_TO_POINTER(client_fd), cx_client);
260         }
261
262         /* Read the incoming bytes */
263         return llcp_read_bytes(cx_client);
264
265 error:
266         DBG("Memory allocation failed");
267         g_free(cx_client);
268
269         return false;
270 }
271
272 /* clean on close */
273 static void llcp_validation_close(int client_fd, int err, gpointer data)
274 {
275         DBG("");
276
277         /* remove client */
278         g_hash_table_remove(llcp_client_hash, GINT_TO_POINTER(client_fd));
279 }
280
281 /* Connection Oriented: Wrapper for read function */
282 static bool llcp_validation_read_co(int client_fd, uint32_t adapter_idx,
283                                                         uint32_t target_idx,
284                                                         near_tag_io_cb cb,
285                                                         gpointer data)
286 {
287         DBG("CO client with fd: %d", client_fd);
288         return llcp_common_read(client_fd, adapter_idx, target_idx, cb,
289                                                 llcp_co_data_recv, SOCK_STREAM);
290 }
291
292 /* Connection less: Wrapper for read function */
293 static bool llcp_validation_read_cl(int client_fd, uint32_t adapter_idx,
294                                                         uint32_t target_idx,
295                                                         near_tag_io_cb cb,
296                                                         gpointer data)
297 {
298         DBG("CL client with fd: %d", client_fd);
299         return llcp_common_read(client_fd, adapter_idx, target_idx, cb,
300                                                 llcp_cl_data_recv, SOCK_DGRAM);
301 }
302
303 /* Connection-less server */
304 struct near_p2p_driver validation_llcp_driver_cl = {
305         .name = "VALIDATION_LLCP_CL",
306         .service_name = "urn:nfc:sn:cl-echo",
307         .fallback_service_name = NULL,
308         .sock_type = SOCK_DGRAM,
309         .read = llcp_validation_read_cl,
310         .close = llcp_validation_close,
311 };
312
313 /* Connection oriented server */
314 struct near_p2p_driver validation_llcp_driver_co = {
315         .name = "VALIDATION_LLCP_CO",
316         .service_name = "urn:nfc:sn:co-echo",
317         .fallback_service_name = NULL,
318         .sock_type = SOCK_STREAM,
319         .single_connection = TRUE,
320         .read = llcp_validation_read_co,
321         .close = llcp_validation_close,
322 };
323
324 int llcp_validation_init(void)
325 {
326         int err;
327
328         DBG("");
329
330         llcp_client_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
331                                                         NULL, llcp_free_client);
332
333         /* register drivers */
334         err = near_p2p_register(&validation_llcp_driver_cl);
335         if (err < 0)
336                 return err;
337
338         err =  near_p2p_register(&validation_llcp_driver_co);
339         if (err < 0)
340                 near_p2p_unregister(&validation_llcp_driver_cl);
341
342         return err;
343 }
344
345 void llcp_validation_exit(void)
346 {
347         DBG("");
348
349         near_p2p_unregister(&validation_llcp_driver_co);
350         near_p2p_unregister(&validation_llcp_driver_cl);
351 }