Browse Source
This allows using a host's physical HCI as one of the HCIs attached to the virtual machine. This brings various limitations because not all commands/events are passed through by Linux kernel, some are interpreted by the host's kernel for a speed gain. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5344 c046a42c-6fe2-441c-8c8c-71466251a162stable-0.10
4 changed files with 226 additions and 1 deletions
@ -0,0 +1,198 @@ |
|||
/*
|
|||
* 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, write to the Free Software |
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
|||
* MA 02111-1307 USA |
|||
*/ |
|||
|
|||
#include "qemu-common.h" |
|||
#include "qemu-char.h" |
|||
#include "sysemu.h" |
|||
#include "net.h" |
|||
|
|||
#include <errno.h> |
|||
#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]; |
|||
int ret; |
|||
|
|||
iv[0].iov_base = &pkt; |
|||
iv[0].iov_len = 1; |
|||
iv[1].iov_base = (void *) data; |
|||
iv[1].iov_len = len; |
|||
|
|||
while ((ret = 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 int bt_host_read_poll(void *opaque) |
|||
{ |
|||
struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; |
|||
|
|||
return !!s->hci.evt_recv; |
|||
} |
|||
|
|||
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; |
|||
|
|||
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 0; |
|||
} |
|||
|
|||
#ifdef CONFIG_BLUEZ |
|||
hci_filter_clear(&flt); |
|||
hci_filter_all_ptypes(&flt); |
|||
hci_filter_all_events(&flt); |
|||
|
|||
if (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 = qemu_mallocz(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_handler2(s->fd, bt_host_read_poll, bt_host_read, 0, s); |
|||
|
|||
return &s->hci; |
|||
} |
|||
Loading…
Reference in new issue