Upgrade bluez5_37 :Merge the code from private
[platform/upstream/bluez.git] / src / shared / uhid.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2014  Intel Corporation. All rights reserved.
6  *
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library 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 GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <fcntl.h>
33
34 #include "src/shared/io.h"
35 #include "src/shared/util.h"
36 #include "src/shared/queue.h"
37 #include "src/shared/uhid.h"
38
39 #define UHID_DEVICE_FILE "/dev/uhid"
40
41 struct bt_uhid {
42         int ref_count;
43         struct io *io;
44         unsigned int notify_id;
45         struct queue *notify_list;
46 };
47
48 struct uhid_notify {
49         unsigned int id;
50         uint32_t event;
51         bt_uhid_callback_t func;
52         void *user_data;
53 };
54
55 static void uhid_free(struct bt_uhid *uhid)
56 {
57         if (uhid->io)
58                 io_destroy(uhid->io);
59
60         if (uhid->notify_list)
61                 queue_destroy(uhid->notify_list, free);
62
63         free(uhid);
64 }
65
66 static void notify_handler(void *data, void *user_data)
67 {
68         struct uhid_notify *notify = data;
69         struct uhid_event *ev = user_data;
70
71         if (notify->event != ev->type)
72                 return;
73
74         if (notify->func)
75                 notify->func(ev, notify->user_data);
76 }
77
78 static bool uhid_read_handler(struct io *io, void *user_data)
79 {
80         struct bt_uhid *uhid = user_data;
81         int fd;
82         ssize_t len;
83         struct uhid_event ev;
84
85         fd = io_get_fd(io);
86         if (fd < 0)
87                 return false;
88
89         memset(&ev, 0, sizeof(ev));
90
91         len = read(fd, &ev, sizeof(ev));
92         if (len < 0)
93                 return false;
94
95         if ((size_t) len < sizeof(ev.type))
96                 return false;
97
98         queue_foreach(uhid->notify_list, notify_handler, &ev);
99
100         return true;
101 }
102
103 struct bt_uhid *bt_uhid_new_default(void)
104 {
105         struct bt_uhid *uhid;
106         int fd;
107
108         fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
109         if (fd < 0)
110                 return NULL;
111
112         uhid = bt_uhid_new(fd);
113         if (!uhid) {
114                 close(fd);
115                 return NULL;
116         }
117
118         io_set_close_on_destroy(uhid->io, true);
119
120         return uhid;
121 }
122
123 struct bt_uhid *bt_uhid_new(int fd)
124 {
125         struct bt_uhid *uhid;
126
127         uhid = new0(struct bt_uhid, 1);
128         uhid->io = io_new(fd);
129         if (!uhid->io)
130                 goto failed;
131
132         uhid->notify_list = queue_new();
133
134         if (!io_set_read_handler(uhid->io, uhid_read_handler, uhid, NULL))
135                 goto failed;
136
137         return bt_uhid_ref(uhid);
138
139 failed:
140         uhid_free(uhid);
141         return NULL;
142 }
143
144 struct bt_uhid *bt_uhid_ref(struct bt_uhid *uhid)
145 {
146         if (!uhid)
147                 return NULL;
148
149         __sync_fetch_and_add(&uhid->ref_count, 1);
150
151         return uhid;
152 }
153
154 void bt_uhid_unref(struct bt_uhid *uhid)
155 {
156         if (!uhid)
157                 return;
158
159         if (__sync_sub_and_fetch(&uhid->ref_count, 1))
160                 return;
161
162         uhid_free(uhid);
163 }
164
165 bool bt_uhid_set_close_on_unref(struct bt_uhid *uhid, bool do_close)
166 {
167         if (!uhid || !uhid->io)
168                 return false;
169
170         io_set_close_on_destroy(uhid->io, do_close);
171
172         return true;
173 }
174
175 unsigned int bt_uhid_register(struct bt_uhid *uhid, uint32_t event,
176                                 bt_uhid_callback_t func, void *user_data)
177 {
178         struct uhid_notify *notify;
179
180         if (!uhid)
181                 return 0;
182
183         notify = new0(struct uhid_notify, 1);
184         notify->id = uhid->notify_id++;
185         notify->event = event;
186         notify->func = func;
187         notify->user_data = user_data;
188
189         if (!queue_push_tail(uhid->notify_list, notify)) {
190                 free(notify);
191                 return 0;
192         }
193
194         return notify->id;
195 }
196
197 static bool match_notify_id(const void *a, const void *b)
198 {
199         const struct uhid_notify *notify = a;
200         unsigned int id = PTR_TO_UINT(b);
201
202         return notify->id == id;
203 }
204
205 bool bt_uhid_unregister(struct bt_uhid *uhid, unsigned int id)
206 {
207         struct uhid_notify *notify;
208
209         if (!uhid || !id)
210                 return false;
211
212         notify = queue_remove_if(uhid->notify_list, match_notify_id,
213                                                         UINT_TO_PTR(id));
214         if (!notify)
215                 return false;
216
217         free(notify);
218         return true;
219 }
220
221 int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev)
222 {
223         ssize_t len;
224         struct iovec iov;
225
226         if (!uhid->io)
227                 return -ENOTCONN;
228
229         iov.iov_base = (void *) ev;
230         iov.iov_len = sizeof(*ev);
231
232         len = io_send(uhid->io, &iov, 1);
233         if (len < 0)
234                 return -errno;
235
236         /* uHID kernel driver does not handle partial writes */
237         return len != sizeof(*ev) ? -EIO : 0;
238 }