diff options
author | Thomas Huth <thuth@redhat.com> | 2019-11-20 10:10:13 +0100 |
---|---|---|
committer | Thomas Huth <thuth@redhat.com> | 2019-12-17 09:01:14 +0100 |
commit | 1d4ffe8dc77cbc9aafe8bcf514ca0e43f85aaae3 (patch) | |
tree | 7f4ae35a4b2b3aa47c889478fab9c95562fa67f1 | |
parent | 43d68d0a94ef13058f6479b3dd490169a9a62966 (diff) |
Remove the core bluetooth code
It's been deprecated since QEMU v3.1. We've explicitly asked in the
deprecation message that people should speak up on qemu-devel in case
they are still actively using the bluetooth part of QEMU, but nobody
ever replied that they are really still using it.
I've tried it on my own to use this bluetooth subsystem for one of my
guests, but I was also not able to get it running anymore: When I was
trying to pass-through a real bluetooth device, either the guest did
not see the device at all, or the guest crashed.
Even worse for the emulated device: When running
qemu-system-x86_64 -bt device:keyboard
QEMU crashes once you hit a key.
So it seems like the bluetooth stack is not only neglected, it is
completely bitrotten, as far as I can tell. The only attention that
this code got during the past years were some CVEs that have been
spotted there. So this code is a burden for the developers, without
any real benefit anymore. Time to remove it.
Note: hw/bt/Kconfig only gets cleared but not removed here yet.
Otherwise there is a problem with the *-softmmu/config-devices.mak.d
dependency files - they still contain a reference to this file which
gets evaluated first on some build hosts, before the file gets
properly recreated. To avoid breaking these builders, we still need
the file around for some time. It will get removed in a couple of
weeks instead.
Message-Id: <20191120091014.16883-4-thuth@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
-rw-r--r-- | Makefile.objs | 2 | ||||
-rw-r--r-- | bt-host.c | 198 | ||||
-rw-r--r-- | bt-vhci.c | 167 | ||||
-rwxr-xr-x | configure | 31 | ||||
-rw-r--r-- | hw/Kconfig | 1 | ||||
-rw-r--r-- | hw/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/bt/Kconfig | 2 | ||||
-rw-r--r-- | hw/bt/Makefile.objs | 3 | ||||
-rw-r--r-- | hw/bt/core.c | 143 | ||||
-rw-r--r-- | hw/bt/hci-csr.c | 512 | ||||
-rw-r--r-- | hw/bt/hci.c | 2263 | ||||
-rw-r--r-- | hw/bt/hid.c | 553 | ||||
-rw-r--r-- | hw/bt/l2cap.c | 1367 | ||||
-rw-r--r-- | hw/bt/sdp.c | 989 | ||||
-rw-r--r-- | include/hw/bt.h | 2177 | ||||
-rw-r--r-- | include/sysemu/bt.h | 20 | ||||
-rw-r--r-- | qemu-deprecated.texi | 7 | ||||
-rw-r--r-- | qemu-options.hx | 79 | ||||
-rw-r--r-- | vl.c | 136 |
19 files changed, 0 insertions, 8651 deletions
diff --git a/Makefile.objs b/Makefile.objs index 11ba1a36bd..4e4d6ddbb3 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -65,8 +65,6 @@ common-obj-y += replay/ common-obj-y += ui/ common-obj-m += ui/ -common-obj-y += bt-host.o bt-vhci.o -bt-host.o-cflags := $(BLUEZ_CFLAGS) common-obj-y += dma-helpers.o common-obj-y += vl.o diff --git a/bt-host.c b/bt-host.c deleted file mode 100644 index 2f8f631c25..0000000000 --- a/bt-host.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Wrap a host Bluetooth HCI socket in a struct HCIInfo. - * - * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/bt.h" -#include "qemu/main-loop.h" - -#ifndef _WIN32 -# include <sys/ioctl.h> -# include <sys/uio.h> -# ifdef CONFIG_BLUEZ -# include <bluetooth/bluetooth.h> -# include <bluetooth/hci.h> -# include <bluetooth/hci_lib.h> -# else -# include "hw/bt.h" -# define HCI_MAX_FRAME_SIZE 1028 -# endif - -struct bt_host_hci_s { - struct HCIInfo hci; - int fd; - - uint8_t hdr[HCI_MAX_FRAME_SIZE]; - int len; -}; - -static void bt_host_send(struct HCIInfo *hci, - int type, const uint8_t *data, int len) -{ - struct bt_host_hci_s *s = (struct bt_host_hci_s *) hci; - uint8_t pkt = type; - struct iovec iv[2]; - - iv[0].iov_base = (void *)&pkt; - iv[0].iov_len = 1; - iv[1].iov_base = (void *) data; - iv[1].iov_len = len; - - while (writev(s->fd, iv, 2) < 0) { - if (errno != EAGAIN && errno != EINTR) { - fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", - errno); - return; - } - } -} - -static void bt_host_cmd(struct HCIInfo *hci, const uint8_t *data, int len) -{ - bt_host_send(hci, HCI_COMMAND_PKT, data, len); -} - -static void bt_host_acl(struct HCIInfo *hci, const uint8_t *data, int len) -{ - bt_host_send(hci, HCI_ACLDATA_PKT, data, len); -} - -static void bt_host_sco(struct HCIInfo *hci, const uint8_t *data, int len) -{ - bt_host_send(hci, HCI_SCODATA_PKT, data, len); -} - -static void bt_host_read(void *opaque) -{ - struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; - uint8_t *pkt; - int pktlen; - - /* Seems that we can't read only the header first and then the amount - * of data indicated in the header because Linux will discard everything - * that's not been read in one go. */ - s->len = read(s->fd, s->hdr, sizeof(s->hdr)); - - if (s->len < 0) { - fprintf(stderr, "qemu: error %i reading HCI frame\n", errno); - return; - } - - pkt = s->hdr; - while (s->len --) - switch (*pkt ++) { - case HCI_EVENT_PKT: - if (s->len < 2) - goto bad_pkt; - - pktlen = MIN(pkt[1] + 2, s->len); - s->hci.evt_recv(s->hci.opaque, pkt, pktlen); - s->len -= pktlen; - pkt += pktlen; - - /* TODO: if this is an Inquiry Result event, it's also - * interpreted by Linux kernel before we received it, possibly - * we should clean the kernel Inquiry cache through - * ioctl(s->fd, HCI_INQUIRY, ...). */ - break; - - case HCI_ACLDATA_PKT: - if (s->len < 4) - goto bad_pkt; - - pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len); - s->hci.acl_recv(s->hci.opaque, pkt, pktlen); - s->len -= pktlen; - pkt += pktlen; - break; - - case HCI_SCODATA_PKT: - if (s->len < 3) - goto bad_pkt; - - pktlen = MIN(pkt[2] + 3, s->len); - s->len -= pktlen; - pkt += pktlen; - break; - - default: - bad_pkt: - fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]); - } -} - -static int bt_host_bdaddr_set(struct HCIInfo *hci, const uint8_t *bd_addr) -{ - return -ENOTSUP; -} - -struct HCIInfo *bt_host_hci(const char *id) -{ - struct bt_host_hci_s *s; - int fd = -1; -# ifdef CONFIG_BLUEZ - int dev_id = hci_devid(id); - struct hci_filter flt; - - if (dev_id < 0) { - fprintf(stderr, "qemu: `%s' not available\n", id); - return 0; - } - - fd = hci_open_dev(dev_id); - - /* XXX: can we ensure nobody else has the device opened? */ -# endif - - if (fd < 0) { - fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", - id, strerror(errno), errno); - return NULL; - } - -# ifdef CONFIG_BLUEZ - hci_filter_clear(&flt); - hci_filter_all_ptypes(&flt); - hci_filter_all_events(&flt); - - if (qemu_setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { - fprintf(stderr, "qemu: Can't set HCI filter on socket (%i)\n", errno); - return 0; - } -# endif - - s = g_malloc0(sizeof(struct bt_host_hci_s)); - s->fd = fd; - s->hci.cmd_send = bt_host_cmd; - s->hci.sco_send = bt_host_sco; - s->hci.acl_send = bt_host_acl; - s->hci.bdaddr_set = bt_host_bdaddr_set; - - qemu_set_fd_handler(s->fd, bt_host_read, NULL, s); - - return &s->hci; -} -#else -struct HCIInfo *bt_host_hci(const char *id) -{ - fprintf(stderr, "qemu: bluetooth passthrough not supported (yet)\n"); - - return 0; -} -#endif diff --git a/bt-vhci.c b/bt-vhci.c deleted file mode 100644 index 886e146743..0000000000 --- a/bt-vhci.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Support for host VHCIs inside qemu scatternets. - * - * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "sysemu/bt.h" -#include "hw/bt.h" -#include "qemu/main-loop.h" - -#define VHCI_DEV "/dev/vhci" -#define VHCI_UDEV "/dev/hci_vhci" - -struct bt_vhci_s { - int fd; - struct HCIInfo *info; - - uint8_t hdr[4096]; - int len; -}; - -static void vhci_read(void *opaque) -{ - struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; - uint8_t *pkt; - int pktlen; - - /* Seems that we can't read only the header first and then the amount - * of data indicated in the header because Linux will discard everything - * that's not been read in one go. */ - s->len = read(s->fd, s->hdr, sizeof(s->hdr)); - - if (s->len < 0) { - fprintf(stderr, "qemu: error %i reading the PDU\n", errno); - return; - } - - pkt = s->hdr; - while (s->len --) - switch (*pkt ++) { - case HCI_COMMAND_PKT: - if (s->len < 3) - goto bad_pkt; - - pktlen = MIN(pkt[2] + 3, s->len); - s->info->cmd_send(s->info, pkt, pktlen); - s->len -= pktlen; - pkt += pktlen; - break; - - case HCI_ACLDATA_PKT: - if (s->len < 4) - goto bad_pkt; - - pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len); - s->info->acl_send(s->info, pkt, pktlen); - s->len -= pktlen; - pkt += pktlen; - break; - - case HCI_SCODATA_PKT: - if (s->len < 3) - goto bad_pkt; - - pktlen = MIN(pkt[2] + 3, s->len); - s->info->sco_send(s->info, pkt, pktlen); - s->len -= pktlen; - pkt += pktlen; - break; - - default: - bad_pkt: - fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]); - } -} - -static void vhci_host_send(void *opaque, - int type, const uint8_t *data, int len) -{ - struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; -#if 0 - uint8_t pkt = type; - struct iovec iv[2]; - - iv[0].iov_base = &pkt; - iv[0].iov_len = 1; - iv[1].iov_base = (void *) data; - iv[1].iov_len = len; - - while (writev(s->fd, iv, 2) < 0) - if (errno != EAGAIN && errno != EINTR) { - fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", - errno); - return; - } -#else - /* Apparently VHCI wants us to write everything in one chunk :-( */ - static uint8_t buf[4096]; - - buf[0] = type; - memcpy(buf + 1, data, len); - - while (write(s->fd, buf, len + 1) < 0) - if (errno != EAGAIN && errno != EINTR) { - fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", - errno); - return; - } -#endif -} - -static void vhci_out_hci_packet_event(void *opaque, - const uint8_t *data, int len) -{ - vhci_host_send(opaque, HCI_EVENT_PKT, data, len); -} - -static void vhci_out_hci_packet_acl(void *opaque, - const uint8_t *data, int len) -{ - vhci_host_send(opaque, HCI_ACLDATA_PKT, data, len); -} - -void bt_vhci_init(struct HCIInfo *info) -{ - struct bt_vhci_s *s; - int err[2]; - int fd; - - fd = open(VHCI_DEV, O_RDWR); - err[0] = errno; - if (fd < 0) { - fd = open(VHCI_UDEV, O_RDWR); - err[1] = errno; - } - - if (fd < 0) { - fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", - VHCI_DEV, strerror(err[0]), err[0]); - fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", - VHCI_UDEV, strerror(err[1]), err[1]); - exit(-1); - } - - s = g_malloc0(sizeof(struct bt_vhci_s)); - s->fd = fd; - s->info = info ?: qemu_next_hci(); - s->info->opaque = s; - s->info->evt_recv = vhci_out_hci_packet_event; - s->info->acl_recv = vhci_out_hci_packet_acl; - - qemu_set_fd_handler(s->fd, vhci_read, NULL, s); -} @@ -349,7 +349,6 @@ unset target_list_exclude # Distributions want to ensure that several features are compiled in, and it # is impossible without a --enable-foo that exits if a feature is not found. -bluez="" brlapi="" curl="" curses="" @@ -1151,10 +1150,6 @@ for opt do ;; --enable-brlapi) brlapi="yes" ;; - --disable-bluez) bluez="no" - ;; - --enable-bluez) bluez="yes" - ;; --disable-kvm) kvm="no" ;; --enable-kvm) kvm="yes" @@ -1762,7 +1757,6 @@ disabled with --disable-FEATURE, default is enabled if available: curl curl connectivity membarrier membarrier system call (for Linux 4.14+ or Windows) fdt fdt device tree - bluez bluez stack connectivity kvm KVM acceleration support hax HAX acceleration support hvf Hypervisor.framework acceleration support @@ -3666,26 +3660,6 @@ EOF fi # test "$curl" ########################################## -# bluez support probe -if test "$bluez" != "no" ; then - cat > $TMPC << EOF -#include <bluetooth/bluetooth.h> -int main(void) { return bt_error(0); } -EOF - bluez_cflags=$($pkg_config --cflags bluez 2>/dev/null) - bluez_libs=$($pkg_config --libs bluez 2>/dev/null) - if compile_prog "$bluez_cflags" "$bluez_libs" ; then - bluez=yes - libs_softmmu="$bluez_libs $libs_softmmu" - else - if test "$bluez" = "yes" ; then - feature_not_found "bluez" "Install bluez-libs/libbluetooth devel" - fi - bluez="no" - fi -fi - -########################################## # glib support probe glib_req_ver=2.48 @@ -6493,7 +6467,6 @@ if test "$xen" = "yes" ; then echo "xen ctrl version $xen_ctrl_version" fi echo "brlapi support $brlapi" -echo "bluez support $bluez" echo "Documentation $docs" echo "PIE $pie" echo "vde support $vde" @@ -6917,10 +6890,6 @@ if test "$brlapi" = "yes" ; then echo "CONFIG_BRLAPI=y" >> $config_host_mak echo "BRLAPI_LIBS=$brlapi_libs" >> $config_host_mak fi -if test "$bluez" = "yes" ; then - echo "CONFIG_BLUEZ=y" >> $config_host_mak - echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak -fi if test "$gtk" = "yes" ; then echo "CONFIG_GTK=m" >> $config_host_mak echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak diff --git a/hw/Kconfig b/hw/Kconfig index b9685b3944..ecf491bf04 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -4,7 +4,6 @@ source acpi/Kconfig source adc/Kconfig source audio/Kconfig source block/Kconfig -source bt/Kconfig source char/Kconfig source core/Kconfig source display/Kconfig diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 66eef20561..660e2b4373 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -5,7 +5,6 @@ devices-dirs-y += acpi/ devices-dirs-y += adc/ devices-dirs-y += audio/ devices-dirs-y += block/ -devices-dirs-y += bt/ devices-dirs-y += char/ devices-dirs-y += cpu/ devices-dirs-y += display/ diff --git a/hw/bt/Kconfig b/hw/bt/Kconfig index 554a9ee75e..e69de29bb2 100644 --- a/hw/bt/Kconfig +++ b/hw/bt/Kconfig @@ -1,2 +0,0 @@ -config BLUETOOTH - bool diff --git a/hw/bt/Makefile.objs b/hw/bt/Makefile.objs deleted file mode 100644 index 867a7d2e8a..0000000000 --- a/hw/bt/Makefile.objs +++ /dev/null @@ -1,3 +0,0 @@ -common-obj-y += core.o l2cap.o sdp.o hci.o hid.o -common-obj-y += hci-csr.o - diff --git a/hw/bt/core.c b/hw/bt/core.c deleted file mode 100644 index dfb196e2a4..0000000000 --- a/hw/bt/core.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Convenience functions for bluetooth. - * - * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "sysemu/bt.h" -#include "hw/bt.h" - -/* Slave implementations can ignore this */ -static void bt_dummy_lmp_mode_change(struct bt_link_s *link) -{ -} - -/* Slaves should never receive these PDUs */ -static void bt_dummy_lmp_connection_complete(struct bt_link_s *link) -{ - if (link->slave->reject_reason) - error_report("%s: stray LMP_not_accepted received, fixme", __func__); - else - error_report("%s: stray LMP_accepted received, fixme", __func__); - exit(-1); -} - -static void bt_dummy_lmp_disconnect_master(struct bt_link_s *link) -{ - error_report("%s: stray LMP_detach received, fixme", __func__); - exit(-1); -} - -static void bt_dummy_lmp_acl_resp(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - error_report("%s: stray ACL response PDU, fixme", __func__); - exit(-1); -} - -/* Slaves that don't hold any additional per link state can use these */ -static void bt_dummy_lmp_connection_request(struct bt_link_s *req) -{ - struct bt_link_s *link = g_malloc0(sizeof(struct bt_link_s)); - - link->slave = req->slave; - link->host = req->host; - - req->host->reject_reason = 0; - req->host->lmp_connection_complete(link); -} - -static void bt_dummy_lmp_disconnect_slave(struct bt_link_s *link) -{ - g_free(link); -} - -static void bt_dummy_destroy(struct bt_device_s *device) -{ - bt_device_done(device); - g_free(device); -} - -static int bt_dev_idx = 0; - -void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net) -{ - memset(dev, 0, sizeof(*dev)); - dev->inquiry_scan = 1; - dev->page_scan = 1; - - dev->bd_addr.b[0] = bt_dev_idx & 0xff; - dev->bd_addr.b[1] = bt_dev_idx >> 8; - dev->bd_addr.b[2] = 0xd0; - dev->bd_addr.b[3] = 0xba; - dev->bd_addr.b[4] = 0xbe; - dev->bd_addr.b[5] = 0xba; - bt_dev_idx ++; - - /* Simple slave-only devices need to implement only .lmp_acl_data */ - dev->lmp_connection_complete = bt_dummy_lmp_connection_complete; - dev->lmp_disconnect_master = bt_dummy_lmp_disconnect_master; - dev->lmp_acl_resp = bt_dummy_lmp_acl_resp; - dev->lmp_mode_change = bt_dummy_lmp_mode_change; - dev->lmp_connection_request = bt_dummy_lmp_connection_request; - dev->lmp_disconnect_slave = bt_dummy_lmp_disconnect_slave; - - dev->handle_destroy = bt_dummy_destroy; - - dev->net = net; - dev->next = net->slave; - net->slave = dev; -} - -void bt_device_done(struct bt_device_s *dev) -{ - struct bt_device_s **p = &dev->net->slave; - - while (*p && *p != dev) - p = &(*p)->next; - if (*p != dev) { - error_report("%s: bad bt device \"%s\"", __func__, - dev->lmp_name ?: "(null)"); - exit(-1); - } - - *p = dev->next; -} - -static struct bt_vlan_s { - struct bt_scatternet_s net; - int id; - struct bt_vlan_s *next; -} *first_bt_vlan; - -/* find or alloc a new bluetooth "VLAN" */ -struct bt_scatternet_s *qemu_find_bt_vlan(int id) -{ - struct bt_vlan_s **pvlan, *vlan; - for (vlan = first_bt_vlan; vlan != NULL; vlan = vlan->next) { - if (vlan->id == id) - return &vlan->net; - } - vlan = g_malloc0(sizeof(struct bt_vlan_s)); - vlan->id = id; - pvlan = &first_bt_vlan; - while (*pvlan != NULL) - pvlan = &(*pvlan)->next; - *pvlan = vlan; - return &vlan->net; -} diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c deleted file mode 100644 index 3d60654f44..0000000000 --- a/hw/bt/hci-csr.c +++ /dev/null @@ -1,512 +0,0 @@ -/* - * Bluetooth serial HCI transport. - * CSR41814 HCI with H4p vendor extensions. - * - * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/module.h" -#include "chardev/char-serial.h" -#include "qemu/timer.h" -#include "qemu/bswap.h" -#include "hw/irq.h" -#include "sysemu/bt.h" -#include "hw/bt.h" -#include "qapi/error.h" - -struct csrhci_s { - Chardev parent; - int enable; - qemu_irq *pins; - int pin_state; - int modem_state; -#define FIFO_LEN 4096 - int out_start; - int out_len; - int out_size; - uint8_t outfifo[FIFO_LEN * 2]; - uint8_t inpkt[FIFO_LEN]; - enum { - CSR_HDR_LEN, - CSR_DATA_LEN, - CSR_DATA - } in_state; - int in_len; - int in_hdr; - int in_needed; - QEMUTimer *out_tm; - int64_t baud_delay; - - bdaddr_t bd_addr; - struct HCIInfo *hci; -}; - -#define TYPE_CHARDEV_HCI "chardev-hci" -#define HCI_CHARDEV(obj) OBJECT_CHECK(struct csrhci_s, (obj), TYPE_CHARDEV_HCI) - -/* H4+ packet types */ -enum { - H4_CMD_PKT = 1, - H4_ACL_PKT = 2, - H4_SCO_PKT = 3, - H4_EVT_PKT = 4, - H4_NEG_PKT = 6, - H4_ALIVE_PKT = 7, -}; - -/* CSR41814 negotiation start magic packet */ -static const uint8_t csrhci_neg_packet[] = { - H4_NEG_PKT, 10, - 0x00, 0xa0, 0x01, 0x00, 0x00, - 0x4c, 0x00, 0x96, 0x00, 0x00, -}; - -/* CSR41814 vendor-specific command OCFs */ -enum { - OCF_CSR_SEND_FIRMWARE = 0x000, -}; - -static inline void csrhci_fifo_wake(struct csrhci_s *s) -{ - Chardev *chr = CHARDEV(s); - - if (!s->enable || !s->out_len) - return; - - /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */ - if (qemu_chr_be_can_write(chr)) { - qemu_chr_be_write(chr, s->outfifo + s->out_start++, 1); - s->out_len--; - if (s->out_start >= s->out_size) { - s->out_start = 0; - s->out_size = FIFO_LEN; - } - } - - if (s->out_len) - timer_mod(s->out_tm, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->baud_delay); -} - -#define csrhci_out_packetz(s, len) memset(csrhci_out_packet(s, len), 0, len) -static uint8_t *csrhci_out_packet(struct csrhci_s *s, int len) -{ - int off = s->out_start + s->out_len; - - /* TODO: do the padding here, i.e. align len */ - s->out_len += len; - - if (off < FIFO_LEN) { - if (off + len > FIFO_LEN && (s->out_size = off + len) > FIFO_LEN * 2) { - error_report("%s: can't alloc %i bytes", __func__, len); - exit(-1); - } - return s->outfifo + off; - } - - if (s->out_len > s->out_size) { - error_report("%s: can't alloc %i bytes", __func__, len); - exit(-1); - } - - return s->outfifo + off - s->out_size; -} - -static inline uint8_t *csrhci_out_packet_csr(struct csrhci_s *s, - int type, int len) -{ - uint8_t *ret = csrhci_out_packetz(s, len + 2); - - *ret ++ = type; - *ret ++ = len; - - return ret; -} - -static inline uint8_t *csrhci_out_packet_event(struct csrhci_s *s, - int evt, int len) -{ - uint8_t *ret = csrhci_out_packetz(s, - len + 1 + sizeof(struct hci_event_hdr)); - - *ret ++ = H4_EVT_PKT; - ((struct hci_event_hdr *) ret)->evt = evt; - ((struct hci_event_hdr *) ret)->plen = len; - - return ret + sizeof(struct hci_event_hdr); -} - -static void csrhci_in_packet_vendor(struct csrhci_s *s, int ocf, - uint8_t *data, int len) -{ - int offset; - uint8_t *rpkt; - - switch (ocf) { - case OCF_CSR_SEND_FIRMWARE: - /* Check if this is the bd_address packet */ - if (len >= 18 + 8 && data[12] == 0x01 && data[13] == 0x00) { - offset = 18; - s->bd_addr.b[0] = data[offset + 7]; /* Beyond cmd packet end(!?) */ - s->bd_addr.b[1] = data[offset + 6]; - s->bd_addr.b[2] = data[offset + 4]; - s->bd_addr.b[3] = data[offset + 0]; - s->bd_addr.b[4] = data[offset + 3]; - s->bd_addr.b[5] = data[offset + 2]; - - s->hci->bdaddr_set(s->hci, s->bd_addr.b); - error_report("%s: bd_address loaded from firmware: " - "%02x:%02x:%02x:%02x:%02x:%02x", __func__, - s->bd_addr.b[0], s->bd_addr.b[1], s->bd_addr.b[2], - s->bd_addr.b[3], s->bd_addr.b[4], s->bd_addr.b[5]); - } - - rpkt = csrhci_out_packet_event(s, EVT_VENDOR, 11); - /* Status bytes: no error */ - rpkt[9] = 0x00; - rpkt[10] = 0x00; - break; - - default: - error_report("%s: got a bad CMD packet", __func__); - return; - } - - csrhci_fifo_wake(s); -} - -static void csrhci_in_packet(struct csrhci_s *s, uint8_t *pkt) -{ - uint8_t *rpkt; - int opc; - - switch (*pkt ++) { - case H4_CMD_PKT: - opc = le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode); - if (cmd_opcode_ogf(opc) == OGF_VENDOR_CMD) { - csrhci_in_packet_vendor(s, cmd_opcode_ocf(opc), - pkt + sizeof(struct hci_command_hdr), - s->in_len - sizeof(struct hci_command_hdr) - 1); - return; - } - - /* TODO: if the command is OCF_READ_LOCAL_COMMANDS or the likes, - * we need to send it to the HCI layer and then add our supported - * commands to the returned mask (such as OGF_VENDOR_CMD). With - * bt-hci.c we could just have hooks for this kind of commands but - * we can't with bt-host.c. */ - - s->hci->cmd_send(s->hci, pkt, s->in_len - 1); - break; - - case H4_EVT_PKT: - goto bad_pkt; - - case H4_ACL_PKT: - s->hci->acl_send(s->hci, pkt, s->in_len - 1); - break; - - case H4_SCO_PKT: - s->hci->sco_send(s->hci, pkt, s->in_len - 1); - break; - - case H4_NEG_PKT: - if (s->in_hdr != sizeof(csrhci_neg_packet) || - memcmp(pkt - 1, csrhci_neg_packet, s->in_hdr)) { - error_report("%s: got a bad NEG packet", __func__); - return; - } - pkt += 2; - - rpkt = csrhci_out_packet_csr(s, H4_NEG_PKT, 10); - - *rpkt ++ = 0x20; /* Operational settings negotiation Ok */ - memcpy(rpkt, pkt, 7); rpkt += 7; - *rpkt ++ = 0xff; - *rpkt = 0xff; - break; - - case H4_ALIVE_PKT: - if (s->in_hdr != 4 || pkt[1] != 0x55 || pkt[2] != 0x00) { - error_report("%s: got a bad ALIVE packet", __func__); - return; - } - - rpkt = csrhci_out_packet_csr(s, H4_ALIVE_PKT, 2); - - *rpkt ++ = 0xcc; - *rpkt = 0x00; - break; - - default: - bad_pkt: - /* TODO: error out */ - error_report("%s: got a bad packet", __func__); - break; - } - - csrhci_fifo_wake(s); -} - -static int csrhci_header_len(const uint8_t *pkt) -{ - switch (pkt[0]) { - case H4_CMD_PKT: - return HCI_COMMAND_HDR_SIZE; - case H4_EVT_PKT: - return HCI_EVENT_HDR_SIZE; - case H4_ACL_PKT: - return HCI_ACL_HDR_SIZE; - case H4_SCO_PKT: - return HCI_SCO_HDR_SIZE; - case H4_NEG_PKT: - return pkt[1] + 1; - case H4_ALIVE_PKT: - return 3; - } - - exit(-1); -} - -static int csrhci_data_len(const uint8_t *pkt) -{ - switch (*pkt ++) { - case H4_CMD_PKT: - /* It seems that vendor-specific command packets for H4+ are all - * one byte longer than indicated in the standard header. */ - if (le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode) == 0xfc00) - return (((struct hci_command_hdr *) pkt)->plen + 1) & ~1; - - return ((struct hci_command_hdr *) pkt)->plen; - case H4_EVT_PKT: - return ((struct hci_event_hdr *) pkt)->plen; - case H4_ACL_PKT: - return le16_to_cpu(((struct hci_acl_hdr *) pkt)->dlen); - case H4_SCO_PKT: - return ((struct hci_sco_hdr *) pkt)->dlen; - case H4_NEG_PKT: - case H4_ALIVE_PKT: - return 0; - } - - exit(-1); -} - -static void csrhci_ready_for_next_inpkt(struct csrhci_s *s) -{ - s->in_state = CSR_HDR_LEN; - s->in_len = 0; - s->in_needed = 2; - s->in_hdr = INT_MAX; -} - -static int csrhci_write(struct Chardev *chr, - const uint8_t *buf, int len) -{ - struct csrhci_s *s = (struct csrhci_s *)chr; - int total = 0; - - if (!s->enable) - return 0; - - for (;;) { - int cnt = MIN(len, s->in_needed - s->in_len); - if (cnt) { - memcpy(s->inpkt + s->in_len, buf, cnt); - s->in_len += cnt; - buf += cnt; - len -= cnt; - total += cnt; - } - - if (s->in_len < s->in_needed) { - break; - } - - if (s->in_state == CSR_HDR_LEN) { - s->in_hdr = csrhci_header_len(s->inpkt) + 1; - assert(s->in_hdr >= s->in_needed); - s->in_needed = s->in_hdr; - s->in_state = CSR_DATA_LEN; - continue; - } - - if (s->in_state == CSR_DATA_LEN) { - s->in_needed += csrhci_data_len(s->inpkt); - /* hci_acl_hdr could specify more than 4096 bytes, so assert. */ - assert(s->in_needed <= sizeof(s->inpkt)); - s->in_state = CSR_DATA; - continue; - } - - if (s->in_state == CSR_DATA) { - csrhci_in_packet(s, s->inpkt); - csrhci_ready_for_next_inpkt(s); - } - } - - return total; -} - -static void csrhci_out_hci_packet_event(void *opaque, - const uint8_t *data, int len) -{ - struct csrhci_s *s = (struct csrhci_s *) opaque; - uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1); /* Align */ - - *pkt ++ = H4_EVT_PKT; - memcpy(pkt, data, len); - - csrhci_fifo_wake(s); -} - -static void csrhci_out_hci_packet_acl(void *opaque, - const uint8_t *data, int len) -{ - struct csrhci_s *s = (struct csrhci_s *) opaque; - uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1); /* Align */ - - *pkt ++ = H4_ACL_PKT; - pkt[len & ~1] = 0; - memcpy(pkt, data, len); - - csrhci_fifo_wake(s); -} - -static int csrhci_ioctl(struct Chardev *chr, int cmd, void *arg) -{ - QEMUSerialSetParams *ssp; - struct csrhci_s *s = (struct csrhci_s *) chr; - int prev_state = s->modem_state; - - switch (cmd) { - case CHR_IOCTL_SERIAL_SET_PARAMS: - ssp = (QEMUSerialSetParams *) arg; - s->baud_delay = NANOSECONDS_PER_SECOND / ssp->speed; - /* Moments later... (but shorter than 100ms) */ - s->modem_state |= CHR_TIOCM_CTS; - break; - - case CHR_IOCTL_SERIAL_GET_TIOCM: - *(int *) arg = s->modem_state; - break; - - case CHR_IOCTL_SERIAL_SET_TIOCM: - s->modem_state = *(int *) arg; - if (~s->modem_state & prev_state & CHR_TIOCM_RTS) - s->modem_state &= ~CHR_TIOCM_CTS; - break; - - default: - return -ENOTSUP; - } - return 0; -} - -static void csrhci_reset(struct csrhci_s *s) -{ - s->out_len = 0; - s->out_size = FIFO_LEN; - csrhci_ready_for_next_inpkt(s); - s->baud_delay = NANOSECONDS_PER_SECOND; - s->enable = 0; - - s->modem_state = 0; - /* After a while... (but sooner than 10ms) */ - s->modem_state |= CHR_TIOCM_CTS; - - memset(&s->bd_addr, 0, sizeof(bdaddr_t)); -} - -static void csrhci_out_tick(void *opaque) -{ - csrhci_fifo_wake((struct csrhci_s *) opaque); -} - -static void csrhci_pins(void *opaque, int line, int level) -{ - struct csrhci_s *s = (struct csrhci_s *) opaque; - int state = s->pin_state; - - s->pin_state &= ~(1 << line); - s->pin_state |= (!!level) << line; - - if ((state & ~s->pin_state) & (1 << csrhci_pin_reset)) { - /* TODO: Disappear from lower layers */ - csrhci_reset(s); - } - - if (s->pin_state == 3 && state != 3) { - s->enable = 1; - /* TODO: Wake lower layers up */ - } -} - -qemu_irq *csrhci_pins_get(Chardev *chr) -{ - struct csrhci_s *s = (struct csrhci_s *) chr; - - return s->pins; -} - -static void csrhci_open(Chardev *chr, - ChardevBackend *backend, - bool *be_opened, - Error **errp) -{ - struct csrhci_s *s = HCI_CHARDEV(chr); - - s->hci = qemu_next_hci(); - s->hci->opaque = s; - s->hci->evt_recv = csrhci_out_hci_packet_event; - s->hci->acl_recv = csrhci_out_hci_packet_acl; - - s->out_tm = timer_new_ns(QEMU_CLOCK_VIRTUAL, csrhci_out_tick, s); - s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins); - csrhci_reset(s); - *be_opened = false; -} - -static void char_hci_class_init(ObjectClass *oc, void *data) -{ - ChardevClass *cc = CHARDEV_CLASS(oc); - - cc->internal = true; - cc->open = csrhci_open; - cc->chr_write = csrhci_write; - cc->chr_ioctl = csrhci_ioctl; -} - -static const TypeInfo char_hci_type_info = { - .name = TYPE_CHARDEV_HCI, - .parent = TYPE_CHARDEV, - .instance_size = sizeof(struct csrhci_s), - .class_init = char_hci_class_init, -}; - -Chardev *uart_hci_init(void) -{ - return qemu_chardev_new(NULL, TYPE_CHARDEV_HCI, - NULL, NULL, &error_abort); -} - -static void register_types(void) -{ - type_register_static(&char_hci_type_info); -} - -type_init(register_types); diff --git a/hw/bt/hci.c b/hw/bt/hci.c deleted file mode 100644 index c7958f6c35..0000000000 --- a/hw/bt/hci.c +++ /dev/null @@ -1,2263 +0,0 @@ -/* - * QEMU Bluetooth HCI logic. - * - * Copyright (C) 2007 OpenMoko, Inc. - * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "sysemu/bt.h" -#include "hw/bt.h" -#include "qapi/qmp/qerror.h" -#include "sysemu/replay.h" -#include "qemu/cutils.h" - -struct bt_hci_s { - uint8_t *(*evt_packet)(void *opaque); - void (*evt_submit)(void *opaque, int len); - void *opaque; - uint8_t evt_buf[256]; - - uint8_t acl_buf[4096]; - int acl_len; - - uint16_t asb_handle; - uint16_t psb_handle; - - int last_cmd; /* Note: Always little-endian */ - - struct bt_device_s *conn_req_host; - - struct { - int inquire; - int periodic; - int responses_left; - int responses; - QEMUTimer *inquiry_done; - QEMUTimer *inquiry_next; - int inquiry_length; - int inquiry_period; - int inquiry_mode; - -#define HCI_HANDLE_OFFSET 0x20 -#define HCI_HANDLES_MAX 0x10 - struct bt_hci_master_link_s { - struct bt_link_s *link; - void (*lmp_acl_data)(struct bt_link_s *link, - const uint8_t *data, int start, int len); - QEMUTimer *acl_mode_timer; - } handle[HCI_HANDLES_MAX]; - uint32_t role_bmp; - int last_handle; - int connecting; - bdaddr_t awaiting_bdaddr[HCI_HANDLES_MAX]; - } lm; - - uint8_t event_mask[8]; - uint16_t voice_setting; /* Notw: Always little-endian */ - uint16_t conn_accept_tout; - QEMUTimer *conn_accept_timer; - - struct HCIInfo info; - struct bt_device_s device; - - Error *replay_blocker; -}; - -#define DEFAULT_RSSI_DBM 20 - -#define hci_from_info(ptr) container_of((ptr), struct bt_hci_s, info) -#define hci_from_device(ptr) container_of((ptr), struct bt_hci_s, device) - -struct bt_hci_link_s { - struct bt_link_s btlink; - uint16_t handle; /* Local */ -}; - -/* LMP layer emulation */ -#if 0 -static void bt_submit_lmp(struct bt_device_s *bt, int length, uint8_t *data) -{ - int resp, resplen, error, op, tr; - uint8_t respdata[17]; - - if (length < 1) - return; - - tr = *data & 1; - op = *(data ++) >> 1; - resp = LMP_ACCEPTED; - resplen = 2; - respdata[1] = op; - error = 0; - length --; - - if (op >= 0x7c) { /* Extended opcode */ - op |= *(data ++) << 8; - resp = LMP_ACCEPTED_EXT; - resplen = 4; - respdata[0] = op >> 8; - respdata[1] = op & 0xff; - length --; - } - - switch (op) { - case LMP_ACCEPTED: - /* data[0] Op code - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_ACCEPTED_EXT: - /* data[0] Escape op code - * data[1] Extended op code - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_NOT_ACCEPTED: - /* data[0] Op code - * data[1] Error code - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_NOT_ACCEPTED_EXT: - /* data[0] Op code - * data[1] Extended op code - * data[2] Error code - */ - if (length < 3) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_HOST_CONNECTION_REQ: - break; - - case LMP_SETUP_COMPLETE: - resp = LMP_SETUP_COMPLETE; - resplen = 1; - bt->setup = 1; - break; - - case LMP_DETACH: - /* data[0] Error code - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - bt->setup = 0; - resp = 0; - break; - - case LMP_SUPERVISION_TIMEOUT: - /* data[0,1] Supervision timeout - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_QUALITY_OF_SERVICE: - resp = 0; - /* Fall through */ - case LMP_QOS_REQ: - /* data[0,1] Poll interval - * data[2] N(BC) - */ - if (length < 3) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_MAX_SLOT: - resp = 0; - /* Fall through */ - case LMP_MAX_SLOT_REQ: - /* data[0] Max slots - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_AU_RAND: - case LMP_IN_RAND: - case LMP_COMB_KEY: - /* data[0-15] Random number - */ - if (length < 16) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - if (op == LMP_AU_RAND) { - if (bt->key_present) { - resp = LMP_SRES; - resplen = 5; - /* XXX: [Part H] Section 6.1 on page 801 */ - } else { - error = HCI_PIN_OR_KEY_MISSING; - goto not_accepted; - } - } else if (op == LMP_IN_RAND) { - error = HCI_PAIRING_NOT_ALLOWED; - goto not_accepted; - } else { - /* XXX: [Part H] Section 3.2 on page 779 */ - resp = LMP_UNIT_KEY; - resplen = 17; - memcpy(respdata + 1, bt->key, 16); - - error = HCI_UNIT_LINK_KEY_USED; - goto not_accepted; - } - break; - - case LMP_UNIT_KEY: - /* data[0-15] Key - */ - if (length < 16) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - memcpy(bt->key, data, 16); - bt->key_present = 1; - break; - - case LMP_SRES: - /* data[0-3] Authentication response - */ - if (length < 4) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_CLKOFFSET_REQ: - resp = LMP_CLKOFFSET_RES; - resplen = 3; - respdata[1] = 0x33; - respdata[2] = 0x33; - break; - - case LMP_CLKOFFSET_RES: - /* data[0,1] Clock offset - * (Slave to master only) - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_VERSION_REQ: - case LMP_VERSION_RES: - /* data[0] VersNr - * data[1,2] CompId - * data[3,4] SubVersNr - */ - if (length < 5) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - if (op == LMP_VERSION_REQ) { - resp = LMP_VERSION_RES; - resplen = 6; - respdata[1] = 0x20; - respdata[2] = 0xff; - respdata[3] = 0xff; - respdata[4] = 0xff; - respdata[5] = 0xff; - } else - resp = 0; - break; - - case LMP_FEATURES_REQ: - case LMP_FEATURES_RES: - /* data[0-7] Features - */ - if (length < 8) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - if (op == LMP_FEATURES_REQ) { - resp = LMP_FEATURES_RES; - resplen = 9; - respdata[1] = (bt->lmp_caps >> 0) & 0xff; - respdata[2] = (bt->lmp_caps >> 8) & 0xff; - respdata[3] = (bt->lmp_caps >> 16) & 0xff; - respdata[4] = (bt->lmp_caps >> 24) & 0xff; - respdata[5] = (bt->lmp_caps >> 32) & 0xff; - respdata[6] = (bt->lmp_caps >> 40) & 0xff; - respdata[7] = (bt->lmp_caps >> 48) & 0xff; - respdata[8] = (bt->lmp_caps >> 56) & 0xff; - } else - resp = 0; - break; - - case LMP_NAME_REQ: - /* data[0] Name offset - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = LMP_NAME_RES; - resplen = 17; - respdata[1] = data[0]; - respdata[2] = strlen(bt->lmp_name); - memset(respdata + 3, 0x00, 14); - if (respdata[2] > respdata[1]) - memcpy(respdata + 3, bt->lmp_name + respdata[1], - respdata[2] - respdata[1]); - break; - - case LMP_NAME_RES: - /* data[0] Name offset - * data[1] Name length - * data[2-15] Name fragment - */ - if (length < 16) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - default: - error = HCI_UNKNOWN_LMP_PDU; - /* Fall through */ - not_accepted: - if (op >> 8) { - resp = LMP_NOT_ACCEPTED_EXT; - resplen = 5; - respdata[0] = op >> 8; - respdata[1] = op & 0xff; - respdata[2] = error; - } else { - resp = LMP_NOT_ACCEPTED; - resplen = 3; - respdata[0] = op & 0xff; - respdata[1] = error; - } - } - - if (resp == 0) - return; - - if (resp >> 8) { - respdata[0] = resp >> 8; - respdata[1] = resp & 0xff; - } else - respdata[0] = resp & 0xff; - - respdata[0] <<= 1; - respdata[0] |= tr; -} - -static void bt_submit_raw_acl(struct bt_piconet_s *net, int length, uint8_t *data) -{ - struct bt_device_s *slave; - if (length < 1) - return; - - slave = 0; -#if 0 - slave = net->slave; -#endif - - switch (data[0] & 3) { - case LLID_ACLC: - bt_submit_lmp(slave, length - 1, data + 1); - break; - case LLID_ACLU_START: -#if 0 - bt_sumbit_l2cap(slave, length - 1, data + 1, (data[0] >> 2) & 1); - breka; -#endif - default: - case LLID_ACLU_CONT: - break; - } -} -#endif - -/* HCI layer emulation */ - -/* Note: we could ignore endianness because unswapped handles will still - * be valid as connection identifiers for the guest - they don't have to - * be continuously allocated. We do it though, to preserve similar - * behaviour between hosts. Some things, like the BD_ADDR cannot be - * preserved though (for example if a real hci is used). */ -#define HNDL(raw) cpu_to_le16(raw) - -static const uint8_t bt_event_reserved_mask[8] = { - 0xff, 0x9f, 0xfb, 0xff, 0x07, 0x18, 0x00, 0x00, -}; - - -static void null_hci_send(struct HCIInfo *hci, const uint8_t *data, int len) -{ -} - -static int null_hci_addr_set(struct HCIInfo *hci, const uint8_t *bd_addr) -{ - return -ENOTSUP; -} - -struct HCIInfo null_hci = { - .cmd_send = null_hci_send, - .sco_send = null_hci_send, - .acl_send = null_hci_send, - .bdaddr_set = null_hci_addr_set, -}; - - -static inline uint8_t *bt_hci_event_start(struct bt_hci_s *hci, - int evt, int len) -{ - uint8_t *packet, mask; - int mask_byte; - - if (len > 255) { - error_report("%s: HCI event params too long (%ib)", __func__, len); - exit(-1); - } - - mask_byte = (evt - 1) >> 3; - mask = 1 << ((evt - 1) & 3); - if (mask & bt_event_reserved_mask[mask_byte] & ~hci->event_mask[mask_byte]) - return NULL; - - packet = hci->evt_packet(hci->opaque); - packet[0] = evt; - packet[1] = len; - - return &packet[2]; -} - -static inline void bt_hci_event(struct bt_hci_s *hci, int evt, - void *params, int len) -{ - uint8_t *packet = bt_hci_event_start(hci, evt, len); - - if (!packet) - return; - - if (len) - memcpy(packet, params, len); - - hci->evt_submit(hci->opaque, len + 2); -} - -static inline void bt_hci_event_status(struct bt_hci_s *hci, int status) -{ - evt_cmd_status params = { - .status = status, - .ncmd = 1, - .opcode = hci->last_cmd, - }; - - bt_hci_event(hci, EVT_CMD_STATUS, ¶ms, EVT_CMD_STATUS_SIZE); -} - -static inline void bt_hci_event_complete(struct bt_hci_s *hci, - void *ret, int len) -{ - uint8_t *packet = bt_hci_event_start(hci, EVT_CMD_COMPLETE, - len + EVT_CMD_COMPLETE_SIZE); - evt_cmd_complete *params = (evt_cmd_complete *) packet; - - if (!packet) - return; - - params->ncmd = 1; - params->opcode = hci->last_cmd; - if (len) - memcpy(&packet[EVT_CMD_COMPLETE_SIZE], ret, len); - - hci->evt_submit(hci->opaque, len + EVT_CMD_COMPLETE_SIZE + 2); -} - -static void bt_hci_inquiry_done(void *opaque) -{ - struct bt_hci_s *hci = (struct bt_hci_s *) opaque; - uint8_t status = HCI_SUCCESS; - - if (!hci->lm.periodic) - hci->lm.inquire = 0; - - /* The specification is inconsistent about this one. Page 565 reads - * "The event parameters of Inquiry Complete event will have a summary - * of the result from the Inquiry process, which reports the number of - * nearby Bluetooth devices that responded [so hci->responses].", but - * Event Parameters (see page 729) has only Status. */ - bt_hci_event(hci, EVT_INQUIRY_COMPLETE, &status, 1); -} - -static void bt_hci_inquiry_result_standard(struct bt_hci_s *hci, - struct bt_device_s *slave) -{ - inquiry_info params = { - .num_responses = 1, - .bdaddr = BAINIT(&slave->bd_addr), - .pscan_rep_mode = 0x00, /* R0 */ - .pscan_period_mode = 0x00, /* P0 - deprecated */ - .pscan_mode = 0x00, /* Standard scan - deprecated */ - .dev_class[0] = slave->class[0], - .dev_class[1] = slave->class[1], - .dev_class[2] = slave->class[2], - /* TODO: return the clkoff *differenece* */ - .clock_offset = slave->clkoff, /* Note: no swapping */ - }; - - bt_hci_event(hci, EVT_INQUIRY_RESULT, ¶ms, INQUIRY_INFO_SIZE); -} - -static void bt_hci_inquiry_result_with_rssi(struct bt_hci_s *hci, - struct bt_device_s *slave) -{ - inquiry_info_with_rssi params = { - .num_responses = 1, - .bdaddr = BAINIT(&slave->bd_addr), - .pscan_rep_mode = 0x00, /* R0 */ - .pscan_period_mode = 0x00, /* P0 - deprecated */ - .dev_class[0] = slave->class[0], - .dev_class[1] = slave->class[1], - .dev_class[2] = slave->class[2], - /* TODO: return the clkoff *differenece* */ - .clock_offset = slave->clkoff, /* Note: no swapping */ - .rssi = DEFAULT_RSSI_DBM, - }; - - bt_hci_event(hci, EVT_INQUIRY_RESULT_WITH_RSSI, - ¶ms, INQUIRY_INFO_WITH_RSSI_SIZE); -} - -static void bt_hci_inquiry_result(struct bt_hci_s *hci, - struct bt_device_s *slave) -{ - if (!slave->inquiry_scan || !hci->lm.responses_left) - return; - - hci->lm.responses_left --; - hci->lm.responses ++; - - switch (hci->lm.inquiry_mode) { - case 0x00: - bt_hci_inquiry_result_standard(hci, slave); - return; - case 0x01: - bt_hci_inquiry_result_with_rssi(hci, slave); - return; - default: - error_report("%s: bad inquiry mode %02x", __func__, - hci->lm.inquiry_mode); - exit(-1); - } -} - -static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period) -{ - timer_mod(timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (uint64_t)(period << 7) * 10000000); -} - -static void bt_hci_inquiry_start(struct bt_hci_s *hci, int length) -{ - struct bt_device_s *slave; - - hci->lm.inquiry_length = length; - for (slave = hci->device.net->slave; slave; slave = slave->next) - /* Don't uncover ourselves. */ - if (slave != &hci->device) - bt_hci_inquiry_result(hci, slave); - - /* TODO: register for a callback on a new device's addition to the - * scatternet so that if it's added before inquiry_length expires, - * an Inquiry Result is generated immediately. Alternatively re-loop - * through the devices on the inquiry_length expiration and report - * devices not seen before. */ - if (hci->lm.responses_left) - bt_hci_mod_timer_1280ms(hci->lm.inquiry_done, hci->lm.inquiry_length); - else - bt_hci_inquiry_done(hci); - - if (hci->lm.periodic) - bt_hci_mod_timer_1280ms(hci->lm.inquiry_next, hci->lm.inquiry_period); -} - -static void bt_hci_inquiry_next(void *opaque) -{ - struct bt_hci_s *hci = (struct bt_hci_s *) opaque; - - hci->lm.responses_left += hci->lm.responses; - hci->lm.responses = 0; - bt_hci_inquiry_start(hci, hci->lm.inquiry_length); -} - -static inline int bt_hci_handle_bad(struct bt_hci_s *hci, uint16_t handle) -{ - return !(handle & HCI_HANDLE_OFFSET) || - handle >= (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX) || - !hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; -} - -static inline int bt_hci_role_master(struct bt_hci_s *hci, uint16_t handle) -{ - return !!(hci->lm.role_bmp & (1 << (handle & ~HCI_HANDLE_OFFSET))); -} - -static inline struct bt_device_s *bt_hci_remote_dev(struct bt_hci_s *hci, - uint16_t handle) -{ - struct bt_link_s *link = hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; - - return bt_hci_role_master(hci, handle) ? link->slave : link->host; -} - -static void bt_hci_mode_tick(void *opaque); -static void bt_hci_lmp_link_establish(struct bt_hci_s *hci, - struct bt_link_s *link, int master) -{ - hci->lm.handle[hci->lm.last_handle].link = link; - - if (master) { - /* We are the master side of an ACL link */ - hci->lm.role_bmp |= 1 << hci->lm.last_handle; - - hci->lm.handle[hci->lm.last_handle].lmp_acl_data = - link->slave->lmp_acl_data; - } else { - /* We are the slave side of an ACL link */ - hci->lm.role_bmp &= ~(1 << hci->lm.last_handle); - - hci->lm.handle[hci->lm.last_handle].lmp_acl_data = - link->host->lmp_acl_resp; - } - - /* Mode */ - if (master) { - link->acl_mode = acl_active; - hci->lm.handle[hci->lm.last_handle].acl_mode_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_mode_tick, link); - } -} - -static void bt_hci_lmp_link_teardown(struct bt_hci_s *hci, uint16_t handle) -{ - handle &= ~HCI_HANDLE_OFFSET; - hci->lm.handle[handle].link = NULL; - - if (bt_hci_role_master(hci, handle)) { - timer_del(hci->lm.handle[handle].acl_mode_timer); - timer_free(hci->lm.handle[handle].acl_mode_timer); - } -} - -static int bt_hci_connect(struct bt_hci_s *hci, bdaddr_t *bdaddr) -{ - struct bt_device_s *slave; - struct bt_link_s link; - - for (slave = hci->device.net->slave; slave; slave = slave->next) - if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr)) - break; - if (!slave || slave == &hci->device) - return -ENODEV; - - bacpy(&hci->lm.awaiting_bdaddr[hci->lm.connecting ++], &slave->bd_addr); - - link.slave = slave; - link.host = &hci->device; - link.slave->lmp_connection_request(&link); /* Always last */ - - return 0; -} - -static void bt_hci_connection_reject(struct bt_hci_s *hci, - struct bt_device_s *host, uint8_t because) -{ - struct bt_link_s link = { - .slave = &hci->device, - .host = host, - /* Rest uninitialised */ - }; - - host->reject_reason = because; - host->lmp_connection_complete(&link); -} - -static void bt_hci_connection_reject_event(struct bt_hci_s *hci, - bdaddr_t *bdaddr) -{ - evt_conn_complete params; - - params.status = HCI_NO_CONNECTION; - params.handle = 0; - bacpy(¶ms.bdaddr, bdaddr); - params.link_type = ACL_LINK; - params.encr_mode = 0x00; /* Encryption not required */ - bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); -} - -static void bt_hci_connection_accept(struct bt_hci_s *hci, - struct bt_device_s *host) -{ - struct bt_hci_link_s *link = g_malloc0(sizeof(struct bt_hci_link_s)); - evt_conn_complete params; - uint16_t handle; - uint8_t status = HCI_SUCCESS; - int tries = HCI_HANDLES_MAX; - - /* Make a connection handle */ - do { - while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries) - hci->lm.last_handle &= HCI_HANDLES_MAX - 1; - handle = hci->lm.last_handle | HCI_HANDLE_OFFSET; - } while ((handle == hci->asb_handle || handle == hci->psb_handle) && - tries); - - if (!tries) { - g_free(link); - bt_hci_connection_reject(hci, host, HCI_REJECTED_LIMITED_RESOURCES); - status = HCI_NO_CONNECTION; - goto complete; - } - - link->btlink.slave = &hci->device; - link->btlink.host = host; - link->handle = handle; - - /* Link established */ - bt_hci_lmp_link_establish(hci, &link->btlink, 0); - -complete: - params.status = status; - params.handle = HNDL(handle); - bacpy(¶ms.bdaddr, &host->bd_addr); - params.link_type = ACL_LINK; - params.encr_mode = 0x00; /* Encryption not required */ - bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); - - /* Neets to be done at the very end because it can trigger a (nested) - * disconnected, in case the other and had cancelled the request - * locally. */ - if (status == HCI_SUCCESS) { - host->reject_reason = 0; - host->lmp_connection_complete(&link->btlink); - } -} - -static void bt_hci_lmp_connection_request(struct bt_link_s *link) -{ - struct bt_hci_s *hci = hci_from_device(link->slave); - evt_conn_request params; - - if (hci->conn_req_host) { - bt_hci_connection_reject(hci, link->host, - HCI_REJECTED_LIMITED_RESOURCES); - return; - } - hci->conn_req_host = link->host; - /* TODO: if masked and auto-accept, then auto-accept, - * if masked and not auto-accept, then auto-reject */ - /* TODO: kick the hci->conn_accept_timer, timeout after - * hci->conn_accept_tout * 0.625 msec */ - - bacpy(¶ms.bdaddr, &link->host->bd_addr); - memcpy(¶ms.dev_class, &link->host->class, sizeof(params.dev_class)); - params.link_type = ACL_LINK; - bt_hci_event(hci, EVT_CONN_REQUEST, ¶ms, EVT_CONN_REQUEST_SIZE); -} - -static void bt_hci_conn_accept_timeout(void *opaque) -{ - struct bt_hci_s *hci = (struct bt_hci_s *) opaque; - - if (!hci->conn_req_host) - /* Already accepted or rejected. If the other end cancelled the - * connection request then we still have to reject or accept it - * and then we'll get a disconnect. */ - return; - - /* TODO */ -} - -/* Remove from the list of devices which we wanted to connect to and - * are awaiting a response from. If the callback sees a response from - * a device which is not on the list it will assume it's a connection - * that's been cancelled by the host in the meantime and immediately - * try to detach the link and send a Connection Complete. */ -static int bt_hci_lmp_connection_ready(struct bt_hci_s *hci, - bdaddr_t *bdaddr) -{ - int i; - - for (i = 0; i < hci->lm.connecting; i ++) - if (!bacmp(&hci->lm.awaiting_bdaddr[i], bdaddr)) { - if (i < -- hci->lm.connecting) - bacpy(&hci->lm.awaiting_bdaddr[i], - &hci->lm.awaiting_bdaddr[hci->lm.connecting]); - return 0; - } - - return 1; -} - -static void bt_hci_lmp_connection_complete(struct bt_link_s *link) -{ - struct bt_hci_s *hci = hci_from_device(link->host); - evt_conn_complete params; - uint16_t handle; - uint8_t status = HCI_SUCCESS; - int tries = HCI_HANDLES_MAX; - - if (bt_hci_lmp_connection_ready(hci, &link->slave->bd_addr)) { - if (!hci->device.reject_reason) - link->slave->lmp_disconnect_slave(link); - handle = 0; - status = HCI_NO_CONNECTION; - goto complete; - } - - if (hci->device.reject_reason) { - handle = 0; - status = hci->device.reject_reason; - goto complete; - } - - /* Make a connection handle */ - do { - while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries) - hci->lm.last_handle &= HCI_HANDLES_MAX - 1; - handle = hci->lm.last_handle | HCI_HANDLE_OFFSET; - } while ((handle == hci->asb_handle || handle == hci->psb_handle) && - tries); - - if (!tries) { - link->slave->lmp_disconnect_slave(link); - status = HCI_NO_CONNECTION; - goto complete; - } - - /* Link established */ - link->handle = handle; - bt_hci_lmp_link_establish(hci, link, 1); - -complete: - params.status = status; - params.handle = HNDL(handle); - params.link_type = ACL_LINK; - bacpy(¶ms.bdaddr, &link->slave->bd_addr); - params.encr_mode = 0x00; /* Encryption not required */ - bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); -} - -static void bt_hci_disconnect(struct bt_hci_s *hci, - uint16_t handle, int reason) -{ - struct bt_link_s *btlink = - hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; - struct bt_hci_link_s *link; - evt_disconn_complete params; - - if (bt_hci_role_master(hci, handle)) { - btlink->slave->reject_reason = reason; - btlink->slave->lmp_disconnect_slave(btlink); - /* The link pointer is invalid from now on */ - - goto complete; - } - - btlink->host->reject_reason = reason; - btlink->host->lmp_disconnect_master(btlink); - - /* We are the slave, we get to clean this burden */ - link = (struct bt_hci_link_s *) btlink; - g_free(link); - -complete: - bt_hci_lmp_link_teardown(hci, handle); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.reason = HCI_CONNECTION_TERMINATED; - bt_hci_event(hci, EVT_DISCONN_COMPLETE, - ¶ms, EVT_DISCONN_COMPLETE_SIZE); -} - -/* TODO: use only one function */ -static void bt_hci_lmp_disconnect_host(struct bt_link_s *link) -{ - struct bt_hci_s *hci = hci_from_device(link->host); - uint16_t handle = link->handle; - evt_disconn_complete params; - - bt_hci_lmp_link_teardown(hci, handle); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.reason = hci->device.reject_reason; - bt_hci_event(hci, EVT_DISCONN_COMPLETE, - ¶ms, EVT_DISCONN_COMPLETE_SIZE); -} - -static void bt_hci_lmp_disconnect_slave(struct bt_link_s *btlink) -{ - struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; - struct bt_hci_s *hci = hci_from_device(btlink->slave); - uint16_t handle = link->handle; - evt_disconn_complete params; - - g_free(link); - - bt_hci_lmp_link_teardown(hci, handle); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.reason = hci->device.reject_reason; - bt_hci_event(hci, EVT_DISCONN_COMPLETE, - ¶ms, EVT_DISCONN_COMPLETE_SIZE); -} - -static int bt_hci_name_req(struct bt_hci_s *hci, bdaddr_t *bdaddr) -{ - struct bt_device_s *slave; - evt_remote_name_req_complete params; - - for (slave = hci->device.net->slave; slave; slave = slave->next) - if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr)) - break; - if (!slave) - return -ENODEV; - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - bacpy(¶ms.bdaddr, &slave->bd_addr); - pstrcpy(params.name, sizeof(params.name), slave->lmp_name ?: ""); - bt_hci_event(hci, EVT_REMOTE_NAME_REQ_COMPLETE, - ¶ms, EVT_REMOTE_NAME_REQ_COMPLETE_SIZE); - - return 0; -} - -static int bt_hci_features_req(struct bt_hci_s *hci, uint16_t handle) -{ - struct bt_device_s *slave; - evt_read_remote_features_complete params; - - if (bt_hci_handle_bad(hci, handle)) - return -ENODEV; - - slave = bt_hci_remote_dev(hci, handle); - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.features[0] = (slave->lmp_caps >> 0) & 0xff; - params.features[1] = (slave->lmp_caps >> 8) & 0xff; - params.features[2] = (slave->lmp_caps >> 16) & 0xff; - params.features[3] = (slave->lmp_caps >> 24) & 0xff; - params.features[4] = (slave->lmp_caps >> 32) & 0xff; - params.features[5] = (slave->lmp_caps >> 40) & 0xff; - params.features[6] = (slave->lmp_caps >> 48) & 0xff; - params.features[7] = (slave->lmp_caps >> 56) & 0xff; - bt_hci_event(hci, EVT_READ_REMOTE_FEATURES_COMPLETE, - ¶ms, EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE); - - return 0; -} - -static int bt_hci_version_req(struct bt_hci_s *hci, uint16_t handle) -{ - evt_read_remote_version_complete params; - - if (bt_hci_handle_bad(hci, handle)) - return -ENODEV; - - bt_hci_remote_dev(hci, handle); - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.lmp_ver = 0x03; - params.manufacturer = cpu_to_le16(0xa000); - params.lmp_subver = cpu_to_le16(0xa607); - bt_hci_event(hci, EVT_READ_REMOTE_VERSION_COMPLETE, - ¶ms, EVT_READ_REMOTE_VERSION_COMPLETE_SIZE); - - return 0; -} - -static int bt_hci_clkoffset_req(struct bt_hci_s *hci, uint16_t handle) -{ - struct bt_device_s *slave; - evt_read_clock_offset_complete params; - - if (bt_hci_handle_bad(hci, handle)) - return -ENODEV; - - slave = bt_hci_remote_dev(hci, handle); - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - /* TODO: return the clkoff *differenece* */ - params.clock_offset = slave->clkoff; /* Note: no swapping */ - bt_hci_event(hci, EVT_READ_CLOCK_OFFSET_COMPLETE, - ¶ms, EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE); - - return 0; -} - -static void bt_hci_event_mode(struct bt_hci_s *hci, struct bt_link_s *link, - uint16_t handle) -{ - evt_mode_change params = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - .mode = link->acl_mode, - .interval = cpu_to_le16(link->acl_interval), - }; - - bt_hci_event(hci, EVT_MODE_CHANGE, ¶ms, EVT_MODE_CHANGE_SIZE); -} - -static void bt_hci_lmp_mode_change_master(struct bt_hci_s *hci, - struct bt_link_s *link, int mode, uint16_t interval) -{ - link->acl_mode = mode; - link->acl_interval = interval; - - bt_hci_event_mode(hci, link, link->handle); - - link->slave->lmp_mode_change(link); -} - -static void bt_hci_lmp_mode_change_slave(struct bt_link_s *btlink) -{ - struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; - struct bt_hci_s *hci = hci_from_device(btlink->slave); - - bt_hci_event_mode(hci, btlink, link->handle); -} - -static int bt_hci_mode_change(struct bt_hci_s *hci, uint16_t handle, - int interval, int mode) -{ - struct bt_hci_master_link_s *link; - - if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle)) - return -ENODEV; - - link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET]; - if (link->link->acl_mode != acl_active) { - bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED); - return 0; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - - timer_mod(link->acl_mode_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - ((uint64_t)interval * 625) * 1000); - bt_hci_lmp_mode_change_master(hci, link->link, mode, interval); - - return 0; -} - -static int bt_hci_mode_cancel(struct bt_hci_s *hci, uint16_t handle, int mode) -{ - struct bt_hci_master_link_s *link; - - if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle)) - return -ENODEV; - - link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET]; - if (link->link->acl_mode != mode) { - bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED); - - return 0; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - - timer_del(link->acl_mode_timer); - bt_hci_lmp_mode_change_master(hci, link->link, acl_active, 0); - - return 0; -} - -static void bt_hci_mode_tick(void *opaque) -{ - struct bt_link_s *link = opaque; - struct bt_hci_s *hci = hci_from_device(link->host); - - bt_hci_lmp_mode_change_master(hci, link, acl_active, 0); -} - -static void bt_hci_reset(struct bt_hci_s *hci) -{ - hci->acl_len = 0; - hci->last_cmd = 0; - hci->lm.connecting = 0; - - hci->event_mask[0] = 0xff; - hci->event_mask[1] = 0xff; - hci->event_mask[2] = 0xff; - hci->event_mask[3] = 0xff; - hci->event_mask[4] = 0xff; - hci->event_mask[5] = 0x1f; - hci->event_mask[6] = 0x00; - hci->event_mask[7] = 0x00; - hci->device.inquiry_scan = 0; - hci->device.page_scan = 0; - g_free((void *) hci->device.lmp_name); - hci->device.lmp_name = NULL; - hci->device.class[0] = 0x00; - hci->device.class[1] = 0x00; - hci->device.class[2] = 0x00; - hci->voice_setting = 0x0000; - hci->conn_accept_tout = 0x1f40; - hci->lm.inquiry_mode = 0x00; - - hci->psb_handle = 0x000; - hci->asb_handle = 0x000; - - /* XXX: timer_del(sl->acl_mode_timer); for all links */ - timer_del(hci->lm.inquiry_done); - timer_del(hci->lm.inquiry_next); - timer_del(hci->conn_accept_timer); -} - -static void bt_hci_read_local_version_rp(struct bt_hci_s *hci) -{ - read_local_version_rp lv = { - .status = HCI_SUCCESS, - .hci_ver = 0x03, - .hci_rev = cpu_to_le16(0xa607), - .lmp_ver = 0x03, - .manufacturer = cpu_to_le16(0xa000), - .lmp_subver = cpu_to_le16(0xa607), - }; - - bt_hci_event_complete(hci, &lv, READ_LOCAL_VERSION_RP_SIZE); -} - -static void bt_hci_read_local_commands_rp(struct bt_hci_s *hci) -{ - read_local_commands_rp lc = { - .status = HCI_SUCCESS, - .commands = { - /* Keep updated! */ - /* Also, keep in sync with hci->device.lmp_caps in bt_new_hci */ - 0xbf, 0x80, 0xf9, 0x03, 0xb2, 0xc0, 0x03, 0xc3, - 0x00, 0x0f, 0x80, 0x00, 0xc0, 0x00, 0xe8, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }, - }; - - bt_hci_event_complete(hci, &lc, READ_LOCAL_COMMANDS_RP_SIZE); -} - -static void bt_hci_read_local_features_rp(struct bt_hci_s *hci) -{ - read_local_features_rp lf = { - .status = HCI_SUCCESS, - .features = { - (hci->device.lmp_caps >> 0) & 0xff, - (hci->device.lmp_caps >> 8) & 0xff, - (hci->device.lmp_caps >> 16) & 0xff, - (hci->device.lmp_caps >> 24) & 0xff, - (hci->device.lmp_caps >> 32) & 0xff, - (hci->device.lmp_caps >> 40) & 0xff, - (hci->device.lmp_caps >> 48) & 0xff, - (hci->device.lmp_caps >> 56) & 0xff, - }, - }; - - bt_hci_event_complete(hci, &lf, READ_LOCAL_FEATURES_RP_SIZE); -} - -static void bt_hci_read_local_ext_features_rp(struct bt_hci_s *hci, int page) -{ - read_local_ext_features_rp lef = { - .status = HCI_SUCCESS, - .page_num = page, - .max_page_num = 0x00, - .features = { - /* Keep updated! */ - 0x5f, 0x35, 0x85, 0x7e, 0x9b, 0x19, 0x00, 0x80, - }, - }; - if (page) - memset(lef.features, 0, sizeof(lef.features)); - - bt_hci_event_complete(hci, &lef, READ_LOCAL_EXT_FEATURES_RP_SIZE); -} - -static void bt_hci_read_buffer_size_rp(struct bt_hci_s *hci) -{ - read_buffer_size_rp bs = { - /* This can be made configurable, for one standard USB dongle HCI - * the four values are cpu_to_le16(0x0180), 0x40, - * cpu_to_le16(0x0008), cpu_to_le16(0x0008). */ - .status = HCI_SUCCESS, - .acl_mtu = cpu_to_le16(0x0200), - .sco_mtu = 0, - .acl_max_pkt = cpu_to_le16(0x0001), - .sco_max_pkt = cpu_to_le16(0x0000), - }; - - bt_hci_event_complete(hci, &bs, READ_BUFFER_SIZE_RP_SIZE); -} - -/* Deprecated in V2.0 (page 661) */ -static void bt_hci_read_country_code_rp(struct bt_hci_s *hci) -{ - read_country_code_rp cc ={ - .status = HCI_SUCCESS, - .country_code = 0x00, /* North America & Europe^1 and Japan */ - }; - - bt_hci_event_complete(hci, &cc, READ_COUNTRY_CODE_RP_SIZE); - - /* ^1. Except France, sorry */ -} - -static void bt_hci_read_bd_addr_rp(struct bt_hci_s *hci) -{ - read_bd_addr_rp ba = { - .status = HCI_SUCCESS, - .bdaddr = BAINIT(&hci->device.bd_addr), - }; - - bt_hci_event_complete(hci, &ba, READ_BD_ADDR_RP_SIZE); -} - -static int bt_hci_link_quality_rp(struct bt_hci_s *hci, uint16_t handle) -{ - read_link_quality_rp lq = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - .link_quality = 0xff, - }; - - if (bt_hci_handle_bad(hci, handle)) - lq.status = HCI_NO_CONNECTION; - - bt_hci_event_complete(hci, &lq, READ_LINK_QUALITY_RP_SIZE); - return 0; -} - -/* Generate a Command Complete event with only the Status parameter */ -static inline void bt_hci_event_complete_status(struct bt_hci_s *hci, - uint8_t status) -{ - bt_hci_event_complete(hci, &status, 1); -} - -static inline void bt_hci_event_complete_conn_cancel(struct bt_hci_s *hci, - uint8_t status, bdaddr_t *bd_addr) -{ - create_conn_cancel_rp params = { - .status = status, - .bdaddr = BAINIT(bd_addr), - }; - - bt_hci_event_complete(hci, ¶ms, CREATE_CONN_CANCEL_RP_SIZE); -} - -static inline void bt_hci_event_auth_complete(struct bt_hci_s *hci, - uint16_t handle) -{ - evt_auth_complete params = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - }; - - bt_hci_event(hci, EVT_AUTH_COMPLETE, ¶ms, EVT_AUTH_COMPLETE_SIZE); -} - -static inline void bt_hci_event_encrypt_change(struct bt_hci_s *hci, - uint16_t handle, uint8_t mode) -{ - evt_encrypt_change params = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - .encrypt = mode, - }; - - bt_hci_event(hci, EVT_ENCRYPT_CHANGE, ¶ms, EVT_ENCRYPT_CHANGE_SIZE); -} - -static inline void bt_hci_event_complete_name_cancel(struct bt_hci_s *hci, - bdaddr_t *bd_addr) -{ - remote_name_req_cancel_rp params = { - .status = HCI_INVALID_PARAMETERS, - .bdaddr = BAINIT(bd_addr), - }; - - bt_hci_event_complete(hci, ¶ms, REMOTE_NAME_REQ_CANCEL_RP_SIZE); -} - -static inline void bt_hci_event_read_remote_ext_features(struct bt_hci_s *hci, - uint16_t handle) -{ - evt_read_remote_ext_features_complete params = { - .status = HCI_UNSUPPORTED_FEATURE, - .handle = HNDL(handle), - /* Rest uninitialised */ - }; - - bt_hci_event(hci, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE, - ¶ms, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE); -} - -static inline void bt_hci_event_complete_lmp_handle(struct bt_hci_s *hci, - uint16_t handle) -{ - read_lmp_handle_rp params = { - .status = HCI_NO_CONNECTION, - .handle = HNDL(handle), - .reserved = 0, - /* Rest uninitialised */ - }; - - bt_hci_event_complete(hci, ¶ms, READ_LMP_HANDLE_RP_SIZE); -} - -static inline void bt_hci_event_complete_role_discovery(struct bt_hci_s *hci, - int status, uint16_t handle, int master) -{ - role_discovery_rp params = { - .status = status, - .handle = HNDL(handle), - .role = master ? 0x00 : 0x01, - }; - - bt_hci_event_complete(hci, ¶ms, ROLE_DISCOVERY_RP_SIZE); -} - -static inline void bt_hci_event_complete_flush(struct bt_hci_s *hci, - int status, uint16_t handle) -{ - flush_rp params = { - .status = status, - .handle = HNDL(handle), - }; - - bt_hci_event_complete(hci, ¶ms, FLUSH_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_local_name(struct bt_hci_s *hci) -{ - read_local_name_rp params; - params.status = HCI_SUCCESS; - memset(params.name, 0, sizeof(params.name)); - if (hci->device.lmp_name) - pstrcpy(params.name, sizeof(params.name), hci->device.lmp_name); - - bt_hci_event_complete(hci, ¶ms, READ_LOCAL_NAME_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_conn_accept_timeout( - struct bt_hci_s *hci) -{ - read_conn_accept_timeout_rp params = { - .status = HCI_SUCCESS, - .timeout = cpu_to_le16(hci->conn_accept_tout), - }; - - bt_hci_event_complete(hci, ¶ms, READ_CONN_ACCEPT_TIMEOUT_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_scan_enable(struct bt_hci_s *hci) -{ - read_scan_enable_rp params = { - .status = HCI_SUCCESS, - .enable = - (hci->device.inquiry_scan ? SCAN_INQUIRY : 0) | - (hci->device.page_scan ? SCAN_PAGE : 0), - }; - - bt_hci_event_complete(hci, ¶ms, READ_SCAN_ENABLE_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_local_class(struct bt_hci_s *hci) -{ - read_class_of_dev_rp params; - - params.status = HCI_SUCCESS; - memcpy(params.dev_class, hci->device.class, sizeof(params.dev_class)); - - bt_hci_event_complete(hci, ¶ms, READ_CLASS_OF_DEV_RP_SIZE); -} - -static inline void bt_hci_event_complete_voice_setting(struct bt_hci_s *hci) -{ - read_voice_setting_rp params = { - .status = HCI_SUCCESS, - .voice_setting = hci->voice_setting, /* Note: no swapping */ - }; - - bt_hci_event_complete(hci, ¶ms, READ_VOICE_SETTING_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_inquiry_mode( - struct bt_hci_s *hci) -{ - read_inquiry_mode_rp params = { - .status = HCI_SUCCESS, - .mode = hci->lm.inquiry_mode, - }; - - bt_hci_event_complete(hci, ¶ms, READ_INQUIRY_MODE_RP_SIZE); -} - -static inline void bt_hci_event_num_comp_pkts(struct bt_hci_s *hci, - uint16_t handle, int packets) -{ - uint16_t buf[EVT_NUM_COMP_PKTS_SIZE(1) / 2 + 1]; - evt_num_comp_pkts *params = (void *) ((uint8_t *) buf + 1); - - params->num_hndl = 1; - params->connection->handle = HNDL(handle); - params->connection->num_packets = cpu_to_le16(packets); - - bt_hci_event(hci, EVT_NUM_COMP_PKTS, params, EVT_NUM_COMP_PKTS_SIZE(1)); -} - -static void bt_submit_hci(struct HCIInfo *info, - const uint8_t *data, int length) -{ - struct bt_hci_s *hci = hci_from_info(info); - uint16_t cmd; - int paramlen, i; - - if (length < HCI_COMMAND_HDR_SIZE) - goto short_hci; - - memcpy(&hci->last_cmd, data, 2); - - cmd = (data[1] << 8) | data[0]; - paramlen = data[2]; - if (cmd_opcode_ogf(cmd) == 0 || cmd_opcode_ocf(cmd) == 0) /* NOP */ - return; - - data += HCI_COMMAND_HDR_SIZE; - length -= HCI_COMMAND_HDR_SIZE; - - if (paramlen > length) - return; - -#define PARAM(cmd, param) (((cmd##_cp *) data)->param) -#define PARAM16(cmd, param) lduw_le_p(&PARAM(cmd, param)) -#define PARAMHANDLE(cmd) PARAM16(cmd, handle) -#define LENGTH_CHECK(cmd) if (length < sizeof(cmd##_cp)) goto short_hci - /* Note: the supported commands bitmask in bt_hci_read_local_commands_rp - * needs to be updated every time a command is implemented here! */ - switch (cmd) { - case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY): - LENGTH_CHECK(inquiry); - - if (PARAM(inquiry, length) < 1) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->lm.inquire = 1; - hci->lm.periodic = 0; - hci->lm.responses_left = PARAM(inquiry, num_rsp) ?: INT_MAX; - hci->lm.responses = 0; - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_inquiry_start(hci, PARAM(inquiry, length)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL): - if (!hci->lm.inquire || hci->lm.periodic) { - fprintf(stderr, "%s: Inquiry Cancel should only be issued after " - "the Inquiry command has been issued, a Command " - "Status event has been received for the Inquiry " - "command, and before the Inquiry Complete event " - "occurs", __func__); - bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED); - break; - } - - hci->lm.inquire = 0; - timer_del(hci->lm.inquiry_done); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_PERIODIC_INQUIRY): - LENGTH_CHECK(periodic_inquiry); - - if (!(PARAM(periodic_inquiry, length) < - PARAM16(periodic_inquiry, min_period) && - PARAM16(periodic_inquiry, min_period) < - PARAM16(periodic_inquiry, max_period)) || - PARAM(periodic_inquiry, length) < 1 || - PARAM16(periodic_inquiry, min_period) < 2 || - PARAM16(periodic_inquiry, max_period) < 3) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->lm.inquire = 1; - hci->lm.periodic = 1; - hci->lm.responses_left = PARAM(periodic_inquiry, num_rsp); - hci->lm.responses = 0; - hci->lm.inquiry_period = PARAM16(periodic_inquiry, max_period); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - bt_hci_inquiry_start(hci, PARAM(periodic_inquiry, length)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_EXIT_PERIODIC_INQUIRY): - if (!hci->lm.inquire || !hci->lm.periodic) { - fprintf(stderr, "%s: Inquiry Cancel should only be issued after " - "the Inquiry command has been issued, a Command " - "Status event has been received for the Inquiry " - "command, and before the Inquiry Complete event " - "occurs", __func__); - bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED); - break; - } - hci->lm.inquire = 0; - timer_del(hci->lm.inquiry_done); - timer_del(hci->lm.inquiry_next); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN): - LENGTH_CHECK(create_conn); - - if (hci->lm.connecting >= HCI_HANDLES_MAX) { - bt_hci_event_status(hci, HCI_REJECTED_LIMITED_RESOURCES); - break; - } - bt_hci_event_status(hci, HCI_SUCCESS); - - if (bt_hci_connect(hci, &PARAM(create_conn, bdaddr))) - bt_hci_connection_reject_event(hci, &PARAM(create_conn, bdaddr)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_DISCONNECT): - LENGTH_CHECK(disconnect); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(disconnect))) { - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_disconnect(hci, PARAMHANDLE(disconnect), - PARAM(disconnect, reason)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN_CANCEL): - LENGTH_CHECK(create_conn_cancel); - - if (bt_hci_lmp_connection_ready(hci, - &PARAM(create_conn_cancel, bdaddr))) { - for (i = 0; i < HCI_HANDLES_MAX; i ++) - if (bt_hci_role_master(hci, i) && hci->lm.handle[i].link && - !bacmp(&hci->lm.handle[i].link->slave->bd_addr, - &PARAM(create_conn_cancel, bdaddr))) - break; - - bt_hci_event_complete_conn_cancel(hci, i < HCI_HANDLES_MAX ? - HCI_ACL_CONNECTION_EXISTS : HCI_NO_CONNECTION, - &PARAM(create_conn_cancel, bdaddr)); - } else - bt_hci_event_complete_conn_cancel(hci, HCI_SUCCESS, - &PARAM(create_conn_cancel, bdaddr)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ): - LENGTH_CHECK(accept_conn_req); - - if (!hci->conn_req_host || - bacmp(&PARAM(accept_conn_req, bdaddr), - &hci->conn_req_host->bd_addr)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_connection_accept(hci, hci->conn_req_host); - hci->conn_req_host = NULL; - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_REJECT_CONN_REQ): - LENGTH_CHECK(reject_conn_req); - - if (!hci->conn_req_host || - bacmp(&PARAM(reject_conn_req, bdaddr), - &hci->conn_req_host->bd_addr)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_connection_reject(hci, hci->conn_req_host, - PARAM(reject_conn_req, reason)); - bt_hci_connection_reject_event(hci, &hci->conn_req_host->bd_addr); - hci->conn_req_host = NULL; - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_AUTH_REQUESTED): - LENGTH_CHECK(auth_requested); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(auth_requested))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - else { - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_event_auth_complete(hci, PARAMHANDLE(auth_requested)); - } - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT): - LENGTH_CHECK(set_conn_encrypt); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(set_conn_encrypt))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - else { - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_event_encrypt_change(hci, - PARAMHANDLE(set_conn_encrypt), - PARAM(set_conn_encrypt, encrypt)); - } - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ): - LENGTH_CHECK(remote_name_req); - - if (bt_hci_name_req(hci, &PARAM(remote_name_req, bdaddr))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL): - LENGTH_CHECK(remote_name_req_cancel); - - bt_hci_event_complete_name_cancel(hci, - &PARAM(remote_name_req_cancel, bdaddr)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_FEATURES): - LENGTH_CHECK(read_remote_features); - - if (bt_hci_features_req(hci, PARAMHANDLE(read_remote_features))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_EXT_FEATURES): - LENGTH_CHECK(read_remote_ext_features); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(read_remote_ext_features))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - else { - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_event_read_remote_ext_features(hci, - PARAMHANDLE(read_remote_ext_features)); - } - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_VERSION): - LENGTH_CHECK(read_remote_version); - - if (bt_hci_version_req(hci, PARAMHANDLE(read_remote_version))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_CLOCK_OFFSET): - LENGTH_CHECK(read_clock_offset); - - if (bt_hci_clkoffset_req(hci, PARAMHANDLE(read_clock_offset))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_LMP_HANDLE): - LENGTH_CHECK(read_lmp_handle); - - /* TODO: */ - bt_hci_event_complete_lmp_handle(hci, PARAMHANDLE(read_lmp_handle)); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_HOLD_MODE): - LENGTH_CHECK(hold_mode); - - if (PARAM16(hold_mode, min_interval) > - PARAM16(hold_mode, max_interval) || - PARAM16(hold_mode, min_interval) < 0x0002 || - PARAM16(hold_mode, max_interval) > 0xff00 || - (PARAM16(hold_mode, min_interval) & 1) || - (PARAM16(hold_mode, max_interval) & 1)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - if (bt_hci_mode_change(hci, PARAMHANDLE(hold_mode), - PARAM16(hold_mode, max_interval), - acl_hold)) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_PARK_MODE): - LENGTH_CHECK(park_mode); - - if (PARAM16(park_mode, min_interval) > - PARAM16(park_mode, max_interval) || - PARAM16(park_mode, min_interval) < 0x000e || - (PARAM16(park_mode, min_interval) & 1) || - (PARAM16(park_mode, max_interval) & 1)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - if (bt_hci_mode_change(hci, PARAMHANDLE(park_mode), - PARAM16(park_mode, max_interval), - acl_parked)) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_EXIT_PARK_MODE): - LENGTH_CHECK(exit_park_mode); - - if (bt_hci_mode_cancel(hci, PARAMHANDLE(exit_park_mode), - acl_parked)) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_ROLE_DISCOVERY): - LENGTH_CHECK(role_discovery); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(role_discovery))) - bt_hci_event_complete_role_discovery(hci, - HCI_NO_CONNECTION, PARAMHANDLE(role_discovery), 0); - else - bt_hci_event_complete_role_discovery(hci, - HCI_SUCCESS, PARAMHANDLE(role_discovery), - bt_hci_role_master(hci, - PARAMHANDLE(role_discovery))); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_MASK): - LENGTH_CHECK(set_event_mask); - - memcpy(hci->event_mask, PARAM(set_event_mask, mask), 8); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_RESET): - bt_hci_reset(hci); - bt_hci_event_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_FLT): - if (length >= 1 && PARAM(set_event_flt, flt_type) == FLT_CLEAR_ALL) - /* No length check */; - else - LENGTH_CHECK(set_event_flt); - - /* Filters are not implemented */ - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_FLUSH): - LENGTH_CHECK(flush); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(flush))) - bt_hci_event_complete_flush(hci, - HCI_NO_CONNECTION, PARAMHANDLE(flush)); - else { - /* TODO: ordering? */ - bt_hci_event(hci, EVT_FLUSH_OCCURRED, - &PARAM(flush, handle), - EVT_FLUSH_OCCURRED_SIZE); - bt_hci_event_complete_flush(hci, - HCI_SUCCESS, PARAMHANDLE(flush)); - } - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME): - LENGTH_CHECK(change_local_name); - - g_free((void *) hci->device.lmp_name); - hci->device.lmp_name = g_strndup(PARAM(change_local_name, name), - sizeof(PARAM(change_local_name, name))); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_NAME): - bt_hci_event_complete_read_local_name(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CONN_ACCEPT_TIMEOUT): - bt_hci_event_complete_read_conn_accept_timeout(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CONN_ACCEPT_TIMEOUT): - /* TODO */ - LENGTH_CHECK(write_conn_accept_timeout); - - if (PARAM16(write_conn_accept_timeout, timeout) < 0x0001 || - PARAM16(write_conn_accept_timeout, timeout) > 0xb540) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->conn_accept_tout = PARAM16(write_conn_accept_timeout, timeout); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SCAN_ENABLE): - bt_hci_event_complete_read_scan_enable(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE): - LENGTH_CHECK(write_scan_enable); - - /* TODO: check that the remaining bits are all 0 */ - hci->device.inquiry_scan = - !!(PARAM(write_scan_enable, scan_enable) & SCAN_INQUIRY); - hci->device.page_scan = - !!(PARAM(write_scan_enable, scan_enable) & SCAN_PAGE); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CLASS_OF_DEV): - bt_hci_event_complete_read_local_class(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV): - LENGTH_CHECK(write_class_of_dev); - - memcpy(hci->device.class, PARAM(write_class_of_dev, dev_class), - sizeof(PARAM(write_class_of_dev, dev_class))); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_VOICE_SETTING): - bt_hci_event_complete_voice_setting(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_VOICE_SETTING): - LENGTH_CHECK(write_voice_setting); - - hci->voice_setting = PARAM(write_voice_setting, voice_setting); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_HOST_NUMBER_OF_COMPLETED_PACKETS): - if (length < data[0] * 2 + 1) - goto short_hci; - - for (i = 0; i < data[0]; i ++) - if (bt_hci_handle_bad(hci, - data[i * 2 + 1] | (data[i * 2 + 2] << 8))) - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_INQUIRY_MODE): - /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x40) - * else - * goto unknown_command */ - bt_hci_event_complete_read_inquiry_mode(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_INQUIRY_MODE): - /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x80) - * else - * goto unknown_command */ - LENGTH_CHECK(write_inquiry_mode); - - if (PARAM(write_inquiry_mode, mode) > 0x01) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->lm.inquiry_mode = PARAM(write_inquiry_mode, mode); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION): - bt_hci_read_local_version_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_COMMANDS): - bt_hci_read_local_commands_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES): - bt_hci_read_local_features_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_EXT_FEATURES): - LENGTH_CHECK(read_local_ext_features); - - bt_hci_read_local_ext_features_rp(hci, - PARAM(read_local_ext_features, page_num)); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE): - bt_hci_read_buffer_size_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_COUNTRY_CODE): - bt_hci_read_country_code_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BD_ADDR): - bt_hci_read_bd_addr_rp(hci); - break; - - case cmd_opcode_pack(OGF_STATUS_PARAM, OCF_READ_LINK_QUALITY): - LENGTH_CHECK(read_link_quality); - - bt_hci_link_quality_rp(hci, PARAMHANDLE(read_link_quality)); - break; - - default: - bt_hci_event_status(hci, HCI_UNKNOWN_COMMAND); - break; - - short_hci: - error_report("%s: HCI packet too short (%iB)", __func__, length); - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } -} - -/* We could perform fragmentation here, we can't do "recombination" because - * at this layer the length of the payload is not know ahead, so we only - * know that a packet contained the last fragment of the SDU when the next - * SDU starts. */ -static inline void bt_hci_lmp_acl_data(struct bt_hci_s *hci, uint16_t handle, - const uint8_t *data, int start, int len) -{ - struct hci_acl_hdr *pkt = (void *) hci->acl_buf; - - /* TODO: packet flags */ - /* TODO: avoid memcpy'ing */ - - if (len + HCI_ACL_HDR_SIZE > sizeof(hci->acl_buf)) { - error_report("%s: can't take ACL packets %i bytes long", - __func__, len); - return; - } - memcpy(hci->acl_buf + HCI_ACL_HDR_SIZE, data, len); - - pkt->handle = cpu_to_le16( - acl_handle_pack(handle, start ? ACL_START : ACL_CONT)); - pkt->dlen = cpu_to_le16(len); - hci->info.acl_recv(hci->info.opaque, - hci->acl_buf, len + HCI_ACL_HDR_SIZE); -} - -static void bt_hci_lmp_acl_data_slave(struct bt_link_s *btlink, - const uint8_t *data, int start, int len) -{ - struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; - - bt_hci_lmp_acl_data(hci_from_device(btlink->slave), - link->handle, data, start, len); -} - -static void bt_hci_lmp_acl_data_host(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - bt_hci_lmp_acl_data(hci_from_device(link->host), - link->handle, data, start, len); -} - -static void bt_submit_acl(struct HCIInfo *info, - const uint8_t *data, int length) -{ - struct bt_hci_s *hci = hci_from_info(info); - uint16_t handle; - int datalen, flags; - struct bt_link_s *link; - - if (length < HCI_ACL_HDR_SIZE) { - error_report("%s: ACL packet too short (%iB)", __func__, length); - return; - } - - handle = acl_handle((data[1] << 8) | data[0]); - flags = acl_flags((data[1] << 8) | data[0]); - datalen = (data[3] << 8) | data[2]; - data += HCI_ACL_HDR_SIZE; - length -= HCI_ACL_HDR_SIZE; - - if (bt_hci_handle_bad(hci, handle)) { - error_report("%s: invalid ACL handle %03x", __func__, handle); - /* TODO: signal an error */ - return; - } - handle &= ~HCI_HANDLE_OFFSET; - - if (datalen > length) { - error_report("%s: ACL packet too short (%iB < %iB)", - __func__, length, datalen); - return; - } - - link = hci->lm.handle[handle].link; - - if ((flags & ~3) == ACL_ACTIVE_BCAST) { - if (!hci->asb_handle) - hci->asb_handle = handle; - else if (handle != hci->asb_handle) { - error_report("%s: Bad handle %03x in Active Slave Broadcast", - __func__, handle); - /* TODO: signal an error */ - return; - } - - /* TODO */ - } - - if ((flags & ~3) == ACL_PICO_BCAST) { - if (!hci->psb_handle) - hci->psb_handle = handle; - else if (handle != hci->psb_handle) { - error_report("%s: Bad handle %03x in Parked Slave Broadcast", - __func__, handle); - /* TODO: signal an error */ - return; - } - - /* TODO */ - } - - /* TODO: increase counter and send EVT_NUM_COMP_PKTS */ - bt_hci_event_num_comp_pkts(hci, handle | HCI_HANDLE_OFFSET, 1); - - /* Do this last as it can trigger further events even in this HCI */ - hci->lm.handle[handle].lmp_acl_data(link, data, - (flags & 3) == ACL_START, length); -} - -static void bt_submit_sco(struct HCIInfo *info, - const uint8_t *data, int length) -{ - struct bt_hci_s *hci = hci_from_info(info); - uint16_t handle; - int datalen; - - if (length < 3) - return; - - handle = acl_handle((data[1] << 8) | data[0]); - datalen = data[2]; - length -= 3; - - if (bt_hci_handle_bad(hci, handle)) { - error_report("%s: invalid SCO handle %03x", __func__, handle); - return; - } - - if (datalen > length) { - error_report("%s: SCO packet too short (%iB < %iB)", - __func__, length, datalen); - return; - } - - /* TODO */ - - /* TODO: increase counter and send EVT_NUM_COMP_PKTS if synchronous - * Flow Control is enabled. - * (See Read/Write_Synchronous_Flow_Control_Enable on page 513 and - * page 514.) */ -} - -static uint8_t *bt_hci_evt_packet(void *opaque) -{ - /* TODO: allocate a packet from upper layer */ - struct bt_hci_s *s = opaque; - - return s->evt_buf; -} - -static void bt_hci_evt_submit(void *opaque, int len) -{ - /* TODO: notify upper layer */ - struct bt_hci_s *s = opaque; - - s->info.evt_recv(s->info.opaque, s->evt_buf, len); -} - -static int bt_hci_bdaddr_set(struct HCIInfo *info, const uint8_t *bd_addr) -{ - struct bt_hci_s *hci = hci_from_info(info); - - bacpy(&hci->device.bd_addr, (const bdaddr_t *) bd_addr); - return 0; -} - -static void bt_hci_done(struct HCIInfo *info); -static void bt_hci_destroy(struct bt_device_s *dev) -{ - struct bt_hci_s *hci = hci_from_device(dev); - - bt_hci_done(&hci->info); -} - -struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net) -{ - struct bt_hci_s *s = g_malloc0(sizeof(struct bt_hci_s)); - - s->lm.inquiry_done = timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_inquiry_done, s); - s->lm.inquiry_next = timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_inquiry_next, s); - s->conn_accept_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_conn_accept_timeout, s); - - s->evt_packet = bt_hci_evt_packet; - s->evt_submit = bt_hci_evt_submit; - s->opaque = s; - - bt_device_init(&s->device, net); - s->device.lmp_connection_request = bt_hci_lmp_connection_request; - s->device.lmp_connection_complete = bt_hci_lmp_connection_complete; - s->device.lmp_disconnect_master = bt_hci_lmp_disconnect_host; - s->device.lmp_disconnect_slave = bt_hci_lmp_disconnect_slave; - s->device.lmp_acl_data = bt_hci_lmp_acl_data_slave; - s->device.lmp_acl_resp = bt_hci_lmp_acl_data_host; - s->device.lmp_mode_change = bt_hci_lmp_mode_change_slave; - - /* Keep updated! */ - /* Also keep in sync with supported commands bitmask in - * bt_hci_read_local_commands_rp */ - s->device.lmp_caps = 0x8000199b7e85355fll; - - bt_hci_reset(s); - - s->info.cmd_send = bt_submit_hci; - s->info.sco_send = bt_submit_sco; - s->info.acl_send = bt_submit_acl; - s->info.bdaddr_set = bt_hci_bdaddr_set; - - s->device.handle_destroy = bt_hci_destroy; - - error_setg(&s->replay_blocker, QERR_REPLAY_NOT_SUPPORTED, "-bt hci"); - replay_add_blocker(s->replay_blocker); - - return &s->info; -} - -struct HCIInfo *hci_init(const char *str) -{ - char *endp; - struct bt_scatternet_s *vlan = 0; - - if (!strcmp(str, "null")) - /* null */ - return &null_hci; - else if (!strncmp(str, "host", 4) && (str[4] == '\0' || str[4] == ':')) - /* host[:hciN] */ - return bt_host_hci(str[4] ? str + 5 : "hci0"); - else if (!strncmp(str, "hci", 3)) { - /* hci[,vlan=n] */ - if (str[3]) { - if (!strncmp(str + 3, ",vlan=", 6)) { - vlan = qemu_find_bt_vlan(strtol(str + 9, &endp, 0)); - if (*endp) - vlan = 0; - } - } else - vlan = qemu_find_bt_vlan(0); - if (vlan) - return bt_new_hci(vlan); - } - - error_report("Unknown bluetooth HCI `%s'.", str); - - return 0; -} - -static void bt_hci_done(struct HCIInfo *info) -{ - struct bt_hci_s *hci = hci_from_info(info); - int handle; - - bt_device_done(&hci->device); - - g_free((void *) hci->device.lmp_name); - - /* Be gentle and send DISCONNECT to all connected peers and those - * currently waiting for us to accept or reject a connection request. - * This frees the links. */ - if (hci->conn_req_host) { - bt_hci_connection_reject(hci, - hci->conn_req_host, HCI_OE_POWER_OFF); - return; - } - - for (handle = HCI_HANDLE_OFFSET; - handle < (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX); handle ++) - if (!bt_hci_handle_bad(hci, handle)) - bt_hci_disconnect(hci, handle, HCI_OE_POWER_OFF); - - /* TODO: this is not enough actually, there may be slaves from whom - * we have requested a connection who will soon (or not) respond with - * an accept or a reject, so we should also check if hci->lm.connecting - * is non-zero and if so, avoid freeing the hci but otherwise disappear - * from all qemu social life (e.g. stop scanning and request to be - * removed from s->device.net) and arrange for - * s->device.lmp_connection_complete to free the remaining bits once - * hci->lm.awaiting_bdaddr[] is empty. */ - - timer_free(hci->lm.inquiry_done); - timer_free(hci->lm.inquiry_next); - timer_free(hci->conn_accept_timer); - - g_free(hci); -} diff --git a/hw/bt/hid.c b/hw/bt/hid.c deleted file mode 100644 index 066ca99ed2..0000000000 --- a/hw/bt/hid.c +++ /dev/null @@ -1,553 +0,0 @@ -/* - * QEMU Bluetooth HID Profile wrapper for USB HID. - * - * Copyright (C) 2007-2008 OpenMoko, Inc. - * Written by Andrzej Zaborowski <andrew@openedhand.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "qemu/timer.h" -#include "ui/console.h" -#include "hw/input/hid.h" -#include "hw/bt.h" - -enum hid_transaction_req { - BT_HANDSHAKE = 0x0, - BT_HID_CONTROL = 0x1, - BT_GET_REPORT = 0x4, - BT_SET_REPORT = 0x5, - BT_GET_PROTOCOL = 0x6, - BT_SET_PROTOCOL = 0x7, - BT_GET_IDLE = 0x8, - BT_SET_IDLE = 0x9, - BT_DATA = 0xa, - BT_DATC = 0xb, -}; - -enum hid_transaction_handshake { - BT_HS_SUCCESSFUL = 0x0, - BT_HS_NOT_READY = 0x1, - BT_HS_ERR_INVALID_REPORT_ID = 0x2, - BT_HS_ERR_UNSUPPORTED_REQUEST = 0x3, - BT_HS_ERR_INVALID_PARAMETER = 0x4, - BT_HS_ERR_UNKNOWN = 0xe, - BT_HS_ERR_FATAL = 0xf, -}; - -enum hid_transaction_control { - BT_HC_NOP = 0x0, - BT_HC_HARD_RESET = 0x1, - BT_HC_SOFT_RESET = 0x2, - BT_HC_SUSPEND = 0x3, - BT_HC_EXIT_SUSPEND = 0x4, - BT_HC_VIRTUAL_CABLE_UNPLUG = 0x5, -}; - -enum hid_protocol { - BT_HID_PROTO_BOOT = 0, - BT_HID_PROTO_REPORT = 1, -}; - -enum hid_boot_reportid { - BT_HID_BOOT_INVALID = 0, - BT_HID_BOOT_KEYBOARD, - BT_HID_BOOT_MOUSE, -}; - -enum hid_data_pkt { - BT_DATA_OTHER = 0, - BT_DATA_INPUT, - BT_DATA_OUTPUT, - BT_DATA_FEATURE, -}; - -#define BT_HID_MTU 48 - -/* HID interface requests */ -#define GET_REPORT 0xa101 -#define GET_IDLE 0xa102 -#define GET_PROTOCOL 0xa103 -#define SET_REPORT 0x2109 -#define SET_IDLE 0x210a -#define SET_PROTOCOL 0x210b - -struct bt_hid_device_s { - struct bt_l2cap_device_s btdev; - struct bt_l2cap_conn_params_s *control; - struct bt_l2cap_conn_params_s *interrupt; - HIDState hid; - - int proto; - int connected; - int data_type; - int intr_state; - struct { - int len; - uint8_t buffer[1024]; - } dataother, datain, dataout, feature, intrdataout; - enum { - bt_state_ready, - bt_state_transaction, - bt_state_suspend, - } state; -}; - -static void bt_hid_reset(struct bt_hid_device_s *s) -{ - struct bt_scatternet_s *net = s->btdev.device.net; - - /* Go as far as... */ - bt_l2cap_device_done(&s->btdev); - bt_l2cap_device_init(&s->btdev, net); - - hid_reset(&s->hid); - s->proto = BT_HID_PROTO_REPORT; - s->state = bt_state_ready; - s->dataother.len = 0; - s->datain.len = 0; - s->dataout.len = 0; - s->feature.len = 0; - s->intrdataout.len = 0; - s->intr_state = 0; -} - -static int bt_hid_out(struct bt_hid_device_s *s) -{ - if (s->data_type == BT_DATA_OUTPUT) { - /* nothing */ - ; - } - - if (s->data_type == BT_DATA_FEATURE) { - /* XXX: - * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE - * or a SET_REPORT? */ - ; - } - - return -1; -} - -static int bt_hid_in(struct bt_hid_device_s *s) -{ - s->datain.len = hid_keyboard_poll(&s->hid, s->datain.buffer, - sizeof(s->datain.buffer)); - return s->datain.len; -} - -static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result) -{ - *s->control->sdu_out(s->control, 1) = - (BT_HANDSHAKE << 4) | result; - s->control->sdu_submit(s->control); -} - -static void bt_hid_send_control(struct bt_hid_device_s *s, int operation) -{ - *s->control->sdu_out(s->control, 1) = - (BT_HID_CONTROL << 4) | operation; - s->control->sdu_submit(s->control); -} - -static void bt_hid_disconnect(struct bt_hid_device_s *s) -{ - /* Disconnect s->control and s->interrupt */ -} - -static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type, - const uint8_t *data, int len) -{ - uint8_t *pkt, hdr = (BT_DATA << 4) | type; - int plen; - - do { - plen = MIN(len, ch->remote_mtu - 1); - pkt = ch->sdu_out(ch, plen + 1); - - pkt[0] = hdr; - if (plen) - memcpy(pkt + 1, data, plen); - ch->sdu_submit(ch); - - len -= plen; - data += plen; - hdr = (BT_DATC << 4) | type; - } while (plen == ch->remote_mtu - 1); -} - -static void bt_hid_control_transaction(struct bt_hid_device_s *s, - const uint8_t *data, int len) -{ - uint8_t type, parameter; - int rlen, ret = -1; - if (len < 1) - return; - - type = data[0] >> 4; - parameter = data[0] & 0xf; - - switch (type) { - case BT_HANDSHAKE: - case BT_DATA: - switch (parameter) { - default: - /* These are not expected to be sent this direction. */ - ret = BT_HS_ERR_INVALID_PARAMETER; - } - break; - - case BT_HID_CONTROL: - if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG && - s->state == bt_state_transaction)) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - switch (parameter) { - case BT_HC_NOP: - break; - case BT_HC_HARD_RESET: - case BT_HC_SOFT_RESET: - bt_hid_reset(s); - break; - case BT_HC_SUSPEND: - if (s->state == bt_state_ready) - s->state = bt_state_suspend; - else - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - case BT_HC_EXIT_SUSPEND: - if (s->state == bt_state_suspend) - s->state = bt_state_ready; - else - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - case BT_HC_VIRTUAL_CABLE_UNPLUG: - bt_hid_disconnect(s); - break; - default: - ret = BT_HS_ERR_INVALID_PARAMETER; - } - break; - - case BT_GET_REPORT: - /* No ReportIDs declared. */ - if (((parameter & 8) && len != 3) || - (!(parameter & 8) && len != 1) || - s->state != bt_state_ready) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - if (parameter & 8) - rlen = data[2] | (data[3] << 8); - else - rlen = INT_MAX; - switch (parameter & 3) { - case BT_DATA_OTHER: - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - case BT_DATA_INPUT: - /* Here we can as well poll s->usbdev */ - bt_hid_send_data(s->control, BT_DATA_INPUT, - s->datain.buffer, MIN(rlen, s->datain.len)); - break; - case BT_DATA_OUTPUT: - bt_hid_send_data(s->control, BT_DATA_OUTPUT, - s->dataout.buffer, MIN(rlen, s->dataout.len)); - break; - case BT_DATA_FEATURE: - bt_hid_send_data(s->control, BT_DATA_FEATURE, - s->feature.buffer, MIN(rlen, s->feature.len)); - break; - } - break; - - case BT_SET_REPORT: - if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready || - (parameter & 3) == BT_DATA_OTHER || - (parameter & 3) == BT_DATA_INPUT) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - s->data_type = parameter & 3; - if (s->data_type == BT_DATA_OUTPUT) { - s->dataout.len = len - 1; - memcpy(s->dataout.buffer, data + 1, s->dataout.len); - } else { - s->feature.len = len - 1; - memcpy(s->feature.buffer, data + 1, s->feature.len); - } - if (len == BT_HID_MTU) - s->state = bt_state_transaction; - else - bt_hid_out(s); - break; - - case BT_GET_PROTOCOL: - if (len != 1 || s->state == bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - *s->control->sdu_out(s->control, 1) = s->proto; - s->control->sdu_submit(s->control); - break; - - case BT_SET_PROTOCOL: - if (len != 1 || s->state == bt_state_transaction || - (parameter != BT_HID_PROTO_BOOT && - parameter != BT_HID_PROTO_REPORT)) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - s->proto = parameter; - s->hid.protocol = parameter; - ret = BT_HS_SUCCESSFUL; - break; - - case BT_GET_IDLE: - if (len != 1 || s->state == bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - *s->control->sdu_out(s->control, 1) = s->hid.idle; - s->control->sdu_submit(s->control); - break; - - case BT_SET_IDLE: - if (len != 2 || s->state == bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - - s->hid.idle = data[1]; - /* XXX: Does this generate a handshake? */ - break; - - case BT_DATC: - if (len > BT_HID_MTU || s->state != bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - if (s->data_type == BT_DATA_OUTPUT) { - memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1); - s->dataout.len += len - 1; - } else { - memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1); - s->feature.len += len - 1; - } - if (len < BT_HID_MTU) { - bt_hid_out(s); - s->state = bt_state_ready; - } - break; - - default: - ret = BT_HS_ERR_UNSUPPORTED_REQUEST; - } - - if (ret != -1) - bt_hid_send_handshake(s, ret); -} - -static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len) -{ - struct bt_hid_device_s *hid = opaque; - - bt_hid_control_transaction(hid, data, len); -} - -static void bt_hid_datain(HIDState *hs) -{ - struct bt_hid_device_s *hid = - container_of(hs, struct bt_hid_device_s, hid); - - /* If suspended, wake-up and send a wake-up event first. We might - * want to also inspect the input report and ignore event like - * mouse movements until a button event occurs. */ - if (hid->state == bt_state_suspend) { - hid->state = bt_state_ready; - } - - if (bt_hid_in(hid) > 0) - /* TODO: when in boot-mode precede any Input reports with the ReportID - * byte, here and in GetReport/SetReport on the Control channel. */ - bt_hid_send_data(hid->interrupt, BT_DATA_INPUT, - hid->datain.buffer, hid->datain.len); -} - -static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len) -{ - struct bt_hid_device_s *hid = opaque; - - if (len > BT_HID_MTU || len < 1) - goto bad; - if ((data[0] & 3) != BT_DATA_OUTPUT) - goto bad; - if ((data[0] >> 4) == BT_DATA) { - if (hid->intr_state) - goto bad; - - hid->data_type = BT_DATA_OUTPUT; - hid->intrdataout.len = 0; - } else if ((data[0] >> 4) == BT_DATC) { - if (!hid->intr_state) - goto bad; - } else - goto bad; - - memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1); - hid->intrdataout.len += len - 1; - hid->intr_state = (len == BT_HID_MTU); - if (!hid->intr_state) { - memcpy(hid->dataout.buffer, hid->intrdataout.buffer, - hid->dataout.len = hid->intrdataout.len); - bt_hid_out(hid); - } - - return; -bad: - error_report("%s: bad transaction on Interrupt channel.", - __func__); -} - -/* "Virtual cable" plug/unplug event. */ -static void bt_hid_connected_update(struct bt_hid_device_s *hid) -{ - int prev = hid->connected; - - hid->connected = hid->control && hid->interrupt; - - /* Stop page-/inquiry-scanning when a host is connected. */ - hid->btdev.device.page_scan = !hid->connected; - hid->btdev.device.inquiry_scan = !hid->connected; - - if (hid->connected && !prev) { - hid_reset(&hid->hid); - hid->proto = BT_HID_PROTO_REPORT; - } - - /* Should set HIDVirtualCable in SDP (possibly need to check that SDP - * isn't destroyed yet, in case we're being called from handle_destroy) */ -} - -static void bt_hid_close_control(void *opaque) -{ - struct bt_hid_device_s *hid = opaque; - - hid->control = NULL; - bt_hid_connected_update(hid); -} - -static void bt_hid_close_interrupt(void *opaque) -{ - struct bt_hid_device_s *hid = opaque; - - hid->interrupt = NULL; - bt_hid_connected_update(hid); -} - -static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params) -{ - struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; - - if (hid->control) - return 1; - - hid->control = params; - hid->control->opaque = hid; - hid->control->close = bt_hid_close_control; - hid->control->sdu_in = bt_hid_control_sdu; - - bt_hid_connected_update(hid); - - return 0; -} - -static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params) -{ - struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; - - if (hid->interrupt) - return 1; - - hid->interrupt = params; - hid->interrupt->opaque = hid; - hid->interrupt->close = bt_hid_close_interrupt; - hid->interrupt->sdu_in = bt_hid_interrupt_sdu; - - bt_hid_connected_update(hid); - - return 0; -} - -static void bt_hid_destroy(struct bt_device_s *dev) -{ - struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; - - if (hid->connected) - bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG); - bt_l2cap_device_done(&hid->btdev); - - hid_free(&hid->hid); - - g_free(hid); -} - -enum peripheral_minor_class { - class_other = 0 << 4, - class_keyboard = 1 << 4, - class_pointing = 2 << 4, - class_combo = 3 << 4, -}; - -static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, - enum peripheral_minor_class minor) -{ - struct bt_hid_device_s *s = g_malloc0(sizeof(*s)); - uint32_t class = - /* Format type */ - (0 << 0) | - /* Device class */ - (minor << 2) | - (5 << 8) | /* "Peripheral" */ - /* Service classes */ - (1 << 13) | /* Limited discoverable mode */ - (1 << 19); /* Capturing device (?) */ - - bt_l2cap_device_init(&s->btdev, net); - bt_l2cap_sdp_init(&s->btdev); - bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL, - BT_HID_MTU, bt_hid_new_control_ch); - bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR, - BT_HID_MTU, bt_hid_new_interrupt_ch); - - hid_init(&s->hid, HID_KEYBOARD, bt_hid_datain); - s->btdev.device.lmp_name = "BT Keyboard"; - - s->btdev.device.handle_destroy = bt_hid_destroy; - - s->btdev.device.class[0] = (class >> 0) & 0xff; - s->btdev.device.class[1] = (class >> 8) & 0xff; - s->btdev.device.class[2] = (class >> 16) & 0xff; - - return &s->btdev.device; -} - -struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net) -{ - return bt_hid_init(net, class_keyboard); -} diff --git a/hw/bt/l2cap.c b/hw/bt/l2cap.c deleted file mode 100644 index d67098a719..0000000000 --- a/hw/bt/l2cap.c +++ /dev/null @@ -1,1367 +0,0 @@ -/* - * QEMU Bluetooth L2CAP logic. - * - * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/timer.h" -#include "qemu/bswap.h" -#include "hw/bt.h" - -#define L2CAP_CID_MAX 0x100 /* Between 0x40 and 0x10000 */ - -struct l2cap_instance_s { - struct bt_link_s *link; - struct bt_l2cap_device_s *dev; - int role; - - uint8_t frame_in[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4))); - int frame_in_len; - - uint8_t frame_out[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4))); - int frame_out_len; - - /* Signalling channel timers. They exist per-request but we can make - * sure we have no more than one outstanding request at any time. */ - QEMUTimer *rtx; - QEMUTimer *ertx; - - int last_id; - int next_id; - - struct l2cap_chan_s { - struct bt_l2cap_conn_params_s params; - - void (*frame_in)(struct l2cap_chan_s *chan, uint16_t cid, - const l2cap_hdr *hdr, int len); - int mps; - int min_mtu; - - struct l2cap_instance_s *l2cap; - - /* Only allocated channels */ - uint16_t remote_cid; -#define L2CAP_CFG_INIT 2 -#define L2CAP_CFG_ACC 1 - int config_req_id; /* TODO: handle outgoing requests generically */ - int config; - - /* Only connection-oriented channels. Note: if we allow the tx and - * rx traffic to be in different modes at any time, we need two. */ - int mode; - - /* Only flow-controlled, connection-oriented channels */ - uint8_t sdu[65536]; /* TODO: dynamically allocate */ - int len_cur, len_total; - int rexmit; - int monitor_timeout; - QEMUTimer *monitor_timer; - QEMUTimer *retransmission_timer; - } *cid[L2CAP_CID_MAX]; - /* The channel state machine states map as following: - * CLOSED -> !cid[N] - * WAIT_CONNECT -> never occurs - * WAIT_CONNECT_RSP -> never occurs - * CONFIG -> cid[N] && config < 3 - * WAIT_CONFIG -> never occurs, cid[N] && config == 0 && !config_r - * WAIT_SEND_CONFIG -> never occurs, cid[N] && config == 1 && !config_r - * WAIT_CONFIG_REQ_RSP -> cid[N] && config == 0 && config_req_id - * WAIT_CONFIG_RSP -> cid[N] && config == 1 && config_req_id - * WAIT_CONFIG_REQ -> cid[N] && config == 2 - * OPEN -> cid[N] && config == 3 - * WAIT_DISCONNECT -> never occurs - */ - - struct l2cap_chan_s signalling_ch; - struct l2cap_chan_s group_ch; -}; - -struct slave_l2cap_instance_s { - struct bt_link_s link; /* Underlying logical link (ACL) */ - struct l2cap_instance_s l2cap; -}; - -struct bt_l2cap_psm_s { - int psm; - int min_mtu; - int (*new_channel)(struct bt_l2cap_device_s *device, - struct bt_l2cap_conn_params_s *params); - struct bt_l2cap_psm_s *next; -}; - -static const uint16_t l2cap_fcs16_table[256] = { - 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, - 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, - 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, - 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, - 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, - 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, - 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, - 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, - 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, - 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, - 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, - 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, - 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, - 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, - 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, - 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, - 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, - 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, - 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, - 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, - 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, - 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, - 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, - 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, - 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, - 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, - 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, - 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, - 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, - 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, - 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, - 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040, -}; - -static uint16_t l2cap_fcs16(const uint8_t *message, int len) -{ - uint16_t fcs = 0x0000; - - while (len --) -#if 0 - { - int i; - - fcs ^= *message ++; - for (i = 8; i; -- i) - if (fcs & 1) - fcs = (fcs >> 1) ^ 0xa001; - else - fcs = (fcs >> 1); - } -#else - fcs = (fcs >> 8) ^ l2cap_fcs16_table[(fcs ^ *message ++) & 0xff]; -#endif - - return fcs; -} - -/* L2CAP layer logic (protocol) */ - -static void l2cap_retransmission_timer_update(struct l2cap_chan_s *ch) -{ -#if 0 - if (ch->mode != L2CAP_MODE_BASIC && ch->rexmit) - timer_mod(ch->retransmission_timer); - else - timer_del(ch->retransmission_timer); -#endif -} - -static void l2cap_monitor_timer_update(struct l2cap_chan_s *ch) -{ -#if 0 - if (ch->mode != L2CAP_MODE_BASIC && !ch->rexmit) - timer_mod(ch->monitor_timer); - else - timer_del(ch->monitor_timer); -#endif -} - -static void l2cap_command_reject(struct l2cap_instance_s *l2cap, int id, - uint16_t reason, const void *data, int plen) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_cmd_rej *params; - uint16_t len; - - reason = cpu_to_le16(reason); - len = cpu_to_le16(L2CAP_CMD_REJ_SIZE + plen); - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE + plen); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_COMMAND_REJ; - hdr->ident = id; - memcpy(&hdr->len, &len, sizeof(hdr->len)); - memcpy(¶ms->reason, &reason, sizeof(reason)); - if (plen) - memcpy(pkt + L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE, data, plen); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_command_reject_cid(struct l2cap_instance_s *l2cap, int id, - uint16_t reason, uint16_t dcid, uint16_t scid) -{ - l2cap_cmd_rej_cid params = { - .dcid = dcid, - .scid = scid, - }; - - l2cap_command_reject(l2cap, id, reason, ¶ms, L2CAP_CMD_REJ_CID_SIZE); -} - -static void l2cap_connection_response(struct l2cap_instance_s *l2cap, - int dcid, int scid, int result, int status) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_conn_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CONN_RSP_SIZE); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_CONN_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_CONN_RSP_SIZE); - - params->dcid = cpu_to_le16(dcid); - params->scid = cpu_to_le16(scid); - params->result = cpu_to_le16(result); - params->status = cpu_to_le16(status); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_configuration_request(struct l2cap_instance_s *l2cap, - int dcid, int flag, const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_conf_req *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CONF_REQ_SIZE(len)); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - /* TODO: unify the id sequencing */ - l2cap->last_id = l2cap->next_id; - l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1; - - hdr->code = L2CAP_CONF_REQ; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_CONF_REQ_SIZE(len)); - - params->dcid = cpu_to_le16(dcid); - params->flags = cpu_to_le16(flag); - if (len) - memcpy(params->data, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_configuration_response(struct l2cap_instance_s *l2cap, - int scid, int flag, int result, const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_conf_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CONF_RSP_SIZE(len)); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_CONF_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_CONF_RSP_SIZE(len)); - - params->scid = cpu_to_le16(scid); - params->flags = cpu_to_le16(flag); - params->result = cpu_to_le16(result); - if (len) - memcpy(params->data, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_disconnection_response(struct l2cap_instance_s *l2cap, - int dcid, int scid) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_disconn_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_DISCONN_RSP_SIZE); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_DISCONN_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_DISCONN_RSP_SIZE); - - params->dcid = cpu_to_le16(dcid); - params->scid = cpu_to_le16(scid); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_echo_response(struct l2cap_instance_s *l2cap, - const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - uint8_t *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + len); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_ECHO_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(len); - - memcpy(params, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_info_response(struct l2cap_instance_s *l2cap, int type, - int result, const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_info_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + len); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_INFO_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_INFO_RSP_SIZE + len); - - params->type = cpu_to_le16(type); - params->result = cpu_to_le16(result); - if (len) - memcpy(params->data, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len); -static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms); -#if 0 -static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len); -static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm); -#endif -static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len); -static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len); - -static int l2cap_cid_new(struct l2cap_instance_s *l2cap) -{ - int i; - - for (i = L2CAP_CID_ALLOC; i < L2CAP_CID_MAX; i ++) - if (!l2cap->cid[i]) - return i; - - return L2CAP_CID_INVALID; -} - -static inline struct bt_l2cap_psm_s *l2cap_psm( - struct bt_l2cap_device_s *device, int psm) -{ - struct bt_l2cap_psm_s *ret = device->first_psm; - - while (ret && ret->psm != psm) - ret = ret->next; - - return ret; -} - -static struct l2cap_chan_s *l2cap_channel_open(struct l2cap_instance_s *l2cap, - int psm, int source_cid) -{ - struct l2cap_chan_s *ch = NULL; - struct bt_l2cap_psm_s *psm_info; - int result, status; - int cid = l2cap_cid_new(l2cap); - - if (cid) { - /* See what the channel is to be used for.. */ - psm_info = l2cap_psm(l2cap->dev, psm); - - if (psm_info) { - /* Device supports this use-case. */ - ch = g_malloc0(sizeof(*ch)); - ch->params.sdu_out = l2cap_bframe_out; - ch->params.sdu_submit = l2cap_bframe_submit; - ch->frame_in = l2cap_bframe_in; - ch->mps = 65536; - ch->min_mtu = MAX(48, psm_info->min_mtu); - ch->params.remote_mtu = MAX(672, ch->min_mtu); - ch->remote_cid = source_cid; - ch->mode = L2CAP_MODE_BASIC; - ch->l2cap = l2cap; - - /* Does it feel like opening yet another channel though? */ - if (!psm_info->new_channel(l2cap->dev, &ch->params)) { - l2cap->cid[cid] = ch; - - result = L2CAP_CR_SUCCESS; - status = L2CAP_CS_NO_INFO; - } else { - g_free(ch); - ch = NULL; - result = L2CAP_CR_NO_MEM; - status = L2CAP_CS_NO_INFO; - } - } else { - result = L2CAP_CR_BAD_PSM; - status = L2CAP_CS_NO_INFO; - } - } else { - result = L2CAP_CR_NO_MEM; - status = L2CAP_CS_NO_INFO; - } - - l2cap_connection_response(l2cap, cid, source_cid, result, status); - - return ch; -} - -static void l2cap_channel_close(struct l2cap_instance_s *l2cap, - int cid, int source_cid) -{ - struct l2cap_chan_s *ch = NULL; - - /* According to Volume 3, section 6.1.1, pg 1048 of BT Core V2.0, a - * connection in CLOSED state still responds with a L2CAP_DisconnectRsp - * message on an L2CAP_DisconnectReq event. */ - if (unlikely(cid < L2CAP_CID_ALLOC)) { - l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, - cid, source_cid); - return; - } - if (likely(cid >= L2CAP_CID_ALLOC && cid < L2CAP_CID_MAX)) - ch = l2cap->cid[cid]; - - if (likely(ch)) { - if (ch->remote_cid != source_cid) { - error_report("%s: Ignoring a Disconnection Request with the " - "invalid SCID %04x.", __func__, source_cid); - return; - } - - l2cap->cid[cid] = NULL; - - ch->params.close(ch->params.opaque); - g_free(ch); - } - - l2cap_disconnection_response(l2cap, cid, source_cid); -} - -static void l2cap_channel_config_null(struct l2cap_instance_s *l2cap, - struct l2cap_chan_s *ch) -{ - l2cap_configuration_request(l2cap, ch->remote_cid, 0, NULL, 0); - ch->config_req_id = l2cap->last_id; - ch->config &= ~L2CAP_CFG_INIT; -} - -static void l2cap_channel_config_req_event(struct l2cap_instance_s *l2cap, - struct l2cap_chan_s *ch) -{ - /* Use all default channel options and terminate negotiation. */ - l2cap_channel_config_null(l2cap, ch); -} - -static int l2cap_channel_config(struct l2cap_instance_s *l2cap, - struct l2cap_chan_s *ch, int flag, - const uint8_t *data, int len) -{ - l2cap_conf_opt *opt; - l2cap_conf_opt_qos *qos; - uint32_t val; - uint8_t rsp[len]; - int result = L2CAP_CONF_SUCCESS; - - data = memcpy(rsp, data, len); - while (len) { - opt = (void *) data; - - if (len < L2CAP_CONF_OPT_SIZE || - len < L2CAP_CONF_OPT_SIZE + opt->len) { - result = L2CAP_CONF_REJECT; - break; - } - data += L2CAP_CONF_OPT_SIZE + opt->len; - len -= L2CAP_CONF_OPT_SIZE + opt->len; - - switch (opt->type & 0x7f) { - case L2CAP_CONF_MTU: - if (opt->len != 2) { - result = L2CAP_CONF_REJECT; - break; - } - - /* MTU */ - val = lduw_le_p(opt->val); - if (val < ch->min_mtu) { - stw_le_p(opt->val, ch->min_mtu); - result = L2CAP_CONF_UNACCEPT; - break; - } - - ch->params.remote_mtu = val; - break; - - case L2CAP_CONF_FLUSH_TO: - if (opt->len != 2) { - result = L2CAP_CONF_REJECT; - break; - } - - /* Flush Timeout */ - val = lduw_le_p(opt->val); - if (val < 0x0001) { - opt->val[0] = 0xff; - opt->val[1] = 0xff; - result = L2CAP_CONF_UNACCEPT; - break; - } - break; - - case L2CAP_CONF_QOS: - if (opt->len != L2CAP_CONF_OPT_QOS_SIZE) { - result = L2CAP_CONF_REJECT; - break; - } - qos = (void *) opt->val; - - /* Flags */ - val = qos->flags; - if (val) { - qos->flags = 0; - result = L2CAP_CONF_UNACCEPT; - } - - /* Service type */ - val = qos->service_type; - if (val != L2CAP_CONF_QOS_BEST_EFFORT && - val != L2CAP_CONF_QOS_NO_TRAFFIC) { - qos->service_type = L2CAP_CONF_QOS_BEST_EFFORT; - result = L2CAP_CONF_UNACCEPT; - } - - if (val != L2CAP_CONF_QOS_NO_TRAFFIC) { - /* XXX: These values should possibly be calculated - * based on LM / baseband properties also. */ - - /* Token rate */ - val = le32_to_cpu(qos->token_rate); - if (val == L2CAP_CONF_QOS_WILDCARD) - qos->token_rate = cpu_to_le32(0x100000); - - /* Token bucket size */ - val = le32_to_cpu(qos->token_bucket_size); - if (val == L2CAP_CONF_QOS_WILDCARD) - qos->token_bucket_size = cpu_to_le32(65500); - - /* Any Peak bandwidth value is correct to return as-is */ - /* Any Access latency value is correct to return as-is */ - /* Any Delay variation value is correct to return as-is */ - } - break; - - case L2CAP_CONF_RFC: - if (opt->len != 9) { - result = L2CAP_CONF_REJECT; - break; - } - - /* Mode */ - val = opt->val[0]; - switch (val) { - case L2CAP_MODE_BASIC: - ch->mode = val; - ch->frame_in = l2cap_bframe_in; - - /* All other parameters shall be ignored */ - break; - - case L2CAP_MODE_RETRANS: - case L2CAP_MODE_FLOWCTL: - ch->mode = val; - ch->frame_in = l2cap_iframe_in; - /* Note: most of these parameters refer to incoming traffic - * so we don't need to save them as long as we can accept - * incoming PDUs at any values of the parameters. */ - - /* TxWindow size */ - val = opt->val[1]; - if (val < 1 || val > 32) { - opt->val[1] = 32; - result = L2CAP_CONF_UNACCEPT; - break; - } - - /* MaxTransmit */ - val = opt->val[2]; - if (val < 1) { - opt->val[2] = 1; - result = L2CAP_CONF_UNACCEPT; - break; - } - - /* Remote Retransmission time-out shouldn't affect local - * operation (?) */ - - /* The Monitor time-out drives the local Monitor timer (?), - * so save the value. */ - val = (opt->val[6] << 8) | opt->val[5]; - if (val < 30) { - opt->val[5] = 100 & 0xff; - opt->val[6] = 100 >> 8; - result = L2CAP_CONF_UNACCEPT; - break; - } - ch->monitor_timeout = val; - l2cap_monitor_timer_update(ch); - - /* MPS */ - val = (opt->val[8] << 8) | opt->val[7]; - if (val < ch->min_mtu) { - opt->val[7] = ch->min_mtu & 0xff; - opt->val[8] = ch->min_mtu >> 8; - result = L2CAP_CONF_UNACCEPT; - break; - } - ch->mps = val; - break; - - default: - result = L2CAP_CONF_UNACCEPT; - break; - } - break; - - default: - if (!(opt->type >> 7)) - result = L2CAP_CONF_UNKNOWN; - break; - } - - if (result != L2CAP_CONF_SUCCESS) - break; /* XXX: should continue? */ - } - - l2cap_configuration_response(l2cap, ch->remote_cid, - flag, result, rsp, len); - - return result == L2CAP_CONF_SUCCESS && !flag; -} - -static void l2cap_channel_config_req_msg(struct l2cap_instance_s *l2cap, - int flag, int cid, const uint8_t *data, int len) -{ - struct l2cap_chan_s *ch; - - if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { - l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, - cid, 0x0000); - return; - } - ch = l2cap->cid[cid]; - - /* From OPEN go to WAIT_CONFIG_REQ and from WAIT_CONFIG_REQ_RSP to - * WAIT_CONFIG_REQ_RSP. This is assuming the transition chart for OPEN - * on pg 1053, section 6.1.5, volume 3 of BT Core V2.0 has a mistake - * and on options-acceptable we go back to OPEN and otherwise to - * WAIT_CONFIG_REQ and not the other way. */ - ch->config &= ~L2CAP_CFG_ACC; - - if (l2cap_channel_config(l2cap, ch, flag, data, len)) - /* Go to OPEN or WAIT_CONFIG_RSP */ - ch->config |= L2CAP_CFG_ACC; - - /* TODO: if the incoming traffic flow control or retransmission mode - * changed then we probably need to also generate the - * ConfigureChannel_Req event and set the outgoing traffic to the same - * mode. */ - if (!(ch->config & L2CAP_CFG_INIT) && (ch->config & L2CAP_CFG_ACC) && - !ch->config_req_id) - l2cap_channel_config_req_event(l2cap, ch); -} - -static int l2cap_channel_config_rsp_msg(struct l2cap_instance_s *l2cap, - int result, int flag, int cid, const uint8_t *data, int len) -{ - struct l2cap_chan_s *ch; - - if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { - l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, - cid, 0x0000); - return 0; - } - ch = l2cap->cid[cid]; - - if (ch->config_req_id != l2cap->last_id) - return 1; - ch->config_req_id = 0; - - if (result == L2CAP_CONF_SUCCESS) { - if (!flag) - ch->config |= L2CAP_CFG_INIT; - else - l2cap_channel_config_null(l2cap, ch); - } else - /* Retry until we succeed */ - l2cap_channel_config_req_event(l2cap, ch); - - return 0; -} - -static void l2cap_channel_open_req_msg(struct l2cap_instance_s *l2cap, - int psm, int source_cid) -{ - struct l2cap_chan_s *ch = l2cap_channel_open(l2cap, psm, source_cid); - - if (!ch) - return; - - /* Optional */ - if (!(ch->config & L2CAP_CFG_INIT) && !ch->config_req_id) - l2cap_channel_config_req_event(l2cap, ch); -} - -static void l2cap_info(struct l2cap_instance_s *l2cap, int type) -{ - uint8_t data[4]; - int len = 0; - int result = L2CAP_IR_SUCCESS; - - switch (type) { - case L2CAP_IT_CL_MTU: - data[len ++] = l2cap->group_ch.mps & 0xff; - data[len ++] = l2cap->group_ch.mps >> 8; - break; - - case L2CAP_IT_FEAT_MASK: - /* (Prematurely) report Flow control and Retransmission modes. */ - data[len ++] = 0x03; - data[len ++] = 0x00; - data[len ++] = 0x00; - data[len ++] = 0x00; - break; - - default: - result = L2CAP_IR_NOTSUPP; - } - - l2cap_info_response(l2cap, type, result, data, len); -} - -static void l2cap_command(struct l2cap_instance_s *l2cap, int code, int id, - const uint8_t *params, int len) -{ - int err; - -#if 0 - /* TODO: do the IDs really have to be in sequence? */ - if (!id || (id != l2cap->last_id && id != l2cap->next_id)) { - error_report("%s: out of sequence command packet ignored.", - __func__); - return; - } -#else - l2cap->next_id = id; -#endif - if (id == l2cap->next_id) { - l2cap->last_id = l2cap->next_id; - l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1; - } else { - /* TODO: Need to re-send the same response, without re-executing - * the corresponding command! */ - } - - switch (code) { - case L2CAP_COMMAND_REJ: - if (unlikely(len != 2 && len != 4 && len != 6)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue commands other than Command Reject currently. */ - error_report("%s: stray Command Reject (%02x, %04x) " - "packet, ignoring.", __func__, id, - le16_to_cpu(((l2cap_cmd_rej *) params)->reason)); - break; - - case L2CAP_CONN_REQ: - if (unlikely(len != L2CAP_CONN_REQ_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_channel_open_req_msg(l2cap, - le16_to_cpu(((l2cap_conn_req *) params)->psm), - le16_to_cpu(((l2cap_conn_req *) params)->scid)); - break; - - case L2CAP_CONN_RSP: - if (unlikely(len != L2CAP_CONN_RSP_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue Connection Requests currently. TODO */ - error_report("%s: unexpected Connection Response (%02x) " - "packet, ignoring.", __func__, id); - break; - - case L2CAP_CONF_REQ: - if (unlikely(len < L2CAP_CONF_REQ_SIZE(0))) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_channel_config_req_msg(l2cap, - le16_to_cpu(((l2cap_conf_req *) params)->flags) & 1, - le16_to_cpu(((l2cap_conf_req *) params)->dcid), - ((l2cap_conf_req *) params)->data, - len - L2CAP_CONF_REQ_SIZE(0)); - break; - - case L2CAP_CONF_RSP: - if (unlikely(len < L2CAP_CONF_RSP_SIZE(0))) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - if (l2cap_channel_config_rsp_msg(l2cap, - le16_to_cpu(((l2cap_conf_rsp *) params)->result), - le16_to_cpu(((l2cap_conf_rsp *) params)->flags) & 1, - le16_to_cpu(((l2cap_conf_rsp *) params)->scid), - ((l2cap_conf_rsp *) params)->data, - len - L2CAP_CONF_RSP_SIZE(0))) - error_report("%s: unexpected Configure Response (%02x) " - "packet, ignoring.", __func__, id); - break; - - case L2CAP_DISCONN_REQ: - if (unlikely(len != L2CAP_DISCONN_REQ_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_channel_close(l2cap, - le16_to_cpu(((l2cap_disconn_req *) params)->dcid), - le16_to_cpu(((l2cap_disconn_req *) params)->scid)); - break; - - case L2CAP_DISCONN_RSP: - if (unlikely(len != L2CAP_DISCONN_RSP_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue Disconnection Requests currently. TODO */ - error_report("%s: unexpected Disconnection Response (%02x) " - "packet, ignoring.", __func__, id); - break; - - case L2CAP_ECHO_REQ: - l2cap_echo_response(l2cap, params, len); - break; - - case L2CAP_ECHO_RSP: - /* We never issue Echo Requests currently. TODO */ - error_report("%s: unexpected Echo Response (%02x) " - "packet, ignoring.", __func__, id); - break; - - case L2CAP_INFO_REQ: - if (unlikely(len != L2CAP_INFO_REQ_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_info(l2cap, le16_to_cpu(((l2cap_info_req *) params)->type)); - break; - - case L2CAP_INFO_RSP: - if (unlikely(len != L2CAP_INFO_RSP_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue Information Requests currently. TODO */ - error_report("%s: unexpected Information Response (%02x) " - "packet, ignoring.", __func__, id); - break; - - default: - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - reject: - l2cap_command_reject(l2cap, id, err, 0, 0); - break; - } -} - -static void l2cap_rexmit_enable(struct l2cap_chan_s *ch, int enable) -{ - ch->rexmit = enable; - - l2cap_retransmission_timer_update(ch); - l2cap_monitor_timer_update(ch); -} - -/* Command frame SDU */ -static void l2cap_cframe_in(void *opaque, const uint8_t *data, int len) -{ - struct l2cap_instance_s *l2cap = opaque; - const l2cap_cmd_hdr *hdr; - int clen; - - while (len) { - hdr = (void *) data; - if (len < L2CAP_CMD_HDR_SIZE) - /* TODO: signal an error */ - return; - len -= L2CAP_CMD_HDR_SIZE; - data += L2CAP_CMD_HDR_SIZE; - - clen = le16_to_cpu(hdr->len); - if (len < clen) { - l2cap_command_reject(l2cap, hdr->ident, - L2CAP_REJ_CMD_NOT_UNDERSTOOD, 0, 0); - break; - } - - l2cap_command(l2cap, hdr->code, hdr->ident, data, clen); - len -= clen; - data += clen; - } -} - -/* Group frame SDU */ -static void l2cap_gframe_in(void *opaque, const uint8_t *data, int len) -{ -} - -/* Supervisory frame */ -static void l2cap_sframe_in(struct l2cap_chan_s *ch, uint16_t ctrl) -{ -} - -/* Basic L2CAP mode Information frame */ -static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len) -{ - /* We have a full SDU, no further processing */ - ch->params.sdu_in(ch->params.opaque, hdr->data, len); -} - -/* Flow Control and Retransmission mode frame */ -static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len) -{ - uint16_t fcs = lduw_le_p(hdr->data + len - 2); - - if (len < 4) - goto len_error; - if (l2cap_fcs16((const uint8_t *) hdr, L2CAP_HDR_SIZE + len - 2) != fcs) - goto fcs_error; - - if ((hdr->data[0] >> 7) == ch->rexmit) - l2cap_rexmit_enable(ch, !(hdr->data[0] >> 7)); - - if (hdr->data[0] & 1) { - if (len != 4) { - /* TODO: Signal an error? */ - return; - } - l2cap_sframe_in(ch, lduw_le_p(hdr->data)); - return; - } - - switch (hdr->data[1] >> 6) { /* SAR */ - case L2CAP_SAR_NO_SEG: - if (ch->len_total) - goto seg_error; - if (len - 4 > ch->mps) - goto len_error; - - ch->params.sdu_in(ch->params.opaque, hdr->data + 2, len - 4); - break; - - case L2CAP_SAR_START: - if (ch->len_total || len < 6) - goto seg_error; - if (len - 6 > ch->mps) - goto len_error; - - ch->len_total = lduw_le_p(hdr->data + 2); - if (len >= 6 + ch->len_total) - goto seg_error; - - ch->len_cur = len - 6; - memcpy(ch->sdu, hdr->data + 4, ch->len_cur); - break; - - case L2CAP_SAR_END: - if (!ch->len_total || ch->len_cur + len - 4 < ch->len_total) - goto seg_error; - if (len - 4 > ch->mps) - goto len_error; - - memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4); - ch->params.sdu_in(ch->params.opaque, ch->sdu, ch->len_total); - break; - - case L2CAP_SAR_CONT: - if (!ch->len_total || ch->len_cur + len - 4 >= ch->len_total) - goto seg_error; - if (len - 4 > ch->mps) - goto len_error; - - memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4); - ch->len_cur += len - 4; - break; - - seg_error: - len_error: /* TODO */ - fcs_error: /* TODO */ - ch->len_cur = 0; - ch->len_total = 0; - break; - } -} - -static void l2cap_frame_in(struct l2cap_instance_s *l2cap, - const l2cap_hdr *frame) -{ - uint16_t cid = le16_to_cpu(frame->cid); - uint16_t len = le16_to_cpu(frame->len); - - if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { - error_report("%s: frame addressed to a non-existent L2CAP " - "channel %04x received.", __func__, cid); - return; - } - - l2cap->cid[cid]->frame_in(l2cap->cid[cid], cid, frame, len); -} - -/* "Recombination" */ -static void l2cap_pdu_in(struct l2cap_instance_s *l2cap, - const uint8_t *data, int len) -{ - const l2cap_hdr *hdr = (void *) l2cap->frame_in; - - if (unlikely(len + l2cap->frame_in_len > sizeof(l2cap->frame_in))) { - if (l2cap->frame_in_len < sizeof(l2cap->frame_in)) { - memcpy(l2cap->frame_in + l2cap->frame_in_len, data, - sizeof(l2cap->frame_in) - l2cap->frame_in_len); - l2cap->frame_in_len = sizeof(l2cap->frame_in); - /* TODO: truncate */ - l2cap_frame_in(l2cap, hdr); - } - - return; - } - - memcpy(l2cap->frame_in + l2cap->frame_in_len, data, len); - l2cap->frame_in_len += len; - - if (len >= L2CAP_HDR_SIZE) - if (len >= L2CAP_HDR_SIZE + le16_to_cpu(hdr->len)) - l2cap_frame_in(l2cap, hdr); - /* There is never a start of a new PDU in the same ACL packet, so - * no need to memmove the remaining payload and loop. */ -} - -static inline uint8_t *l2cap_pdu_out(struct l2cap_instance_s *l2cap, - uint16_t cid, uint16_t len) -{ - l2cap_hdr *hdr = (void *) l2cap->frame_out; - - l2cap->frame_out_len = len + L2CAP_HDR_SIZE; - - hdr->cid = cpu_to_le16(cid); - hdr->len = cpu_to_le16(len); - - return l2cap->frame_out + L2CAP_HDR_SIZE; -} - -static inline void l2cap_pdu_submit(struct l2cap_instance_s *l2cap) -{ - /* TODO: Fragmentation */ - (l2cap->role ? - l2cap->link->slave->lmp_acl_data : l2cap->link->host->lmp_acl_resp) - (l2cap->link, l2cap->frame_out, 1, l2cap->frame_out_len); -} - -static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len) -{ - struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm; - - if (len > chan->params.remote_mtu) { - error_report("%s: B-Frame for CID %04x longer than %i octets.", - __func__, - chan->remote_cid, chan->params.remote_mtu); - exit(-1); - } - - return l2cap_pdu_out(chan->l2cap, chan->remote_cid, len); -} - -static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms) -{ - struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parms; - - l2cap_pdu_submit(chan->l2cap); -} - -#if 0 -/* Stub: Only used if an emulated device requests outgoing flow control */ -static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len) -{ - struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm; - - if (len > chan->params.remote_mtu) { - /* TODO: slice into segments and queue each segment as a separate - * I-Frame in a FIFO of I-Frames, local to the CID. */ - } else { - /* TODO: add to the FIFO of I-Frames, local to the CID. */ - /* Possibly we need to return a pointer to a contiguous buffer - * for now and then memcpy from it into FIFOs in l2cap_iframe_submit - * while segmenting at the same time. */ - } - return 0; -} - -static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm) -{ - /* TODO: If flow control indicates clear to send, start submitting the - * invidual I-Frames from the FIFO, but don't remove them from there. - * Kick the appropriate timer until we get an S-Frame, and only then - * remove from FIFO or resubmit and re-kick the timer if the timer - * expired. */ -} -#endif - -static void l2cap_init(struct l2cap_instance_s *l2cap, - struct bt_link_s *link, int role) -{ - l2cap->link = link; - l2cap->role = role; - l2cap->dev = (struct bt_l2cap_device_s *) - (role ? link->host : link->slave); - - l2cap->next_id = 1; - - /* Establish the signalling channel */ - l2cap->signalling_ch.params.sdu_in = l2cap_cframe_in; - l2cap->signalling_ch.params.sdu_out = l2cap_bframe_out; - l2cap->signalling_ch.params.sdu_submit = l2cap_bframe_submit; - l2cap->signalling_ch.params.opaque = l2cap; - l2cap->signalling_ch.params.remote_mtu = 48; - l2cap->signalling_ch.remote_cid = L2CAP_CID_SIGNALLING; - l2cap->signalling_ch.frame_in = l2cap_bframe_in; - l2cap->signalling_ch.mps = 65536; - l2cap->signalling_ch.min_mtu = 48; - l2cap->signalling_ch.mode = L2CAP_MODE_BASIC; - l2cap->signalling_ch.l2cap = l2cap; - l2cap->cid[L2CAP_CID_SIGNALLING] = &l2cap->signalling_ch; - - /* Establish the connection-less data channel */ - l2cap->group_ch.params.sdu_in = l2cap_gframe_in; - l2cap->group_ch.params.opaque = l2cap; - l2cap->group_ch.frame_in = l2cap_bframe_in; - l2cap->group_ch.mps = 65533; - l2cap->group_ch.l2cap = l2cap; - l2cap->group_ch.remote_cid = L2CAP_CID_INVALID; - l2cap->cid[L2CAP_CID_GROUP] = &l2cap->group_ch; -} - -static void l2cap_teardown(struct l2cap_instance_s *l2cap, int send_disconnect) -{ - int cid; - - /* Don't send DISCONNECT if we are currently handling a DISCONNECT - * sent from the other side. */ - if (send_disconnect) { - if (l2cap->role) - l2cap->dev->device.lmp_disconnect_slave(l2cap->link); - /* l2cap->link is invalid from now on. */ - else - l2cap->dev->device.lmp_disconnect_master(l2cap->link); - } - - for (cid = L2CAP_CID_ALLOC; cid < L2CAP_CID_MAX; cid ++) - if (l2cap->cid[cid]) { - l2cap->cid[cid]->params.close(l2cap->cid[cid]->params.opaque); - g_free(l2cap->cid[cid]); - } - - if (l2cap->role) - g_free(l2cap); - else - g_free(l2cap->link); -} - -/* L2CAP glue to lower layers in bluetooth stack (LMP) */ - -static void l2cap_lmp_connection_request(struct bt_link_s *link) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->slave; - struct slave_l2cap_instance_s *l2cap; - - /* Always accept - we only get called if (dev->device->page_scan). */ - - l2cap = g_malloc0(sizeof(struct slave_l2cap_instance_s)); - l2cap->link.slave = &dev->device; - l2cap->link.host = link->host; - l2cap_init(&l2cap->l2cap, &l2cap->link, 0); - - /* Always at the end */ - link->host->reject_reason = 0; - link->host->lmp_connection_complete(&l2cap->link); -} - -/* Stub */ -static void l2cap_lmp_connection_complete(struct bt_link_s *link) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; - struct l2cap_instance_s *l2cap; - - if (dev->device.reject_reason) { - /* Signal to upper layer */ - return; - } - - l2cap = g_malloc0(sizeof(struct l2cap_instance_s)); - l2cap_init(l2cap, link, 1); - - link->acl_mode = acl_active; - - /* Signal to upper layer */ -} - -/* Stub */ -static void l2cap_lmp_disconnect_host(struct bt_link_s *link) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; - struct l2cap_instance_s *l2cap = - /* TODO: Retrieve from upper layer */ (void *) dev; - - /* Signal to upper layer */ - - l2cap_teardown(l2cap, 0); -} - -static void l2cap_lmp_disconnect_slave(struct bt_link_s *link) -{ - struct slave_l2cap_instance_s *l2cap = - (struct slave_l2cap_instance_s *) link; - - l2cap_teardown(&l2cap->l2cap, 0); -} - -static void l2cap_lmp_acl_data_slave(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - struct slave_l2cap_instance_s *l2cap = - (struct slave_l2cap_instance_s *) link; - - if (start) - l2cap->l2cap.frame_in_len = 0; - - l2cap_pdu_in(&l2cap->l2cap, data, len); -} - -/* Stub */ -static void l2cap_lmp_acl_data_host(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; - struct l2cap_instance_s *l2cap = - /* TODO: Retrieve from upper layer */ (void *) dev; - - if (start) - l2cap->frame_in_len = 0; - - l2cap_pdu_in(l2cap, data, len); -} - -static void l2cap_dummy_destroy(struct bt_device_s *dev) -{ - struct bt_l2cap_device_s *l2cap_dev = (struct bt_l2cap_device_s *) dev; - - bt_l2cap_device_done(l2cap_dev); -} - -void bt_l2cap_device_init(struct bt_l2cap_device_s *dev, - struct bt_scatternet_s *net) -{ - bt_device_init(&dev->device, net); - - dev->device.lmp_connection_request = l2cap_lmp_connection_request; - dev->device.lmp_connection_complete = l2cap_lmp_connection_complete; - dev->device.lmp_disconnect_master = l2cap_lmp_disconnect_host; - dev->device.lmp_disconnect_slave = l2cap_lmp_disconnect_slave; - dev->device.lmp_acl_data = l2cap_lmp_acl_data_slave; - dev->device.lmp_acl_resp = l2cap_lmp_acl_data_host; - - dev->device.handle_destroy = l2cap_dummy_destroy; -} - -void bt_l2cap_device_done(struct bt_l2cap_device_s *dev) -{ - bt_device_done(&dev->device); - - /* Should keep a list of all instances and go through it and - * invoke l2cap_teardown() for each. */ -} - -void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm, int min_mtu, - int (*new_channel)(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params)) -{ - struct bt_l2cap_psm_s *new_psm = l2cap_psm(dev, psm); - - if (new_psm) { - error_report("%s: PSM %04x already registered for device `%s'.", - __func__, psm, dev->device.lmp_name); - exit(-1); - } - - new_psm = g_malloc0(sizeof(*new_psm)); - new_psm->psm = psm; - new_psm->min_mtu = min_mtu; - new_psm->new_channel = new_channel; - new_psm->next = dev->first_psm; - dev->first_psm = new_psm; -} diff --git a/hw/bt/sdp.c b/hw/bt/sdp.c deleted file mode 100644 index 2860d76c85..0000000000 --- a/hw/bt/sdp.c +++ /dev/null @@ -1,989 +0,0 @@ -/* - * Service Discover Protocol server for QEMU L2CAP devices - * - * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/host-utils.h" -#include "hw/bt.h" - -struct bt_l2cap_sdp_state_s { - struct bt_l2cap_conn_params_s *channel; - - struct sdp_service_record_s { - int match; - - int *uuid; - int uuids; - struct sdp_service_attribute_s { - int match; - - int attribute_id; - int len; - void *pair; - } *attribute_list; - int attributes; - } *service_list; - int services; -}; - -static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left) -{ - uint32_t len = *(*element) ++ & SDP_DSIZE_MASK; - - if (!*left) - return -1; - (*left) --; - - if (len < SDP_DSIZE_NEXT1) - return 1 << len; - else if (len == SDP_DSIZE_NEXT1) { - if (*left < 1) - return -1; - (*left) --; - - return *(*element) ++; - } else if (len == SDP_DSIZE_NEXT2) { - if (*left < 2) - return -1; - (*left) -= 2; - - len = (*(*element) ++) << 8; - return len | (*(*element) ++); - } else { - if (*left < 4) - return -1; - (*left) -= 4; - - len = (*(*element) ++) << 24; - len |= (*(*element) ++) << 16; - len |= (*(*element) ++) << 8; - return len | (*(*element) ++); - } -} - -static const uint8_t bt_base_uuid[12] = { - 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb, -}; - -static int sdp_uuid_match(struct sdp_service_record_s *record, - const uint8_t *uuid, ssize_t datalen) -{ - int *lo, hi, val; - - if (datalen == 16 || datalen == 4) { - if (datalen == 16 && memcmp(uuid + 4, bt_base_uuid, 12)) - return 0; - - if (uuid[0] | uuid[1]) - return 0; - uuid += 2; - } - - val = (uuid[0] << 8) | uuid[1]; - lo = record->uuid; - hi = record->uuids; - while (hi >>= 1) - if (lo[hi] <= val) - lo += hi; - - return *lo == val; -} - -#define CONTINUATION_PARAM_SIZE (1 + sizeof(int)) -#define MAX_PDU_OUT_SIZE 96 /* Arbitrary */ -#define PDU_HEADER_SIZE 5 -#define MAX_RSP_PARAM_SIZE (MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE - \ - CONTINUATION_PARAM_SIZE) - -static int sdp_svc_match(struct bt_l2cap_sdp_state_s *sdp, - const uint8_t **req, ssize_t *len) -{ - size_t datalen; - int i; - - if ((**req & ~SDP_DSIZE_MASK) != SDP_DTYPE_UUID) - return 1; - - datalen = sdp_datalen(req, len); - if (datalen != 2 && datalen != 4 && datalen != 16) - return 1; - - for (i = 0; i < sdp->services; i ++) - if (sdp_uuid_match(&sdp->service_list[i], *req, datalen)) - sdp->service_list[i].match = 1; - - (*req) += datalen; - (*len) -= datalen; - - return 0; -} - -static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp, - uint8_t *rsp, const uint8_t *req, ssize_t len) -{ - ssize_t seqlen; - int i, count, start, end, max; - int32_t handle; - - /* Perform the search */ - for (i = 0; i < sdp->services; i ++) - sdp->service_list[i].match = 0; - - if (len < 1) - return -SDP_INVALID_SYNTAX; - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - if (seqlen < 3 || len < seqlen) - return -SDP_INVALID_SYNTAX; - len -= seqlen; - while (seqlen) - if (sdp_svc_match(sdp, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; - } else { - if (sdp_svc_match(sdp, &req, &len)) { - return -SDP_INVALID_SYNTAX; - } - } - - if (len < 3) - return -SDP_INVALID_SYNTAX; - max = (req[0] << 8) | req[1]; - req += 2; - len -= 2; - - if (*req) { - if (len <= sizeof(int)) - return -SDP_INVALID_SYNTAX; - len -= sizeof(int); - memcpy(&start, req + 1, sizeof(int)); - } else - start = 0; - - if (len > 1) - return -SDP_INVALID_SYNTAX; - - /* Output the results */ - len = 4; - count = 0; - end = start; - for (i = 0; i < sdp->services; i ++) - if (sdp->service_list[i].match) { - if (count >= start && count < max && len + 4 < MAX_RSP_PARAM_SIZE) { - handle = i; - memcpy(rsp + len, &handle, 4); - len += 4; - end = count + 1; - } - - count ++; - } - - rsp[0] = count >> 8; - rsp[1] = count & 0xff; - rsp[2] = (end - start) >> 8; - rsp[3] = (end - start) & 0xff; - - if (end < count) { - rsp[len ++] = sizeof(int); - memcpy(rsp + len, &end, sizeof(int)); - len += 4; - } else - rsp[len ++] = 0; - - return len; -} - -static int sdp_attr_match(struct sdp_service_record_s *record, - const uint8_t **req, ssize_t *len) -{ - int i, start, end; - - if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) { - (*req) ++; - if (*len < 3) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = start; - *len -= 3; - } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) { - (*req) ++; - if (*len < 5) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = (*(*req) ++) << 8; - end |= *(*req) ++; - *len -= 5; - } else - return 1; - - for (i = 0; i < record->attributes; i ++) - if (record->attribute_list[i].attribute_id >= start && - record->attribute_list[i].attribute_id <= end) - record->attribute_list[i].match = 1; - - return 0; -} - -static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp, - uint8_t *rsp, const uint8_t *req, ssize_t len) -{ - ssize_t seqlen; - int i, start, end, max; - int32_t handle; - struct sdp_service_record_s *record; - uint8_t *lst; - - /* Perform the search */ - if (len < 7) - return -SDP_INVALID_SYNTAX; - memcpy(&handle, req, 4); - req += 4; - len -= 4; - - if (handle < 0 || handle > sdp->services) - return -SDP_INVALID_RECORD_HANDLE; - record = &sdp->service_list[handle]; - - for (i = 0; i < record->attributes; i ++) - record->attribute_list[i].match = 0; - - max = (req[0] << 8) | req[1]; - req += 2; - len -= 2; - if (max < 0x0007) - return -SDP_INVALID_SYNTAX; - - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - if (seqlen < 3 || len < seqlen) - return -SDP_INVALID_SYNTAX; - len -= seqlen; - - while (seqlen) - if (sdp_attr_match(record, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; - } else { - if (sdp_attr_match(record, &req, &len)) { - return -SDP_INVALID_SYNTAX; - } - } - - if (len < 1) - return -SDP_INVALID_SYNTAX; - - if (*req) { - if (len <= sizeof(int)) - return -SDP_INVALID_SYNTAX; - len -= sizeof(int); - memcpy(&start, req + 1, sizeof(int)); - } else - start = 0; - - if (len > 1) - return -SDP_INVALID_SYNTAX; - - /* Output the results */ - lst = rsp + 2; - max = MIN(max, MAX_RSP_PARAM_SIZE); - len = 3 - start; - end = 0; - for (i = 0; i < record->attributes; i ++) - if (record->attribute_list[i].match) { - if (len >= 0 && len + record->attribute_list[i].len < max) { - memcpy(lst + len, record->attribute_list[i].pair, - record->attribute_list[i].len); - end = len + record->attribute_list[i].len; - } - len += record->attribute_list[i].len; - } - if (0 >= start) { - lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; - lst[1] = (len + start - 3) >> 8; - lst[2] = (len + start - 3) & 0xff; - } - - rsp[0] = end >> 8; - rsp[1] = end & 0xff; - - if (end < len) { - len = end + start; - lst[end ++] = sizeof(int); - memcpy(lst + end, &len, sizeof(int)); - end += sizeof(int); - } else - lst[end ++] = 0; - - return end + 2; -} - -static int sdp_svc_attr_match(struct bt_l2cap_sdp_state_s *sdp, - const uint8_t **req, ssize_t *len) -{ - int i, j, start, end; - struct sdp_service_record_s *record; - - if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) { - (*req) ++; - if (*len < 3) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = start; - *len -= 3; - } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) { - (*req) ++; - if (*len < 5) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = (*(*req) ++) << 8; - end |= *(*req) ++; - *len -= 5; - } else - return 1; - - for (i = 0; i < sdp->services; i ++) - if ((record = &sdp->service_list[i])->match) - for (j = 0; j < record->attributes; j ++) - if (record->attribute_list[j].attribute_id >= start && - record->attribute_list[j].attribute_id <= end) - record->attribute_list[j].match = 1; - - return 0; -} - -static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp, - uint8_t *rsp, const uint8_t *req, ssize_t len) -{ - ssize_t seqlen; - int i, j, start, end, max; - struct sdp_service_record_s *record; - uint8_t *lst; - - /* Perform the search */ - for (i = 0; i < sdp->services; i ++) { - sdp->service_list[i].match = 0; - for (j = 0; j < sdp->service_list[i].attributes; j ++) - sdp->service_list[i].attribute_list[j].match = 0; - } - - if (len < 1) - return -SDP_INVALID_SYNTAX; - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - if (seqlen < 3 || len < seqlen) - return -SDP_INVALID_SYNTAX; - len -= seqlen; - - while (seqlen) - if (sdp_svc_match(sdp, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; - } else { - if (sdp_svc_match(sdp, &req, &len)) { - return -SDP_INVALID_SYNTAX; - } - } - - if (len < 3) - return -SDP_INVALID_SYNTAX; - max = (req[0] << 8) | req[1]; - req += 2; - len -= 2; - if (max < 0x0007) - return -SDP_INVALID_SYNTAX; - - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - if (seqlen < 3 || len < seqlen) - return -SDP_INVALID_SYNTAX; - len -= seqlen; - - while (seqlen) - if (sdp_svc_attr_match(sdp, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; - } else { - if (sdp_svc_attr_match(sdp, &req, &len)) { - return -SDP_INVALID_SYNTAX; - } - } - - if (len < 1) - return -SDP_INVALID_SYNTAX; - - if (*req) { - if (len <= sizeof(int)) - return -SDP_INVALID_SYNTAX; - len -= sizeof(int); - memcpy(&start, req + 1, sizeof(int)); - } else - start = 0; - - if (len > 1) - return -SDP_INVALID_SYNTAX; - - /* Output the results */ - /* This assumes empty attribute lists are never to be returned even - * for matching Service Records. In practice this shouldn't happen - * as the requestor will usually include the always present - * ServiceRecordHandle AttributeID in AttributeIDList. */ - lst = rsp + 2; - max = MIN(max, MAX_RSP_PARAM_SIZE); - len = 3 - start; - end = 0; - for (i = 0; i < sdp->services; i ++) - if ((record = &sdp->service_list[i])->match) { - len += 3; - seqlen = len; - for (j = 0; j < record->attributes; j ++) - if (record->attribute_list[j].match) { - if (len >= 0) - if (len + record->attribute_list[j].len < max) { - memcpy(lst + len, record->attribute_list[j].pair, - record->attribute_list[j].len); - end = len + record->attribute_list[j].len; - } - len += record->attribute_list[j].len; - } - if (seqlen == len) - len -= 3; - else if (seqlen >= 3 && seqlen < max) { - lst[seqlen - 3] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; - lst[seqlen - 2] = (len - seqlen) >> 8; - lst[seqlen - 1] = (len - seqlen) & 0xff; - } - } - if (len == 3 - start) - len -= 3; - else if (0 >= start) { - lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; - lst[1] = (len + start - 3) >> 8; - lst[2] = (len + start - 3) & 0xff; - } - - rsp[0] = end >> 8; - rsp[1] = end & 0xff; - - if (end < len) { - len = end + start; - lst[end ++] = sizeof(int); - memcpy(lst + end, &len, sizeof(int)); - end += sizeof(int); - } else - lst[end ++] = 0; - - return end + 2; -} - -static void bt_l2cap_sdp_sdu_in(void *opaque, const uint8_t *data, int len) -{ - struct bt_l2cap_sdp_state_s *sdp = opaque; - enum bt_sdp_cmd pdu_id; - uint8_t rsp[MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE], *sdu_out; - int transaction_id, plen; - int err = 0; - int rsp_len = 0; - - if (len < 5) { - error_report("%s: short SDP PDU (%iB).", __func__, len); - return; - } - - pdu_id = *data ++; - transaction_id = (data[0] << 8) | data[1]; - plen = (data[2] << 8) | data[3]; - data += 4; - len -= 5; - - if (len != plen) { - error_report("%s: wrong SDP PDU length (%iB != %iB).", - __func__, plen, len); - err = SDP_INVALID_PDU_SIZE; - goto respond; - } - - switch (pdu_id) { - case SDP_SVC_SEARCH_REQ: - rsp_len = sdp_svc_search(sdp, rsp, data, len); - pdu_id = SDP_SVC_SEARCH_RSP; - break; - - case SDP_SVC_ATTR_REQ: - rsp_len = sdp_attr_get(sdp, rsp, data, len); - pdu_id = SDP_SVC_ATTR_RSP; - break; - - case SDP_SVC_SEARCH_ATTR_REQ: - rsp_len = sdp_svc_search_attr_get(sdp, rsp, data, len); - pdu_id = SDP_SVC_SEARCH_ATTR_RSP; - break; - - case SDP_ERROR_RSP: - case SDP_SVC_ATTR_RSP: - case SDP_SVC_SEARCH_RSP: - case SDP_SVC_SEARCH_ATTR_RSP: - default: - error_report("%s: unexpected SDP PDU ID %02x.", - __func__, pdu_id); - err = SDP_INVALID_SYNTAX; - break; - } - - if (rsp_len < 0) { - err = -rsp_len; - rsp_len = 0; - } - -respond: - if (err) { - pdu_id = SDP_ERROR_RSP; - rsp[rsp_len ++] = err >> 8; - rsp[rsp_len ++] = err & 0xff; - } - - sdu_out = sdp->channel->sdu_out(sdp->channel, rsp_len + PDU_HEADER_SIZE); - - sdu_out[0] = pdu_id; - sdu_out[1] = transaction_id >> 8; - sdu_out[2] = transaction_id & 0xff; - sdu_out[3] = rsp_len >> 8; - sdu_out[4] = rsp_len & 0xff; - memcpy(sdu_out + PDU_HEADER_SIZE, rsp, rsp_len); - - sdp->channel->sdu_submit(sdp->channel); -} - -static void bt_l2cap_sdp_close_ch(void *opaque) -{ - struct bt_l2cap_sdp_state_s *sdp = opaque; - int i; - - for (i = 0; i < sdp->services; i ++) { - g_free(sdp->service_list[i].attribute_list[0].pair); - g_free(sdp->service_list[i].attribute_list); - g_free(sdp->service_list[i].uuid); - } - g_free(sdp->service_list); - g_free(sdp); -} - -struct sdp_def_service_s { - uint16_t class_uuid; - struct sdp_def_attribute_s { - uint16_t id; - struct sdp_def_data_element_s { - uint8_t type; - union { - uint32_t uint; - const char *str; - struct sdp_def_data_element_s *list; - } value; - } data; - } attributes[]; -}; - -/* Calculate a safe byte count to allocate that will store the given - * element, at the same time count elements of a UUID type. */ -static int sdp_attr_max_size(struct sdp_def_data_element_s *element, - int *uuids) -{ - int type = element->type & ~SDP_DSIZE_MASK; - int len; - - if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_UUID || - type == SDP_DTYPE_BOOL) { - if (type == SDP_DTYPE_UUID) - (*uuids) ++; - return 1 + (1 << (element->type & SDP_DSIZE_MASK)); - } - - if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) { - if (element->type & SDP_DSIZE_MASK) { - for (len = 0; element->value.str[len] | - element->value.str[len + 1]; len ++); - return len; - } else - return 2 + strlen(element->value.str); - } - - if (type != SDP_DTYPE_SEQ) - exit(-1); - len = 2; - element = element->value.list; - while (element->type) - len += sdp_attr_max_size(element ++, uuids); - if (len > 255) - exit (-1); - - return len; -} - -static int sdp_attr_write(uint8_t *data, - struct sdp_def_data_element_s *element, int **uuid) -{ - int type = element->type & ~SDP_DSIZE_MASK; - int len = 0; - - if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_BOOL) { - data[len ++] = element->type; - if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_1) - data[len ++] = (element->value.uint >> 0) & 0xff; - else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_2) { - data[len ++] = (element->value.uint >> 8) & 0xff; - data[len ++] = (element->value.uint >> 0) & 0xff; - } else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_4) { - data[len ++] = (element->value.uint >> 24) & 0xff; - data[len ++] = (element->value.uint >> 16) & 0xff; - data[len ++] = (element->value.uint >> 8) & 0xff; - data[len ++] = (element->value.uint >> 0) & 0xff; - } - - return len; - } - - if (type == SDP_DTYPE_UUID) { - *(*uuid) ++ = element->value.uint; - - data[len ++] = element->type; - data[len ++] = (element->value.uint >> 24) & 0xff; - data[len ++] = (element->value.uint >> 16) & 0xff; - data[len ++] = (element->value.uint >> 8) & 0xff; - data[len ++] = (element->value.uint >> 0) & 0xff; - memcpy(data + len, bt_base_uuid, 12); - - return len + 12; - } - - data[0] = type | SDP_DSIZE_NEXT1; - if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) { - if (element->type & SDP_DSIZE_MASK) - for (len = 0; element->value.str[len] | - element->value.str[len + 1]; len ++); - else - len = strlen(element->value.str); - memcpy(data + 2, element->value.str, data[1] = len); - - return len + 2; - } - - len = 2; - element = element->value.list; - while (element->type) - len += sdp_attr_write(data + len, element ++, uuid); - data[1] = len - 2; - - return len; -} - -static int sdp_attributeid_compare(const struct sdp_service_attribute_s *a, - const struct sdp_service_attribute_s *b) -{ - return (int) b->attribute_id - a->attribute_id; -} - -static int sdp_uuid_compare(const int *a, const int *b) -{ - return *a - *b; -} - -static void sdp_service_record_build(struct sdp_service_record_s *record, - struct sdp_def_service_s *def, int handle) -{ - int len = 0; - uint8_t *data; - int *uuid; - - record->uuids = 0; - while (def->attributes[record->attributes].data.type) { - len += 3; - len += sdp_attr_max_size(&def->attributes[record->attributes ++].data, - &record->uuids); - } - - assert(len > 0); - record->uuids = pow2ceil(record->uuids); - record->attribute_list = - g_malloc0(record->attributes * sizeof(*record->attribute_list)); - record->uuid = - g_malloc0(record->uuids * sizeof(*record->uuid)); - data = g_malloc(len); - - record->attributes = 0; - uuid = record->uuid; - while (def->attributes[record->attributes].data.type) { - int attribute_id = def->attributes[record->attributes].id; - record->attribute_list[record->attributes].pair = data; - record->attribute_list[record->attributes].attribute_id = attribute_id; - - len = 0; - data[len ++] = SDP_DTYPE_UINT | SDP_DSIZE_2; - data[len ++] = attribute_id >> 8; - data[len ++] = attribute_id & 0xff; - len += sdp_attr_write(data + len, - &def->attributes[record->attributes].data, &uuid); - - /* Special case: assign a ServiceRecordHandle in sequence */ - if (def->attributes[record->attributes].id == SDP_ATTR_RECORD_HANDLE) - def->attributes[record->attributes].data.value.uint = handle; - /* Note: we could also assign a ServiceDescription based on - * sdp->device.device->lmp_name. */ - - record->attribute_list[record->attributes ++].len = len; - data += len; - } - - /* Sort the attribute list by the AttributeID. The first must be - * SDP_ATTR_RECORD_HANDLE so that bt_l2cap_sdp_close_ch can free - * the buffer. - */ - qsort(record->attribute_list, record->attributes, - sizeof(*record->attribute_list), - (void *) sdp_attributeid_compare); - assert(record->attribute_list[0].pair == data); - - /* Sort the searchable UUIDs list for bisection */ - qsort(record->uuid, record->uuids, - sizeof(*record->uuid), - (void *) sdp_uuid_compare); -} - -static void sdp_service_db_build(struct bt_l2cap_sdp_state_s *sdp, - struct sdp_def_service_s **service) -{ - sdp->services = 0; - while (service[sdp->services]) - sdp->services ++; - sdp->service_list = - g_malloc0(sdp->services * sizeof(*sdp->service_list)); - - sdp->services = 0; - while (*service) { - sdp_service_record_build(&sdp->service_list[sdp->services], - *service, sdp->services); - service ++; - sdp->services ++; - } -} - -#define LAST { .type = 0 } -#define SERVICE(name, attrs) \ - static struct sdp_def_service_s glue(glue(sdp_service_, name), _s) = { \ - .attributes = { attrs { .data = LAST } }, \ - }; -#define ATTRIBUTE(attrid, val) { .id = glue(SDP_ATTR_, attrid), .data = val }, -#define UINT8(val) { \ - .type = SDP_DTYPE_UINT | SDP_DSIZE_1, \ - .value.uint = val, \ - }, -#define UINT16(val) { \ - .type = SDP_DTYPE_UINT | SDP_DSIZE_2, \ - .value.uint = val, \ - }, -#define UINT32(val) { \ - .type = SDP_DTYPE_UINT | SDP_DSIZE_4, \ - .value.uint = val, \ - }, -#define UUID128(val) { \ - .type = SDP_DTYPE_UUID | SDP_DSIZE_16, \ - .value.uint = val, \ - }, -#define SDP_TRUE { \ - .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ - .value.uint = 1, \ - }, -#define SDP_FALSE { \ - .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ - .value.uint = 0, \ - }, -#define STRING(val) { \ - .type = SDP_DTYPE_STRING, \ - .value.str = val, \ - }, -#define ARRAY(...) { \ - .type = SDP_DTYPE_STRING | SDP_DSIZE_2, \ - .value.str = (char []) { __VA_ARGS__, 0, 0 }, \ - }, -#define URL(val) { \ - .type = SDP_DTYPE_URL, \ - .value.str = val, \ - }, -#if 1 -#define LIST(val) { \ - .type = SDP_DTYPE_SEQ, \ - .value.list = (struct sdp_def_data_element_s []) { val LAST }, \ - }, -#endif - -/* Try to keep each single attribute below MAX_PDU_OUT_SIZE bytes - * in resulting SDP data representation size. */ - -SERVICE(hid, - ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ - ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(HID_SVCLASS_ID))) - ATTRIBUTE(RECORD_STATE, UINT32(1)) - ATTRIBUTE(PROTO_DESC_LIST, LIST( - LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_HID_CTRL)) - LIST(UUID128(HIDP_UUID)) - )) - ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) - ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( - UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) - )) - ATTRIBUTE(PFILE_DESC_LIST, LIST( - LIST(UUID128(HID_PROFILE_ID) UINT16(0x0100)) - )) - ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) - ATTRIBUTE(SVCNAME_PRIMARY, STRING("QEMU Bluetooth HID")) - ATTRIBUTE(SVCDESC_PRIMARY, STRING("QEMU Keyboard/Mouse")) - ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU")) - - /* Profile specific */ - ATTRIBUTE(DEVICE_RELEASE_NUMBER, UINT16(0x0091)) /* Deprecated, remove */ - ATTRIBUTE(PARSER_VERSION, UINT16(0x0111)) - /* TODO: extract from l2cap_device->device.class[0] */ - ATTRIBUTE(DEVICE_SUBCLASS, UINT8(0x40)) - ATTRIBUTE(COUNTRY_CODE, UINT8(0x15)) - ATTRIBUTE(VIRTUAL_CABLE, SDP_TRUE) - ATTRIBUTE(RECONNECT_INITIATE, SDP_FALSE) - /* TODO: extract from hid->usbdev->report_desc */ - ATTRIBUTE(DESCRIPTOR_LIST, LIST( - LIST(UINT8(0x22) ARRAY( - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x06, /* Usage (Keyboard) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x75, 0x01, /* Report Size (1) */ - 0x95, 0x08, /* Report Count (8) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0xe0, /* Usage Minimum (224) */ - 0x29, 0xe7, /* Usage Maximum (231) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x08, /* Report Size (8) */ - 0x81, 0x01, /* Input (Constant) */ - 0x95, 0x05, /* Report Count (5) */ - 0x75, 0x01, /* Report Size (1) */ - 0x05, 0x08, /* Usage Page (LEDs) */ - 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x05, /* Usage Maximum (5) */ - 0x91, 0x02, /* Output (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x03, /* Report Size (3) */ - 0x91, 0x01, /* Output (Constant) */ - 0x95, 0x06, /* Report Count (6) */ - 0x75, 0x08, /* Report Size (8) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0xff, /* Logical Maximum (255) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0x00, /* Usage Minimum (0) */ - 0x29, 0xff, /* Usage Maximum (255) */ - 0x81, 0x00, /* Input (Data, Array) */ - 0xc0 /* End Collection */ - )))) - ATTRIBUTE(LANG_ID_BASE_LIST, LIST( - LIST(UINT16(0x0409) UINT16(0x0100)) - )) - ATTRIBUTE(SDP_DISABLE, SDP_FALSE) - ATTRIBUTE(BATTERY_POWER, SDP_TRUE) - ATTRIBUTE(REMOTE_WAKEUP, SDP_TRUE) - ATTRIBUTE(BOOT_DEVICE, SDP_TRUE) /* XXX: untested */ - ATTRIBUTE(SUPERVISION_TIMEOUT, UINT16(0x0c80)) - ATTRIBUTE(NORMALLY_CONNECTABLE, SDP_TRUE) - ATTRIBUTE(PROFILE_VERSION, UINT16(0x0100)) -) - -SERVICE(sdp, - ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ - ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(SDP_SERVER_SVCLASS_ID))) - ATTRIBUTE(RECORD_STATE, UINT32(1)) - ATTRIBUTE(PROTO_DESC_LIST, LIST( - LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP)) - LIST(UUID128(SDP_UUID)) - )) - ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) - ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( - UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) - )) - ATTRIBUTE(PFILE_DESC_LIST, LIST( - LIST(UUID128(SDP_SERVER_PROFILE_ID) UINT16(0x0100)) - )) - ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) - ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU")) - - /* Profile specific */ - ATTRIBUTE(VERSION_NUM_LIST, LIST(UINT16(0x0100))) - ATTRIBUTE(SVCDB_STATE , UINT32(1)) -) - -SERVICE(pnp, - ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ - ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(PNP_INFO_SVCLASS_ID))) - ATTRIBUTE(RECORD_STATE, UINT32(1)) - ATTRIBUTE(PROTO_DESC_LIST, LIST( - LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP)) - LIST(UUID128(SDP_UUID)) - )) - ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) - ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( - UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) - )) - ATTRIBUTE(PFILE_DESC_LIST, LIST( - LIST(UUID128(PNP_INFO_PROFILE_ID) UINT16(0x0100)) - )) - ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) - ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU")) - - /* Profile specific */ - ATTRIBUTE(SPECIFICATION_ID, UINT16(0x0100)) - ATTRIBUTE(VERSION, UINT16(0x0100)) - ATTRIBUTE(PRIMARY_RECORD, SDP_TRUE) -) - -static int bt_l2cap_sdp_new_ch(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params) -{ - struct bt_l2cap_sdp_state_s *sdp = g_malloc0(sizeof(*sdp)); - struct sdp_def_service_s *services[] = { - &sdp_service_sdp_s, - &sdp_service_hid_s, - &sdp_service_pnp_s, - NULL, - }; - - sdp->channel = params; - sdp->channel->opaque = sdp; - sdp->channel->close = bt_l2cap_sdp_close_ch; - sdp->channel->sdu_in = bt_l2cap_sdp_sdu_in; - - sdp_service_db_build(sdp, services); - - return 0; -} - -void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev) -{ - bt_l2cap_psm_register(dev, BT_PSM_SDP, - MAX_PDU_OUT_SIZE, bt_l2cap_sdp_new_ch); -} diff --git a/include/hw/bt.h b/include/hw/bt.h deleted file mode 100644 index d9ee2fc29a..0000000000 --- a/include/hw/bt.h +++ /dev/null @@ -1,2177 +0,0 @@ -/* - * QEMU Bluetooth HCI helpers. - * - * Copyright (C) 2007 OpenMoko, Inc. - * Written by Andrzej Zaborowski <andrew@openedhand.com> - * - * Useful definitions taken from BlueZ project's headers. - * Copyright (C) 2000-2001 Qualcomm Incorporated - * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> - * Copyright (C) 2002-2006 Marcel Holtmann <marcel@holtmann.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef HW_BT_H -#define HW_BT_H - - -/* BD Address */ -typedef struct { - uint8_t b[6]; -} QEMU_PACKED bdaddr_t; - -#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) -#define BDADDR_ALL (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}) -#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}}) - -/* Copy, swap, convert BD Address */ -static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2) -{ - return memcmp(ba1, ba2, sizeof(bdaddr_t)); -} -static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src) -{ - memcpy(dst, src, sizeof(bdaddr_t)); -} - -#define BAINIT(orig) { .b = { \ - (orig)->b[0], (orig)->b[1], (orig)->b[2], \ - (orig)->b[3], (orig)->b[4], (orig)->b[5], \ -}, } - -/* The twisted structures of a bluetooth environment */ -struct bt_device_s; -struct bt_scatternet_s; -struct bt_piconet_s; -struct bt_link_s; - -struct bt_scatternet_s { - struct bt_device_s *slave; -}; - -struct bt_link_s { - struct bt_device_s *slave, *host; - uint16_t handle; /* Master (host) side handle */ - uint16_t acl_interval; - enum { - acl_active, - acl_hold, - acl_sniff, - acl_parked, - } acl_mode; -}; - -struct bt_device_s { - int lt_addr; - bdaddr_t bd_addr; - int mtu; - int setup; - struct bt_scatternet_s *net; - - uint8_t key[16]; - int key_present; - uint8_t class[3]; - - uint8_t reject_reason; - - uint64_t lmp_caps; - const char *lmp_name; - void (*lmp_connection_request)(struct bt_link_s *link); - void (*lmp_connection_complete)(struct bt_link_s *link); - void (*lmp_disconnect_master)(struct bt_link_s *link); - void (*lmp_disconnect_slave)(struct bt_link_s *link); - void (*lmp_acl_data)(struct bt_link_s *link, const uint8_t *data, - int start, int len); - void (*lmp_acl_resp)(struct bt_link_s *link, const uint8_t *data, - int start, int len); - void (*lmp_mode_change)(struct bt_link_s *link); - - void (*handle_destroy)(struct bt_device_s *device); - struct bt_device_s *next; /* Next in the piconet/scatternet */ - - int inquiry_scan; - int page_scan; - - uint16_t clkoff; /* Note: Always little-endian */ -}; - -extern struct HCIInfo null_hci; -/* bt.c */ -void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net); -void bt_device_done(struct bt_device_s *dev); -struct bt_scatternet_s *qemu_find_bt_vlan(int id); - -/* bt-hci.c */ -struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net); -struct HCIInfo *hci_init(const char *str); - -/* bt-vhci.c */ -void bt_vhci_init(struct HCIInfo *info); - -/* bt-hci-csr.c */ -enum { - csrhci_pin_reset, - csrhci_pin_wakeup, - __csrhci_pins, -}; -qemu_irq *csrhci_pins_get(Chardev *chr); -Chardev *uart_hci_init(void); - -/* bt-l2cap.c */ -struct bt_l2cap_device_s; -struct bt_l2cap_conn_params_s; -struct bt_l2cap_psm_s; -void bt_l2cap_device_init(struct bt_l2cap_device_s *dev, - struct bt_scatternet_s *net); -void bt_l2cap_device_done(struct bt_l2cap_device_s *dev); -void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm, - int min_mtu, int (*new_channel)(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params)); - -struct bt_l2cap_device_s { - struct bt_device_s device; - struct bt_l2cap_psm_s *first_psm; -}; - -struct bt_l2cap_conn_params_s { - /* Input */ - uint8_t *(*sdu_out)(struct bt_l2cap_conn_params_s *chan, int len); - void (*sdu_submit)(struct bt_l2cap_conn_params_s *chan); - int remote_mtu; - /* Output */ - void *opaque; - void (*sdu_in)(void *opaque, const uint8_t *data, int len); - void (*close)(void *opaque); -}; - -enum bt_l2cap_psm_predef { - BT_PSM_SDP = 0x0001, - BT_PSM_RFCOMM = 0x0003, - BT_PSM_TELEPHONY = 0x0005, - BT_PSM_TCS = 0x0007, - BT_PSM_BNEP = 0x000f, - BT_PSM_HID_CTRL = 0x0011, - BT_PSM_HID_INTR = 0x0013, - BT_PSM_UPNP = 0x0015, - BT_PSM_AVCTP = 0x0017, - BT_PSM_AVDTP = 0x0019, -}; - -/* bt-sdp.c */ -void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev); - -/* bt-hid.c */ -struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net); - -/* Link Management Protocol layer defines */ - -#define LLID_ACLU_CONT 0x1 -#define LLID_ACLU_START 0x2 -#define LLID_ACLC 0x3 - -enum lmp_pdu_type { - LMP_NAME_REQ = 0x0001, - LMP_NAME_RES = 0x0002, - LMP_ACCEPTED = 0x0003, - LMP_NOT_ACCEPTED = 0x0004, - LMP_CLKOFFSET_REQ = 0x0005, - LMP_CLKOFFSET_RES = 0x0006, - LMP_DETACH = 0x0007, - LMP_IN_RAND = 0x0008, - LMP_COMB_KEY = 0x0009, - LMP_UNIT_KEY = 0x000a, - LMP_AU_RAND = 0x000b, - LMP_SRES = 0x000c, - LMP_TEMP_RAND = 0x000d, - LMP_TEMP_KEY = 0x000e, - LMP_CRYPT_MODE_REQ = 0x000f, - LMP_CRYPT_KEY_SIZE_REQ = 0x0010, - LMP_START_ENCRYPT_REQ = 0x0011, - LMP_STOP_ENCRYPT_REQ = 0x0012, - LMP_SWITCH_REQ = 0x0013, - LMP_HOLD = 0x0014, - LMP_HOLD_REQ = 0x0015, - LMP_SNIFF_REQ = 0x0017, - LMP_UNSNIFF_REQ = 0x0018, - LMP_LMP_PARK_REQ = 0x0019, - LMP_SET_BCAST_SCAN_WND = 0x001b, - LMP_MODIFY_BEACON = 0x001c, - LMP_UNPARK_BD_ADDR_REQ = 0x001d, - LMP_UNPARK_PM_ADDR_REQ = 0x001e, - LMP_INCR_POWER_REQ = 0x001f, - LMP_DECR_POWER_REQ = 0x0020, - LMP_MAX_POWER = 0x0021, - LMP_MIN_POWER = 0x0022, - LMP_AUTO_RATE = 0x0023, - LMP_PREFERRED_RATE = 0x0024, - LMP_VERSION_REQ = 0x0025, - LMP_VERSION_RES = 0x0026, - LMP_FEATURES_REQ = 0x0027, - LMP_FEATURES_RES = 0x0028, - LMP_QUALITY_OF_SERVICE = 0x0029, - LMP_QOS_REQ = 0x002a, - LMP_RM_SCO_LINK_REQ = 0x002b, - LMP_SCO_LINK_REQ = 0x002c, - LMP_MAX_SLOT = 0x002d, - LMP_MAX_SLOT_REQ = 0x002e, - LMP_TIMING_ACCURACY_REQ = 0x002f, - LMP_TIMING_ACCURACY_RES = 0x0030, - LMP_SETUP_COMPLETE = 0x0031, - LMP_USE_SEMIPERM_KEY = 0x0032, - LMP_HOST_CONNECTION_REQ = 0x0033, - LMP_SLOT_OFFSET = 0x0034, - LMP_PAGE_MODE_REQ = 0x0035, - LMP_PAGE_SCAN_MODE_REQ = 0x0036, - LMP_SUPERVISION_TIMEOUT = 0x0037, - LMP_TEST_ACTIVATE = 0x0038, - LMP_TEST_CONTROL = 0x0039, - LMP_CRYPT_KEY_MASK_REQ = 0x003a, - LMP_CRYPT_KEY_MASK_RES = 0x003b, - LMP_SET_AFH = 0x003c, - LMP_ACCEPTED_EXT = 0x7f01, - LMP_NOT_ACCEPTED_EXT = 0x7f02, - LMP_FEATURES_REQ_EXT = 0x7f03, - LMP_FEATURES_RES_EXT = 0x7f04, - LMP_PACKET_TYPE_TBL_REQ = 0x7f0b, - LMP_ESCO_LINK_REQ = 0x7f0c, - LMP_RM_ESCO_LINK_REQ = 0x7f0d, - LMP_CHANNEL_CLASS_REQ = 0x7f10, - LMP_CHANNEL_CLASS = 0x7f11, -}; - -/* Host Controller Interface layer defines */ - -enum hci_packet_type { - HCI_COMMAND_PKT = 0x01, - HCI_ACLDATA_PKT = 0x02, - HCI_SCODATA_PKT = 0x03, - HCI_EVENT_PKT = 0x04, - HCI_VENDOR_PKT = 0xff, -}; - -enum bt_packet_type { - HCI_2DH1 = 1 << 1, - HCI_3DH1 = 1 << 2, - HCI_DM1 = 1 << 3, - HCI_DH1 = 1 << 4, - HCI_2DH3 = 1 << 8, - HCI_3DH3 = 1 << 9, - HCI_DM3 = 1 << 10, - HCI_DH3 = 1 << 11, - HCI_2DH5 = 1 << 12, - HCI_3DH5 = 1 << 13, - HCI_DM5 = 1 << 14, - HCI_DH5 = 1 << 15, -}; - -enum sco_packet_type { - HCI_HV1 = 1 << 5, - HCI_HV2 = 1 << 6, - HCI_HV3 = 1 << 7, -}; - -enum ev_packet_type { - HCI_EV3 = 1 << 3, - HCI_EV4 = 1 << 4, - HCI_EV5 = 1 << 5, - HCI_2EV3 = 1 << 6, - HCI_3EV3 = 1 << 7, - HCI_2EV5 = 1 << 8, - HCI_3EV5 = 1 << 9, -}; - -enum hci_error_code { - HCI_SUCCESS = 0x00, - HCI_UNKNOWN_COMMAND = 0x01, - HCI_NO_CONNECTION = 0x02, - HCI_HARDWARE_FAILURE = 0x03, - HCI_PAGE_TIMEOUT = 0x04, - HCI_AUTHENTICATION_FAILURE = 0x05, - HCI_PIN_OR_KEY_MISSING = 0x06, - HCI_MEMORY_FULL = 0x07, - HCI_CONNECTION_TIMEOUT = 0x08, - HCI_MAX_NUMBER_OF_CONNECTIONS = 0x09, - HCI_MAX_NUMBER_OF_SCO_CONNECTIONS = 0x0a, - HCI_ACL_CONNECTION_EXISTS = 0x0b, - HCI_COMMAND_DISALLOWED = 0x0c, - HCI_REJECTED_LIMITED_RESOURCES = 0x0d, - HCI_REJECTED_SECURITY = 0x0e, - HCI_REJECTED_PERSONAL = 0x0f, - HCI_HOST_TIMEOUT = 0x10, - HCI_UNSUPPORTED_FEATURE = 0x11, - HCI_INVALID_PARAMETERS = 0x12, - HCI_OE_USER_ENDED_CONNECTION = 0x13, - HCI_OE_LOW_RESOURCES = 0x14, - HCI_OE_POWER_OFF = 0x15, - HCI_CONNECTION_TERMINATED = 0x16, - HCI_REPEATED_ATTEMPTS = 0x17, - HCI_PAIRING_NOT_ALLOWED = 0x18, - HCI_UNKNOWN_LMP_PDU = 0x19, - HCI_UNSUPPORTED_REMOTE_FEATURE = 0x1a, - HCI_SCO_OFFSET_REJECTED = 0x1b, - HCI_SCO_INTERVAL_REJECTED = 0x1c, - HCI_AIR_MODE_REJECTED = 0x1d, - HCI_INVALID_LMP_PARAMETERS = 0x1e, - HCI_UNSPECIFIED_ERROR = 0x1f, - HCI_UNSUPPORTED_LMP_PARAMETER_VALUE = 0x20, - HCI_ROLE_CHANGE_NOT_ALLOWED = 0x21, - HCI_LMP_RESPONSE_TIMEOUT = 0x22, - HCI_LMP_ERROR_TRANSACTION_COLLISION = 0x23, - HCI_LMP_PDU_NOT_ALLOWED = 0x24, - HCI_ENCRYPTION_MODE_NOT_ACCEPTED = 0x25, - HCI_UNIT_LINK_KEY_USED = 0x26, - HCI_QOS_NOT_SUPPORTED = 0x27, - HCI_INSTANT_PASSED = 0x28, - HCI_PAIRING_NOT_SUPPORTED = 0x29, - HCI_TRANSACTION_COLLISION = 0x2a, - HCI_QOS_UNACCEPTABLE_PARAMETER = 0x2c, - HCI_QOS_REJECTED = 0x2d, - HCI_CLASSIFICATION_NOT_SUPPORTED = 0x2e, - HCI_INSUFFICIENT_SECURITY = 0x2f, - HCI_PARAMETER_OUT_OF_RANGE = 0x30, - HCI_ROLE_SWITCH_PENDING = 0x32, - HCI_SLOT_VIOLATION = 0x34, - HCI_ROLE_SWITCH_FAILED = 0x35, -}; - -enum acl_flag_bits { - ACL_CONT = 1 << 0, - ACL_START = 1 << 1, - ACL_ACTIVE_BCAST = 1 << 2, - ACL_PICO_BCAST = 1 << 3, -}; - -enum baseband_link_type { - SCO_LINK = 0x00, - ACL_LINK = 0x01, -}; - -enum lmp_feature_bits0 { - LMP_3SLOT = 1 << 0, - LMP_5SLOT = 1 << 1, - LMP_ENCRYPT = 1 << 2, - LMP_SOFFSET = 1 << 3, - LMP_TACCURACY = 1 << 4, - LMP_RSWITCH = 1 << 5, - LMP_HOLD_MODE = 1 << 6, - LMP_SNIFF_MODE = 1 << 7, -}; - -enum lmp_feature_bits1 { - LMP_PARK = 1 << 0, - LMP_RSSI = 1 << 1, - LMP_QUALITY = 1 << 2, - LMP_SCO = 1 << 3, - LMP_HV2 = 1 << 4, - LMP_HV3 = 1 << 5, - LMP_ULAW = 1 << 6, - LMP_ALAW = 1 << 7, -}; - -enum lmp_feature_bits2 { - LMP_CVSD = 1 << 0, - LMP_PSCHEME = 1 << 1, - LMP_PCONTROL = 1 << 2, - LMP_TRSP_SCO = 1 << 3, - LMP_BCAST_ENC = 1 << 7, -}; - -enum lmp_feature_bits3 { - LMP_EDR_ACL_2M = 1 << 1, - LMP_EDR_ACL_3M = 1 << 2, - LMP_ENH_ISCAN = 1 << 3, - LMP_ILACE_ISCAN = 1 << 4, - LMP_ILACE_PSCAN = 1 << 5, - LMP_RSSI_INQ = 1 << 6, - LMP_ESCO = 1 << 7, -}; - -enum lmp_feature_bits4 { - LMP_EV4 = 1 << 0, - LMP_EV5 = 1 << 1, - LMP_AFH_CAP_SLV = 1 << 3, - LMP_AFH_CLS_SLV = 1 << 4, - LMP_EDR_3SLOT = 1 << 7, -}; - -enum lmp_feature_bits5 { - LMP_EDR_5SLOT = 1 << 0, - LMP_SNIFF_SUBR = 1 << 1, - LMP_AFH_CAP_MST = 1 << 3, - LMP_AFH_CLS_MST = 1 << 4, - LMP_EDR_ESCO_2M = 1 << 5, - LMP_EDR_ESCO_3M = 1 << 6, - LMP_EDR_3S_ESCO = 1 << 7, -}; - -enum lmp_feature_bits6 { - LMP_EXT_INQ = 1 << 0, -}; - -enum lmp_feature_bits7 { - LMP_EXT_FEAT = 1 << 7, -}; - -enum hci_link_policy { - HCI_LP_RSWITCH = 1 << 0, - HCI_LP_HOLD = 1 << 1, - HCI_LP_SNIFF = 1 << 2, - HCI_LP_PARK = 1 << 3, -}; - -enum hci_link_mode { - HCI_LM_ACCEPT = 1 << 15, - HCI_LM_MASTER = 1 << 0, - HCI_LM_AUTH = 1 << 1, - HCI_LM_ENCRYPT = 1 << 2, - HCI_LM_TRUSTED = 1 << 3, - HCI_LM_RELIABLE = 1 << 4, - HCI_LM_SECURE = 1 << 5, -}; - -/* HCI Commands */ - -/* Link Control */ -#define OGF_LINK_CTL 0x01 - -#define OCF_INQUIRY 0x0001 -typedef struct { - uint8_t lap[3]; - uint8_t length; /* 1.28s units */ - uint8_t num_rsp; -} QEMU_PACKED inquiry_cp; -#define INQUIRY_CP_SIZE 5 - -typedef struct { - uint8_t status; - bdaddr_t bdaddr; -} QEMU_PACKED status_bdaddr_rp; -#define STATUS_BDADDR_RP_SIZE 7 - -#define OCF_INQUIRY_CANCEL 0x0002 - -#define OCF_PERIODIC_INQUIRY 0x0003 -typedef struct { - uint16_t max_period; /* 1.28s units */ - uint16_t min_period; /* 1.28s units */ - uint8_t lap[3]; - uint8_t length; /* 1.28s units */ - uint8_t num_rsp; -} QEMU_PACKED periodic_inquiry_cp; -#define PERIODIC_INQUIRY_CP_SIZE 9 - -#define OCF_EXIT_PERIODIC_INQUIRY 0x0004 - -#define OCF_CREATE_CONN 0x0005 -typedef struct { - bdaddr_t bdaddr; - uint16_t pkt_type; - uint8_t pscan_rep_mode; - uint8_t pscan_mode; - uint16_t clock_offset; - uint8_t role_switch; -} QEMU_PACKED create_conn_cp; -#define CREATE_CONN_CP_SIZE 13 - -#define OCF_DISCONNECT 0x0006 -typedef struct { - uint16_t handle; - uint8_t reason; -} QEMU_PACKED disconnect_cp; -#define DISCONNECT_CP_SIZE 3 - -#define OCF_ADD_SCO 0x0007 -typedef struct { - uint16_t handle; - uint16_t pkt_type; -} QEMU_PACKED add_sco_cp; -#define ADD_SCO_CP_SIZE 4 - -#define OCF_CREATE_CONN_CANCEL 0x0008 -typedef struct { - bdaddr_t bdaddr; -} QEMU_PACKED create_conn_cancel_cp; -#define CREATE_CONN_CANCEL_CP_SIZE 6 - -typedef struct { - uint8_t status; - bdaddr_t bdaddr; -} QEMU_PACKED create_conn_cancel_rp; -#define CREATE_CONN_CANCEL_RP_SIZE 7 - -#define OCF_ACCEPT_CONN_REQ 0x0009 -typedef struct { - bdaddr_t bdaddr; - uint8_t role; -} QEMU_PACKED accept_conn_req_cp; -#define ACCEPT_CONN_REQ_CP_SIZE 7 - -#define OCF_REJECT_CONN_REQ 0x000A -typedef struct { - bdaddr_t bdaddr; - uint8_t reason; -} QEMU_PACKED reject_conn_req_cp; -#define REJECT_CONN_REQ_CP_SIZE 7 - -#define OCF_LINK_KEY_REPLY 0x000B -typedef struct { - bdaddr_t bdaddr; - uint8_t link_key[16]; -} QEMU_PACKED link_key_reply_cp; -#define LINK_KEY_REPLY_CP_SIZE 22 - -#define OCF_LINK_KEY_NEG_REPLY 0x000C - -#define OCF_PIN_CODE_REPLY 0x000D -typedef struct { - bdaddr_t bdaddr; - uint8_t pin_len; - uint8_t pin_code[16]; -} QEMU_PACKED pin_code_reply_cp; -#define PIN_CODE_REPLY_CP_SIZE 23 - -#define OCF_PIN_CODE_NEG_REPLY 0x000E - -#define OCF_SET_CONN_PTYPE 0x000F -typedef struct { - uint16_t handle; - uint16_t pkt_type; -} QEMU_PACKED set_conn_ptype_cp; -#define SET_CONN_PTYPE_CP_SIZE 4 - -#define OCF_AUTH_REQUESTED 0x0011 -typedef struct { - uint16_t handle; -} QEMU_PACKED auth_requested_cp; -#define AUTH_REQUESTED_CP_SIZE 2 - -#define OCF_SET_CONN_ENCRYPT 0x0013 -typedef struct { - uint16_t handle; - uint8_t encrypt; -} QEMU_PACKED set_conn_encrypt_cp; -#define SET_CONN_ENCRYPT_CP_SIZE 3 - -#define OCF_CHANGE_CONN_LINK_KEY 0x0015 -typedef struct { - uint16_t handle; -} QEMU_PACKED change_conn_link_key_cp; -#define CHANGE_CONN_LINK_KEY_CP_SIZE 2 - -#define OCF_MASTER_LINK_KEY 0x0017 -typedef struct { - uint8_t key_flag; -} QEMU_PACKED master_link_key_cp; -#define MASTER_LINK_KEY_CP_SIZE 1 - -#define OCF_REMOTE_NAME_REQ 0x0019 -typedef struct { - bdaddr_t bdaddr; - uint8_t pscan_rep_mode; - uint8_t pscan_mode; - uint16_t clock_offset; -} QEMU_PACKED remote_name_req_cp; -#define REMOTE_NAME_REQ_CP_SIZE 10 - -#define OCF_REMOTE_NAME_REQ_CANCEL 0x001A -typedef struct { - bdaddr_t bdaddr; -} QEMU_PACKED remote_name_req_cancel_cp; -#define REMOTE_NAME_REQ_CANCEL_CP_SIZE 6 - -typedef struct { - uint8_t status; - bdaddr_t bdaddr; -} QEMU_PACKED remote_name_req_cancel_rp; -#define REMOTE_NAME_REQ_CANCEL_RP_SIZE 7 - -#define OCF_READ_REMOTE_FEATURES 0x001B -typedef struct { - uint16_t handle; -} QEMU_PACKED read_remote_features_cp; -#define READ_REMOTE_FEATURES_CP_SIZE 2 - -#define OCF_READ_REMOTE_EXT_FEATURES 0x001C -typedef struct { - uint16_t handle; - uint8_t page_num; -} QEMU_PACKED read_remote_ext_features_cp; -#define READ_REMOTE_EXT_FEATURES_CP_SIZE 3 - -#define OCF_READ_REMOTE_VERSION 0x001D -typedef struct { - uint16_t handle; -} QEMU_PACKED read_remote_version_cp; -#define READ_REMOTE_VERSION_CP_SIZE 2 - -#define OCF_READ_CLOCK_OFFSET 0x001F -typedef struct { - uint16_t handle; -} QEMU_PACKED read_clock_offset_cp; -#define READ_CLOCK_OFFSET_CP_SIZE 2 - -#define OCF_READ_LMP_HANDLE 0x0020 -typedef struct { - uint16_t handle; -} QEMU_PACKED read_lmp_handle_cp; -#define READ_LMP_HANDLE_CP_SIZE 2 - -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t lmp_handle; - uint32_t reserved; -} QEMU_PACKED read_lmp_handle_rp; -#define READ_LMP_HANDLE_RP_SIZE 8 - -#define OCF_SETUP_SYNC_CONN 0x0028 -typedef struct { - uint16_t handle; - uint32_t tx_bandwidth; - uint32_t rx_bandwidth; - uint16_t max_latency; - uint16_t voice_setting; - uint8_t retrans_effort; - uint16_t pkt_type; -} QEMU_PACKED setup_sync_conn_cp; -#define SETUP_SYNC_CONN_CP_SIZE 17 - -#define OCF_ACCEPT_SYNC_CONN_REQ 0x0029 -typedef struct { - bdaddr_t bdaddr; - uint32_t tx_bandwidth; - uint32_t rx_bandwidth; - uint16_t max_latency; - uint16_t voice_setting; - uint8_t retrans_effort; - uint16_t pkt_type; -} QEMU_PACKED accept_sync_conn_req_cp; -#define ACCEPT_SYNC_CONN_REQ_CP_SIZE 21 - -#define OCF_REJECT_SYNC_CONN_REQ 0x002A -typedef struct { - bdaddr_t bdaddr; - uint8_t reason; -} QEMU_PACKED reject_sync_conn_req_cp; -#define REJECT_SYNC_CONN_REQ_CP_SIZE 7 - -/* Link Policy */ -#define OGF_LINK_POLICY 0x02 - -#define OCF_HOLD_MODE 0x0001 -typedef struct { - uint16_t handle; - uint16_t max_interval; - uint16_t min_interval; -} QEMU_PACKED hold_mode_cp; -#define HOLD_MODE_CP_SIZE 6 - -#define OCF_SNIFF_MODE 0x0003 -typedef struct { - uint16_t handle; - uint16_t max_interval; - uint16_t min_interval; - uint16_t attempt; - uint16_t timeout; -} QEMU_PACKED sniff_mode_cp; -#define SNIFF_MODE_CP_SIZE 10 - -#define OCF_EXIT_SNIFF_MODE 0x0004 -typedef struct { - uint16_t handle; -} QEMU_PACKED exit_sniff_mode_cp; -#define EXIT_SNIFF_MODE_CP_SIZE 2 - -#define OCF_PARK_MODE 0x0005 -typedef struct { - uint16_t handle; - uint16_t max_interval; - uint16_t min_interval; -} QEMU_PACKED park_mode_cp; -#define PARK_MODE_CP_SIZE 6 - -#define OCF_EXIT_PARK_MODE 0x0006 -typedef struct { - uint16_t handle; -} QEMU_PACKED exit_park_mode_cp; -#define EXIT_PARK_MODE_CP_SIZE 2 - -#define OCF_QOS_SETUP 0x0007 -typedef struct { - uint8_t service_type; /* 1 = best effort */ - uint32_t token_rate; /* Byte per seconds */ - uint32_t peak_bandwidth; /* Byte per seconds */ - uint32_t latency; /* Microseconds */ - uint32_t delay_variation; /* Microseconds */ -} QEMU_PACKED hci_qos; -#define HCI_QOS_CP_SIZE 17 -typedef struct { - uint16_t handle; - uint8_t flags; /* Reserved */ - hci_qos qos; -} QEMU_PACKED qos_setup_cp; -#define QOS_SETUP_CP_SIZE (3 + HCI_QOS_CP_SIZE) - -#define OCF_ROLE_DISCOVERY 0x0009 -typedef struct { - uint16_t handle; -} QEMU_PACKED role_discovery_cp; -#define ROLE_DISCOVERY_CP_SIZE 2 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t role; -} QEMU_PACKED role_discovery_rp; -#define ROLE_DISCOVERY_RP_SIZE 4 - -#define OCF_SWITCH_ROLE 0x000B -typedef struct { - bdaddr_t bdaddr; - uint8_t role; -} QEMU_PACKED switch_role_cp; -#define SWITCH_ROLE_CP_SIZE 7 - -#define OCF_READ_LINK_POLICY 0x000C -typedef struct { - uint16_t handle; -} QEMU_PACKED read_link_policy_cp; -#define READ_LINK_POLICY_CP_SIZE 2 -typedef struct { - uint8_t status; - uint16_t handle; - uint16_t policy; -} QEMU_PACKED read_link_policy_rp; -#define READ_LINK_POLICY_RP_SIZE 5 - -#define OCF_WRITE_LINK_POLICY 0x000D -typedef struct { - uint16_t handle; - uint16_t policy; -} QEMU_PACKED write_link_policy_cp; -#define WRITE_LINK_POLICY_CP_SIZE 4 -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED write_link_policy_rp; -#define WRITE_LINK_POLICY_RP_SIZE 3 - -#define OCF_READ_DEFAULT_LINK_POLICY 0x000E - -#define OCF_WRITE_DEFAULT_LINK_POLICY 0x000F - -#define OCF_FLOW_SPECIFICATION 0x0010 - -#define OCF_SNIFF_SUBRATE 0x0011 -typedef struct { - uint16_t handle; - uint16_t max_remote_latency; - uint16_t max_local_latency; - uint16_t min_remote_timeout; - uint16_t min_local_timeout; -} QEMU_PACKED sniff_subrate_cp; -#define SNIFF_SUBRATE_CP_SIZE 10 - -/* Host Controller and Baseband */ -#define OGF_HOST_CTL 0x03 - -#define OCF_SET_EVENT_MASK 0x0001 -typedef struct { - uint8_t mask[8]; -} QEMU_PACKED set_event_mask_cp; -#define SET_EVENT_MASK_CP_SIZE 8 - -#define OCF_RESET 0x0003 - -#define OCF_SET_EVENT_FLT 0x0005 -typedef struct { - uint8_t flt_type; - uint8_t cond_type; - uint8_t condition[0]; -} QEMU_PACKED set_event_flt_cp; -#define SET_EVENT_FLT_CP_SIZE 2 - -enum bt_filter_type { - FLT_CLEAR_ALL = 0x00, - FLT_INQ_RESULT = 0x01, - FLT_CONN_SETUP = 0x02, -}; -enum inq_result_cond_type { - INQ_RESULT_RETURN_ALL = 0x00, - INQ_RESULT_RETURN_CLASS = 0x01, - INQ_RESULT_RETURN_BDADDR = 0x02, -}; -enum conn_setup_cond_type { - CONN_SETUP_ALLOW_ALL = 0x00, - CONN_SETUP_ALLOW_CLASS = 0x01, - CONN_SETUP_ALLOW_BDADDR = 0x02, -}; -enum conn_setup_cond { - CONN_SETUP_AUTO_OFF = 0x01, - CONN_SETUP_AUTO_ON = 0x02, -}; - -#define OCF_FLUSH 0x0008 -typedef struct { - uint16_t handle; -} QEMU_PACKED flush_cp; -#define FLUSH_CP_SIZE 2 - -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED flush_rp; -#define FLUSH_RP_SIZE 3 - -#define OCF_READ_PIN_TYPE 0x0009 -typedef struct { - uint8_t status; - uint8_t pin_type; -} QEMU_PACKED read_pin_type_rp; -#define READ_PIN_TYPE_RP_SIZE 2 - -#define OCF_WRITE_PIN_TYPE 0x000A -typedef struct { - uint8_t pin_type; -} QEMU_PACKED write_pin_type_cp; -#define WRITE_PIN_TYPE_CP_SIZE 1 - -#define OCF_CREATE_NEW_UNIT_KEY 0x000B - -#define OCF_READ_STORED_LINK_KEY 0x000D -typedef struct { - bdaddr_t bdaddr; - uint8_t read_all; -} QEMU_PACKED read_stored_link_key_cp; -#define READ_STORED_LINK_KEY_CP_SIZE 7 -typedef struct { - uint8_t status; - uint16_t max_keys; - uint16_t num_keys; -} QEMU_PACKED read_stored_link_key_rp; -#define READ_STORED_LINK_KEY_RP_SIZE 5 - -#define OCF_WRITE_STORED_LINK_KEY 0x0011 -typedef struct { - uint8_t num_keys; - /* variable length part */ -} QEMU_PACKED write_stored_link_key_cp; -#define WRITE_STORED_LINK_KEY_CP_SIZE 1 -typedef struct { - uint8_t status; - uint8_t num_keys; -} QEMU_PACKED write_stored_link_key_rp; -#define READ_WRITE_LINK_KEY_RP_SIZE 2 - -#define OCF_DELETE_STORED_LINK_KEY 0x0012 -typedef struct { - bdaddr_t bdaddr; - uint8_t delete_all; -} QEMU_PACKED delete_stored_link_key_cp; -#define DELETE_STORED_LINK_KEY_CP_SIZE 7 -typedef struct { - uint8_t status; - uint16_t num_keys; -} QEMU_PACKED delete_stored_link_key_rp; -#define DELETE_STORED_LINK_KEY_RP_SIZE 3 - -#define OCF_CHANGE_LOCAL_NAME 0x0013 -typedef struct { - char name[248]; -} QEMU_PACKED change_local_name_cp; -#define CHANGE_LOCAL_NAME_CP_SIZE 248 - -#define OCF_READ_LOCAL_NAME 0x0014 -typedef struct { - uint8_t status; - char name[248]; -} QEMU_PACKED read_local_name_rp; -#define READ_LOCAL_NAME_RP_SIZE 249 - -#define OCF_READ_CONN_ACCEPT_TIMEOUT 0x0015 -typedef struct { - uint8_t status; - uint16_t timeout; -} QEMU_PACKED read_conn_accept_timeout_rp; -#define READ_CONN_ACCEPT_TIMEOUT_RP_SIZE 3 - -#define OCF_WRITE_CONN_ACCEPT_TIMEOUT 0x0016 -typedef struct { - uint16_t timeout; -} QEMU_PACKED write_conn_accept_timeout_cp; -#define WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE 2 - -#define OCF_READ_PAGE_TIMEOUT 0x0017 -typedef struct { - uint8_t status; - uint16_t timeout; -} QEMU_PACKED read_page_timeout_rp; -#define READ_PAGE_TIMEOUT_RP_SIZE 3 - -#define OCF_WRITE_PAGE_TIMEOUT 0x0018 -typedef struct { - uint16_t timeout; -} QEMU_PACKED write_page_timeout_cp; -#define WRITE_PAGE_TIMEOUT_CP_SIZE 2 - -#define OCF_READ_SCAN_ENABLE 0x0019 -typedef struct { - uint8_t status; - uint8_t enable; -} QEMU_PACKED read_scan_enable_rp; -#define READ_SCAN_ENABLE_RP_SIZE 2 - -#define OCF_WRITE_SCAN_ENABLE 0x001A -typedef struct { - uint8_t scan_enable; -} QEMU_PACKED write_scan_enable_cp; -#define WRITE_SCAN_ENABLE_CP_SIZE 1 - -enum scan_enable_bits { - SCAN_DISABLED = 0, - SCAN_INQUIRY = 1 << 0, - SCAN_PAGE = 1 << 1, -}; - -#define OCF_READ_PAGE_ACTIVITY 0x001B -typedef struct { - uint8_t status; - uint16_t interval; - uint16_t window; -} QEMU_PACKED read_page_activity_rp; -#define READ_PAGE_ACTIVITY_RP_SIZE 5 - -#define OCF_WRITE_PAGE_ACTIVITY 0x001C -typedef struct { - uint16_t interval; - uint16_t window; -} QEMU_PACKED write_page_activity_cp; -#define WRITE_PAGE_ACTIVITY_CP_SIZE 4 - -#define OCF_READ_INQ_ACTIVITY 0x001D -typedef struct { - uint8_t status; - uint16_t interval; - uint16_t window; -} QEMU_PACKED read_inq_activity_rp; -#define READ_INQ_ACTIVITY_RP_SIZE 5 - -#define OCF_WRITE_INQ_ACTIVITY 0x001E -typedef struct { - uint16_t interval; - uint16_t window; -} QEMU_PACKED write_inq_activity_cp; -#define WRITE_INQ_ACTIVITY_CP_SIZE 4 - -#define OCF_READ_AUTH_ENABLE 0x001F - -#define OCF_WRITE_AUTH_ENABLE 0x0020 - -#define AUTH_DISABLED 0x00 -#define AUTH_ENABLED 0x01 - -#define OCF_READ_ENCRYPT_MODE 0x0021 - -#define OCF_WRITE_ENCRYPT_MODE 0x0022 - -#define ENCRYPT_DISABLED 0x00 -#define ENCRYPT_P2P 0x01 -#define ENCRYPT_BOTH 0x02 - -#define OCF_READ_CLASS_OF_DEV 0x0023 -typedef struct { - uint8_t status; - uint8_t dev_class[3]; -} QEMU_PACKED read_class_of_dev_rp; -#define READ_CLASS_OF_DEV_RP_SIZE 4 - -#define OCF_WRITE_CLASS_OF_DEV 0x0024 -typedef struct { - uint8_t dev_class[3]; -} QEMU_PACKED write_class_of_dev_cp; -#define WRITE_CLASS_OF_DEV_CP_SIZE 3 - -#define OCF_READ_VOICE_SETTING 0x0025 -typedef struct { - uint8_t status; - uint16_t voice_setting; -} QEMU_PACKED read_voice_setting_rp; -#define READ_VOICE_SETTING_RP_SIZE 3 - -#define OCF_WRITE_VOICE_SETTING 0x0026 -typedef struct { - uint16_t voice_setting; -} QEMU_PACKED write_voice_setting_cp; -#define WRITE_VOICE_SETTING_CP_SIZE 2 - -#define OCF_READ_AUTOMATIC_FLUSH_TIMEOUT 0x0027 - -#define OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT 0x0028 - -#define OCF_READ_NUM_BROADCAST_RETRANS 0x0029 - -#define OCF_WRITE_NUM_BROADCAST_RETRANS 0x002A - -#define OCF_READ_HOLD_MODE_ACTIVITY 0x002B - -#define OCF_WRITE_HOLD_MODE_ACTIVITY 0x002C - -#define OCF_READ_TRANSMIT_POWER_LEVEL 0x002D -typedef struct { - uint16_t handle; - uint8_t type; -} QEMU_PACKED read_transmit_power_level_cp; -#define READ_TRANSMIT_POWER_LEVEL_CP_SIZE 3 -typedef struct { - uint8_t status; - uint16_t handle; - int8_t level; -} QEMU_PACKED read_transmit_power_level_rp; -#define READ_TRANSMIT_POWER_LEVEL_RP_SIZE 4 - -#define OCF_HOST_BUFFER_SIZE 0x0033 -typedef struct { - uint16_t acl_mtu; - uint8_t sco_mtu; - uint16_t acl_max_pkt; - uint16_t sco_max_pkt; -} QEMU_PACKED host_buffer_size_cp; -#define HOST_BUFFER_SIZE_CP_SIZE 7 - -#define OCF_HOST_NUMBER_OF_COMPLETED_PACKETS 0x0035 - -#define OCF_READ_LINK_SUPERVISION_TIMEOUT 0x0036 -typedef struct { - uint8_t status; - uint16_t handle; - uint16_t link_sup_to; -} QEMU_PACKED read_link_supervision_timeout_rp; -#define READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE 5 - -#define OCF_WRITE_LINK_SUPERVISION_TIMEOUT 0x0037 -typedef struct { - uint16_t handle; - uint16_t link_sup_to; -} QEMU_PACKED write_link_supervision_timeout_cp; -#define WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE 4 -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED write_link_supervision_timeout_rp; -#define WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE 3 - -#define OCF_READ_NUM_SUPPORTED_IAC 0x0038 - -#define MAX_IAC_LAP 0x40 -#define OCF_READ_CURRENT_IAC_LAP 0x0039 -typedef struct { - uint8_t status; - uint8_t num_current_iac; - uint8_t lap[MAX_IAC_LAP][3]; -} QEMU_PACKED read_current_iac_lap_rp; -#define READ_CURRENT_IAC_LAP_RP_SIZE 2+3*MAX_IAC_LAP - -#define OCF_WRITE_CURRENT_IAC_LAP 0x003A -typedef struct { - uint8_t num_current_iac; - uint8_t lap[MAX_IAC_LAP][3]; -} QEMU_PACKED write_current_iac_lap_cp; -#define WRITE_CURRENT_IAC_LAP_CP_SIZE 1+3*MAX_IAC_LAP - -#define OCF_READ_PAGE_SCAN_PERIOD_MODE 0x003B - -#define OCF_WRITE_PAGE_SCAN_PERIOD_MODE 0x003C - -#define OCF_READ_PAGE_SCAN_MODE 0x003D - -#define OCF_WRITE_PAGE_SCAN_MODE 0x003E - -#define OCF_SET_AFH_CLASSIFICATION 0x003F -typedef struct { - uint8_t map[10]; -} QEMU_PACKED set_afh_classification_cp; -#define SET_AFH_CLASSIFICATION_CP_SIZE 10 -typedef struct { - uint8_t status; -} QEMU_PACKED set_afh_classification_rp; -#define SET_AFH_CLASSIFICATION_RP_SIZE 1 - -#define OCF_READ_INQUIRY_SCAN_TYPE 0x0042 -typedef struct { - uint8_t status; - uint8_t type; -} QEMU_PACKED read_inquiry_scan_type_rp; -#define READ_INQUIRY_SCAN_TYPE_RP_SIZE 2 - -#define OCF_WRITE_INQUIRY_SCAN_TYPE 0x0043 -typedef struct { - uint8_t type; -} QEMU_PACKED write_inquiry_scan_type_cp; -#define WRITE_INQUIRY_SCAN_TYPE_CP_SIZE 1 -typedef struct { - uint8_t status; -} QEMU_PACKED write_inquiry_scan_type_rp; -#define WRITE_INQUIRY_SCAN_TYPE_RP_SIZE 1 - -#define OCF_READ_INQUIRY_MODE 0x0044 -typedef struct { - uint8_t status; - uint8_t mode; -} QEMU_PACKED read_inquiry_mode_rp; -#define READ_INQUIRY_MODE_RP_SIZE 2 - -#define OCF_WRITE_INQUIRY_MODE 0x0045 -typedef struct { - uint8_t mode; -} QEMU_PACKED write_inquiry_mode_cp; -#define WRITE_INQUIRY_MODE_CP_SIZE 1 -typedef struct { - uint8_t status; -} QEMU_PACKED write_inquiry_mode_rp; -#define WRITE_INQUIRY_MODE_RP_SIZE 1 - -#define OCF_READ_PAGE_SCAN_TYPE 0x0046 - -#define OCF_WRITE_PAGE_SCAN_TYPE 0x0047 - -#define OCF_READ_AFH_MODE 0x0048 -typedef struct { - uint8_t status; - uint8_t mode; -} QEMU_PACKED read_afh_mode_rp; -#define READ_AFH_MODE_RP_SIZE 2 - -#define OCF_WRITE_AFH_MODE 0x0049 -typedef struct { - uint8_t mode; -} QEMU_PACKED write_afh_mode_cp; -#define WRITE_AFH_MODE_CP_SIZE 1 -typedef struct { - uint8_t status; -} QEMU_PACKED write_afh_mode_rp; -#define WRITE_AFH_MODE_RP_SIZE 1 - -#define OCF_READ_EXT_INQUIRY_RESPONSE 0x0051 -typedef struct { - uint8_t status; - uint8_t fec; - uint8_t data[240]; -} QEMU_PACKED read_ext_inquiry_response_rp; -#define READ_EXT_INQUIRY_RESPONSE_RP_SIZE 242 - -#define OCF_WRITE_EXT_INQUIRY_RESPONSE 0x0052 -typedef struct { - uint8_t fec; - uint8_t data[240]; -} QEMU_PACKED write_ext_inquiry_response_cp; -#define WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE 241 -typedef struct { - uint8_t status; -} QEMU_PACKED write_ext_inquiry_response_rp; -#define WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE 1 - -/* Informational Parameters */ -#define OGF_INFO_PARAM 0x04 - -#define OCF_READ_LOCAL_VERSION 0x0001 -typedef struct { - uint8_t status; - uint8_t hci_ver; - uint16_t hci_rev; - uint8_t lmp_ver; - uint16_t manufacturer; - uint16_t lmp_subver; -} QEMU_PACKED read_local_version_rp; -#define READ_LOCAL_VERSION_RP_SIZE 9 - -#define OCF_READ_LOCAL_COMMANDS 0x0002 -typedef struct { - uint8_t status; - uint8_t commands[64]; -} QEMU_PACKED read_local_commands_rp; -#define READ_LOCAL_COMMANDS_RP_SIZE 65 - -#define OCF_READ_LOCAL_FEATURES 0x0003 -typedef struct { - uint8_t status; - uint8_t features[8]; -} QEMU_PACKED read_local_features_rp; -#define READ_LOCAL_FEATURES_RP_SIZE 9 - -#define OCF_READ_LOCAL_EXT_FEATURES 0x0004 -typedef struct { - uint8_t page_num; -} QEMU_PACKED read_local_ext_features_cp; -#define READ_LOCAL_EXT_FEATURES_CP_SIZE 1 -typedef struct { - uint8_t status; - uint8_t page_num; - uint8_t max_page_num; - uint8_t features[8]; -} QEMU_PACKED read_local_ext_features_rp; -#define READ_LOCAL_EXT_FEATURES_RP_SIZE 11 - -#define OCF_READ_BUFFER_SIZE 0x0005 -typedef struct { - uint8_t status; - uint16_t acl_mtu; - uint8_t sco_mtu; - uint16_t acl_max_pkt; - uint16_t sco_max_pkt; -} QEMU_PACKED read_buffer_size_rp; -#define READ_BUFFER_SIZE_RP_SIZE 8 - -#define OCF_READ_COUNTRY_CODE 0x0007 -typedef struct { - uint8_t status; - uint8_t country_code; -} QEMU_PACKED read_country_code_rp; -#define READ_COUNTRY_CODE_RP_SIZE 2 - -#define OCF_READ_BD_ADDR 0x0009 -typedef struct { - uint8_t status; - bdaddr_t bdaddr; -} QEMU_PACKED read_bd_addr_rp; -#define READ_BD_ADDR_RP_SIZE 7 - -/* Status params */ -#define OGF_STATUS_PARAM 0x05 - -#define OCF_READ_FAILED_CONTACT_COUNTER 0x0001 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t counter; -} QEMU_PACKED read_failed_contact_counter_rp; -#define READ_FAILED_CONTACT_COUNTER_RP_SIZE 4 - -#define OCF_RESET_FAILED_CONTACT_COUNTER 0x0002 -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED reset_failed_contact_counter_rp; -#define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 3 - -#define OCF_READ_LINK_QUALITY 0x0003 -typedef struct { - uint16_t handle; -} QEMU_PACKED read_link_quality_cp; -#define READ_LINK_QUALITY_CP_SIZE 2 - -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t link_quality; -} QEMU_PACKED read_link_quality_rp; -#define READ_LINK_QUALITY_RP_SIZE 4 - -#define OCF_READ_RSSI 0x0005 -typedef struct { - uint8_t status; - uint16_t handle; - int8_t rssi; -} QEMU_PACKED read_rssi_rp; -#define READ_RSSI_RP_SIZE 4 - -#define OCF_READ_AFH_MAP 0x0006 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t mode; - uint8_t map[10]; -} QEMU_PACKED read_afh_map_rp; -#define READ_AFH_MAP_RP_SIZE 14 - -#define OCF_READ_CLOCK 0x0007 -typedef struct { - uint16_t handle; - uint8_t which_clock; -} QEMU_PACKED read_clock_cp; -#define READ_CLOCK_CP_SIZE 3 -typedef struct { - uint8_t status; - uint16_t handle; - uint32_t clock; - uint16_t accuracy; -} QEMU_PACKED read_clock_rp; -#define READ_CLOCK_RP_SIZE 9 - -/* Testing commands */ -#define OGF_TESTING_CMD 0x3e - -/* Vendor specific commands */ -#define OGF_VENDOR_CMD 0x3f - -/* HCI Events */ - -#define EVT_INQUIRY_COMPLETE 0x01 - -#define EVT_INQUIRY_RESULT 0x02 -typedef struct { - uint8_t num_responses; - bdaddr_t bdaddr; - uint8_t pscan_rep_mode; - uint8_t pscan_period_mode; - uint8_t pscan_mode; - uint8_t dev_class[3]; - uint16_t clock_offset; -} QEMU_PACKED inquiry_info; -#define INQUIRY_INFO_SIZE 15 - -#define EVT_CONN_COMPLETE 0x03 -typedef struct { - uint8_t status; - uint16_t handle; - bdaddr_t bdaddr; - uint8_t link_type; - uint8_t encr_mode; -} QEMU_PACKED evt_conn_complete; -#define EVT_CONN_COMPLETE_SIZE 11 - -#define EVT_CONN_REQUEST 0x04 -typedef struct { - bdaddr_t bdaddr; - uint8_t dev_class[3]; - uint8_t link_type; -} QEMU_PACKED evt_conn_request; -#define EVT_CONN_REQUEST_SIZE 10 - -#define EVT_DISCONN_COMPLETE 0x05 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t reason; -} QEMU_PACKED evt_disconn_complete; -#define EVT_DISCONN_COMPLETE_SIZE 4 - -#define EVT_AUTH_COMPLETE 0x06 -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED evt_auth_complete; -#define EVT_AUTH_COMPLETE_SIZE 3 - -#define EVT_REMOTE_NAME_REQ_COMPLETE 0x07 -typedef struct { - uint8_t status; - bdaddr_t bdaddr; - char name[248]; -} QEMU_PACKED evt_remote_name_req_complete; -#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255 - -#define EVT_ENCRYPT_CHANGE 0x08 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t encrypt; -} QEMU_PACKED evt_encrypt_change; -#define EVT_ENCRYPT_CHANGE_SIZE 4 - -#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE 0x09 -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED evt_change_conn_link_key_complete; -#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE 3 - -#define EVT_MASTER_LINK_KEY_COMPLETE 0x0A -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t key_flag; -} QEMU_PACKED evt_master_link_key_complete; -#define EVT_MASTER_LINK_KEY_COMPLETE_SIZE 4 - -#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t features[8]; -} QEMU_PACKED evt_read_remote_features_complete; -#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11 - -#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t lmp_ver; - uint16_t manufacturer; - uint16_t lmp_subver; -} QEMU_PACKED evt_read_remote_version_complete; -#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8 - -#define EVT_QOS_SETUP_COMPLETE 0x0D -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t flags; /* Reserved */ - hci_qos qos; -} QEMU_PACKED evt_qos_setup_complete; -#define EVT_QOS_SETUP_COMPLETE_SIZE (4 + HCI_QOS_CP_SIZE) - -#define EVT_CMD_COMPLETE 0x0E -typedef struct { - uint8_t ncmd; - uint16_t opcode; -} QEMU_PACKED evt_cmd_complete; -#define EVT_CMD_COMPLETE_SIZE 3 - -#define EVT_CMD_STATUS 0x0F -typedef struct { - uint8_t status; - uint8_t ncmd; - uint16_t opcode; -} QEMU_PACKED evt_cmd_status; -#define EVT_CMD_STATUS_SIZE 4 - -#define EVT_HARDWARE_ERROR 0x10 -typedef struct { - uint8_t code; -} QEMU_PACKED evt_hardware_error; -#define EVT_HARDWARE_ERROR_SIZE 1 - -#define EVT_FLUSH_OCCURRED 0x11 -typedef struct { - uint16_t handle; -} QEMU_PACKED evt_flush_occurred; -#define EVT_FLUSH_OCCURRED_SIZE 2 - -#define EVT_ROLE_CHANGE 0x12 -typedef struct { - uint8_t status; - bdaddr_t bdaddr; - uint8_t role; -} QEMU_PACKED evt_role_change; -#define EVT_ROLE_CHANGE_SIZE 8 - -#define EVT_NUM_COMP_PKTS 0x13 -typedef struct { - uint8_t num_hndl; - struct { - uint16_t handle; - uint16_t num_packets; - } connection[0]; -} QEMU_PACKED evt_num_comp_pkts; -#define EVT_NUM_COMP_PKTS_SIZE(num_hndl) (1 + 4 * (num_hndl)) - -#define EVT_MODE_CHANGE 0x14 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t mode; - uint16_t interval; -} QEMU_PACKED evt_mode_change; -#define EVT_MODE_CHANGE_SIZE 6 - -#define EVT_RETURN_LINK_KEYS 0x15 -typedef struct { - uint8_t num_keys; - /* variable length part */ -} QEMU_PACKED evt_return_link_keys; -#define EVT_RETURN_LINK_KEYS_SIZE 1 - -#define EVT_PIN_CODE_REQ 0x16 -typedef struct { - bdaddr_t bdaddr; -} QEMU_PACKED evt_pin_code_req; -#define EVT_PIN_CODE_REQ_SIZE 6 - -#define EVT_LINK_KEY_REQ 0x17 -typedef struct { - bdaddr_t bdaddr; -} QEMU_PACKED evt_link_key_req; -#define EVT_LINK_KEY_REQ_SIZE 6 - -#define EVT_LINK_KEY_NOTIFY 0x18 -typedef struct { - bdaddr_t bdaddr; - uint8_t link_key[16]; - uint8_t key_type; -} QEMU_PACKED evt_link_key_notify; -#define EVT_LINK_KEY_NOTIFY_SIZE 23 - -#define EVT_LOOPBACK_COMMAND 0x19 - -#define EVT_DATA_BUFFER_OVERFLOW 0x1A -typedef struct { - uint8_t link_type; -} QEMU_PACKED evt_data_buffer_overflow; -#define EVT_DATA_BUFFER_OVERFLOW_SIZE 1 - -#define EVT_MAX_SLOTS_CHANGE 0x1B -typedef struct { - uint16_t handle; - uint8_t max_slots; -} QEMU_PACKED evt_max_slots_change; -#define EVT_MAX_SLOTS_CHANGE_SIZE 3 - -#define EVT_READ_CLOCK_OFFSET_COMPLETE 0x1C -typedef struct { - uint8_t status; - uint16_t handle; - uint16_t clock_offset; -} QEMU_PACKED evt_read_clock_offset_complete; -#define EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE 5 - -#define EVT_CONN_PTYPE_CHANGED 0x1D -typedef struct { - uint8_t status; - uint16_t handle; - uint16_t ptype; -} QEMU_PACKED evt_conn_ptype_changed; -#define EVT_CONN_PTYPE_CHANGED_SIZE 5 - -#define EVT_QOS_VIOLATION 0x1E -typedef struct { - uint16_t handle; -} QEMU_PACKED evt_qos_violation; -#define EVT_QOS_VIOLATION_SIZE 2 - -#define EVT_PSCAN_REP_MODE_CHANGE 0x20 -typedef struct { - bdaddr_t bdaddr; - uint8_t pscan_rep_mode; -} QEMU_PACKED evt_pscan_rep_mode_change; -#define EVT_PSCAN_REP_MODE_CHANGE_SIZE 7 - -#define EVT_FLOW_SPEC_COMPLETE 0x21 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t flags; - uint8_t direction; - hci_qos qos; -} QEMU_PACKED evt_flow_spec_complete; -#define EVT_FLOW_SPEC_COMPLETE_SIZE (5 + HCI_QOS_CP_SIZE) - -#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22 -typedef struct { - uint8_t num_responses; - bdaddr_t bdaddr; - uint8_t pscan_rep_mode; - uint8_t pscan_period_mode; - uint8_t dev_class[3]; - uint16_t clock_offset; - int8_t rssi; -} QEMU_PACKED inquiry_info_with_rssi; -#define INQUIRY_INFO_WITH_RSSI_SIZE 15 -typedef struct { - uint8_t num_responses; - bdaddr_t bdaddr; - uint8_t pscan_rep_mode; - uint8_t pscan_period_mode; - uint8_t pscan_mode; - uint8_t dev_class[3]; - uint16_t clock_offset; - int8_t rssi; -} QEMU_PACKED inquiry_info_with_rssi_and_pscan_mode; -#define INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE 16 - -#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE 0x23 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t page_num; - uint8_t max_page_num; - uint8_t features[8]; -} QEMU_PACKED evt_read_remote_ext_features_complete; -#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE 13 - -#define EVT_SYNC_CONN_COMPLETE 0x2C -typedef struct { - uint8_t status; - uint16_t handle; - bdaddr_t bdaddr; - uint8_t link_type; - uint8_t trans_interval; - uint8_t retrans_window; - uint16_t rx_pkt_len; - uint16_t tx_pkt_len; - uint8_t air_mode; -} QEMU_PACKED evt_sync_conn_complete; -#define EVT_SYNC_CONN_COMPLETE_SIZE 17 - -#define EVT_SYNC_CONN_CHANGED 0x2D -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t trans_interval; - uint8_t retrans_window; - uint16_t rx_pkt_len; - uint16_t tx_pkt_len; -} QEMU_PACKED evt_sync_conn_changed; -#define EVT_SYNC_CONN_CHANGED_SIZE 9 - -#define EVT_SNIFF_SUBRATE 0x2E -typedef struct { - uint8_t status; - uint16_t handle; - uint16_t max_remote_latency; - uint16_t max_local_latency; - uint16_t min_remote_timeout; - uint16_t min_local_timeout; -} QEMU_PACKED evt_sniff_subrate; -#define EVT_SNIFF_SUBRATE_SIZE 11 - -#define EVT_TESTING 0xFE - -#define EVT_VENDOR 0xFF - -/* Command opcode pack/unpack */ -#define cmd_opcode_pack(ogf, ocf) (uint16_t)((ocf & 0x03ff)|(ogf << 10)) -#define cmd_opcode_ogf(op) (op >> 10) -#define cmd_opcode_ocf(op) (op & 0x03ff) - -/* ACL handle and flags pack/unpack */ -#define acl_handle_pack(h, f) (uint16_t)(((h) & 0x0fff)|((f) << 12)) -#define acl_handle(h) ((h) & 0x0fff) -#define acl_flags(h) ((h) >> 12) - -/* HCI Packet structures */ -#define HCI_COMMAND_HDR_SIZE 3 -#define HCI_EVENT_HDR_SIZE 2 -#define HCI_ACL_HDR_SIZE 4 -#define HCI_SCO_HDR_SIZE 3 - -struct hci_command_hdr { - uint16_t opcode; /* OCF & OGF */ - uint8_t plen; -} QEMU_PACKED; - -struct hci_event_hdr { - uint8_t evt; - uint8_t plen; -} QEMU_PACKED; - -struct hci_acl_hdr { - uint16_t handle; /* Handle & Flags(PB, BC) */ - uint16_t dlen; -} QEMU_PACKED; - -struct hci_sco_hdr { - uint16_t handle; - uint8_t dlen; -} QEMU_PACKED; - -/* L2CAP layer defines */ - -enum bt_l2cap_lm_bits { - L2CAP_LM_MASTER = 1 << 0, - L2CAP_LM_AUTH = 1 << 1, - L2CAP_LM_ENCRYPT = 1 << 2, - L2CAP_LM_TRUSTED = 1 << 3, - L2CAP_LM_RELIABLE = 1 << 4, - L2CAP_LM_SECURE = 1 << 5, -}; - -enum bt_l2cap_cid_predef { - L2CAP_CID_INVALID = 0x0000, - L2CAP_CID_SIGNALLING= 0x0001, - L2CAP_CID_GROUP = 0x0002, - L2CAP_CID_ALLOC = 0x0040, -}; - -/* L2CAP command codes */ -enum bt_l2cap_cmd { - L2CAP_COMMAND_REJ = 1, - L2CAP_CONN_REQ, - L2CAP_CONN_RSP, - L2CAP_CONF_REQ, - L2CAP_CONF_RSP, - L2CAP_DISCONN_REQ, - L2CAP_DISCONN_RSP, - L2CAP_ECHO_REQ, - L2CAP_ECHO_RSP, - L2CAP_INFO_REQ, - L2CAP_INFO_RSP, -}; - -enum bt_l2cap_sar_bits { - L2CAP_SAR_NO_SEG = 0, - L2CAP_SAR_START, - L2CAP_SAR_END, - L2CAP_SAR_CONT, -}; - -/* L2CAP structures */ -typedef struct { - uint16_t len; - uint16_t cid; - uint8_t data[0]; -} QEMU_PACKED l2cap_hdr; -#define L2CAP_HDR_SIZE 4 - -typedef struct { - uint8_t code; - uint8_t ident; - uint16_t len; -} QEMU_PACKED l2cap_cmd_hdr; -#define L2CAP_CMD_HDR_SIZE 4 - -typedef struct { - uint16_t reason; -} QEMU_PACKED l2cap_cmd_rej; -#define L2CAP_CMD_REJ_SIZE 2 - -typedef struct { - uint16_t dcid; - uint16_t scid; -} QEMU_PACKED l2cap_cmd_rej_cid; -#define L2CAP_CMD_REJ_CID_SIZE 4 - -/* reject reason */ -enum bt_l2cap_rej_reason { - L2CAP_REJ_CMD_NOT_UNDERSTOOD = 0, - L2CAP_REJ_SIG_TOOBIG, - L2CAP_REJ_CID_INVAL, -}; - -typedef struct { - uint16_t psm; - uint16_t scid; -} QEMU_PACKED l2cap_conn_req; -#define L2CAP_CONN_REQ_SIZE 4 - -typedef struct { - uint16_t dcid; - uint16_t scid; - uint16_t result; - uint16_t status; -} QEMU_PACKED l2cap_conn_rsp; -#define L2CAP_CONN_RSP_SIZE 8 - -/* connect result */ -enum bt_l2cap_conn_res { - L2CAP_CR_SUCCESS = 0, - L2CAP_CR_PEND, - L2CAP_CR_BAD_PSM, - L2CAP_CR_SEC_BLOCK, - L2CAP_CR_NO_MEM, -}; - -/* connect status */ -enum bt_l2cap_conn_stat { - L2CAP_CS_NO_INFO = 0, - L2CAP_CS_AUTHEN_PEND, - L2CAP_CS_AUTHOR_PEND, -}; - -typedef struct { - uint16_t dcid; - uint16_t flags; - uint8_t data[0]; -} QEMU_PACKED l2cap_conf_req; -#define L2CAP_CONF_REQ_SIZE(datalen) (4 + (datalen)) - -typedef struct { - uint16_t scid; - uint16_t flags; - uint16_t result; - uint8_t data[0]; -} QEMU_PACKED l2cap_conf_rsp; -#define L2CAP_CONF_RSP_SIZE(datalen) (6 + datalen) - -enum bt_l2cap_conf_res { - L2CAP_CONF_SUCCESS = 0, - L2CAP_CONF_UNACCEPT, - L2CAP_CONF_REJECT, - L2CAP_CONF_UNKNOWN, -}; - -typedef struct { - uint8_t type; - uint8_t len; - uint8_t val[0]; -} QEMU_PACKED l2cap_conf_opt; -#define L2CAP_CONF_OPT_SIZE 2 - -enum bt_l2cap_conf_val { - L2CAP_CONF_MTU = 1, - L2CAP_CONF_FLUSH_TO, - L2CAP_CONF_QOS, - L2CAP_CONF_RFC, - L2CAP_CONF_RFC_MODE = L2CAP_CONF_RFC, -}; - -typedef struct { - uint8_t flags; - uint8_t service_type; - uint32_t token_rate; - uint32_t token_bucket_size; - uint32_t peak_bandwidth; - uint32_t latency; - uint32_t delay_variation; -} QEMU_PACKED l2cap_conf_opt_qos; -#define L2CAP_CONF_OPT_QOS_SIZE 22 - -enum bt_l2cap_conf_opt_qos_st { - L2CAP_CONF_QOS_NO_TRAFFIC = 0x00, - L2CAP_CONF_QOS_BEST_EFFORT, - L2CAP_CONF_QOS_GUARANTEED, -}; - -#define L2CAP_CONF_QOS_WILDCARD 0xffffffff - -enum bt_l2cap_mode { - L2CAP_MODE_BASIC = 0, - L2CAP_MODE_RETRANS = 1, - L2CAP_MODE_FLOWCTL = 2, -}; - -typedef struct { - uint16_t dcid; - uint16_t scid; -} QEMU_PACKED l2cap_disconn_req; -#define L2CAP_DISCONN_REQ_SIZE 4 - -typedef struct { - uint16_t dcid; - uint16_t scid; -} QEMU_PACKED l2cap_disconn_rsp; -#define L2CAP_DISCONN_RSP_SIZE 4 - -typedef struct { - uint16_t type; -} QEMU_PACKED l2cap_info_req; -#define L2CAP_INFO_REQ_SIZE 2 - -typedef struct { - uint16_t type; - uint16_t result; - uint8_t data[0]; -} QEMU_PACKED l2cap_info_rsp; -#define L2CAP_INFO_RSP_SIZE 4 - -/* info type */ -enum bt_l2cap_info_type { - L2CAP_IT_CL_MTU = 1, - L2CAP_IT_FEAT_MASK, -}; - -/* info result */ -enum bt_l2cap_info_result { - L2CAP_IR_SUCCESS = 0, - L2CAP_IR_NOTSUPP, -}; - -/* Service Discovery Protocol defines */ -/* Note that all multibyte values in lower layer protocols (above in this file) - * are little-endian while SDP is big-endian. */ - -/* Protocol UUIDs */ -enum sdp_proto_uuid { - SDP_UUID = 0x0001, - UDP_UUID = 0x0002, - RFCOMM_UUID = 0x0003, - TCP_UUID = 0x0004, - TCS_BIN_UUID = 0x0005, - TCS_AT_UUID = 0x0006, - OBEX_UUID = 0x0008, - IP_UUID = 0x0009, - FTP_UUID = 0x000a, - HTTP_UUID = 0x000c, - WSP_UUID = 0x000e, - BNEP_UUID = 0x000f, - UPNP_UUID = 0x0010, - HIDP_UUID = 0x0011, - HCRP_CTRL_UUID = 0x0012, - HCRP_DATA_UUID = 0x0014, - HCRP_NOTE_UUID = 0x0016, - AVCTP_UUID = 0x0017, - AVDTP_UUID = 0x0019, - CMTP_UUID = 0x001b, - UDI_UUID = 0x001d, - MCAP_CTRL_UUID = 0x001e, - MCAP_DATA_UUID = 0x001f, - L2CAP_UUID = 0x0100, -}; - -/* - * Service class identifiers of standard services and service groups - */ -enum service_class_id { - SDP_SERVER_SVCLASS_ID = 0x1000, - BROWSE_GRP_DESC_SVCLASS_ID = 0x1001, - PUBLIC_BROWSE_GROUP = 0x1002, - SERIAL_PORT_SVCLASS_ID = 0x1101, - LAN_ACCESS_SVCLASS_ID = 0x1102, - DIALUP_NET_SVCLASS_ID = 0x1103, - IRMC_SYNC_SVCLASS_ID = 0x1104, - OBEX_OBJPUSH_SVCLASS_ID = 0x1105, - OBEX_FILETRANS_SVCLASS_ID = 0x1106, - IRMC_SYNC_CMD_SVCLASS_ID = 0x1107, - HEADSET_SVCLASS_ID = 0x1108, - CORDLESS_TELEPHONY_SVCLASS_ID = 0x1109, - AUDIO_SOURCE_SVCLASS_ID = 0x110a, - AUDIO_SINK_SVCLASS_ID = 0x110b, - AV_REMOTE_TARGET_SVCLASS_ID = 0x110c, - ADVANCED_AUDIO_SVCLASS_ID = 0x110d, - AV_REMOTE_SVCLASS_ID = 0x110e, - VIDEO_CONF_SVCLASS_ID = 0x110f, - INTERCOM_SVCLASS_ID = 0x1110, - FAX_SVCLASS_ID = 0x1111, - HEADSET_AGW_SVCLASS_ID = 0x1112, - WAP_SVCLASS_ID = 0x1113, - WAP_CLIENT_SVCLASS_ID = 0x1114, - PANU_SVCLASS_ID = 0x1115, - NAP_SVCLASS_ID = 0x1116, - GN_SVCLASS_ID = 0x1117, - DIRECT_PRINTING_SVCLASS_ID = 0x1118, - REFERENCE_PRINTING_SVCLASS_ID = 0x1119, - IMAGING_SVCLASS_ID = 0x111a, - IMAGING_RESPONDER_SVCLASS_ID = 0x111b, - IMAGING_ARCHIVE_SVCLASS_ID = 0x111c, - IMAGING_REFOBJS_SVCLASS_ID = 0x111d, - HANDSFREE_SVCLASS_ID = 0x111e, - HANDSFREE_AGW_SVCLASS_ID = 0x111f, - DIRECT_PRT_REFOBJS_SVCLASS_ID = 0x1120, - REFLECTED_UI_SVCLASS_ID = 0x1121, - BASIC_PRINTING_SVCLASS_ID = 0x1122, - PRINTING_STATUS_SVCLASS_ID = 0x1123, - HID_SVCLASS_ID = 0x1124, - HCR_SVCLASS_ID = 0x1125, - HCR_PRINT_SVCLASS_ID = 0x1126, - HCR_SCAN_SVCLASS_ID = 0x1127, - CIP_SVCLASS_ID = 0x1128, - VIDEO_CONF_GW_SVCLASS_ID = 0x1129, - UDI_MT_SVCLASS_ID = 0x112a, - UDI_TA_SVCLASS_ID = 0x112b, - AV_SVCLASS_ID = 0x112c, - SAP_SVCLASS_ID = 0x112d, - PBAP_PCE_SVCLASS_ID = 0x112e, - PBAP_PSE_SVCLASS_ID = 0x112f, - PBAP_SVCLASS_ID = 0x1130, - PNP_INFO_SVCLASS_ID = 0x1200, - GENERIC_NETWORKING_SVCLASS_ID = 0x1201, - GENERIC_FILETRANS_SVCLASS_ID = 0x1202, - GENERIC_AUDIO_SVCLASS_ID = 0x1203, - GENERIC_TELEPHONY_SVCLASS_ID = 0x1204, - UPNP_SVCLASS_ID = 0x1205, - UPNP_IP_SVCLASS_ID = 0x1206, - UPNP_PAN_SVCLASS_ID = 0x1300, - UPNP_LAP_SVCLASS_ID = 0x1301, - UPNP_L2CAP_SVCLASS_ID = 0x1302, - VIDEO_SOURCE_SVCLASS_ID = 0x1303, - VIDEO_SINK_SVCLASS_ID = 0x1304, - VIDEO_DISTRIBUTION_SVCLASS_ID = 0x1305, - MDP_SVCLASS_ID = 0x1400, - MDP_SOURCE_SVCLASS_ID = 0x1401, - MDP_SINK_SVCLASS_ID = 0x1402, - APPLE_AGENT_SVCLASS_ID = 0x2112, -}; - -/* - * Standard profile descriptor identifiers; note these - * may be identical to some of the service classes defined above - */ -#define SDP_SERVER_PROFILE_ID SDP_SERVER_SVCLASS_ID -#define BROWSE_GRP_DESC_PROFILE_ID BROWSE_GRP_DESC_SVCLASS_ID -#define SERIAL_PORT_PROFILE_ID SERIAL_PORT_SVCLASS_ID -#define LAN_ACCESS_PROFILE_ID LAN_ACCESS_SVCLASS_ID -#define DIALUP_NET_PROFILE_ID DIALUP_NET_SVCLASS_ID -#define IRMC_SYNC_PROFILE_ID IRMC_SYNC_SVCLASS_ID -#define OBEX_OBJPUSH_PROFILE_ID OBEX_OBJPUSH_SVCLASS_ID -#define OBEX_FILETRANS_PROFILE_ID OBEX_FILETRANS_SVCLASS_ID -#define IRMC_SYNC_CMD_PROFILE_ID IRMC_SYNC_CMD_SVCLASS_ID -#define HEADSET_PROFILE_ID HEADSET_SVCLASS_ID -#define CORDLESS_TELEPHONY_PROFILE_ID CORDLESS_TELEPHONY_SVCLASS_ID -#define AUDIO_SOURCE_PROFILE_ID AUDIO_SOURCE_SVCLASS_ID -#define AUDIO_SINK_PROFILE_ID AUDIO_SINK_SVCLASS_ID -#define AV_REMOTE_TARGET_PROFILE_ID AV_REMOTE_TARGET_SVCLASS_ID -#define ADVANCED_AUDIO_PROFILE_ID ADVANCED_AUDIO_SVCLASS_ID -#define AV_REMOTE_PROFILE_ID AV_REMOTE_SVCLASS_ID -#define VIDEO_CONF_PROFILE_ID VIDEO_CONF_SVCLASS_ID -#define INTERCOM_PROFILE_ID INTERCOM_SVCLASS_ID -#define FAX_PROFILE_ID FAX_SVCLASS_ID -#define HEADSET_AGW_PROFILE_ID HEADSET_AGW_SVCLASS_ID -#define WAP_PROFILE_ID WAP_SVCLASS_ID -#define WAP_CLIENT_PROFILE_ID WAP_CLIENT_SVCLASS_ID -#define PANU_PROFILE_ID PANU_SVCLASS_ID -#define NAP_PROFILE_ID NAP_SVCLASS_ID -#define GN_PROFILE_ID GN_SVCLASS_ID -#define DIRECT_PRINTING_PROFILE_ID DIRECT_PRINTING_SVCLASS_ID -#define REFERENCE_PRINTING_PROFILE_ID REFERENCE_PRINTING_SVCLASS_ID -#define IMAGING_PROFILE_ID IMAGING_SVCLASS_ID -#define IMAGING_RESPONDER_PROFILE_ID IMAGING_RESPONDER_SVCLASS_ID -#define IMAGING_ARCHIVE_PROFILE_ID IMAGING_ARCHIVE_SVCLASS_ID -#define IMAGING_REFOBJS_PROFILE_ID IMAGING_REFOBJS_SVCLASS_ID -#define HANDSFREE_PROFILE_ID HANDSFREE_SVCLASS_ID -#define HANDSFREE_AGW_PROFILE_ID HANDSFREE_AGW_SVCLASS_ID -#define DIRECT_PRT_REFOBJS_PROFILE_ID DIRECT_PRT_REFOBJS_SVCLASS_ID -#define REFLECTED_UI_PROFILE_ID REFLECTED_UI_SVCLASS_ID -#define BASIC_PRINTING_PROFILE_ID BASIC_PRINTING_SVCLASS_ID -#define PRINTING_STATUS_PROFILE_ID PRINTING_STATUS_SVCLASS_ID -#define HID_PROFILE_ID HID_SVCLASS_ID -#define HCR_PROFILE_ID HCR_SCAN_SVCLASS_ID -#define HCR_PRINT_PROFILE_ID HCR_PRINT_SVCLASS_ID -#define HCR_SCAN_PROFILE_ID HCR_SCAN_SVCLASS_ID -#define CIP_PROFILE_ID CIP_SVCLASS_ID -#define VIDEO_CONF_GW_PROFILE_ID VIDEO_CONF_GW_SVCLASS_ID -#define UDI_MT_PROFILE_ID UDI_MT_SVCLASS_ID -#define UDI_TA_PROFILE_ID UDI_TA_SVCLASS_ID -#define AV_PROFILE_ID AV_SVCLASS_ID -#define SAP_PROFILE_ID SAP_SVCLASS_ID -#define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID -#define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID -#define PBAP_PROFILE_ID PBAP_SVCLASS_ID -#define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID -#define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID -#define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID -#define GENERIC_AUDIO_PROFILE_ID GENERIC_AUDIO_SVCLASS_ID -#define GENERIC_TELEPHONY_PROFILE_ID GENERIC_TELEPHONY_SVCLASS_ID -#define UPNP_PROFILE_ID UPNP_SVCLASS_ID -#define UPNP_IP_PROFILE_ID UPNP_IP_SVCLASS_ID -#define UPNP_PAN_PROFILE_ID UPNP_PAN_SVCLASS_ID -#define UPNP_LAP_PROFILE_ID UPNP_LAP_SVCLASS_ID -#define UPNP_L2CAP_PROFILE_ID UPNP_L2CAP_SVCLASS_ID -#define VIDEO_SOURCE_PROFILE_ID VIDEO_SOURCE_SVCLASS_ID -#define VIDEO_SINK_PROFILE_ID VIDEO_SINK_SVCLASS_ID -#define VIDEO_DISTRIBUTION_PROFILE_ID VIDEO_DISTRIBUTION_SVCLASS_ID -#define MDP_PROFILE_ID MDP_SVCLASS_ID -#define MDP_SOURCE_PROFILE_ID MDP_SROUCE_SVCLASS_ID -#define MDP_SINK_PROFILE_ID MDP_SINK_SVCLASS_ID -#define APPLE_AGENT_PROFILE_ID APPLE_AGENT_SVCLASS_ID - -/* Data Representation */ -enum bt_sdp_data_type { - SDP_DTYPE_NIL = 0 << 3, - SDP_DTYPE_UINT = 1 << 3, - SDP_DTYPE_SINT = 2 << 3, - SDP_DTYPE_UUID = 3 << 3, - SDP_DTYPE_STRING = 4 << 3, - SDP_DTYPE_BOOL = 5 << 3, - SDP_DTYPE_SEQ = 6 << 3, - SDP_DTYPE_ALT = 7 << 3, - SDP_DTYPE_URL = 8 << 3, -}; - -enum bt_sdp_data_size { - SDP_DSIZE_1 = 0, - SDP_DSIZE_2, - SDP_DSIZE_4, - SDP_DSIZE_8, - SDP_DSIZE_16, - SDP_DSIZE_NEXT1, - SDP_DSIZE_NEXT2, - SDP_DSIZE_NEXT4, - SDP_DSIZE_MASK = SDP_DSIZE_NEXT4, -}; - -enum bt_sdp_cmd { - SDP_ERROR_RSP = 0x01, - SDP_SVC_SEARCH_REQ = 0x02, - SDP_SVC_SEARCH_RSP = 0x03, - SDP_SVC_ATTR_REQ = 0x04, - SDP_SVC_ATTR_RSP = 0x05, - SDP_SVC_SEARCH_ATTR_REQ = 0x06, - SDP_SVC_SEARCH_ATTR_RSP = 0x07, -}; - -enum bt_sdp_errorcode { - SDP_INVALID_VERSION = 0x0001, - SDP_INVALID_RECORD_HANDLE = 0x0002, - SDP_INVALID_SYNTAX = 0x0003, - SDP_INVALID_PDU_SIZE = 0x0004, - SDP_INVALID_CSTATE = 0x0005, -}; - -/* - * String identifiers are based on the SDP spec stating that - * "base attribute id of the primary (universal) language must be 0x0100" - * - * Other languages should have their own offset; e.g.: - * #define XXXLangBase yyyy - * #define AttrServiceName_XXX 0x0000+XXXLangBase - */ -#define SDP_PRIMARY_LANG_BASE 0x0100 - -enum bt_sdp_attribute_id { - SDP_ATTR_RECORD_HANDLE = 0x0000, - SDP_ATTR_SVCLASS_ID_LIST = 0x0001, - SDP_ATTR_RECORD_STATE = 0x0002, - SDP_ATTR_SERVICE_ID = 0x0003, - SDP_ATTR_PROTO_DESC_LIST = 0x0004, - SDP_ATTR_BROWSE_GRP_LIST = 0x0005, - SDP_ATTR_LANG_BASE_ATTR_ID_LIST = 0x0006, - SDP_ATTR_SVCINFO_TTL = 0x0007, - SDP_ATTR_SERVICE_AVAILABILITY = 0x0008, - SDP_ATTR_PFILE_DESC_LIST = 0x0009, - SDP_ATTR_DOC_URL = 0x000a, - SDP_ATTR_CLNT_EXEC_URL = 0x000b, - SDP_ATTR_ICON_URL = 0x000c, - SDP_ATTR_ADD_PROTO_DESC_LIST = 0x000d, - - SDP_ATTR_SVCNAME_PRIMARY = SDP_PRIMARY_LANG_BASE + 0, - SDP_ATTR_SVCDESC_PRIMARY = SDP_PRIMARY_LANG_BASE + 1, - SDP_ATTR_SVCPROV_PRIMARY = SDP_PRIMARY_LANG_BASE + 2, - - SDP_ATTR_GROUP_ID = 0x0200, - SDP_ATTR_IP_SUBNET = 0x0200, - - /* SDP */ - SDP_ATTR_VERSION_NUM_LIST = 0x0200, - SDP_ATTR_SVCDB_STATE = 0x0201, - - SDP_ATTR_SERVICE_VERSION = 0x0300, - SDP_ATTR_EXTERNAL_NETWORK = 0x0301, - SDP_ATTR_SUPPORTED_DATA_STORES_LIST = 0x0301, - SDP_ATTR_FAX_CLASS1_SUPPORT = 0x0302, - SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL = 0x0302, - SDP_ATTR_FAX_CLASS20_SUPPORT = 0x0303, - SDP_ATTR_SUPPORTED_FORMATS_LIST = 0x0303, - SDP_ATTR_FAX_CLASS2_SUPPORT = 0x0304, - SDP_ATTR_AUDIO_FEEDBACK_SUPPORT = 0x0305, - SDP_ATTR_NETWORK_ADDRESS = 0x0306, - SDP_ATTR_WAP_GATEWAY = 0x0307, - SDP_ATTR_HOMEPAGE_URL = 0x0308, - SDP_ATTR_WAP_STACK_TYPE = 0x0309, - SDP_ATTR_SECURITY_DESC = 0x030a, - SDP_ATTR_NET_ACCESS_TYPE = 0x030b, - SDP_ATTR_MAX_NET_ACCESSRATE = 0x030c, - SDP_ATTR_IP4_SUBNET = 0x030d, - SDP_ATTR_IP6_SUBNET = 0x030e, - SDP_ATTR_SUPPORTED_CAPABILITIES = 0x0310, - SDP_ATTR_SUPPORTED_FEATURES = 0x0311, - SDP_ATTR_SUPPORTED_FUNCTIONS = 0x0312, - SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY = 0x0313, - SDP_ATTR_SUPPORTED_REPOSITORIES = 0x0314, - - /* PnP Information */ - SDP_ATTR_SPECIFICATION_ID = 0x0200, - SDP_ATTR_VENDOR_ID = 0x0201, - SDP_ATTR_PRODUCT_ID = 0x0202, - SDP_ATTR_VERSION = 0x0203, - SDP_ATTR_PRIMARY_RECORD = 0x0204, - SDP_ATTR_VENDOR_ID_SOURCE = 0x0205, - - /* BT HID */ - SDP_ATTR_DEVICE_RELEASE_NUMBER = 0x0200, - SDP_ATTR_PARSER_VERSION = 0x0201, - SDP_ATTR_DEVICE_SUBCLASS = 0x0202, - SDP_ATTR_COUNTRY_CODE = 0x0203, - SDP_ATTR_VIRTUAL_CABLE = 0x0204, - SDP_ATTR_RECONNECT_INITIATE = 0x0205, - SDP_ATTR_DESCRIPTOR_LIST = 0x0206, - SDP_ATTR_LANG_ID_BASE_LIST = 0x0207, - SDP_ATTR_SDP_DISABLE = 0x0208, - SDP_ATTR_BATTERY_POWER = 0x0209, - SDP_ATTR_REMOTE_WAKEUP = 0x020a, - SDP_ATTR_PROFILE_VERSION = 0x020b, - SDP_ATTR_SUPERVISION_TIMEOUT = 0x020c, - SDP_ATTR_NORMALLY_CONNECTABLE = 0x020d, - SDP_ATTR_BOOT_DEVICE = 0x020e, -}; - -#endif diff --git a/include/sysemu/bt.h b/include/sysemu/bt.h deleted file mode 100644 index 2fd8c0f14b..0000000000 --- a/include/sysemu/bt.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef SYSEMU_BT_H -#define SYSEMU_BT_H - -/* BT HCI info */ - -typedef struct HCIInfo { - int (*bdaddr_set)(struct HCIInfo *hci, const uint8_t *bd_addr); - void (*cmd_send)(struct HCIInfo *hci, const uint8_t *data, int len); - void (*sco_send)(struct HCIInfo *hci, const uint8_t *data, int len); - void (*acl_send)(struct HCIInfo *hci, const uint8_t *data, int len); - void *opaque; - void (*evt_recv)(void *opaque, const uint8_t *data, int len); - void (*acl_recv)(void *opaque, const uint8_t *data, int len); -} HCIInfo; - -/* bt-host.c */ -struct HCIInfo *bt_host_hci(const char *id); -struct HCIInfo *qemu_next_hci(void); - -#endif diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi index f909be56ae..62680f7bd5 100644 --- a/qemu-deprecated.texi +++ b/qemu-deprecated.texi @@ -242,13 +242,6 @@ via the CPU ``mmu`` option when using the ``rv32`` or ``rv64`` CPUs. @section System emulator devices -@subsection bluetooth (since 3.1) - -The bluetooth subsystem is unmaintained since many years and likely bitrotten -quite a bit. It will be removed without replacement unless some users speaks -up at the @email{qemu-devel@@nongnu.org} mailing list with information about -their usecases. - @subsection ide-drive (since 4.2) The 'ide-drive' device is deprecated. Users should use 'ide-hd' or diff --git a/qemu-options.hx b/qemu-options.hx index 3ff8c4e07a..c63e794b64 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3115,85 +3115,6 @@ STEXI ETEXI DEFHEADING() -DEFHEADING(Bluetooth(R) options:) -STEXI -@table @option -ETEXI - -DEF("bt", HAS_ARG, QEMU_OPTION_bt, \ - "-bt hci,null dumb bluetooth HCI - doesn't respond to commands\n" \ - "-bt hci,host[:id]\n" \ - " use host's HCI with the given name\n" \ - "-bt hci[,vlan=n]\n" \ - " emulate a standard HCI in virtual scatternet 'n'\n" \ - "-bt vhci[,vlan=n]\n" \ - " add host computer to virtual scatternet 'n' using VHCI\n" \ - "-bt device:dev[,vlan=n]\n" \ - " emulate a bluetooth device 'dev' in scatternet 'n'\n", - QEMU_ARCH_ALL) -STEXI -@item -bt hci[...] -@findex -bt -Defines the function of the corresponding Bluetooth HCI. -bt options -are matched with the HCIs present in the chosen machine type. For -example when emulating a machine with only one HCI built into it, only -the first @code{-bt hci[...]} option is valid and defines the HCI's -logic. The Transport Layer is decided by the machine type. Currently -the machines @code{n800} and @code{n810} have one HCI and all other -machines have none. - -Note: This option and the whole bluetooth subsystem is considered as deprecated. -If you still use it, please send a mail to @email{qemu-devel@@nongnu.org} where -you describe your usecase. - -@anchor{bt-hcis} -The following three types are recognized: - -@table @option -@item -bt hci,null -(default) The corresponding Bluetooth HCI assumes no internal logic -and will not respond to any HCI commands or emit events. - -@item -bt hci,host[:@var{id}] -(@code{bluez} only) The corresponding HCI passes commands / events -to / from the physical HCI identified by the name @var{id} (default: -@code{hci0}) on the computer running QEMU. Only available on @code{bluez} -capable systems like Linux. - -@item -bt hci[,vlan=@var{n}] -Add a virtual, standard HCI that will participate in the Bluetooth -scatternet @var{n} (default @code{0}). Similarly to @option{-net} -VLANs, devices inside a bluetooth network @var{n} can only communicate -with other devices in the same network (scatternet). -@end table - -@item -bt vhci[,vlan=@var{n}] -(Linux-host only) Create a HCI in scatternet @var{n} (default 0) attached -to the host bluetooth stack instead of to the emulated target. This -allows the host and target machines to participate in a common scatternet -and communicate. Requires the Linux @code{vhci} driver installed. Can -be used as following: - -@example -@value{qemu_system} [...OPTIONS...] -bt hci,vlan=5 -bt vhci,vlan=5 -@end example - -@item -bt device:@var{dev}[,vlan=@var{n}] -Emulate a bluetooth device @var{dev} and place it in network @var{n} -(default @code{0}). QEMU can only emulate one type of bluetooth devices -currently: - -@table @option -@item keyboard -Virtual wireless keyboard implementing the HIDP bluetooth profile. -@end table -ETEXI - -STEXI -@end table -ETEXI -DEFHEADING() - #ifdef CONFIG_TPM DEFHEADING(TPM device options:) @@ -62,14 +62,12 @@ int main(int argc, char **argv) #include "hw/isa/isa.h" #include "hw/scsi/scsi.h" #include "hw/display/vga.h" -#include "hw/bt.h" #include "sysemu/watchdog.h" #include "hw/firmware/smbios.h" #include "hw/acpi/acpi.h" #include "hw/xen/xen.h" #include "hw/loader.h" #include "monitor/qdev.h" -#include "sysemu/bt.h" #include "net/net.h" #include "net/slirp.h" #include "monitor/monitor.h" @@ -914,128 +912,6 @@ static void configure_rtc(QemuOpts *opts) } } -/***********************************************************/ -/* Bluetooth support */ -static int nb_hcis; -static int cur_hci; -static struct HCIInfo *hci_table[MAX_NICS]; - -struct HCIInfo *qemu_next_hci(void) -{ - if (cur_hci == nb_hcis) - return &null_hci; - - return hci_table[cur_hci++]; -} - -static int bt_hci_parse(const char *str) -{ - struct HCIInfo *hci; - bdaddr_t bdaddr; - - if (nb_hcis >= MAX_NICS) { - error_report("too many bluetooth HCIs (max %i)", MAX_NICS); - return -1; - } - - hci = hci_init(str); - if (!hci) - return -1; - - bdaddr.b[0] = 0x52; - bdaddr.b[1] = 0x54; - bdaddr.b[2] = 0x00; - bdaddr.b[3] = 0x12; - bdaddr.b[4] = 0x34; - bdaddr.b[5] = 0x56 + nb_hcis; - hci->bdaddr_set(hci, bdaddr.b); - - hci_table[nb_hcis++] = hci; - - return 0; -} - -static void bt_vhci_add(int vlan_id) -{ - struct bt_scatternet_s *vlan = qemu_find_bt_vlan(vlan_id); - - if (!vlan->slave) - warn_report("adding a VHCI to an empty scatternet %i", - vlan_id); - - bt_vhci_init(bt_new_hci(vlan)); -} - -static struct bt_device_s *bt_device_add(const char *opt) -{ - struct bt_scatternet_s *vlan; - int vlan_id = 0; - char *endp = strstr(opt, ",vlan="); - int len = (endp ? endp - opt : strlen(opt)) + 1; - char devname[10]; - - pstrcpy(devname, MIN(sizeof(devname), len), opt); - - if (endp) { - vlan_id = strtol(endp + 6, &endp, 0); - if (*endp) { - error_report("unrecognised bluetooth vlan Id"); - return 0; - } - } - - vlan = qemu_find_bt_vlan(vlan_id); - - if (!vlan->slave) - warn_report("adding a slave device to an empty scatternet %i", - vlan_id); - - if (!strcmp(devname, "keyboard")) - return bt_keyboard_init(vlan); - - error_report("unsupported bluetooth device '%s'", devname); - return 0; -} - -static int bt_parse(const char *opt) -{ - const char *endp, *p; - int vlan; - - if (strstart(opt, "hci", &endp)) { - if (!*endp || *endp == ',') { - if (*endp) - if (!strstart(endp, ",vlan=", 0)) - opt = endp + 1; - - return bt_hci_parse(opt); - } - } else if (strstart(opt, "vhci", &endp)) { - if (!*endp || *endp == ',') { - if (*endp) { - if (strstart(endp, ",vlan=", &p)) { - vlan = strtol(p, (char **) &endp, 0); - if (*endp) { - error_report("bad scatternet '%s'", p); - return 1; - } - } else { - error_report("bad parameter '%s'", endp + 1); - return 1; - } - } else - vlan = 0; - - bt_vhci_add(vlan); - return 0; - } - } else if (strstart(opt, "device:", &endp)) - return !bt_device_add(endp); - - error_report("bad bluetooth parameter '%s'", opt); - return 1; -} - static int parse_name(void *opaque, QemuOpts *opts, Error **errp) { const char *proc_name; @@ -2319,7 +2195,6 @@ static void monitor_parse(const char *optarg, const char *mode, bool pretty) struct device_config { enum { DEV_USB, /* -usbdevice */ - DEV_BT, /* -bt */ DEV_SERIAL, /* -serial */ DEV_PARALLEL, /* -parallel */ DEV_DEBUGCON, /* -debugcon */ @@ -3128,13 +3003,6 @@ int main(int argc, char **argv, char **envp) } break; #endif - case QEMU_OPTION_bt: - warn_report("The bluetooth subsystem is deprecated and will " - "be removed soon. If the bluetooth subsystem is " - "still useful for you, please send a mail to " - "qemu-devel@nongnu.org with your usecase."); - add_device_config(DEV_BT, optarg); - break; case QEMU_OPTION_audio_help: audio_legacy_help(); exit (0); @@ -4259,10 +4127,6 @@ int main(int argc, char **argv, char **envp) tpm_init(); - /* init the bluetooth world */ - if (foreach_device_config(DEV_BT, bt_parse)) - exit(1); - if (!xen_enabled()) { /* On 32-bit hosts, QEMU is limited by virtual address space */ if (ram_size > (2047 << 20) && HOST_LONG_BITS == 32) { |