diff --git a/Makefile.objs b/Makefile.objs
index e791dd5..b7c8fa6 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -78,7 +78,7 @@ common-obj-y += msmouse.o ps2.o
 common-obj-y += qdev.o qdev-properties.o
 common-obj-y += qemu-config.o block-migration.o
 
-common-obj-$(CONFIG_BRLAPI) += baum.o
+common-obj-$(CONFIG_BRLAPI) += baum.o bt-baum.o
 common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
 
 audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
diff --git a/hw/baum.h b/hw/baum.h
index 8af710f..1fe30a7 100644
--- a/hw/baum.h
+++ b/hw/baum.h
@@ -22,5 +22,9 @@
  * THE SOFTWARE.
  */
 
+#include "qemu-char.h"
+#include "bt.h"
 /* char device */
 CharDriverState *chr_baum_init(QemuOpts *opts);
+/* bluetooth device */
+struct bt_device_s *bt_baum_init(struct bt_scatternet_s *net);
diff --git a/hw/bt-baum.c b/hw/bt-baum.c
new file mode 100644
index 0000000..568b8a1
--- /dev/null
+++ b/hw/bt-baum.c
@@ -0,0 +1,158 @@
+/*
+ * QEMU Bluetooth Baum driver.
+ *
+ * Copyright (C) 2010 Samuel Thibault <samuel.thibault@ens-lyon.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, if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "baum.h"
+#include "bt.h"
+
+#define BT_BAUM_MTU			1000
+
+struct bt_baum_device_s {
+    struct bt_l2cap_device_s btdev;
+    struct bt_l2cap_conn_params_s *ch;
+
+    CharDriverState *cdrv;
+};
+
+static int bt_baum_can_read(void *opaque)
+{
+    struct bt_baum_device_s *s = opaque;
+    /* Is there room in the bluetooth socket? */
+
+    if (!s->ch)
+        return 0;
+
+    return s->ch->remote_mtu;
+}
+
+static void bt_baum_read(void *opaque, const uint8_t *buf, int size)
+{
+    /* Got data from the Baum device to send over bluetooth to the guest */
+    struct bt_baum_device_s *s = opaque;
+    uint8_t *pkt;
+    int len;
+    struct bt_l2cap_conn_params_s *ch = s->ch;
+
+    if (!ch)
+        return;
+
+    while (size) {
+        len = MIN(size, ch->remote_mtu);
+        pkt = ch->sdu_out(ch, size);
+        memcpy(pkt, buf, size);
+        ch->sdu_submit(ch);
+        size -= len;
+        buf += len;
+    };
+}
+
+static void bt_baum_sdu(void *opaque, const uint8_t *data, int len)
+{
+    /* Got data from bluetooth, to be sent to the Baum device */
+    struct bt_baum_device_s *s = opaque;
+    qemu_chr_write(s->cdrv, data, len);
+}
+
+static void bt_baum_close_ch(void *opaque)
+{
+    struct bt_baum_device_s *s = opaque;
+
+    s->btdev.device.page_scan = 1;
+    s->btdev.device.inquiry_scan = 1;
+
+    s->ch = NULL;
+    printf("close connection\n");
+}
+
+static int bt_baum_new_ch(struct bt_l2cap_device_s *dev,
+                struct bt_l2cap_conn_params_s *params)
+{
+    struct bt_baum_device_s *s = (struct bt_baum_device_s *) dev;
+
+    if (s->ch) {
+        printf("already there: %p\n", s->ch);
+        return 1;
+    }
+
+    printf("new connection\n");
+    s->ch = params;
+    s->ch->opaque = s;
+    s->ch->close = bt_baum_close_ch;
+    s->ch->sdu_in = bt_baum_sdu;
+
+    s->btdev.device.page_scan = 0;
+    s->btdev.device.inquiry_scan = 0;
+
+    return 0;
+}
+
+static void bt_baum_destroy(struct bt_device_s *dev)
+{
+    struct bt_baum_device_s *s = (struct bt_baum_device_s *) dev;
+
+    bt_l2cap_device_done(&s->btdev);
+
+    qemu_chr_close(s->cdrv);
+
+    qemu_free(s);
+}
+
+struct bt_device_s *bt_baum_init(struct bt_scatternet_s *net)
+{
+    struct bt_baum_device_s *s;
+    /* FIXME */
+    uint32_t class =
+            /* Format type */
+            (0 << 0) |
+            /* Device class */
+            (0 << 2) |
+            (5 << 8) |  /* "Peripheral" */
+            /* Service classes */
+            (1 << 13) | /* Limited discoverable mode */
+            (1 << 19);  /* Capturing device (?) */
+
+    s = qemu_mallocz(sizeof(*s));
+    if (!s)
+        return NULL;
+
+    s->cdrv = qemu_chr_open("braille", "braille",  NULL);
+
+    if (!s->cdrv) {
+        qemu_free(s);
+        return NULL;
+    }
+
+    bt_l2cap_device_init(&s->btdev, net);
+    bt_l2cap_sdp_init(&s->btdev);
+
+    s->btdev.device.lmp_name = "QEMU USB Braille";
+    s->btdev.device.handle_destroy = bt_baum_destroy;
+
+    printf("chan is %p\n", bt_baum_new_ch);
+    bt_l2cap_psm_register(&s->btdev, BT_PSM_RFCOMM,
+                    BT_BAUM_MTU, bt_baum_new_ch);
+
+    qemu_chr_add_handlers(s->cdrv, bt_baum_can_read, bt_baum_read, NULL, s);
+
+    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;
+}
diff --git a/hw/bt.h b/hw/bt.h
index 4a702ad..720b974 100644
--- a/hw/bt.h
+++ b/hw/bt.h
@@ -23,6 +23,9 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#ifndef BT_H
+#define BT_H
+
 /* BD Address */
 typedef struct {
     uint8_t b[6];
@@ -2181,3 +2184,4 @@ enum bt_sdp_attribute_id {
     SDP_ATTR_NORMALLY_CONNECTABLE		= 0x020d,
     SDP_ATTR_BOOT_DEVICE			= 0x020e,
 };
+#endif
diff --git a/vl.c b/vl.c
index 10d8e34..2d89bfd 100644
--- a/vl.c
+++ b/vl.c
@@ -1741,6 +1741,9 @@ static struct bt_device_s *bt_device_add(const char *opt)
     if (!strcmp(devname, "keyboard"))
         return bt_keyboard_init(vlan);
 
+    if (!strcmp(devname, "braille"))
+        return bt_baum_init(vlan);
+
     fprintf(stderr, "qemu: unsupported bluetooth device `%s'\n", devname);
     return 0;
 }
