Use VHCI to allow the host os to participate in a qemu bluetooth "vlan".
[qemu] / bt-vhci.c
1 /*
2  * Support for host VHCIs inside qemu scatternets.
3  *
4  * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 or
9  * (at your option) version 3 of the License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19  * MA 02111-1307 USA
20  */
21
22 #include "qemu-common.h"
23 #include "qemu-char.h"
24 #include "sysemu.h"
25 #include "net.h"
26 #include "hw/bt.h"
27
28 #include <sys/uio.h>
29
30 #define VHCI_DEV        "/dev/vhci" 
31 #define VHCI_UDEV       "/dev/hci_vhci"
32
33 struct bt_vhci_s {
34     int fd;
35     struct HCIInfo *info;
36
37     uint8_t hdr[4096];
38     int len;
39 };
40
41 static void vhci_read(void *opaque)
42 {
43     struct bt_vhci_s *s = (struct bt_vhci_s *) opaque;
44     uint8_t *pkt;
45     int pktlen;
46
47     /* Seems that we can't read only the header first and then the amount
48      * of data indicated in the header because Linux will discard everything
49      * that's not been read in one go.  */
50     s->len = read(s->fd, s->hdr, sizeof(s->hdr));
51
52     if (s->len < 0) {
53         fprintf(stderr, "qemu: error %i reading the PDU\n", errno);
54         return;
55     }
56
57     pkt = s->hdr;
58     while (s->len --)
59         switch (*pkt ++) {
60         case HCI_COMMAND_PKT:
61             if (s->len < 3)
62                 goto bad_pkt;
63
64             pktlen = MIN(pkt[2] + 3, s->len);
65             s->info->cmd_send(s->info, pkt, pktlen);
66             s->len -= pktlen;
67             pkt += pktlen;
68             break;
69
70         case HCI_ACLDATA_PKT:
71             if (s->len < 4)
72                 goto bad_pkt;
73
74             pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len);
75             s->info->acl_send(s->info, pkt, pktlen);
76             s->len -= pktlen;
77             pkt += pktlen;
78             break;
79
80         case HCI_SCODATA_PKT:
81             if (s->len < 3)
82                 goto bad_pkt;
83
84             pktlen = MIN(pkt[2] + 3, s->len);
85             s->info->sco_send(s->info, pkt, pktlen);
86             s->len -= pktlen;
87             pkt += pktlen;
88             break;
89
90         default:
91         bad_pkt:
92             fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]);
93         }
94 }
95
96 static void vhci_host_send(void *opaque,
97                 int type, const uint8_t *data, int len)
98 {
99     struct bt_vhci_s *s = (struct bt_vhci_s *) opaque;
100 #if 0
101     uint8_t pkt = type;
102     struct iovec iv[2];
103
104     iv[0].iov_base = &pkt;
105     iv[0].iov_len  = 1;
106     iv[1].iov_base = (void *) data;
107     iv[1].iov_len  = len;
108
109     while (writev(s->fd, iv, 2) < 0)
110         if (errno != EAGAIN && errno != EINTR) {
111             fprintf(stderr, "qemu: error %i writing bluetooth packet.\n",
112                             errno);
113             return;
114         }
115 #else
116     /* Apparently VHCI wants us to write everything in one chunk :-(  */
117     static uint8_t buf[4096];
118
119     buf[0] = type;
120     memcpy(buf + 1, data, len);
121
122     while (write(s->fd, buf, len + 1) < 0)
123         if (errno != EAGAIN && errno != EINTR) {
124             fprintf(stderr, "qemu: error %i writing bluetooth packet.\n",
125                             errno);
126             return;
127         }
128 #endif
129 }
130
131 static void vhci_out_hci_packet_event(void *opaque,
132                 const uint8_t *data, int len)
133 {
134     vhci_host_send(opaque, HCI_EVENT_PKT, data, len);
135 }
136
137 static void vhci_out_hci_packet_acl(void *opaque,
138                 const uint8_t *data, int len)
139 {
140     vhci_host_send(opaque, HCI_ACLDATA_PKT, data, len);
141 }
142
143 void bt_vhci_init(struct HCIInfo *info)
144 {
145     struct bt_vhci_s *s;
146     int err[2];
147     int fd;
148
149     fd = open(VHCI_DEV, O_RDWR);
150     err[0] = errno;
151     if (fd < 0) {
152         fd = open(VHCI_UDEV, O_RDWR);
153         err[1] = errno;
154     }
155
156     if (fd < 0) {
157         fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n",
158                         VHCI_DEV, strerror(err[0]), err[0]);
159         fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n",
160                         VHCI_UDEV, strerror(err[1]), err[1]);
161         exit(-1);
162     }
163
164     s = qemu_mallocz(sizeof(struct bt_vhci_s));
165     s->fd = fd;
166     s->info = info ?: qemu_next_hci();
167     s->info->opaque = s;
168     s->info->evt_recv = vhci_out_hci_packet_event;
169     s->info->acl_recv = vhci_out_hci_packet_acl;
170
171     qemu_set_fd_handler(s->fd, vhci_read, 0, s);
172 }