* 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:
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=""
- make -j2
- make check-unit

View File

@ -65,8 +65,6 @@ common-obj-y += replay/
common-obj-y += ui/
common-obj-m += ui/
common-obj-y += bt-host.o bt-vhci.o
bt-host.o-cflags := $(BLUEZ_CFLAGS)
common-obj-y += dma-helpers.o
common-obj-y += vl.o

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

View File

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

View File

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

View File

@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "cpu.h"
#include "chardev/char.h"
#include "qemu/cutils.h"
#include "qemu/bswap.h"
#include "sysemu/reset.h"
@ -39,7 +40,6 @@
#include "hw/qdev-properties.h"
#include "hw/block/flash.h"
#include "hw/hw.h"
#include "hw/bt.h"
#include "hw/loader.h"
#include "hw/sysbus.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)
{
Chardev *radio = uart_hci_init();
qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_RESET_GPIO,
csrhci_pins_get(radio)[csrhci_pin_reset]);
qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_WKUP_GPIO,
csrhci_pins_get(radio)[csrhci_pin_wakeup]);
Chardev *radio = qemu_chr_new("bt-dummy-uart", "null", NULL);
/*
* Note: We used to connect N8X0_BT_RESET_GPIO and N8X0_BT_WKUP_GPIO
* here, but this code has been removed with the bluetooth backend.
*/
omap_uart_attach(s->mpu->uart[BT_UART], radio);
}
@ -1137,7 +1135,7 @@ static struct omap_partition_info_s {
{ 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)
{

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;
}
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)
{
IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
s->intrmask = val;
ivshmem_update_irq(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);
s->intrstatus = val;
ivshmem_update_irq(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 */
s->intrstatus = 0;
ivshmem_update_irq(s);
return ret;
}

View File

@ -82,11 +82,6 @@ config USB_NETWORK
default y
depends on USB
config USB_BLUETOOTH
bool
default y
depends on USB
config USB_SMARTCARD
bool
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_SERIAL) += dev-serial.o
common-obj-$(CONFIG_USB_NETWORK) += dev-network.o
common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o
ifeq ($(CONFIG_USB_SMARTCARD),y)
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
#endif
int setenv(const char *name, const char *value, int overwrite);
typedef struct {
long tv_sec;
long tv_usec;

View File

@ -30,28 +30,6 @@
#include "qemu-options.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)
{
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
@subsection bluetooth (since 3.1)
The bluetooth subsystem is unmaintained since many years and likely bitrotten
quite a bit. It will be removed without replacement unless some users speaks
up at the @email{qemu-devel@@nongnu.org} mailing list with information about
their usecases.
@subsection ide-drive (since 4.2)
The 'ide-drive' device is deprecated. Users should use 'ide-hd' or

View File

@ -983,21 +983,6 @@ For instance, user-mode networking can be used with
Smartcard reader device
@item usb-audio
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
@node host_usb_devices
@ -2308,8 +2293,6 @@ Secure Digital card connected to OMAP MMC/SD host
@item
Three OMAP on-chip UARTs and on-chip STI debugging console
@item
A Bluetooth(R) transceiver and HCI connected to an UART
@item
Mentor Graphics "Inventra" dual-role USB controller embedded in a TI
TUSB6010 chip - only USB host mode is supported
@item

View File

@ -3115,85 +3115,6 @@ STEXI
ETEXI
DEFHEADING()
DEFHEADING(Bluetooth(R) options:)
STEXI
@table @option
ETEXI
DEF("bt", HAS_ARG, QEMU_OPTION_bt, \
"-bt hci,null dumb bluetooth HCI - doesn't respond to commands\n" \
"-bt hci,host[:id]\n" \
" use host's HCI with the given name\n" \
"-bt hci[,vlan=n]\n" \
" emulate a standard HCI in virtual scatternet 'n'\n" \
"-bt vhci[,vlan=n]\n" \
" add host computer to virtual scatternet 'n' using VHCI\n" \
"-bt device:dev[,vlan=n]\n" \
" emulate a bluetooth device 'dev' in scatternet 'n'\n",
QEMU_ARCH_ALL)
STEXI
@item -bt hci[...]
@findex -bt
Defines the function of the corresponding Bluetooth HCI. -bt options
are matched with the HCIs present in the chosen machine type. For
example when emulating a machine with only one HCI built into it, only
the first @code{-bt hci[...]} option is valid and defines the HCI's
logic. The Transport Layer is decided by the machine type. Currently
the machines @code{n800} and @code{n810} have one HCI and all other
machines have none.
Note: This option and the whole bluetooth subsystem is considered as deprecated.
If you still use it, please send a mail to @email{qemu-devel@@nongnu.org} where
you describe your usecase.
@anchor{bt-hcis}
The following three types are recognized:
@table @option
@item -bt hci,null
(default) The corresponding Bluetooth HCI assumes no internal logic
and will not respond to any HCI commands or emit events.
@item -bt hci,host[:@var{id}]
(@code{bluez} only) The corresponding HCI passes commands / events
to / from the physical HCI identified by the name @var{id} (default:
@code{hci0}) on the computer running QEMU. Only available on @code{bluez}
capable systems like Linux.
@item -bt hci[,vlan=@var{n}]
Add a virtual, standard HCI that will participate in the Bluetooth
scatternet @var{n} (default @code{0}). Similarly to @option{-net}
VLANs, devices inside a bluetooth network @var{n} can only communicate
with other devices in the same network (scatternet).
@end table
@item -bt vhci[,vlan=@var{n}]
(Linux-host only) Create a HCI in scatternet @var{n} (default 0) attached
to the host bluetooth stack instead of to the emulated target. This
allows the host and target machines to participate in a common scatternet
and communicate. Requires the Linux @code{vhci} driver installed. Can
be used as following:
@example
@value{qemu_system} [...OPTIONS...] -bt hci,vlan=5 -bt vhci,vlan=5
@end example
@item -bt device:@var{dev}[,vlan=@var{n}]
Emulate a bluetooth device @var{dev} and place it in network @var{n}
(default @code{0}). QEMU can only emulate one type of bluetooth devices
currently:
@table @option
@item keyboard
Virtual wireless keyboard implementing the HIDP bluetooth profile.
@end table
ETEXI
STEXI
@end table
ETEXI
DEFHEADING()
#ifdef CONFIG_TPM
DEFHEADING(TPM device options:)

View File

@ -16,12 +16,10 @@ check-help:
@echo " $(MAKE) check-softfloat Run FPU emulation tests"
@echo " $(MAKE) check-acceptance Run all acceptance (functional) tests"
@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-clean Clean the tests and related data"
@echo
@echo "Please note that HTML reports do not regenerate if the unit tests"
@echo "have not changed."
@echo
@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"

View File

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

View File

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

View File

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

View File

@ -443,7 +443,7 @@ static gchar *mktempshm(int size, int *fd)
while (true) {
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,
S_IRWXU|S_IRWXG|S_IRWXO);
if (*fd > 0) {

View File

@ -254,7 +254,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
s->expected_status = 0;
s->qemu_pid = fork();
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);
exit(1);
}

View File

@ -1349,7 +1349,8 @@ int main(int argc, char **argv)
* some reason)
*/
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");
return g_test_run();
}

View File

@ -64,7 +64,8 @@ int main(int argc, char *argv[])
g_test_init(&argc, &argv, NULL);
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);
g_free(testname);
}

View File

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

View File

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

View File

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

View File

@ -273,7 +273,7 @@ int main(int argc, char **argv)
module_call_init(MODULE_INIT_QOM);
g_test_init(&argc, &argv, NULL);
setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
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)
{
const TestFixture *fixture = fix;
guint32 v, r = g_random_int();
guint32 v, r = g_test_rand_int();
unsigned char c;
QDict *ret;
@ -186,7 +186,7 @@ static void test_qga_sync_delimited(gconstpointer fix)
static void test_qga_sync(gconstpointer fix)
{
const TestFixture *fixture = fix;
guint32 v, r = g_random_int();
guint32 v, r = g_test_rand_int();
QDict *ret;
/*

View File

@ -1325,7 +1325,7 @@ int main(int argc, char **argv)
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_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.
* Maybe it's a good idea to fix this in SDL instead.
*/
setenv("SDL_VIDEODRIVER", "x11", 0);
g_setenv("SDL_VIDEODRIVER", "x11", 0);
#endif
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/scsi/scsi.h"
#include "hw/display/vga.h"
#include "hw/bt.h"
#include "sysemu/watchdog.h"
#include "hw/firmware/smbios.h"
#include "hw/acpi/acpi.h"
#include "hw/xen/xen.h"
#include "hw/loader.h"
#include "monitor/qdev.h"
#include "sysemu/bt.h"
#include "net/net.h"
#include "net/slirp.h"
#include "monitor/monitor.h"
@ -914,128 +912,6 @@ static void configure_rtc(QemuOpts *opts)
}
}
/***********************************************************/
/* Bluetooth support */
static int nb_hcis;
static int cur_hci;
static struct HCIInfo *hci_table[MAX_NICS];
struct HCIInfo *qemu_next_hci(void)
{
if (cur_hci == nb_hcis)
return &null_hci;
return hci_table[cur_hci++];
}
static int bt_hci_parse(const char *str)
{
struct HCIInfo *hci;
bdaddr_t bdaddr;
if (nb_hcis >= MAX_NICS) {
error_report("too many bluetooth HCIs (max %i)", MAX_NICS);
return -1;
}
hci = hci_init(str);
if (!hci)
return -1;
bdaddr.b[0] = 0x52;
bdaddr.b[1] = 0x54;
bdaddr.b[2] = 0x00;
bdaddr.b[3] = 0x12;
bdaddr.b[4] = 0x34;
bdaddr.b[5] = 0x56 + nb_hcis;
hci->bdaddr_set(hci, bdaddr.b);
hci_table[nb_hcis++] = hci;
return 0;
}
static void bt_vhci_add(int vlan_id)
{
struct bt_scatternet_s *vlan = qemu_find_bt_vlan(vlan_id);
if (!vlan->slave)
warn_report("adding a VHCI to an empty scatternet %i",
vlan_id);
bt_vhci_init(bt_new_hci(vlan));
}
static struct bt_device_s *bt_device_add(const char *opt)
{
struct bt_scatternet_s *vlan;
int vlan_id = 0;
char *endp = strstr(opt, ",vlan=");
int len = (endp ? endp - opt : strlen(opt)) + 1;
char devname[10];
pstrcpy(devname, MIN(sizeof(devname), len), opt);
if (endp) {
vlan_id = strtol(endp + 6, &endp, 0);
if (*endp) {
error_report("unrecognised bluetooth vlan Id");
return 0;
}
}
vlan = qemu_find_bt_vlan(vlan_id);
if (!vlan->slave)
warn_report("adding a slave device to an empty scatternet %i",
vlan_id);
if (!strcmp(devname, "keyboard"))
return bt_keyboard_init(vlan);
error_report("unsupported bluetooth device '%s'", devname);
return 0;
}
static int bt_parse(const char *opt)
{
const char *endp, *p;
int vlan;
if (strstart(opt, "hci", &endp)) {
if (!*endp || *endp == ',') {
if (*endp)
if (!strstart(endp, ",vlan=", 0))
opt = endp + 1;
return bt_hci_parse(opt);
}
} else if (strstart(opt, "vhci", &endp)) {
if (!*endp || *endp == ',') {
if (*endp) {
if (strstart(endp, ",vlan=", &p)) {
vlan = strtol(p, (char **) &endp, 0);
if (*endp) {
error_report("bad scatternet '%s'", p);
return 1;
}
} else {
error_report("bad parameter '%s'", endp + 1);
return 1;
}
} else
vlan = 0;
bt_vhci_add(vlan);
return 0;
}
} else if (strstart(opt, "device:", &endp))
return !bt_device_add(endp);
error_report("bad bluetooth parameter '%s'", opt);
return 1;
}
static int parse_name(void *opaque, QemuOpts *opts, Error **errp)
{
const char *proc_name;
@ -2319,7 +2195,6 @@ static void monitor_parse(const char *optarg, const char *mode, bool pretty)
struct device_config {
enum {
DEV_USB, /* -usbdevice */
DEV_BT, /* -bt */
DEV_SERIAL, /* -serial */
DEV_PARALLEL, /* -parallel */
DEV_DEBUGCON, /* -debugcon */
@ -3128,13 +3003,6 @@ int main(int argc, char **argv, char **envp)
}
break;
#endif
case QEMU_OPTION_bt:
warn_report("The bluetooth subsystem is deprecated and will "
"be removed soon. If the bluetooth subsystem is "
"still useful for you, please send a mail to "
"qemu-devel@nongnu.org with your usecase.");
add_device_config(DEV_BT, optarg);
break;
case QEMU_OPTION_audio_help:
audio_legacy_help();
exit (0);
@ -4259,10 +4127,6 @@ int main(int argc, char **argv, char **envp)
tpm_init();
/* init the bluetooth world */
if (foreach_device_config(DEV_BT, bt_parse))
exit(1);
if (!xen_enabled()) {
/* On 32-bit hosts, QEMU is limited by virtual address space */
if (ram_size > (2047 << 20) && HOST_LONG_BITS == 32) {