15d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* 25d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Support for host VHCIs inside qemu scatternets. 35d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 45d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org> 55d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 65d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * This program is free software; you can redistribute it and/or 75d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * modify it under the terms of the GNU General Public License as 85d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * published by the Free Software Foundation; either version 2 or 95d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * (at your option) version 3 of the License. 105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * This program is distributed in the hope that it will be useful, 125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * but WITHOUT ANY WARRANTY; without even the implied warranty of 135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * GNU General Public License for more details. 155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * You should have received a copy of the GNU General Public License along 17dd9cb7929e2b98bf9f8017ebbd7e433d38541664David 'Digit' Turner * with this program; if not, see <http://www.gnu.org/licenses/>. 185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "qemu-common.h" 215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "qemu-char.h" 225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "net.h" 235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "hw/bt.h" 245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define VHCI_DEV "/dev/vhci" 265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define VHCI_UDEV "/dev/hci_vhci" 275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstruct bt_vhci_s { 295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int fd; 305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner struct HCIInfo *info; 315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint8_t hdr[4096]; 335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int len; 345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}; 355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void vhci_read(void *opaque) 375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; 395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint8_t *pkt; 405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int pktlen; 415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* Seems that we can't read only the header first and then the amount 435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * of data indicated in the header because Linux will discard everything 445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * that's not been read in one go. */ 455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->len = read(s->fd, s->hdr, sizeof(s->hdr)); 465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (s->len < 0) { 485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fprintf(stderr, "qemu: error %i reading the PDU\n", errno); 495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return; 505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner pkt = s->hdr; 535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner while (s->len --) 545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner switch (*pkt ++) { 555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner case HCI_COMMAND_PKT: 565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (s->len < 3) 575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto bad_pkt; 585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner pktlen = MIN(pkt[2] + 3, s->len); 605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->info->cmd_send(s->info, pkt, pktlen); 615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->len -= pktlen; 625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner pkt += pktlen; 635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner break; 645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner case HCI_ACLDATA_PKT: 665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (s->len < 4) 675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto bad_pkt; 685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len); 705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->info->acl_send(s->info, pkt, pktlen); 715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->len -= pktlen; 725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner pkt += pktlen; 735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner break; 745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner case HCI_SCODATA_PKT: 765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (s->len < 3) 775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto bad_pkt; 785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner pktlen = MIN(pkt[2] + 3, s->len); 805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->info->sco_send(s->info, pkt, pktlen); 815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->len -= pktlen; 825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner pkt += pktlen; 835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner break; 845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner default: 865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner bad_pkt: 875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]); 885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void vhci_host_send(void *opaque, 925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int type, const uint8_t *data, int len) 935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; 955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#if 0 965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint8_t pkt = type; 975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner struct iovec iv[2]; 985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner iv[0].iov_base = &pkt; 1005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner iv[0].iov_len = 1; 1015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner iv[1].iov_base = (void *) data; 1025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner iv[1].iov_len = len; 1035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner while (writev(s->fd, iv, 2) < 0) 1055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (errno != EAGAIN && errno != EINTR) { 1065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", 1075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner errno); 1085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return; 1095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 1105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#else 1115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* Apparently VHCI wants us to write everything in one chunk :-( */ 1125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner static uint8_t buf[4096]; 1135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner buf[0] = type; 1155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner memcpy(buf + 1, data, len); 1165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner while (write(s->fd, buf, len + 1) < 0) 1185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (errno != EAGAIN && errno != EINTR) { 1195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", 1205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner errno); 1215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return; 1225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 1235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#endif 1245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 1255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void vhci_out_hci_packet_event(void *opaque, 1275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner const uint8_t *data, int len) 1285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 1295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner vhci_host_send(opaque, HCI_EVENT_PKT, data, len); 1305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 1315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void vhci_out_hci_packet_acl(void *opaque, 1335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner const uint8_t *data, int len) 1345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 1355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner vhci_host_send(opaque, HCI_ACLDATA_PKT, data, len); 1365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 1375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid bt_vhci_init(struct HCIInfo *info) 1395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 1405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner struct bt_vhci_s *s; 1415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int err[2]; 1425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int fd; 1435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fd = open(VHCI_DEV, O_RDWR); 1455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner err[0] = errno; 1465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (fd < 0) { 1475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fd = open(VHCI_UDEV, O_RDWR); 1485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner err[1] = errno; 1495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 1505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (fd < 0) { 1525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", 1535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner VHCI_DEV, strerror(err[0]), err[0]); 1545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", 1555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner VHCI_UDEV, strerror(err[1]), err[1]); 1565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner exit(-1); 1575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 1585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s = qemu_mallocz(sizeof(struct bt_vhci_s)); 1605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->fd = fd; 1615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->info = info ?: qemu_next_hci(); 1625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->info->opaque = s; 1635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->info->evt_recv = vhci_out_hci_packet_event; 1645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->info->acl_recv = vhci_out_hci_packet_acl; 1655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner qemu_set_fd_handler(s->fd, vhci_read, NULL, s); 1675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 168