Upgrade bluez5_37 :Merge the code from private
[platform/upstream/bluez.git] / profiles / input / hog.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2012  Marcel Holtmann <marcel@holtmann.org>
6  *  Copyright (C) 2012  Nordic Semiconductor Inc.
7  *  Copyright (C) 2012  Instituto Nokia de Tecnologia - INdT
8  *
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  *
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <stdlib.h>
31 #include <stdbool.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37
38 #include <glib.h>
39
40 #include "lib/bluetooth.h"
41 #include "lib/sdp.h"
42 #include "lib/uuid.h"
43
44 #include "src/log.h"
45 #include "src/adapter.h"
46 #include "src/device.h"
47 #include "src/profile.h"
48 #include "src/service.h"
49 #include "src/shared/util.h"
50 #include "src/shared/uhid.h"
51 #include "src/shared/queue.h"
52 #include "src/plugin.h"
53
54 #include "suspend.h"
55 #include "attrib/att.h"
56 #include "attrib/gattrib.h"
57 #include "src/attio.h"
58 #include "attrib/gatt.h"
59 #include "hog-lib.h"
60
61 #define HOG_UUID                "00001812-0000-1000-8000-00805f9b34fb"
62
63 struct hog_device {
64         guint                   attioid;
65         struct btd_device       *device;
66         struct bt_hog           *hog;
67 };
68
69 static gboolean suspend_supported = FALSE;
70 static struct queue *devices = NULL;
71
72 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
73 {
74         struct hog_device *dev = user_data;
75
76         DBG("HoG connected");
77
78         bt_hog_attach(dev->hog, attrib);
79 }
80
81 static void attio_disconnected_cb(gpointer user_data)
82 {
83         struct hog_device *dev = user_data;
84
85         DBG("HoG disconnected");
86
87         bt_hog_detach(dev->hog);
88 }
89
90 static struct hog_device *hog_device_new(struct btd_device *device,
91                                                 struct gatt_primary *prim)
92 {
93         struct hog_device *dev;
94         char name[248];
95         uint16_t vendor, product, version;
96
97         if (device_name_known(device))
98                 device_get_name(device, name, sizeof(name));
99         else
100                 strcpy(name, "bluez-hog-device");
101
102         vendor = btd_device_get_vendor(device);
103         product = btd_device_get_product(device);
104         version = btd_device_get_version(device);
105
106         DBG("name=%s vendor=0x%X, product=0x%X, version=0x%X", name, vendor,
107                                                         product, version);
108
109         dev = new0(struct hog_device, 1);
110         dev->device = btd_device_ref(device);
111         dev->hog = bt_hog_new_default(name, vendor, product, version, prim);
112
113         /*
114          * TODO: Remove attio callback and use .accept once using
115          * bt_gatt_client.
116          */
117         dev->attioid = btd_device_add_attio_callback(device,
118                                                         attio_connected_cb,
119                                                         attio_disconnected_cb,
120                                                         dev);
121
122         if (!devices)
123                 devices = queue_new();
124
125         queue_push_tail(devices, dev);
126
127         return dev;
128 }
129
130 static void hog_device_free(void *data)
131 {
132         struct hog_device *dev = data;
133
134         queue_remove(devices, dev);
135         if (queue_isempty(devices)) {
136                 queue_destroy(devices, NULL);
137                 devices = NULL;
138         }
139
140         btd_device_remove_attio_callback(dev->device, dev->attioid);
141         btd_device_unref(dev->device);
142         bt_hog_unref(dev->hog);
143         free(dev);
144 }
145
146 static void set_suspend(gpointer data, gpointer user_data)
147 {
148         struct hog_device *dev = data;
149         gboolean suspend = GPOINTER_TO_INT(user_data);
150
151         bt_hog_set_control_point(dev->hog, suspend);
152 }
153
154 static void suspend_callback(void)
155 {
156         gboolean suspend = TRUE;
157
158         DBG("Suspending ...");
159
160         queue_foreach(devices, set_suspend, GINT_TO_POINTER(suspend));
161 }
162
163 static void resume_callback(void)
164 {
165         gboolean suspend = FALSE;
166
167         DBG("Resuming ...");
168
169         queue_foreach(devices, set_suspend, GINT_TO_POINTER(suspend));
170 }
171
172 static int hog_probe(struct btd_service *service)
173 {
174         struct btd_device *device = btd_service_get_device(service);
175         const char *path = device_get_path(device);
176         GSList *primaries, *l;
177
178         DBG("path %s", path);
179
180         primaries = btd_device_get_primaries(device);
181         if (primaries == NULL)
182                 return -EINVAL;
183
184         for (l = primaries; l; l = g_slist_next(l)) {
185                 struct gatt_primary *prim = l->data;
186                 struct hog_device *dev;
187
188                 if (strcmp(prim->uuid, HOG_UUID) != 0)
189                         continue;
190
191                 dev = hog_device_new(device, prim);
192                 btd_service_set_user_data(service, dev);
193                 return 0;
194         }
195
196         return -EINVAL;
197 }
198
199 static void hog_remove(struct btd_service *service)
200 {
201         struct hog_device *dev = btd_service_get_user_data(service);
202         struct btd_device *device = btd_service_get_device(service);
203         const char *path = device_get_path(device);
204
205         DBG("path %s", path);
206
207         hog_device_free(dev);
208 }
209
210 static struct btd_profile hog_profile = {
211         .name           = "input-hog",
212         .remote_uuid    = HOG_UUID,
213         .device_probe   = hog_probe,
214         .device_remove  = hog_remove,
215 };
216
217 static int hog_init(void)
218 {
219         int err;
220
221         err = suspend_init(suspend_callback, resume_callback);
222         if (err < 0)
223                 error("Loading suspend plugin failed: %s (%d)", strerror(-err),
224                                                                         -err);
225         else
226                 suspend_supported = TRUE;
227
228         return btd_profile_register(&hog_profile);
229 }
230
231 static void hog_exit(void)
232 {
233         if (suspend_supported)
234                 suspend_exit();
235
236         btd_profile_unregister(&hog_profile);
237 }
238
239 BLUETOOTH_PLUGIN_DEFINE(hog, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
240                                                         hog_init, hog_exit)