Implement a HCI passthrough to host.
authorbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 28 Sep 2008 23:49:55 +0000 (23:49 +0000)
committerbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 28 Sep 2008 23:49:55 +0000 (23:49 +0000)
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

Makefile
Makefile.target
bt-host.c [new file with mode: 0644]
configure

index 9e6f760..49e4398 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -81,7 +81,7 @@ OBJS+=scsi-generic.o
 OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o
 OBJS+=usb-serial.o usb-net.o
 OBJS+=sd.o ssi-sd.o
-OBJS+=bt.o
+OBJS+=bt.o bt-host.o
 
 ifdef CONFIG_BRLAPI
 OBJS+= baum.o
@@ -166,6 +166,9 @@ vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h d3des.c d3des.h
 curses.o: curses.c keymaps.c curses_keys.h
        $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
 
+bt-host.o: bt-host.c
+       $(CC) $(CFLAGS) $(CPPFLAGS) $(CONFIG_BLUEZ_CFLAGS) -c -o $@ $<
+
 audio/sdlaudio.o: audio/sdlaudio.c
        $(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) -c -o $@ $<
 
index 632afc1..c2016d1 100644 (file)
@@ -524,6 +524,10 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
 LIBS += $(CONFIG_VNC_TLS_LIBS)
 endif
 
+ifdef CONFIG_BLUEZ
+LIBS += $(CONFIG_BLUEZ_LIBS)
+endif
+
 # SCSI layer
 OBJS+= lsi53c895a.o esp.o
 
diff --git a/bt-host.c b/bt-host.c
new file mode 100644 (file)
index 0000000..5d3e840
--- /dev/null
+++ b/bt-host.c
@@ -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;
+}
index 5030558..826da7c 100755 (executable)
--- a/configure
+++ b/configure
@@ -110,6 +110,7 @@ curses="yes"
 aio="yes"
 nptl="yes"
 mixemu="no"
+bluez="yes"
 signalfd="no"
 eventfd="no"
 
@@ -293,6 +294,8 @@ for opt do
   ;;
   --disable-brlapi) brlapi="no"
   ;;
+  --disable-bluez) bluez="no"
+  ;;
   --enable-profiler) profiler="yes"
   ;;
   --enable-cocoa)
@@ -429,6 +432,7 @@ echo "  --enable-mixemu          enable mixer emulation"
 echo "  --disable-brlapi         disable BrlAPI"
 echo "  --disable-vnc-tls        disable TLS encryption for VNC server"
 echo "  --disable-curses         disable curses output"
+echo "  --disable-bluez          disable bluez stack connectivity"
 echo "  --disable-nptl           disable usermode NPTL support"
 echo "  --enable-system          enable all system emulation targets"
 echo "  --disable-system         disable all system emulation targets"
@@ -891,6 +895,16 @@ EOF
 fi # test "$curses"
 
 ##########################################
+# bluez support probe
+if test "$bluez" = "yes" ; then
+  `pkg-config bluez` || bluez="no"
+fi
+if test "$bluez" = "yes" ; then
+  bluez_cflags=`pkg-config --cflags bluez`
+  bluez_libs=`pkg-config --libs bluez`
+fi
+
+##########################################
 # AIO probe
 if test "$aio" = "yes" ; then
   aio=no
@@ -1254,6 +1268,12 @@ if test "$brlapi" = "yes" ; then
   echo "#define CONFIG_BRLAPI 1" >> $config_h
   echo "BRLAPI_LIBS=-lbrlapi" >> $config_mak
 fi
+if test "$bluez" = "yes" ; then
+  echo "CONFIG_BLUEZ=yes" >> $config_mak
+  echo "CONFIG_BLUEZ_CFLAGS=$bluez_cflags" >> $config_mak
+  echo "CONFIG_BLUEZ_LIBS=$bluez_libs" >> $config_mak
+  echo "#define CONFIG_BLUEZ 1" >> $config_h
+fi
 if test "$aio" = "yes" ; then
   echo "#define CONFIG_AIO 1" >> $config_h
   echo "CONFIG_AIO=yes" >> $config_mak