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 along |
17 | * with this program; if not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include "qemu/osdep.h" |
21 | #include "sysemu/bt.h" |
22 | #include "hw/bt.h" |
23 | #include "qemu/main-loop.h" |
24 | |
25 | #define VHCI_DEV "/dev/vhci" |
26 | #define VHCI_UDEV "/dev/hci_vhci" |
27 | |
28 | struct bt_vhci_s { |
29 | int fd; |
30 | struct HCIInfo *info; |
31 | |
32 | uint8_t hdr[4096]; |
33 | int len; |
34 | }; |
35 | |
36 | static void vhci_read(void *opaque) |
37 | { |
38 | struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; |
39 | uint8_t *pkt; |
40 | int pktlen; |
41 | |
42 | /* Seems that we can't read only the header first and then the amount |
43 | * of data indicated in the header because Linux will discard everything |
44 | * that's not been read in one go. */ |
45 | s->len = read(s->fd, s->hdr, sizeof(s->hdr)); |
46 | |
47 | if (s->len < 0) { |
48 | fprintf(stderr, "qemu: error %i reading the PDU\n" , errno); |
49 | return; |
50 | } |
51 | |
52 | pkt = s->hdr; |
53 | while (s->len --) |
54 | switch (*pkt ++) { |
55 | case HCI_COMMAND_PKT: |
56 | if (s->len < 3) |
57 | goto bad_pkt; |
58 | |
59 | pktlen = MIN(pkt[2] + 3, s->len); |
60 | s->info->cmd_send(s->info, pkt, pktlen); |
61 | s->len -= pktlen; |
62 | pkt += pktlen; |
63 | break; |
64 | |
65 | case HCI_ACLDATA_PKT: |
66 | if (s->len < 4) |
67 | goto bad_pkt; |
68 | |
69 | pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len); |
70 | s->info->acl_send(s->info, pkt, pktlen); |
71 | s->len -= pktlen; |
72 | pkt += pktlen; |
73 | break; |
74 | |
75 | case HCI_SCODATA_PKT: |
76 | if (s->len < 3) |
77 | goto bad_pkt; |
78 | |
79 | pktlen = MIN(pkt[2] + 3, s->len); |
80 | s->info->sco_send(s->info, pkt, pktlen); |
81 | s->len -= pktlen; |
82 | pkt += pktlen; |
83 | break; |
84 | |
85 | default: |
86 | bad_pkt: |
87 | fprintf(stderr, "qemu: bad HCI packet type %02x\n" , pkt[-1]); |
88 | } |
89 | } |
90 | |
91 | static void vhci_host_send(void *opaque, |
92 | int type, const uint8_t *data, int len) |
93 | { |
94 | struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; |
95 | #if 0 |
96 | uint8_t pkt = type; |
97 | struct iovec iv[2]; |
98 | |
99 | iv[0].iov_base = &pkt; |
100 | iv[0].iov_len = 1; |
101 | iv[1].iov_base = (void *) data; |
102 | iv[1].iov_len = len; |
103 | |
104 | while (writev(s->fd, iv, 2) < 0) |
105 | if (errno != EAGAIN && errno != EINTR) { |
106 | fprintf(stderr, "qemu: error %i writing bluetooth packet.\n" , |
107 | errno); |
108 | return; |
109 | } |
110 | #else |
111 | /* Apparently VHCI wants us to write everything in one chunk :-( */ |
112 | static uint8_t buf[4096]; |
113 | |
114 | buf[0] = type; |
115 | memcpy(buf + 1, data, len); |
116 | |
117 | while (write(s->fd, buf, len + 1) < 0) |
118 | if (errno != EAGAIN && errno != EINTR) { |
119 | fprintf(stderr, "qemu: error %i writing bluetooth packet.\n" , |
120 | errno); |
121 | return; |
122 | } |
123 | #endif |
124 | } |
125 | |
126 | static void vhci_out_hci_packet_event(void *opaque, |
127 | const uint8_t *data, int len) |
128 | { |
129 | vhci_host_send(opaque, HCI_EVENT_PKT, data, len); |
130 | } |
131 | |
132 | static void vhci_out_hci_packet_acl(void *opaque, |
133 | const uint8_t *data, int len) |
134 | { |
135 | vhci_host_send(opaque, HCI_ACLDATA_PKT, data, len); |
136 | } |
137 | |
138 | void bt_vhci_init(struct HCIInfo *info) |
139 | { |
140 | struct bt_vhci_s *s; |
141 | int err[2]; |
142 | int fd; |
143 | |
144 | fd = open(VHCI_DEV, O_RDWR); |
145 | err[0] = errno; |
146 | if (fd < 0) { |
147 | fd = open(VHCI_UDEV, O_RDWR); |
148 | err[1] = errno; |
149 | } |
150 | |
151 | if (fd < 0) { |
152 | fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n" , |
153 | VHCI_DEV, strerror(err[0]), err[0]); |
154 | fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n" , |
155 | VHCI_UDEV, strerror(err[1]), err[1]); |
156 | exit(-1); |
157 | } |
158 | |
159 | s = g_malloc0(sizeof(struct bt_vhci_s)); |
160 | s->fd = fd; |
161 | s->info = info ?: qemu_next_hci(); |
162 | s->info->opaque = s; |
163 | s->info->evt_recv = vhci_out_hci_packet_event; |
164 | s->info->acl_recv = vhci_out_hci_packet_acl; |
165 | |
166 | qemu_set_fd_handler(s->fd, vhci_read, NULL, s); |
167 | } |
168 | |