http://mulliner.org/bluetooth/xkbdbthid-0.1_src.tar.gz
[xkbdbthid.git] / xkbd-0.8.15_bthid / src / sdp.c
1 /*
2  *  xkbd-bthid
3  *
4  *  Collin R. Mulliner <collin@betaversion.net>
5  *
6  *  http://www.mulliner.org/bluetooth/
7  *
8  *  License: GPL
9  *
10  */
11
12 #define _GNU_SOURCE
13 #include <assert.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <stdarg.h>
17 #include <stdbool.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/ioctl.h>
22 #include <sys/socket.h>
23 #include <sys/poll.h>
24 #include <termios.h>
25 #include <time.h>
26 #include <unistd.h>
27 #include <dirent.h>
28 #include <netinet/in.h>
29 #include <netdb.h>
30 #include <bluetooth/bluetooth.h>
31 #include <bluetooth/hci.h>
32 #include <bluetooth/hci_lib.h>
33 #include <bluetooth/sdp.h>
34 #include <bluetooth/sdp_lib.h>
35 #include <bluetooth/rfcomm.h>
36 #include <openobex/obex.h>
37
38 sdp_record_t *sdp_record;
39 sdp_session_t *sdp_session;
40
41 /*
42  *  100% taken from bluez-utils (sdptool)
43  */
44 static void add_lang_attr(sdp_record_t *r)
45 {
46         sdp_lang_attr_t base_lang;
47         sdp_list_t *langs = 0;
48
49         /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
50         base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
51         base_lang.encoding = 106;
52         base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
53         langs = sdp_list_append(0, &base_lang);
54         sdp_set_lang_attr(r, langs);
55         sdp_list_free(langs, 0);
56 }
57
58 /*
59  *  100% taken from bluez-utils (sdptool)
60  */
61 static sdp_data_t *access_proto_to_dataseq(sdp_record_t *rec, sdp_list_t *proto)
62 {
63         sdp_data_t *seq = NULL;
64         void *dtds[10], *values[10];
65         void **seqDTDs, **seqs;
66         int i, seqlen;
67         sdp_list_t *p;
68
69         seqlen = sdp_list_len(proto);
70         seqDTDs = (void **)malloc(seqlen * sizeof(void *));
71         seqs = (void **)malloc(seqlen * sizeof(void *));
72         for (i = 0, p = proto; p; p = p->next, i++) {
73                 sdp_list_t *elt = (sdp_list_t *)p->data;
74                 sdp_data_t *s;
75                 int pslen = 0;
76                 for (; elt && pslen < sizeof(dtds); elt = elt->next, pslen++) {
77                         sdp_data_t *d = (sdp_data_t *)elt->data;
78                         dtds[pslen] = &d->dtd;
79                         switch (d->dtd) {
80                         case SDP_UUID16:
81                                 values[pslen] = &((uuid_t *)d)->value.uuid16;
82                                 break;
83                         case SDP_UUID32:
84                                 values[pslen] = &((uuid_t *)d)->value.uuid32;
85                                 break;
86                         case SDP_UUID128:
87                                 values[pslen] = &((uuid_t *)d)->value.uuid128;
88                                 break;
89                         case SDP_UINT8:
90                                 values[pslen] = &d->val.uint8;
91                                 break;
92                         case SDP_UINT16:
93                                 values[pslen] = &d->val.uint16;
94                                 break;
95                         case SDP_SEQ8:
96                         case SDP_SEQ16:
97                         case SDP_SEQ32:
98                                 values[pslen] = d;
99                                 break;
100                         // FIXME: more
101                         }
102                 }
103                 s = sdp_seq_alloc(dtds, values, pslen);
104                 if (s) {
105                         seqDTDs[i] = &s->dtd;
106                         seqs[i] = s;
107                 }
108         }
109         seq = sdp_seq_alloc(seqDTDs, seqs, seqlen);
110         free(seqDTDs);
111         free(seqs);
112         return seq;
113 }
114
115 /*
116  *  add additional access protos
117  */
118 static int sdp_set_add_access_protos(sdp_record_t *rec, const sdp_list_t *ap)
119 {
120         const sdp_list_t *p;
121         sdp_data_t *protos = 0;
122         sdp_data_t *protosg = 0;
123
124         for (p = ap; p; p = p->next) {
125                 sdp_data_t *seq = access_proto_to_dataseq(rec, (sdp_list_t *)p->data);
126                 protos = sdp_seq_append(protos, seq);
127         }
128         protosg = sdp_data_alloc(SDP_SEQ8, protos);     
129         sdp_attr_add(rec, SDP_ATTR_ADD_PROTO_DESC_LIST, protosg);
130         return 0;
131 }
132
133 /*
134  *  add keyboard descriptor
135  */
136 void sdp_add_keyboard()
137 {
138         sdp_list_t *svclass_id, *pfseq, *apseq, *root;
139         uuid_t root_uuid, hidkb_uuid, l2cap_uuid, hidp_uuid;
140         sdp_profile_desc_t profile[1];
141         sdp_list_t *aproto, *proto[3];
142         sdp_data_t *channel, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
143         int i;
144         uint8_t dtd = SDP_UINT16;
145         uint8_t dtd2 = SDP_UINT8;
146         uint8_t dtd_data = SDP_TEXT_STR8;
147         sdp_session_t *session;
148         void *dtds[2];
149         void *values[2];
150         void *dtds2[2];
151         void *values2[2];
152         int leng[2];
153         uint8_t hid_spec_type = 0x22;
154         uint16_t hid_attr_lang[] = {0x409,0x100};
155         static const uint8_t ctrl = 0x11;
156         static const uint8_t intr = 0x13;
157         static const uint16_t hid_attr[] = {0x100,0x111,0x40,0x0d,0x01,0x01};
158         static const uint16_t hid_attr2[] = {0x0,0x01,0x100,0x1f40,0x01,0x01};
159         // taken from Apple Wireless Keyboard
160         const uint8_t hid_spec[] = { 
161                 0x05, 0x01, // usage page
162                 0x09, 0x06, // keyboard
163                 0xa1, 0x01, // key codes
164                 0x85, 0x01, // minimum
165                 0x05, 0x07, // max
166                 0x19, 0xe0, // logical min
167                 0x29, 0xe7, // logical max
168                 0x15, 0x00, // report size
169                 0x25, 0x01, // report count
170                 0x75, 0x01, // input data variable absolute
171                 0x95, 0x08, // report count
172                 0x81, 0x02, // report size
173                 0x75, 0x08, 
174                 0x95, 0x01, 
175                 0x81, 0x01, 
176                 0x75, 0x01, 
177                 0x95, 0x05,
178                 0x05, 0x08,
179                 0x19, 0x01,
180                 0x29, 0x05, 
181                 0x91, 0x02,
182                 0x75, 0x03,
183                 0x95, 0x01,
184                 0x91, 0x01,
185                 0x75, 0x08,
186                 0x95, 0x06,
187                 0x15, 0x00,
188                 0x26, 0xff,
189                 0x00, 0x05,
190                 0x07, 0x19,
191                 0x00, 0x2a,
192                 0xff, 0x00,
193                 0x81, 0x00,
194                 0x75, 0x01,
195                 0x95, 0x01,
196                 0x15, 0x00,
197                 0x25, 0x01,
198                 0x05, 0x0c,
199                 0x09, 0xb8,
200                 0x81, 0x06,
201                 0x09, 0xe2,
202                 0x81, 0x06,
203                 0x09, 0xe9,
204                 0x81, 0x02,
205                 0x09, 0xea,
206                 0x81, 0x02,
207                 0x75, 0x01,
208                 0x95, 0x04,
209                 0x81, 0x01,
210                 0xc0         // end tag
211         };
212
213
214         if (!sdp_session) {
215                 printf("%s: sdp_session invalid\n", (char*)__func__);
216                 exit(-1);
217         }
218         session = sdp_session;
219
220         sdp_record = sdp_record_alloc();
221         if (!sdp_record) {
222                 perror("add_keyboard sdp_record_alloc: ");
223                 exit(-1);
224         }
225
226         memset((void*)sdp_record, 0, sizeof(sdp_record_t));
227         sdp_record->handle = 0xffffffff;
228         sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
229         root = sdp_list_append(0, &root_uuid);
230         sdp_set_browse_groups(sdp_record, root);
231
232         add_lang_attr(sdp_record);
233         
234         sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID);
235         svclass_id = sdp_list_append(0, &hidkb_uuid);
236         sdp_set_service_classes(sdp_record, svclass_id);
237
238         sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
239         profile[0].version = 0x0100;
240         pfseq = sdp_list_append(0, profile);
241         sdp_set_profile_descs(sdp_record, pfseq);
242
243         // PROTO
244         sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
245         proto[1] = sdp_list_append(0, &l2cap_uuid);
246         channel = sdp_data_alloc(SDP_UINT8, &ctrl);
247         proto[1] = sdp_list_append(proto[1], channel);
248         apseq = sdp_list_append(0, proto[1]);
249
250         sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
251         proto[2] = sdp_list_append(0, &hidp_uuid);
252         apseq = sdp_list_append(apseq, proto[2]);
253
254         aproto = sdp_list_append(0, apseq);
255         sdp_set_access_protos(sdp_record, aproto);
256
257         // ATTR_ADD_PROTO
258         proto[1] = sdp_list_append(0, &l2cap_uuid);
259         channel = sdp_data_alloc(SDP_UINT8, &intr);
260         proto[1] = sdp_list_append(proto[1], channel);
261         apseq = sdp_list_append(0, proto[1]);
262
263         sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
264         proto[2] = sdp_list_append(0, &hidp_uuid);
265         apseq = sdp_list_append(apseq, proto[2]);
266
267         aproto = sdp_list_append(0, apseq);
268         sdp_set_add_access_protos(sdp_record, aproto);
269         
270         sdp_set_info_attr(sdp_record, "Collin's Fake Bluetooth Keyboard", 
271                 "MUlliNER.ORG", "http://www.mulliner.org/bluetooth/");
272
273         for (i = 0; i < sizeof(hid_attr)/2; i++) {
274                 sdp_attr_add_new(sdp_record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER+i, SDP_UINT16, &hid_attr[i]);
275         }
276
277         dtds[0] = &dtd2;
278         values[0] = &hid_spec_type;
279         dtds[1] = &dtd_data;
280         values[1] = (uint8_t*)hid_spec;
281         leng[0] = 0;
282         leng[1] = sizeof(hid_spec);
283         hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
284         hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst); 
285         sdp_attr_add(sdp_record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
286         
287         for (i = 0; i < sizeof(hid_attr_lang)/2; i++) {
288                 dtds2[i] = &dtd;
289                 values2[i] = &hid_attr_lang[i];
290         }
291         lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang)/2);
292         lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst); 
293         sdp_attr_add(sdp_record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
294
295         sdp_attr_add_new(sdp_record, SDP_ATTR_HID_SDP_DISABLE, SDP_UINT16, &hid_attr2[0]);
296         for (i = 0; i < sizeof(hid_attr2)/2-1; i++) {
297                 sdp_attr_add_new(sdp_record, SDP_ATTR_HID_REMOTE_WAKEUP+i, SDP_UINT16, &hid_attr2[i+1]);
298         }
299         
300         if (sdp_record_register(session, sdp_record, 0) < 0) {
301                 printf("%s: HID Device (Keyboard) Service Record registration failed\n", (char*)__func__);
302                 exit(-1);
303         }
304 }
305
306 void sdp_remove()
307 {
308         if (sdp_record && sdp_record_unregister(sdp_session, sdp_record)) {
309                 printf("%s: HID Device (Keyboard) Service Record unregistration failed\n", (char*)__func__);
310         }
311         
312         sdp_close(sdp_session);
313 }
314
315 void sdp_open()
316 {
317         if (!sdp_session) {
318                 sdp_session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
319         }
320         if (!sdp_session) {
321                 printf("%s: sdp_session invalid\n", (char*)__func__);
322                 exit(-1);
323         }
324 }
325
326 #ifdef SDP_MAIN
327 int main()
328 {
329         sdp_open();
330         sdp_add_keyboard();
331         sleep(60);      
332         sdp_remove();
333         return(1);
334 }
335 #endif