mgmtops.c revision 8c442fd677490441b64b3509c1264460668d9367
1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2010  Nokia Corporation
6 *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
7 *
8 *  This program is free software; you can redistribute it and/or modify
9 *  it under the terms of the GNU General Public License as published by
10 *  the Free Software Foundation; either version 2 of the License, or
11 *  (at your option) any later version.
12 *
13 *  This program 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
16 *  GNU General Public License for more details.
17 *
18 *  You should have received a copy of the GNU General Public License
19 *  along with this program; 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 <errno.h>
30#include <unistd.h>
31#include <stdlib.h>
32#include <sys/types.h>
33#include <sys/ioctl.h>
34#include <sys/wait.h>
35
36#include <glib.h>
37
38#include <bluetooth/bluetooth.h>
39#include <bluetooth/hci.h>
40#include <bluetooth/mgmt.h>
41
42#include "plugin.h"
43#include "log.h"
44#include "manager.h"
45#include "adapter.h"
46#include "device.h"
47#include "event.h"
48
49#define MGMT_BUF_SIZE 1024
50
51static int max_index = -1;
52static struct controller_info {
53	gboolean valid;
54	gboolean notified;
55	uint8_t type;
56	bdaddr_t bdaddr;
57	uint8_t features[8];
58	uint16_t manufacturer;
59	uint8_t hci_ver;
60	uint16_t hci_rev;
61	gboolean enabled;
62	uint8_t mode;
63} *controllers = NULL;
64
65static int mgmt_sock = -1;
66static guint mgmt_watch = 0;
67
68static uint8_t mgmt_version = 0;
69static uint16_t mgmt_revision = 0;
70
71static void read_version_complete(int sk, void *buf, size_t len)
72{
73	struct mgmt_hdr hdr;
74	struct mgmt_rp_read_version *rp = buf;
75
76	if (len < sizeof(*rp)) {
77		error("Too small read version complete event");
78		return;
79	}
80
81	mgmt_revision = btohs(bt_get_unaligned(&rp->revision));
82	mgmt_version = rp->version;
83
84	DBG("version %u revision %u", mgmt_version, mgmt_revision);
85
86	memset(&hdr, 0, sizeof(hdr));
87	hdr.opcode = MGMT_OP_READ_INDEX_LIST;
88	if (write(sk, &hdr, sizeof(hdr)) < 0)
89		error("Unable to read controller index list: %s (%d)",
90						strerror(errno), errno);
91}
92
93static void add_controller(uint16_t index)
94{
95	if (index > max_index) {
96		size_t size = sizeof(struct controller_info) * (index + 1);
97		max_index = index;
98		controllers = g_realloc(controllers, size);
99	}
100
101	memset(&controllers[index], 0, sizeof(struct controller_info));
102
103	controllers[index].valid = TRUE;
104
105	DBG("Added controller %u", index);
106}
107
108static void read_info(int sk, uint16_t index)
109{
110	char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_read_info)];
111	struct mgmt_hdr *hdr = (void *) buf;
112	struct mgmt_cp_read_info *cp = (void *) &buf[sizeof(*hdr)];
113
114	memset(buf, 0, sizeof(buf));
115	hdr->opcode = MGMT_OP_READ_INFO;
116	hdr->len = htobs(sizeof(*cp));
117
118	cp->index = htobs(index);
119
120	if (write(sk, buf, sizeof(buf)) < 0)
121		error("Unable to send read_info command: %s (%d)",
122						strerror(errno), errno);
123}
124
125static void mgmt_index_added(int sk, void *buf, size_t len)
126{
127	struct mgmt_ev_index_added *ev = buf;
128	uint16_t index;
129
130	if (len < sizeof(*ev)) {
131		error("Too small index added event");
132		return;
133	}
134
135	index = btohs(bt_get_unaligned(&ev->index));
136
137	add_controller(index);
138	read_info(sk, index);
139}
140
141static void remove_controller(uint16_t index)
142{
143	if (index > max_index)
144		return;
145
146	if (!controllers[index].valid)
147		return;
148
149	manager_unregister_adapter(index);
150
151	memset(&controllers[index], 0, sizeof(struct controller_info));
152
153	DBG("Removed controller %u", index);
154}
155
156static void mgmt_index_removed(int sk, void *buf, size_t len)
157{
158	struct mgmt_ev_index_removed *ev = buf;
159	uint16_t index;
160
161	if (len < sizeof(*ev)) {
162		error("Too small index removed event");
163		return;
164	}
165
166	index = btohs(bt_get_unaligned(&ev->index));
167
168	remove_controller(index);
169}
170
171static void read_mode(int sk, uint16_t index)
172{
173	char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_read_mode)];
174	struct mgmt_hdr *hdr = (void *) buf;
175	struct mgmt_cp_read_mode *cp = (void *) &buf[sizeof(*hdr)];
176
177	memset(buf, 0, sizeof(buf));
178	hdr->opcode = MGMT_OP_READ_MODE;
179	hdr->len = htobs(sizeof(*cp));
180
181	cp->index = htobs(index);
182
183	if (write(sk, buf, sizeof(buf)) < 0)
184		error("Unable to send read_mode command: %s (%d)",
185						strerror(errno), errno);
186}
187
188static void read_index_list_complete(int sk, void *buf, size_t len)
189{
190	struct mgmt_rp_read_index_list *rp = buf;
191	uint16_t num;
192	int i;
193
194	if (len < sizeof(*rp)) {
195		error("Too small read index list complete event");
196		return;
197	}
198
199	num = btohs(bt_get_unaligned(&rp->num_controllers));
200
201	if (num * sizeof(uint16_t) + sizeof(*rp) != len) {
202		error("Incorrect packet size for index list event");
203		return;
204	}
205
206	for (i = 0; i < num; i++) {
207		uint16_t index;
208
209		index = btohs(bt_get_unaligned(&rp->index[i]));
210
211		add_controller(index);
212		read_info(sk, index);
213	}
214}
215
216static void read_info_complete(int sk, void *buf, size_t len)
217{
218	struct mgmt_rp_read_info *rp = buf;
219	struct controller_info *info;
220	uint16_t index;
221	char addr[18];
222
223	if (len < sizeof(*rp)) {
224		error("Too small read info complete event");
225		return;
226	}
227
228	if (rp->status != 0) {
229		error("Reading controller info failed: %s (%u)",
230					strerror(rp->status), rp->status);
231		return;
232	}
233
234	index = btohs(bt_get_unaligned(&rp->index));
235	if (index > max_index) {
236		error("Unexpected index %u in read info complete", index);
237		return;
238	}
239
240	info = &controllers[index];
241	info->type = rp->type;
242	bacpy(&info->bdaddr, &rp->bdaddr);
243	memcpy(info->features, rp->features, 8);
244	info->manufacturer = btohs(bt_get_unaligned(&rp->manufacturer));
245	info->hci_ver = rp->hci_ver;
246	info->hci_rev = btohs(bt_get_unaligned(&rp->hci_rev));
247
248	ba2str(&info->bdaddr, addr);
249	DBG("hci%u addr %s type %u manufacturer %d hci ver %d:%d",
250				index, addr, info->type, info->manufacturer,
251				info->hci_ver, info->hci_rev);
252
253	read_mode(sk, index);
254}
255
256static void read_mode_complete(int sk, void *buf, size_t len)
257{
258	struct mgmt_rp_read_mode *rp = buf;
259	struct controller_info *info;
260	uint16_t index;
261
262	if (len < sizeof(*rp)) {
263		error("Too small read mode complete event (%zu != %zu)",
264							len, sizeof(*rp));
265		return;
266	}
267
268	if (rp->status != 0) {
269		error("Reading controller mode failed: %s (%u)",
270					strerror(rp->status), rp->status);
271		return;
272	}
273
274	index = btohs(bt_get_unaligned(&rp->index));
275	if (index > max_index) {
276		error("Unexpected index %u in read mode complete", index);
277		return;
278	}
279
280	info = &controllers[index];
281	info->enabled = rp->enabled ? TRUE : FALSE;
282	info->mode = rp->mode;
283
284	DBG("hci%u enabled %u mode %u", index, info->enabled, info->mode);
285
286	manager_register_adapter(index, info->enabled);
287
288	if (info->enabled)
289		manager_start_adapter(index);
290}
291
292static void mgmt_cmd_complete(int sk, void *buf, size_t len)
293{
294	struct mgmt_ev_cmd_complete *ev = buf;
295	uint16_t opcode;
296
297	DBG("");
298
299	if (len < sizeof(*ev)) {
300		error("Too small management command complete event packet");
301		return;
302	}
303
304	opcode = btohs(bt_get_unaligned(&ev->opcode));
305
306	switch (opcode) {
307	case MGMT_OP_READ_VERSION:
308		read_version_complete(sk, ev->data, len - sizeof(*ev));
309		break;
310	case MGMT_OP_READ_INDEX_LIST:
311		read_index_list_complete(sk, ev->data, len - sizeof(*ev));
312		break;
313	case MGMT_OP_READ_INFO:
314		read_info_complete(sk, ev->data, len - sizeof(*ev));
315		break;
316	case MGMT_OP_READ_MODE:
317		read_mode_complete(sk, ev->data, len - sizeof(*ev));
318		break;
319	default:
320		error("Unknown command complete for opcode %u", opcode);
321		break;
322	}
323}
324
325static void mgmt_cmd_status(int sk, void *buf, size_t len)
326{
327	struct mgmt_ev_cmd_status *ev = buf;
328	uint16_t opcode;
329
330	if (len < sizeof(*ev)) {
331		error("Too small management command status event packet");
332		return;
333	}
334
335	opcode = btohs(bt_get_unaligned(&ev->opcode));
336
337	DBG("status %u opcode %u", ev->status, opcode);
338}
339
340static void mgmt_controller_error(int sk, void *buf, size_t len)
341{
342	struct mgmt_ev_controller_error *ev = buf;
343	uint16_t index;
344
345	if (len < sizeof(*ev)) {
346		error("Too small management controller error event packet");
347		return;
348	}
349
350	index = btohs(bt_get_unaligned(&ev->index));
351
352	DBG("index %u error_code %u", index, ev->error_code);
353}
354
355static gboolean mgmt_event(GIOChannel *io, GIOCondition cond, gpointer user_data)
356{
357	char buf[MGMT_BUF_SIZE];
358	struct mgmt_hdr *hdr = (void *) buf;
359	int sk;
360	ssize_t ret;
361	uint16_t len, opcode;
362
363	DBG("cond %d", cond);
364
365	if (cond & G_IO_NVAL)
366		return FALSE;
367
368	sk = g_io_channel_unix_get_fd(io);
369
370	if (cond & (G_IO_ERR | G_IO_HUP)) {
371		error("Error on management socket");
372		return FALSE;
373	}
374
375	ret = read(sk, buf, sizeof(buf));
376	if (ret < 0) {
377		error("Unable to read from management socket: %s (%d)",
378						strerror(errno), errno);
379		return TRUE;
380	}
381
382	DBG("Received %zd bytes from management socket", ret);
383
384	if (ret < MGMT_HDR_SIZE) {
385		error("Too small Management packet");
386		return TRUE;
387	}
388
389	opcode = btohs(bt_get_unaligned(&hdr->opcode));
390	len = btohs(bt_get_unaligned(&hdr->len));
391
392	if (ret != MGMT_HDR_SIZE + len) {
393		error("Packet length mismatch. ret %zd len %u", ret, len);
394		return TRUE;
395	}
396
397	switch (opcode) {
398	case MGMT_EV_CMD_COMPLETE:
399		mgmt_cmd_complete(sk, buf + MGMT_HDR_SIZE, len);
400		break;
401	case MGMT_EV_CMD_STATUS:
402		mgmt_cmd_status(sk, buf + MGMT_HDR_SIZE, len);
403		break;
404	case MGMT_EV_CONTROLLER_ERROR:
405		mgmt_controller_error(sk, buf + MGMT_HDR_SIZE, len);
406		break;
407	case MGMT_EV_INDEX_ADDED:
408		mgmt_index_added(sk, buf + MGMT_HDR_SIZE, len);
409		break;
410	case MGMT_EV_INDEX_REMOVED:
411		mgmt_index_removed(sk, buf + MGMT_HDR_SIZE, len);
412		break;
413	default:
414		error("Unknown Management opcode %u", opcode);
415		break;
416	}
417
418	return TRUE;
419}
420
421static int mgmt_setup(void)
422{
423	struct mgmt_hdr hdr;
424	struct sockaddr_hci addr;
425	GIOChannel *io;
426	GIOCondition condition;
427	int dd, err;
428
429	dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
430	if (dd < 0)
431		return -errno;
432
433	memset(&addr, 0, sizeof(addr));
434	addr.hci_family = AF_BLUETOOTH;
435	addr.hci_dev = HCI_DEV_NONE;
436	addr.hci_channel = HCI_CHANNEL_CONTROL;
437
438	if (bind(dd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
439		err = -errno;
440		goto fail;
441	}
442
443	memset(&hdr, 0, sizeof(hdr));
444	hdr.opcode = MGMT_OP_READ_VERSION;
445	if (write(dd, &hdr, sizeof(hdr)) < 0) {
446		err = -errno;
447		goto fail;
448	}
449
450	io = g_io_channel_unix_new(dd);
451	condition = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
452	mgmt_watch = g_io_add_watch(io, condition, mgmt_event, NULL);
453	g_io_channel_unref(io);
454
455	mgmt_sock = dd;
456
457	info("Bluetooth Management interface initialized");
458
459	return 0;
460
461fail:
462	close(dd);
463	return err;
464}
465
466static void mgmt_cleanup(void)
467{
468	g_free(controllers);
469	controllers = NULL;
470	max_index = -1;
471
472	if (mgmt_sock >= 0) {
473		close(mgmt_sock);
474		mgmt_sock = -1;
475	}
476
477	if (mgmt_watch > 0) {
478		g_source_remove(mgmt_watch);
479		mgmt_watch = 0;
480	}
481}
482
483static int mgmt_start(int index)
484{
485	DBG("index %d", index);
486	return -ENOSYS;
487}
488
489static int mgmt_stop(int index)
490{
491	DBG("index %d", index);
492	return -ENOSYS;
493}
494
495static int mgmt_powered(int index, gboolean powered)
496{
497	DBG("index %d powered %d", index, powered);
498	return -ENOSYS;
499}
500
501static int mgmt_connectable(int index)
502{
503	DBG("index %d", index);
504	return -ENOSYS;
505}
506
507static int mgmt_discoverable(int index)
508{
509	DBG("index %d", index);
510	return -ENOSYS;
511}
512
513static int mgmt_set_class(int index, uint32_t class)
514{
515	DBG("index %d class %u", index, class);
516	return -ENOSYS;
517}
518
519static int mgmt_set_limited_discoverable(int index, uint32_t class,
520							gboolean limited)
521{
522	DBG("index %d class %u, limited %d", index, class, limited);
523	return -ENOSYS;
524}
525
526static int mgmt_start_inquiry(int index, uint8_t length, gboolean periodic)
527{
528	DBG("index %d length %u periodic %d", index, length, periodic);
529	return -ENOSYS;
530}
531
532static int mgmt_stop_inquiry(int index)
533{
534	DBG("index %d", index);
535	return -ENOSYS;
536}
537
538static int mgmt_start_scanning(int index)
539{
540	DBG("index %d", index);
541	return -ENOSYS;
542}
543
544static int mgmt_stop_scanning(int index)
545{
546	DBG("index %d", index);
547	return -ENOSYS;
548}
549
550static int mgmt_resolve_name(int index, bdaddr_t *bdaddr)
551{
552	char addr[18];
553
554	ba2str(bdaddr, addr);
555	DBG("index %d addr %s", index, addr);
556
557	return -ENOSYS;
558}
559
560static int mgmt_set_name(int index, const char *name)
561{
562	DBG("index %d, name %s", index, name);
563	return -ENOSYS;
564}
565
566static int mgmt_read_name(int index)
567{
568	DBG("index %d", index);
569	return -ENOSYS;
570}
571
572static int mgmt_cancel_resolve_name(int index, bdaddr_t *bdaddr)
573{
574	char addr[18];
575
576	ba2str(bdaddr, addr);
577	DBG("index %d addr %s", index, addr);
578
579	return -ENOSYS;
580}
581
582static int mgmt_fast_connectable(int index, gboolean enable)
583{
584	DBG("index %d enable %d", index, enable);
585	return -ENOSYS;
586}
587
588static int mgmt_read_clock(int index, int handle, int which, int timeout,
589					uint32_t *clock, uint16_t *accuracy)
590{
591	DBG("index %d handle %d which %d timeout %d", index, handle,
592							which, timeout);
593	return -ENOSYS;
594}
595
596static int mgmt_conn_handle(int index, const bdaddr_t *bdaddr, int *handle)
597{
598	char addr[18];
599
600	ba2str(bdaddr, addr);
601	DBG("index %d addr %s", index, addr);
602
603	return -ENOSYS;
604}
605
606static int mgmt_write_eir_data(int index, uint8_t *data)
607{
608	DBG("index %d", index);
609	return -ENOSYS;
610}
611
612static int mgmt_read_bdaddr(int index, bdaddr_t *bdaddr)
613{
614	char addr[18];
615	struct controller_info *info = &controllers[index];
616
617	ba2str(&info->bdaddr, addr);
618	DBG("index %d addr %s", index, addr);
619
620	if (!info->valid)
621		return -ENODEV;
622
623	bacpy(bdaddr, &info->bdaddr);
624
625	return 0;
626}
627
628static int mgmt_set_event_mask(int index, uint8_t *events, size_t count)
629{
630	DBG("index %d", index);
631	return -ENOSYS;
632}
633
634static int mgmt_write_inq_mode(int index, uint8_t mode)
635{
636	DBG("index %d mode %u", index, mode);
637	return -ENOSYS;
638}
639
640static int mgmt_read_inq_tx_pwr(int index)
641{
642	DBG("index %d", index);
643	return -ENOSYS;
644}
645
646static int mgmt_block_device(int index, bdaddr_t *bdaddr)
647{
648	char addr[18];
649
650	ba2str(bdaddr, addr);
651	DBG("index %d addr %s", index, addr);
652
653	return -ENOSYS;
654}
655
656static int mgmt_unblock_device(int index, bdaddr_t *bdaddr)
657{
658	char addr[18];
659
660	ba2str(bdaddr, addr);
661	DBG("index %d addr %s", index, addr);
662
663	return -ENOSYS;
664}
665
666static int mgmt_get_conn_list(int index, GSList **conns)
667{
668	DBG("index %d", index);
669	return -ENOSYS;
670}
671
672static int mgmt_read_local_version(int index, struct hci_version *ver)
673{
674	DBG("index %d", index);
675	return -ENOSYS;
676}
677
678static int mgmt_read_local_features(int index, uint8_t *features)
679{
680	DBG("index %d", index);
681	return -ENOSYS;
682}
683
684static int mgmt_read_local_ext_features(int index)
685{
686	DBG("index %d", index);
687	return -ENOSYS;
688}
689
690static int mgmt_init_ssp_mode(int index, uint8_t *mode)
691{
692	DBG("index %d", index);
693	return -ENOSYS;
694}
695
696static int mgmt_read_link_policy(int index)
697{
698	DBG("index %d", index);
699	return -ENOSYS;
700}
701
702static int mgmt_disconnect(int index, uint16_t handle)
703{
704	DBG("index %d handle %u", index, handle);
705	return -ENOSYS;
706}
707
708static int mgmt_remove_bonding(int index, bdaddr_t *bdaddr)
709{
710	char addr[18];
711
712	ba2str(bdaddr, addr);
713	DBG("index %d addr %s", index, addr);
714
715	return -ENOSYS;
716}
717
718static int mgmt_request_authentication(int index, uint16_t handle,
719							uint8_t *status)
720{
721	DBG("index %d handle %u", index, handle);
722	return -ENOSYS;
723}
724
725static int mgmt_pincode_reply(int index, bdaddr_t *bdaddr, const char *pin)
726{
727	char addr[18];
728
729	ba2str(bdaddr, addr);
730	DBG("index %d addr %s pin %s", index, addr, pin);
731
732	return -ENOSYS;
733}
734
735static int mgmt_confirm_reply(int index, bdaddr_t *bdaddr, gboolean success)
736{
737	char addr[18];
738
739	ba2str(bdaddr, addr);
740	DBG("index %d addr %s success %d", index, addr, success);
741
742	return -ENOSYS;
743}
744
745static int mgmt_passkey_reply(int index, bdaddr_t *bdaddr, uint32_t passkey)
746{
747	char addr[18];
748
749	ba2str(bdaddr, addr);
750	DBG("index %d addr %s passkey %06u", index, addr, passkey);
751
752	return -ENOSYS;
753}
754
755static int mgmt_get_auth_info(int index, bdaddr_t *bdaddr, uint8_t *auth)
756{
757	char addr[18];
758
759	ba2str(bdaddr, addr);
760	DBG("index %d addr %s", index, addr);
761
762	return -ENOSYS;
763}
764
765static int mgmt_read_scan_enable(int index)
766{
767	DBG("index %d", index);
768	return -ENOSYS;
769}
770
771static int mgmt_read_ssp_mode(int index)
772{
773	DBG("index %d", index);
774	return -ENOSYS;
775}
776
777static int mgmt_write_le_host(int index, uint8_t le, uint8_t simul)
778{
779	DBG("index %d le %u simul %u", index, le, simul);
780	return -ENOSYS;
781}
782
783static int mgmt_get_remote_version(int index, uint16_t handle,
784							gboolean delayed)
785{
786	DBG("index %d handle %u delayed %d", index, handle, delayed);
787	return -ENOSYS;
788}
789
790static int mgmt_encrypt_link(int index, bdaddr_t *dst, bt_hci_result_t cb,
791							gpointer user_data)
792{
793	char addr[18];
794
795	ba2str(dst, addr);
796	DBG("index %d addr %s", index, addr);
797
798	return -ENOSYS;
799}
800
801static struct btd_adapter_ops mgmt_ops = {
802	.setup = mgmt_setup,
803	.cleanup = mgmt_cleanup,
804	.start = mgmt_start,
805	.stop = mgmt_stop,
806	.set_powered = mgmt_powered,
807	.set_connectable = mgmt_connectable,
808	.set_discoverable = mgmt_discoverable,
809	.set_limited_discoverable = mgmt_set_limited_discoverable,
810	.start_inquiry = mgmt_start_inquiry,
811	.stop_inquiry = mgmt_stop_inquiry,
812	.start_scanning = mgmt_start_scanning,
813	.stop_scanning = mgmt_stop_scanning,
814	.resolve_name = mgmt_resolve_name,
815	.cancel_resolve_name = mgmt_cancel_resolve_name,
816	.set_name = mgmt_set_name,
817	.read_name = mgmt_read_name,
818	.set_class = mgmt_set_class,
819	.set_fast_connectable = mgmt_fast_connectable,
820	.read_clock = mgmt_read_clock,
821	.get_conn_handle = mgmt_conn_handle,
822	.write_eir_data = mgmt_write_eir_data,
823	.read_bdaddr = mgmt_read_bdaddr,
824	.set_event_mask = mgmt_set_event_mask,
825	.write_inq_mode = mgmt_write_inq_mode,
826	.read_inq_tx_pwr = mgmt_read_inq_tx_pwr,
827	.block_device = mgmt_block_device,
828	.unblock_device = mgmt_unblock_device,
829	.get_conn_list = mgmt_get_conn_list,
830	.read_local_version = mgmt_read_local_version,
831	.read_local_features = mgmt_read_local_features,
832	.read_local_ext_features = mgmt_read_local_ext_features,
833	.init_ssp_mode = mgmt_init_ssp_mode,
834	.read_link_policy = mgmt_read_link_policy,
835	.disconnect = mgmt_disconnect,
836	.remove_bonding = mgmt_remove_bonding,
837	.request_authentication = mgmt_request_authentication,
838	.pincode_reply = mgmt_pincode_reply,
839	.confirm_reply = mgmt_confirm_reply,
840	.passkey_reply = mgmt_passkey_reply,
841	.get_auth_info = mgmt_get_auth_info,
842	.read_scan_enable = mgmt_read_scan_enable,
843	.read_ssp_mode = mgmt_read_ssp_mode,
844	.write_le_host = mgmt_write_le_host,
845	.get_remote_version = mgmt_get_remote_version,
846	.encrypt_link = mgmt_encrypt_link,
847};
848
849static int mgmt_init(void)
850{
851	return btd_register_adapter_ops(&mgmt_ops, TRUE);
852}
853
854static void mgmt_exit(void)
855{
856	btd_adapter_cleanup_ops(&mgmt_ops);
857}
858
859BLUETOOTH_PLUGIN_DEFINE(mgmtops, VERSION,
860		BLUETOOTH_PLUGIN_PRIORITY_LOW, mgmt_init, mgmt_exit)
861