forked from 64kramsystem/qemu-pinning
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement a HCI passthrough to host.
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-71466251a162
- Loading branch information
balrog
committed
Sep 28, 2008
1 parent
58a26b4
commit fb599c9
Showing
4 changed files
with
226 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters