Fix build break for rpm
[framework/connectivity/bluez.git] / deviceinfo / deviceinfo.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2012 Texas Instruments, Inc.
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 as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <glib.h>
28 #include <bluetooth/uuid.h>
29
30 #include "adapter.h"
31 #include "device.h"
32 #include "gattrib.h"
33 #include "attio.h"
34 #include "att.h"
35 #include "gattrib.h"
36 #include "gatt.h"
37 #include "log.h"
38 #include "deviceinfo.h"
39
40 struct deviceinfo {
41         struct btd_device       *dev;           /* Device reference */
42         GAttrib                 *attrib;        /* GATT connection */
43         guint                   attioid;        /* Att watcher id */
44         struct att_range        *svc_range;     /* DeviceInfo range */
45         GSList                  *chars;         /* Characteristics */
46 };
47
48 static GSList *servers = NULL;
49
50 struct characteristic {
51         struct gatt_char        attr;   /* Characteristic */
52         struct deviceinfo       *d;     /* deviceinfo where the char belongs */
53 };
54
55 static void deviceinfo_free(gpointer user_data)
56 {
57         struct deviceinfo *d = user_data;
58
59         if (d->attioid > 0)
60                 btd_device_remove_attio_callback(d->dev, d->attioid);
61
62         if (d->attrib != NULL)
63                 g_attrib_unref(d->attrib);
64
65         g_slist_free_full(d->chars, g_free);
66
67         btd_device_unref(d->dev);
68         g_free(d->svc_range);
69         g_free(d);
70 }
71
72 static gint cmp_device(gconstpointer a, gconstpointer b)
73 {
74         const struct deviceinfo *d = a;
75         const struct btd_device *dev = b;
76
77         if (dev == d->dev)
78                 return 0;
79
80         return -1;
81 }
82
83 static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,
84                                                         gpointer user_data)
85 {
86         struct characteristic *ch = user_data;
87         uint8_t value[ATT_MAX_MTU];
88         int vlen;
89
90         if (status != 0) {
91                 error("Error reading PNP_ID value: %s", att_ecode2str(status));
92                 return;
93         }
94
95         if (!dec_read_resp(pdu, len, value, &vlen)) {
96                 error("Error reading PNP_ID: Protocol error");
97                 return;
98         }
99
100         if (vlen < 7) {
101                 error("Error reading PNP_ID: Invalid pdu length received");
102                 return;
103         }
104
105         device_set_pnpid(ch->d->dev, value[0], att_get_u16(&value[1]),
106                                 att_get_u16(&value[3]), att_get_u16(&value[5]));
107 }
108
109 static void process_deviceinfo_char(struct characteristic *ch)
110 {
111         if (g_strcmp0(ch->attr.uuid, PNPID_UUID) == 0)
112                 gatt_read_char(ch->d->attrib, ch->attr.value_handle, 0,
113                                                         read_pnpid_cb, ch);
114 }
115
116 static void configure_deviceinfo_cb(GSList *characteristics, guint8 status,
117                                                         gpointer user_data)
118 {
119         struct deviceinfo *d = user_data;
120         GSList *l;
121
122         if (status != 0) {
123                 error("Discover deviceinfo characteristics: %s",
124                                                         att_ecode2str(status));
125                 return;
126         }
127
128         for (l = characteristics; l; l = l->next) {
129                 struct gatt_char *c = l->data;
130                 struct characteristic *ch;
131
132                 ch = g_new0(struct characteristic, 1);
133                 ch->attr.handle = c->handle;
134                 ch->attr.properties = c->properties;
135                 ch->attr.value_handle = c->value_handle;
136                 memcpy(ch->attr.uuid, c->uuid, MAX_LEN_UUID_STR + 1);
137                 ch->d = d;
138
139                 d->chars = g_slist_append(d->chars, ch);
140
141                 process_deviceinfo_char(ch);
142         }
143 }
144 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
145 {
146         struct deviceinfo *d = user_data;
147
148         d->attrib = g_attrib_ref(attrib);
149
150         gatt_discover_char(d->attrib, d->svc_range->start, d->svc_range->end,
151                                         NULL, configure_deviceinfo_cb, d);
152 }
153
154 static void attio_disconnected_cb(gpointer user_data)
155 {
156         struct deviceinfo *d = user_data;
157
158         g_attrib_unref(d->attrib);
159         d->attrib = NULL;
160 }
161
162 int deviceinfo_register(struct btd_device *device, struct gatt_primary *prim)
163 {
164         struct deviceinfo *d;
165
166         d = g_new0(struct deviceinfo, 1);
167         d->dev = btd_device_ref(device);
168         d->svc_range = g_new0(struct att_range, 1);
169         d->svc_range->start = prim->range.start;
170         d->svc_range->end = prim->range.end;
171
172         servers = g_slist_prepend(servers, d);
173
174         d->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
175                                                 attio_disconnected_cb, d);
176         return 0;
177 }
178
179 void deviceinfo_unregister(struct btd_device *device)
180 {
181         struct deviceinfo *d;
182         GSList *l;
183
184         l = g_slist_find_custom(servers, device, cmp_device);
185         if (l == NULL)
186                 return;
187
188         d = l->data;
189         servers = g_slist_remove(servers, d);
190
191         deviceinfo_free(d);
192 }