* Removal of the deprecated bluetooth code

* Some qtest and misc patches
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEJ7iIR+7gJQEY8+q5LtnXdP5wLbUFAl34jSQRHHRodXRoQHJl
 ZGhhdC5jb20ACgkQLtnXdP5wLbWq4A//XOn32ePftcnAwCM/1GSdyS8Olpn8mGdF
 g7oA94PVGMS5G8RQLhdC7i/TXhjs7B/HdSL9b6XShcqDI5zRg3sZp1ZIy1rPXZ++
 X9fIKETh2HIuwMaUJ/Tj93zPs0bnTrs2V0NADBw5tvXXRrdl5V/dKq3d89NRRQjx
 a4VSM39HkME2gGqb5zmlQ/ROaJx4UMplqIgT3UUsUcAlmKs+YJtU4nmkgmgP7kby
 sYNT5P3T39BWA5D9PAaDHyh7gG9xeraMhwe96A/zalvTACZ+dAYK65jVv4aGg1kQ
 6av3mmxLbkOdql5y13ihfcWn4Y/V9i8ccrgVZ1gDFVaCNyZwoNBPMc05dC2dgbkQ
 jvgZejvbCryIcSfKPdjD92LI9pGmfHiMUf0cehXAtVcBWvCXbcgXGHN1ophz2rD4
 w6JhzcXLCbTZHGxTmJNVSblCzbOmGFwLlRONWQ7/2AD5cItQmbE0hgyFjtmhmYxq
 f2bnvYi8vK9dOKkQQ4vNyQNTDCnksFCx4+d9trwyCYeVSPOgK9daehybLMkUzntD
 GoBpFVrpb3c5mLRkOYf9U/poxtqutFJYX/i/+miF+pJKaDizQxI+Qcs4m1qEAPiw
 bMn8+JWsjM+/Uo4JKPU4Il8QfHgNsGE6yZrSqu00CO0zvJ0B83aQV8DLXo/4L42s
 YDeZR0JNlGk=
 =tcei
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/huth-gitlab/tags/pull-request-2019-12-17' into staging

* Removal of the deprecated bluetooth code
* Some qtest and misc patches

# gpg: Signature made Tue 17 Dec 2019 08:09:08 GMT
# gpg:                using RSA key 27B88847EEE0250118F3EAB92ED9D774FE702DB5
# gpg:                issuer "thuth@redhat.com"
# gpg: Good signature from "Thomas Huth <th.huth@gmx.de>" [full]
# gpg:                 aka "Thomas Huth <thuth@redhat.com>" [full]
# gpg:                 aka "Thomas Huth <huth@tuxfamily.org>" [full]
# gpg:                 aka "Thomas Huth <th.huth@posteo.de>" [unknown]
# Primary key fingerprint: 27B8 8847 EEE0 2501 18F3  EAB9 2ED9 D774 FE70 2DB5

* remotes/huth-gitlab/tags/pull-request-2019-12-17:
  tests: use g_test_rand_int
  tests/Makefile: Fix check-report.* targets shown in check-help
  glib: use portable g_setenv()
  hw/misc/ivshmem: Bury dead legacy INTx code
  pseries: disable migration-test if /dev/kvm cannot be used
  tests: fix modules-test 'duplicate test case' error
  Remove libbluetooth / bluez from the CI tests
  Remove the core bluetooth code
  hw/usb: Remove the USB bluetooth dongle device
  hw/arm/nseries: Replace the bluetooth chardev with a "null" chardev

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-12-17 15:55:20 +00:00
commit aceeaa69d2
43 changed files with 26 additions and 9345 deletions

View File

@ -36,7 +36,7 @@ build-disabled:
build-tcg-disabled: build-tcg-disabled:
script: script:
- apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev - apt-get install -y -qq clang libgtk-3-dev libusb-dev
- ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list="" - ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list=""
- make -j2 - make -j2
- make check-unit - make check-unit

View File

@ -65,8 +65,6 @@ common-obj-y += replay/
common-obj-y += ui/ common-obj-y += ui/
common-obj-m += 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 += dma-helpers.o
common-obj-y += vl.o common-obj-y += vl.o

198
bt-host.c
View File

@ -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

167
bt-vhci.c
View File

@ -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);
}

31
configure vendored
View File

@ -349,7 +349,6 @@ unset target_list_exclude
# Distributions want to ensure that several features are compiled in, and it # 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. # is impossible without a --enable-foo that exits if a feature is not found.
bluez=""
brlapi="" brlapi=""
curl="" curl=""
curses="" curses=""
@ -1151,10 +1150,6 @@ for opt do
;; ;;
--enable-brlapi) brlapi="yes" --enable-brlapi) brlapi="yes"
;; ;;
--disable-bluez) bluez="no"
;;
--enable-bluez) bluez="yes"
;;
--disable-kvm) kvm="no" --disable-kvm) kvm="no"
;; ;;
--enable-kvm) kvm="yes" --enable-kvm) kvm="yes"
@ -1762,7 +1757,6 @@ disabled with --disable-FEATURE, default is enabled if available:
curl curl connectivity curl curl connectivity
membarrier membarrier system call (for Linux 4.14+ or Windows) membarrier membarrier system call (for Linux 4.14+ or Windows)
fdt fdt device tree fdt fdt device tree
bluez bluez stack connectivity
kvm KVM acceleration support kvm KVM acceleration support
hax HAX acceleration support hax HAX acceleration support
hvf Hypervisor.framework acceleration support hvf Hypervisor.framework acceleration support
@ -3665,26 +3659,6 @@ EOF
fi fi
fi # test "$curl" 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 support probe
@ -6493,7 +6467,6 @@ if test "$xen" = "yes" ; then
echo "xen ctrl version $xen_ctrl_version" echo "xen ctrl version $xen_ctrl_version"
fi fi
echo "brlapi support $brlapi" echo "brlapi support $brlapi"
echo "bluez support $bluez"
echo "Documentation $docs" echo "Documentation $docs"
echo "PIE $pie" echo "PIE $pie"
echo "vde support $vde" echo "vde support $vde"
@ -6917,10 +6890,6 @@ if test "$brlapi" = "yes" ; then
echo "CONFIG_BRLAPI=y" >> $config_host_mak echo "CONFIG_BRLAPI=y" >> $config_host_mak
echo "BRLAPI_LIBS=$brlapi_libs" >> $config_host_mak echo "BRLAPI_LIBS=$brlapi_libs" >> $config_host_mak
fi 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 if test "$gtk" = "yes" ; then
echo "CONFIG_GTK=m" >> $config_host_mak echo "CONFIG_GTK=m" >> $config_host_mak
echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak

View File

@ -4,7 +4,6 @@ source acpi/Kconfig
source adc/Kconfig source adc/Kconfig
source audio/Kconfig source audio/Kconfig
source block/Kconfig source block/Kconfig
source bt/Kconfig
source char/Kconfig source char/Kconfig
source core/Kconfig source core/Kconfig
source display/Kconfig source display/Kconfig

View File

@ -5,7 +5,6 @@ devices-dirs-y += acpi/
devices-dirs-y += adc/ devices-dirs-y += adc/
devices-dirs-y += audio/ devices-dirs-y += audio/
devices-dirs-y += block/ devices-dirs-y += block/
devices-dirs-y += bt/
devices-dirs-y += char/ devices-dirs-y += char/
devices-dirs-y += cpu/ devices-dirs-y += cpu/
devices-dirs-y += display/ devices-dirs-y += display/

View File

@ -21,6 +21,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "cpu.h" #include "cpu.h"
#include "chardev/char.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "sysemu/reset.h" #include "sysemu/reset.h"
@ -39,7 +40,6 @@
#include "hw/qdev-properties.h" #include "hw/qdev-properties.h"
#include "hw/block/flash.h" #include "hw/block/flash.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "hw/bt.h"
#include "hw/loader.h" #include "hw/loader.h"
#include "hw/sysbus.h" #include "hw/sysbus.h"
#include "qemu/log.h" #include "qemu/log.h"
@ -792,13 +792,11 @@ static void n8x0_cbus_setup(struct n800_s *s)
static void n8x0_uart_setup(struct n800_s *s) static void n8x0_uart_setup(struct n800_s *s)
{ {
Chardev *radio = uart_hci_init(); Chardev *radio = qemu_chr_new("bt-dummy-uart", "null", NULL);
/*
qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_RESET_GPIO, * Note: We used to connect N8X0_BT_RESET_GPIO and N8X0_BT_WKUP_GPIO
csrhci_pins_get(radio)[csrhci_pin_reset]); * here, but this code has been removed with the bluetooth backend.
qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_WKUP_GPIO, */
csrhci_pins_get(radio)[csrhci_pin_wakeup]);
omap_uart_attach(s->mpu->uart[BT_UART], radio); omap_uart_attach(s->mpu->uart[BT_UART], radio);
} }
@ -1137,7 +1135,7 @@ static struct omap_partition_info_s {
{ 0, 0, 0, NULL } { 0, 0, 0, NULL }
}; };
static bdaddr_t n8x0_bd_addr = {{ N8X0_BD_ADDR }}; static uint8_t n8x0_bd_addr[6] = { N8X0_BD_ADDR };
static int n8x0_atag_setup(void *p, int model) static int n8x0_atag_setup(void *p, int model)
{ {

View File

@ -1,2 +0,0 @@
config BLUETOOTH
bool

View File

@ -1,3 +0,0 @@
common-obj-y += core.o l2cap.o sdp.o hci.o hid.o
common-obj-y += hci-csr.o

View File

@ -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;
}

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -136,44 +136,11 @@ static inline bool ivshmem_is_master(IVShmemState *s)
return s->master == ON_OFF_AUTO_ON; return s->master == ON_OFF_AUTO_ON;
} }
static void ivshmem_update_irq(IVShmemState *s)
{
PCIDevice *d = PCI_DEVICE(s);
uint32_t isr = s->intrstatus & s->intrmask;
/*
* Do nothing unless the device actually uses INTx. Here's how
* the device variants signal interrupts, what they put in PCI
* config space:
* Device variant Interrupt Interrupt Pin MSI-X cap.
* ivshmem-plain none 0 no
* ivshmem-doorbell MSI-X 1 yes(1)
* ivshmem,msi=off INTx 1 no
* ivshmem,msi=on MSI-X 1(2) yes(1)
* (1) if guest enabled MSI-X
* (2) the device lies
* Leads to the condition for doing nothing:
*/
if (ivshmem_has_feature(s, IVSHMEM_MSI)
|| !d->config[PCI_INTERRUPT_PIN]) {
return;
}
/* don't print ISR resets */
if (isr) {
IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n",
isr ? 1 : 0, s->intrstatus, s->intrmask);
}
pci_set_irq(d, isr != 0);
}
static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
{ {
IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val); IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
s->intrmask = val; s->intrmask = val;
ivshmem_update_irq(s);
} }
static uint32_t ivshmem_IntrMask_read(IVShmemState *s) static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
@ -189,7 +156,6 @@ static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val); IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val);
s->intrstatus = val; s->intrstatus = val;
ivshmem_update_irq(s);
} }
static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
@ -198,7 +164,6 @@ static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
/* reading ISR clears all interrupts */ /* reading ISR clears all interrupts */
s->intrstatus = 0; s->intrstatus = 0;
ivshmem_update_irq(s);
return ret; return ret;
} }

View File

@ -82,11 +82,6 @@ config USB_NETWORK
default y default y
depends on USB depends on USB
config USB_BLUETOOTH
bool
default y
depends on USB
config USB_SMARTCARD config USB_SMARTCARD
bool bool
default y default y

View File

@ -25,7 +25,6 @@ common-obj-$(CONFIG_USB_STORAGE_UAS) += dev-uas.o
common-obj-$(CONFIG_USB_AUDIO) += dev-audio.o common-obj-$(CONFIG_USB_AUDIO) += dev-audio.o
common-obj-$(CONFIG_USB_SERIAL) += dev-serial.o common-obj-$(CONFIG_USB_SERIAL) += dev-serial.o
common-obj-$(CONFIG_USB_NETWORK) += dev-network.o common-obj-$(CONFIG_USB_NETWORK) += dev-network.o
common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o
ifeq ($(CONFIG_USB_SMARTCARD),y) ifeq ($(CONFIG_USB_SMARTCARD),y)
common-obj-y += dev-smartcard-reader.o common-obj-y += dev-smartcard-reader.o

View File

@ -1,581 +0,0 @@
/*
* QEMU Bluetooth HCI USB Transport Layer v1.0
*
* 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 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 "hw/usb.h"
#include "migration/vmstate.h"
#include "desc.h"
#include "sysemu/bt.h"
#include "hw/bt.h"
struct USBBtState {
USBDevice dev;
struct HCIInfo *hci;
USBEndpoint *intr;
int config;
#define CFIFO_LEN_MASK 255
#define DFIFO_LEN_MASK 4095
struct usb_hci_in_fifo_s {
uint8_t data[(DFIFO_LEN_MASK + 1) * 2];
struct {
uint8_t *data;
int len;
} fifo[CFIFO_LEN_MASK + 1];
int dstart, dlen, dsize, start, len;
} evt, acl, sco;
struct usb_hci_out_fifo_s {
uint8_t data[4096];
int len;
} outcmd, outacl, outsco;
};
#define TYPE_USB_BT "usb-bt-dongle"
#define USB_BT(obj) OBJECT_CHECK(struct USBBtState, (obj), TYPE_USB_BT)
#define USB_EVT_EP 1
#define USB_ACL_EP 2
#define USB_SCO_EP 3
enum {
STR_MANUFACTURER = 1,
STR_SERIALNUMBER,
};
static const USBDescStrings desc_strings = {
[STR_MANUFACTURER] = "QEMU",
[STR_SERIALNUMBER] = "1",
};
static const USBDescIface desc_iface_bluetooth[] = {
{
.bInterfaceNumber = 0,
.bNumEndpoints = 3,
.bInterfaceClass = 0xe0, /* Wireless */
.bInterfaceSubClass = 0x01, /* Radio Frequency */
.bInterfaceProtocol = 0x01, /* Bluetooth */
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_IN | USB_EVT_EP,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = 0x10,
.bInterval = 0x02,
},
{
.bEndpointAddress = USB_DIR_OUT | USB_ACL_EP,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 0x40,
.bInterval = 0x0a,
},
{
.bEndpointAddress = USB_DIR_IN | USB_ACL_EP,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 0x40,
.bInterval = 0x0a,
},
},
},{
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = 0xe0, /* Wireless */
.bInterfaceSubClass = 0x01, /* Radio Frequency */
.bInterfaceProtocol = 0x01, /* Bluetooth */
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0,
.bInterval = 0x01,
},
},
},{
.bInterfaceNumber = 1,
.bAlternateSetting = 1,
.bNumEndpoints = 2,
.bInterfaceClass = 0xe0, /* Wireless */
.bInterfaceSubClass = 0x01, /* Radio Frequency */
.bInterfaceProtocol = 0x01, /* Bluetooth */
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x09,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x09,
.bInterval = 0x01,
},
},
},{
.bInterfaceNumber = 1,
.bAlternateSetting = 2,
.bNumEndpoints = 2,
.bInterfaceClass = 0xe0, /* Wireless */
.bInterfaceSubClass = 0x01, /* Radio Frequency */
.bInterfaceProtocol = 0x01, /* Bluetooth */
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x11,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x11,
.bInterval = 0x01,
},
},
},{
.bInterfaceNumber = 1,
.bAlternateSetting = 3,
.bNumEndpoints = 2,
.bInterfaceClass = 0xe0, /* Wireless */
.bInterfaceSubClass = 0x01, /* Radio Frequency */
.bInterfaceProtocol = 0x01, /* Bluetooth */
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x19,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x19,
.bInterval = 0x01,
},
},
},{
.bInterfaceNumber = 1,
.bAlternateSetting = 4,
.bNumEndpoints = 2,
.bInterfaceClass = 0xe0, /* Wireless */
.bInterfaceSubClass = 0x01, /* Radio Frequency */
.bInterfaceProtocol = 0x01, /* Bluetooth */
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x21,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x21,
.bInterval = 0x01,
},
},
},{
.bInterfaceNumber = 1,
.bAlternateSetting = 5,
.bNumEndpoints = 2,
.bInterfaceClass = 0xe0, /* Wireless */
.bInterfaceSubClass = 0x01, /* Radio Frequency */
.bInterfaceProtocol = 0x01, /* Bluetooth */
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x31,
.bInterval = 0x01,
},
{
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = 0x31,
.bInterval = 0x01,
},
},
}
};
static const USBDescDevice desc_device_bluetooth = {
.bcdUSB = 0x0110,
.bDeviceClass = 0xe0, /* Wireless */
.bDeviceSubClass = 0x01, /* Radio Frequency */
.bDeviceProtocol = 0x01, /* Bluetooth */
.bMaxPacketSize0 = 64,
.bNumConfigurations = 1,
.confs = (USBDescConfig[]) {
{
.bNumInterfaces = 2,
.bConfigurationValue = 1,
.bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER,
.bMaxPower = 0,
.nif = ARRAY_SIZE(desc_iface_bluetooth),
.ifs = desc_iface_bluetooth,
},
},
};
static const USBDesc desc_bluetooth = {
.id = {
.idVendor = 0x0a12,
.idProduct = 0x0001,
.bcdDevice = 0x1958,
.iManufacturer = STR_MANUFACTURER,
.iProduct = 0,
.iSerialNumber = STR_SERIALNUMBER,
},
.full = &desc_device_bluetooth,
.str = desc_strings,
};
static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo)
{
fifo->dstart = 0;
fifo->dlen = 0;
fifo->dsize = DFIFO_LEN_MASK + 1;
fifo->start = 0;
fifo->len = 0;
}
static void usb_bt_fifo_enqueue(struct usb_hci_in_fifo_s *fifo,
const uint8_t *data, int len)
{
int off = fifo->dstart + fifo->dlen;
uint8_t *buf;
fifo->dlen += len;
if (off <= DFIFO_LEN_MASK) {
if (off + len > DFIFO_LEN_MASK + 1 &&
(fifo->dsize = off + len) > (DFIFO_LEN_MASK + 1) * 2) {
fprintf(stderr, "%s: can't alloc %i bytes\n", __func__, len);
exit(-1);
}
buf = fifo->data + off;
} else {
if (fifo->dlen > fifo->dsize) {
fprintf(stderr, "%s: can't alloc %i bytes\n", __func__, len);
exit(-1);
}
buf = fifo->data + off - fifo->dsize;
}
off = (fifo->start + fifo->len ++) & CFIFO_LEN_MASK;
fifo->fifo[off].data = memcpy(buf, data, len);
fifo->fifo[off].len = len;
}
static inline void usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
USBPacket *p)
{
int len;
assert(fifo->len != 0);
len = MIN(p->iov.size, fifo->fifo[fifo->start].len);
usb_packet_copy(p, fifo->fifo[fifo->start].data, len);
if (len == p->iov.size) {
fifo->fifo[fifo->start].len -= len;
fifo->fifo[fifo->start].data += len;
} else {
fifo->start ++;
fifo->start &= CFIFO_LEN_MASK;
fifo->len --;
}
fifo->dstart += len;
fifo->dlen -= len;
if (fifo->dstart >= fifo->dsize) {
fifo->dstart = 0;
fifo->dsize = DFIFO_LEN_MASK + 1;
}
}
static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s,
struct usb_hci_out_fifo_s *fifo,
void (*send)(struct HCIInfo *, const uint8_t *, int),
int (*complete)(const uint8_t *, int),
USBPacket *p)
{
usb_packet_copy(p, fifo->data + fifo->len, p->iov.size);
fifo->len += p->iov.size;
if (complete(fifo->data, fifo->len)) {
send(s->hci, fifo->data, fifo->len);
fifo->len = 0;
}
/* TODO: do we need to loop? */
}
static int usb_bt_hci_cmd_complete(const uint8_t *data, int len)
{
len -= HCI_COMMAND_HDR_SIZE;
return len >= 0 &&
len >= ((struct hci_command_hdr *) data)->plen;
}
static int usb_bt_hci_acl_complete(const uint8_t *data, int len)
{
len -= HCI_ACL_HDR_SIZE;
return len >= 0 &&
len >= le16_to_cpu(((struct hci_acl_hdr *) data)->dlen);
}
static int usb_bt_hci_sco_complete(const uint8_t *data, int len)
{
len -= HCI_SCO_HDR_SIZE;
return len >= 0 &&
len >= ((struct hci_sco_hdr *) data)->dlen;
}
static void usb_bt_handle_reset(USBDevice *dev)
{
struct USBBtState *s = (struct USBBtState *) dev->opaque;
usb_bt_fifo_reset(&s->evt);
usb_bt_fifo_reset(&s->acl);
usb_bt_fifo_reset(&s->sco);
s->outcmd.len = 0;
s->outacl.len = 0;
s->outsco.len = 0;
}
static void usb_bt_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
struct USBBtState *s = (struct USBBtState *) dev->opaque;
int ret;
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
switch (request) {
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
s->config = 0;
break;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
s->config = 1;
usb_bt_fifo_reset(&s->evt);
usb_bt_fifo_reset(&s->acl);
usb_bt_fifo_reset(&s->sco);
break;
}
return;
}
switch (request) {
case InterfaceRequest | USB_REQ_GET_STATUS:
case EndpointRequest | USB_REQ_GET_STATUS:
data[0] = 0x00;
data[1] = 0x00;
p->actual_length = 2;
break;
case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE:
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
goto fail;
case InterfaceOutRequest | USB_REQ_SET_FEATURE:
case EndpointOutRequest | USB_REQ_SET_FEATURE:
goto fail;
break;
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8):
if (s->config)
usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send,
usb_bt_hci_cmd_complete, p);
break;
default:
fail:
p->status = USB_RET_STALL;
break;
}
}
static void usb_bt_handle_data(USBDevice *dev, USBPacket *p)
{
struct USBBtState *s = (struct USBBtState *) dev->opaque;
if (!s->config)
goto fail;
switch (p->pid) {
case USB_TOKEN_IN:
switch (p->ep->nr) {
case USB_EVT_EP:
if (s->evt.len == 0) {
p->status = USB_RET_NAK;
break;
}
usb_bt_fifo_dequeue(&s->evt, p);
break;
case USB_ACL_EP:
if (s->evt.len == 0) {
p->status = USB_RET_STALL;
break;
}
usb_bt_fifo_dequeue(&s->acl, p);
break;
case USB_SCO_EP:
if (s->evt.len == 0) {
p->status = USB_RET_STALL;
break;
}
usb_bt_fifo_dequeue(&s->sco, p);
break;
default:
goto fail;
}
break;
case USB_TOKEN_OUT:
switch (p->ep->nr) {
case USB_ACL_EP:
usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send,
usb_bt_hci_acl_complete, p);
break;
case USB_SCO_EP:
usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send,
usb_bt_hci_sco_complete, p);
break;
default:
goto fail;
}
break;
default:
fail:
p->status = USB_RET_STALL;
break;
}
}
static void usb_bt_out_hci_packet_event(void *opaque,
const uint8_t *data, int len)
{
struct USBBtState *s = (struct USBBtState *) opaque;
if (s->evt.len == 0) {
usb_wakeup(s->intr, 0);
}
usb_bt_fifo_enqueue(&s->evt, data, len);
}
static void usb_bt_out_hci_packet_acl(void *opaque,
const uint8_t *data, int len)
{
struct USBBtState *s = (struct USBBtState *) opaque;
usb_bt_fifo_enqueue(&s->acl, data, len);
}
static void usb_bt_unrealize(USBDevice *dev, Error **errp)
{
struct USBBtState *s = (struct USBBtState *) dev->opaque;
s->hci->opaque = NULL;
s->hci->evt_recv = NULL;
s->hci->acl_recv = NULL;
}
static void usb_bt_realize(USBDevice *dev, Error **errp)
{
struct USBBtState *s = USB_BT(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
s->dev.opaque = s;
if (!s->hci) {
s->hci = bt_new_hci(qemu_find_bt_vlan(0));
}
s->hci->opaque = s;
s->hci->evt_recv = usb_bt_out_hci_packet_event;
s->hci->acl_recv = usb_bt_out_hci_packet_acl;
usb_bt_handle_reset(&s->dev);
s->intr = usb_ep_get(dev, USB_TOKEN_IN, USB_EVT_EP);
}
static USBDevice *usb_bt_init(USBBus *bus, const char *cmdline)
{
USBDevice *dev;
struct USBBtState *s;
HCIInfo *hci;
const char *name = TYPE_USB_BT;
if (*cmdline) {
hci = hci_init(cmdline);
} else {
hci = bt_new_hci(qemu_find_bt_vlan(0));
}
if (!hci)
return NULL;
dev = usb_create(bus, name);
s = USB_BT(dev);
s->hci = hci;
return dev;
}
static const VMStateDescription vmstate_usb_bt = {
.name = "usb-bt",
.unmigratable = 1,
};
static void usb_bt_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
uc->realize = usb_bt_realize;
uc->product_desc = "QEMU BT dongle";
uc->usb_desc = &desc_bluetooth;
uc->handle_reset = usb_bt_handle_reset;
uc->handle_control = usb_bt_handle_control;
uc->handle_data = usb_bt_handle_data;
uc->unrealize = usb_bt_unrealize;
dc->vmsd = &vmstate_usb_bt;
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
}
static const TypeInfo bt_info = {
.name = TYPE_USB_BT,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(struct USBBtState),
.class_init = usb_bt_class_initfn,
};
static void usb_bt_register_types(void)
{
type_register_static(&bt_info);
usb_legacy_register(TYPE_USB_BT, "bt", usb_bt_init);
}
type_init(usb_bt_register_types)

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -67,8 +67,6 @@ int getpagesize(void);
# define EPROTONOSUPPORT EINVAL # define EPROTONOSUPPORT EINVAL
#endif #endif
int setenv(const char *name, const char *value, int overwrite);
typedef struct { typedef struct {
long tv_sec; long tv_sec;
long tv_usec; long tv_usec;

View File

@ -30,28 +30,6 @@
#include "qemu-options.h" #include "qemu-options.h"
#include "sysemu/runstate.h" #include "sysemu/runstate.h"
/***********************************************************/
/* Functions missing in mingw */
int setenv(const char *name, const char *value, int overwrite)
{
int result = 0;
if (overwrite || !getenv(name)) {
size_t length = strlen(name) + strlen(value) + 2;
char *string = g_malloc(length);
snprintf(string, length, "%s=%s", name, value);
result = putenv(string);
/* Windows takes a copy and does not continue to use our string.
* Therefore it can be safely freed on this platform. POSIX code
* typically has to leak the string because according to the spec it
* becomes part of the environment.
*/
g_free(string);
}
return result;
}
static BOOL WINAPI qemu_ctrl_handler(DWORD type) static BOOL WINAPI qemu_ctrl_handler(DWORD type)
{ {
qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL); qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);

View File

@ -242,13 +242,6 @@ via the CPU ``mmu`` option when using the ``rv32`` or ``rv64`` CPUs.
@section System emulator devices @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) @subsection ide-drive (since 4.2)
The 'ide-drive' device is deprecated. Users should use 'ide-hd' or The 'ide-drive' device is deprecated. Users should use 'ide-hd' or

View File

@ -983,21 +983,6 @@ For instance, user-mode networking can be used with
Smartcard reader device Smartcard reader device
@item usb-audio @item usb-audio
USB audio device USB audio device
@item usb-bt-dongle
Bluetooth dongle for the transport layer of HCI. It is connected to HCI
scatternet 0 by default (corresponds to @code{-bt hci,vlan=0}).
Note that the syntax for the @code{-device usb-bt-dongle} option is not as
useful yet as it was with the legacy @code{-usbdevice} option. So to
configure an USB bluetooth device, you might need to use
"@code{-usbdevice bt}[:@var{hci-type}]" instead. This configures a
bluetooth dongle whose type is specified in the same format as with
the @option{-bt hci} option, @pxref{bt-hcis,,allowed HCI types}. If
no type is given, the HCI logic corresponds to @code{-bt hci,vlan=0}.
This USB device implements the USB Transport Layer of HCI. Example
usage:
@example
@command{@value{qemu_system}} [...@var{OPTIONS}...] @option{-usbdevice} bt:hci,vlan=3 @option{-bt} device:keyboard,vlan=3
@end example
@end table @end table
@node host_usb_devices @node host_usb_devices
@ -2308,8 +2293,6 @@ Secure Digital card connected to OMAP MMC/SD host
@item @item
Three OMAP on-chip UARTs and on-chip STI debugging console Three OMAP on-chip UARTs and on-chip STI debugging console
@item @item
A Bluetooth(R) transceiver and HCI connected to an UART
@item
Mentor Graphics "Inventra" dual-role USB controller embedded in a TI Mentor Graphics "Inventra" dual-role USB controller embedded in a TI
TUSB6010 chip - only USB host mode is supported TUSB6010 chip - only USB host mode is supported
@item @item

View File

@ -3115,85 +3115,6 @@ STEXI
ETEXI ETEXI
DEFHEADING() 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 #ifdef CONFIG_TPM
DEFHEADING(TPM device options:) DEFHEADING(TPM device options:)

View File

@ -16,12 +16,10 @@ check-help:
@echo " $(MAKE) check-softfloat Run FPU emulation tests" @echo " $(MAKE) check-softfloat Run FPU emulation tests"
@echo " $(MAKE) check-acceptance Run all acceptance (functional) tests" @echo " $(MAKE) check-acceptance Run all acceptance (functional) tests"
@echo @echo
@echo " $(MAKE) check-report.html Generates an HTML test report" @echo " $(MAKE) check-report.tap Generates an aggregated TAP test report"
@echo " $(MAKE) check-venv Creates a Python venv for tests" @echo " $(MAKE) check-venv Creates a Python venv for tests"
@echo " $(MAKE) check-clean Clean the tests and related data" @echo " $(MAKE) check-clean Clean the tests and related data"
@echo @echo
@echo "Please note that HTML reports do not regenerate if the unit tests"
@echo "have not changed."
@echo @echo
@echo "The variable SPEED can be set to control the gtester speed setting." @echo "The variable SPEED can be set to control the gtester speed setting."
@echo "Default options are -k and (for $(MAKE) V=1) --verbose; they can be" @echo "Default options are -k and (for $(MAKE) V=1) --verbose; they can be"

View File

@ -2,7 +2,6 @@ FROM fedora:30
ENV PACKAGES \ ENV PACKAGES \
bc \ bc \
bison \ bison \
bluez-libs-devel \
brlapi-devel \ brlapi-devel \
bzip2 \ bzip2 \
bzip2-devel \ bzip2-devel \

View File

@ -19,7 +19,6 @@ ENV PACKAGES flex bison \
glusterfs-common \ glusterfs-common \
libaio-dev \ libaio-dev \
libattr1-dev \ libattr1-dev \
libbluetooth-dev \
libbrlapi-dev \ libbrlapi-dev \
libbz2-dev \ libbz2-dev \
libcacard-dev \ libcacard-dev \

View File

@ -8,7 +8,6 @@ ENV PACKAGES flex bison \
glusterfs-common \ glusterfs-common \
libaio-dev \ libaio-dev \
libattr1-dev \ libattr1-dev \
libbluetooth-dev \
libbrlapi-dev \ libbrlapi-dev \
libbz2-dev \ libbz2-dev \
libcacard-dev \ libcacard-dev \

View File

@ -443,7 +443,7 @@ static gchar *mktempshm(int size, int *fd)
while (true) { while (true) {
gchar *name; gchar *name;
name = g_strdup_printf("/qtest-%u-%u", getpid(), g_random_int()); name = g_strdup_printf("/qtest-%u-%u", getpid(), g_test_rand_int());
*fd = shm_open(name, O_CREAT|O_RDWR|O_EXCL, *fd = shm_open(name, O_CREAT|O_RDWR|O_EXCL,
S_IRWXU|S_IRWXG|S_IRWXO); S_IRWXU|S_IRWXG|S_IRWXO);
if (*fd > 0) { if (*fd > 0) {

View File

@ -254,7 +254,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
s->expected_status = 0; s->expected_status = 0;
s->qemu_pid = fork(); s->qemu_pid = fork();
if (s->qemu_pid == 0) { if (s->qemu_pid == 0) {
setenv("QEMU_AUDIO_DRV", "none", true); g_setenv("QEMU_AUDIO_DRV", "none", true);
execlp("/bin/sh", "sh", "-c", command, NULL); execlp("/bin/sh", "sh", "-c", command, NULL);
exit(1); exit(1);
} }

View File

@ -1349,7 +1349,8 @@ int main(int argc, char **argv)
* some reason) * some reason)
*/ */
if (g_str_equal(qtest_get_arch(), "ppc64") && if (g_str_equal(qtest_get_arch(), "ppc64") &&
access("/sys/module/kvm_hv", F_OK)) { (access("/sys/module/kvm_hv", F_OK) ||
access("/dev/kvm", R_OK | W_OK))) {
g_test_message("Skipping test: kvm_hv not available"); g_test_message("Skipping test: kvm_hv not available");
return g_test_run(); return g_test_run();
} }

View File

@ -64,7 +64,8 @@ int main(int argc, char *argv[])
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
for (i = 0; i < G_N_ELEMENTS(modules); i += 2) { for (i = 0; i < G_N_ELEMENTS(modules); i += 2) {
char *testname = g_strdup_printf("/module/load/%s", modules[i + 1]); char *testname = g_strdup_printf("/module/load/%s%s",
modules[i], modules[i + 1]);
qtest_add_data_func(testname, modules + i, test_modules_load); qtest_add_data_func(testname, modules + i, test_modules_load);
g_free(testname); g_free(testname);
} }

View File

@ -22,10 +22,10 @@ static void check_bitmap_copy_with_offset(void)
bmap2 = bitmap_new(BMAP_SIZE); bmap2 = bitmap_new(BMAP_SIZE);
bmap3 = bitmap_new(BMAP_SIZE); bmap3 = bitmap_new(BMAP_SIZE);
bmap1[0] = random(); bmap1[0] = g_test_rand_int();
bmap1[1] = random(); bmap1[1] = g_test_rand_int();
bmap1[2] = random(); bmap1[2] = g_test_rand_int();
bmap1[3] = random(); bmap1[3] = g_test_rand_int();
total = BITS_PER_LONG * 4; total = BITS_PER_LONG * 4;
/* Shift 115 bits into bmap2 */ /* Shift 115 bits into bmap2 */

View File

@ -141,7 +141,7 @@ int main(int argc, char **argv)
module_call_init(MODULE_INIT_QOM); module_call_init(MODULE_INIT_QOM);
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
mkdir(WORKDIR, 0700); mkdir(WORKDIR, 0700);

View File

@ -398,7 +398,7 @@ int main(int argc, char **argv)
module_call_init(MODULE_INIT_QOM); module_call_init(MODULE_INIT_QOM);
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
mkdir(WORKDIR, 0700); mkdir(WORKDIR, 0700);

View File

@ -273,7 +273,7 @@ int main(int argc, char **argv)
module_call_init(MODULE_INIT_QOM); module_call_init(MODULE_INIT_QOM);
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
mkdir(WORKDIR, 0700); mkdir(WORKDIR, 0700);

View File

@ -143,7 +143,7 @@ static void qmp_assertion_message_error(const char *domain,
static void test_qga_sync_delimited(gconstpointer fix) static void test_qga_sync_delimited(gconstpointer fix)
{ {
const TestFixture *fixture = fix; const TestFixture *fixture = fix;
guint32 v, r = g_random_int(); guint32 v, r = g_test_rand_int();
unsigned char c; unsigned char c;
QDict *ret; QDict *ret;
@ -186,7 +186,7 @@ static void test_qga_sync_delimited(gconstpointer fix)
static void test_qga_sync(gconstpointer fix) static void test_qga_sync(gconstpointer fix)
{ {
const TestFixture *fixture = fix; const TestFixture *fixture = fix;
guint32 v, r = g_random_int(); guint32 v, r = g_test_rand_int();
QDict *ret; QDict *ret;
/* /*

View File

@ -1325,7 +1325,7 @@ int main(int argc, char **argv)
module_call_init(MODULE_INIT_QOM); module_call_init(MODULE_INIT_QOM);
setenv("QTEST_SILENT_ERRORS", "1", 1); g_setenv("QTEST_SILENT_ERRORS", "1", 1);
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
g_test_add_func("/vmstate/simple/primitive", test_simple_primitive); g_test_add_func("/vmstate/simple/primitive", test_simple_primitive);

View File

@ -772,7 +772,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
* This is a bit hackish but saves us from bigger problem. * This is a bit hackish but saves us from bigger problem.
* Maybe it's a good idea to fix this in SDL instead. * Maybe it's a good idea to fix this in SDL instead.
*/ */
setenv("SDL_VIDEODRIVER", "x11", 0); g_setenv("SDL_VIDEODRIVER", "x11", 0);
#endif #endif
if (SDL_Init(SDL_INIT_VIDEO)) { if (SDL_Init(SDL_INIT_VIDEO)) {

136
vl.c
View File

@ -62,14 +62,12 @@ int main(int argc, char **argv)
#include "hw/isa/isa.h" #include "hw/isa/isa.h"
#include "hw/scsi/scsi.h" #include "hw/scsi/scsi.h"
#include "hw/display/vga.h" #include "hw/display/vga.h"
#include "hw/bt.h"
#include "sysemu/watchdog.h" #include "sysemu/watchdog.h"
#include "hw/firmware/smbios.h" #include "hw/firmware/smbios.h"
#include "hw/acpi/acpi.h" #include "hw/acpi/acpi.h"
#include "hw/xen/xen.h" #include "hw/xen/xen.h"
#include "hw/loader.h" #include "hw/loader.h"
#include "monitor/qdev.h" #include "monitor/qdev.h"
#include "sysemu/bt.h"
#include "net/net.h" #include "net/net.h"
#include "net/slirp.h" #include "net/slirp.h"
#include "monitor/monitor.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) static int parse_name(void *opaque, QemuOpts *opts, Error **errp)
{ {
const char *proc_name; const char *proc_name;
@ -2319,7 +2195,6 @@ static void monitor_parse(const char *optarg, const char *mode, bool pretty)
struct device_config { struct device_config {
enum { enum {
DEV_USB, /* -usbdevice */ DEV_USB, /* -usbdevice */
DEV_BT, /* -bt */
DEV_SERIAL, /* -serial */ DEV_SERIAL, /* -serial */
DEV_PARALLEL, /* -parallel */ DEV_PARALLEL, /* -parallel */
DEV_DEBUGCON, /* -debugcon */ DEV_DEBUGCON, /* -debugcon */
@ -3128,13 +3003,6 @@ int main(int argc, char **argv, char **envp)
} }
break; break;
#endif #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: case QEMU_OPTION_audio_help:
audio_legacy_help(); audio_legacy_help();
exit (0); exit (0);
@ -4259,10 +4127,6 @@ int main(int argc, char **argv, char **envp)
tpm_init(); tpm_init();
/* init the bluetooth world */
if (foreach_device_config(DEV_BT, bt_parse))
exit(1);
if (!xen_enabled()) { if (!xen_enabled()) {
/* On 32-bit hosts, QEMU is limited by virtual address space */ /* On 32-bit hosts, QEMU is limited by virtual address space */
if (ram_size > (2047 << 20) && HOST_LONG_BITS == 32) { if (ram_size > (2047 << 20) && HOST_LONG_BITS == 32) {