qemu/hw/bt/hci-csr.c
<<
>>
Prefs
   1/*
   2 * Bluetooth serial HCI transport.
   3 * CSR41814 HCI with H4p vendor extensions.
   4 *
   5 * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License as
   9 * published by the Free Software Foundation; either version 2 or
  10 * (at your option) version 3 of the License.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License along
  18 * with this program; if not, see <http://www.gnu.org/licenses/>.
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "qemu-common.h"
  23#include "sysemu/char.h"
  24#include "qemu/timer.h"
  25#include "hw/irq.h"
  26#include "sysemu/bt.h"
  27#include "hw/bt.h"
  28
  29struct csrhci_s {
  30    int enable;
  31    qemu_irq *pins;
  32    int pin_state;
  33    int modem_state;
  34    CharDriverState chr;
  35#define FIFO_LEN        4096
  36    int out_start;
  37    int out_len;
  38    int out_size;
  39    uint8_t outfifo[FIFO_LEN * 2];
  40    uint8_t inpkt[FIFO_LEN];
  41    int in_len;
  42    int in_hdr;
  43    int in_data;
  44    QEMUTimer *out_tm;
  45    int64_t baud_delay;
  46
  47    bdaddr_t bd_addr;
  48    struct HCIInfo *hci;
  49};
  50
  51/* H4+ packet types */
  52enum {
  53    H4_CMD_PKT   = 1,
  54    H4_ACL_PKT   = 2,
  55    H4_SCO_PKT   = 3,
  56    H4_EVT_PKT   = 4,
  57    H4_NEG_PKT   = 6,
  58    H4_ALIVE_PKT = 7,
  59};
  60
  61/* CSR41814 negotiation start magic packet */
  62static const uint8_t csrhci_neg_packet[] = {
  63    H4_NEG_PKT, 10,
  64    0x00, 0xa0, 0x01, 0x00, 0x00,
  65    0x4c, 0x00, 0x96, 0x00, 0x00,
  66};
  67
  68/* CSR41814 vendor-specific command OCFs */
  69enum {
  70    OCF_CSR_SEND_FIRMWARE = 0x000,
  71};
  72
  73static inline void csrhci_fifo_wake(struct csrhci_s *s)
  74{
  75    if (!s->enable || !s->out_len)
  76        return;
  77
  78    /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */
  79    if (s->chr.chr_can_read && s->chr.chr_can_read(s->chr.handler_opaque) &&
  80                    s->chr.chr_read) {
  81        s->chr.chr_read(s->chr.handler_opaque,
  82                        s->outfifo + s->out_start ++, 1);
  83        s->out_len --;
  84        if (s->out_start >= s->out_size) {
  85            s->out_start = 0;
  86            s->out_size = FIFO_LEN;
  87        }
  88    }
  89
  90    if (s->out_len)
  91        timer_mod(s->out_tm, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->baud_delay);
  92}
  93
  94#define csrhci_out_packetz(s, len) memset(csrhci_out_packet(s, len), 0, len)
  95static uint8_t *csrhci_out_packet(struct csrhci_s *s, int len)
  96{
  97    int off = s->out_start + s->out_len;
  98
  99    /* TODO: do the padding here, i.e. align len */
 100    s->out_len += len;
 101
 102    if (off < FIFO_LEN) {
 103        if (off + len > FIFO_LEN && (s->out_size = off + len) > FIFO_LEN * 2) {
 104            fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
 105            exit(-1);
 106        }
 107        return s->outfifo + off;
 108    }
 109
 110    if (s->out_len > s->out_size) {
 111        fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
 112        exit(-1);
 113    }
 114
 115    return s->outfifo + off - s->out_size;
 116}
 117
 118static inline uint8_t *csrhci_out_packet_csr(struct csrhci_s *s,
 119                int type, int len)
 120{
 121    uint8_t *ret = csrhci_out_packetz(s, len + 2);
 122
 123    *ret ++ = type;
 124    *ret ++ = len;
 125
 126    return ret;
 127}
 128
 129static inline uint8_t *csrhci_out_packet_event(struct csrhci_s *s,
 130                int evt, int len)
 131{
 132    uint8_t *ret = csrhci_out_packetz(s,
 133                    len + 1 + sizeof(struct hci_event_hdr));
 134
 135    *ret ++ = H4_EVT_PKT;
 136    ((struct hci_event_hdr *) ret)->evt = evt;
 137    ((struct hci_event_hdr *) ret)->plen = len;
 138
 139    return ret + sizeof(struct hci_event_hdr);
 140}
 141
 142static void csrhci_in_packet_vendor(struct csrhci_s *s, int ocf,
 143                uint8_t *data, int len)
 144{
 145    int offset;
 146    uint8_t *rpkt;
 147
 148    switch (ocf) {
 149    case OCF_CSR_SEND_FIRMWARE:
 150        /* Check if this is the bd_address packet */
 151        if (len >= 18 + 8 && data[12] == 0x01 && data[13] == 0x00) {
 152            offset = 18;
 153            s->bd_addr.b[0] = data[offset + 7]; /* Beyond cmd packet end(!?) */
 154            s->bd_addr.b[1] = data[offset + 6];
 155            s->bd_addr.b[2] = data[offset + 4];
 156            s->bd_addr.b[3] = data[offset + 0];
 157            s->bd_addr.b[4] = data[offset + 3];
 158            s->bd_addr.b[5] = data[offset + 2];
 159
 160            s->hci->bdaddr_set(s->hci, s->bd_addr.b);
 161            fprintf(stderr, "%s: bd_address loaded from firmware: "
 162                            "%02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__,
 163                            s->bd_addr.b[0], s->bd_addr.b[1], s->bd_addr.b[2],
 164                            s->bd_addr.b[3], s->bd_addr.b[4], s->bd_addr.b[5]);
 165        }
 166
 167        rpkt = csrhci_out_packet_event(s, EVT_VENDOR, 11);
 168        /* Status bytes: no error */
 169        rpkt[9] = 0x00;
 170        rpkt[10] = 0x00;
 171        break;
 172
 173    default:
 174        fprintf(stderr, "%s: got a bad CMD packet\n", __FUNCTION__);
 175        return;
 176    }
 177
 178    csrhci_fifo_wake(s);
 179}
 180
 181static void csrhci_in_packet(struct csrhci_s *s, uint8_t *pkt)
 182{
 183    uint8_t *rpkt;
 184    int opc;
 185
 186    switch (*pkt ++) {
 187    case H4_CMD_PKT:
 188        opc = le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode);
 189        if (cmd_opcode_ogf(opc) == OGF_VENDOR_CMD) {
 190            csrhci_in_packet_vendor(s, cmd_opcode_ocf(opc),
 191                            pkt + sizeof(struct hci_command_hdr),
 192                            s->in_len - sizeof(struct hci_command_hdr) - 1);
 193            return;
 194        }
 195
 196        /* TODO: if the command is OCF_READ_LOCAL_COMMANDS or the likes,
 197         * we need to send it to the HCI layer and then add our supported
 198         * commands to the returned mask (such as OGF_VENDOR_CMD).  With
 199         * bt-hci.c we could just have hooks for this kind of commands but
 200         * we can't with bt-host.c.  */
 201
 202        s->hci->cmd_send(s->hci, pkt, s->in_len - 1);
 203        break;
 204
 205    case H4_EVT_PKT:
 206        goto bad_pkt;
 207
 208    case H4_ACL_PKT:
 209        s->hci->acl_send(s->hci, pkt, s->in_len - 1);
 210        break;
 211
 212    case H4_SCO_PKT:
 213        s->hci->sco_send(s->hci, pkt, s->in_len - 1);
 214        break;
 215
 216    case H4_NEG_PKT:
 217        if (s->in_hdr != sizeof(csrhci_neg_packet) ||
 218                        memcmp(pkt - 1, csrhci_neg_packet, s->in_hdr)) {
 219            fprintf(stderr, "%s: got a bad NEG packet\n", __FUNCTION__);
 220            return;
 221        }
 222        pkt += 2;
 223
 224        rpkt = csrhci_out_packet_csr(s, H4_NEG_PKT, 10);
 225
 226        *rpkt ++ = 0x20;        /* Operational settings negotiation Ok */
 227        memcpy(rpkt, pkt, 7); rpkt += 7;
 228        *rpkt ++ = 0xff;
 229        *rpkt = 0xff;
 230        break;
 231
 232    case H4_ALIVE_PKT:
 233        if (s->in_hdr != 4 || pkt[1] != 0x55 || pkt[2] != 0x00) {
 234            fprintf(stderr, "%s: got a bad ALIVE packet\n", __FUNCTION__);
 235            return;
 236        }
 237
 238        rpkt = csrhci_out_packet_csr(s, H4_ALIVE_PKT, 2);
 239
 240        *rpkt ++ = 0xcc;
 241        *rpkt = 0x00;
 242        break;
 243
 244    default:
 245    bad_pkt:
 246        /* TODO: error out */
 247        fprintf(stderr, "%s: got a bad packet\n", __FUNCTION__);
 248        break;
 249    }
 250
 251    csrhci_fifo_wake(s);
 252}
 253
 254static int csrhci_header_len(const uint8_t *pkt)
 255{
 256    switch (pkt[0]) {
 257    case H4_CMD_PKT:
 258        return HCI_COMMAND_HDR_SIZE;
 259    case H4_EVT_PKT:
 260        return HCI_EVENT_HDR_SIZE;
 261    case H4_ACL_PKT:
 262        return HCI_ACL_HDR_SIZE;
 263    case H4_SCO_PKT:
 264        return HCI_SCO_HDR_SIZE;
 265    case H4_NEG_PKT:
 266        return pkt[1] + 1;
 267    case H4_ALIVE_PKT:
 268        return 3;
 269    }
 270
 271    exit(-1);
 272}
 273
 274static int csrhci_data_len(const uint8_t *pkt)
 275{
 276    switch (*pkt ++) {
 277    case H4_CMD_PKT:
 278        /* It seems that vendor-specific command packets for H4+ are all
 279         * one byte longer than indicated in the standard header.  */
 280        if (le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode) == 0xfc00)
 281            return (((struct hci_command_hdr *) pkt)->plen + 1) & ~1;
 282
 283        return ((struct hci_command_hdr *) pkt)->plen;
 284    case H4_EVT_PKT:
 285        return ((struct hci_event_hdr *) pkt)->plen;
 286    case H4_ACL_PKT:
 287        return le16_to_cpu(((struct hci_acl_hdr *) pkt)->dlen);
 288    case H4_SCO_PKT:
 289        return ((struct hci_sco_hdr *) pkt)->dlen;
 290    case H4_NEG_PKT:
 291    case H4_ALIVE_PKT:
 292        return 0;
 293    }
 294
 295    exit(-1);
 296}
 297
 298static int csrhci_write(struct CharDriverState *chr,
 299                const uint8_t *buf, int len)
 300{
 301    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
 302    int plen = s->in_len;
 303
 304    if (!s->enable)
 305        return 0;
 306
 307    s->in_len += len;
 308    memcpy(s->inpkt + plen, buf, len);
 309
 310    while (1) {
 311        if (s->in_len >= 2 && plen < 2)
 312            s->in_hdr = csrhci_header_len(s->inpkt) + 1;
 313
 314        if (s->in_len >= s->in_hdr && plen < s->in_hdr)
 315            s->in_data = csrhci_data_len(s->inpkt) + s->in_hdr;
 316
 317        if (s->in_len >= s->in_data) {
 318            csrhci_in_packet(s, s->inpkt);
 319
 320            memmove(s->inpkt, s->inpkt + s->in_len, s->in_len - s->in_data);
 321            s->in_len -= s->in_data;
 322            s->in_hdr = INT_MAX;
 323            s->in_data = INT_MAX;
 324            plen = 0;
 325        } else
 326            break;
 327    }
 328
 329    return len;
 330}
 331
 332static void csrhci_out_hci_packet_event(void *opaque,
 333                const uint8_t *data, int len)
 334{
 335    struct csrhci_s *s = (struct csrhci_s *) opaque;
 336    uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1);        /* Align */
 337
 338    *pkt ++ = H4_EVT_PKT;
 339    memcpy(pkt, data, len);
 340
 341    csrhci_fifo_wake(s);
 342}
 343
 344static void csrhci_out_hci_packet_acl(void *opaque,
 345                const uint8_t *data, int len)
 346{
 347    struct csrhci_s *s = (struct csrhci_s *) opaque;
 348    uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1);        /* Align */
 349
 350    *pkt ++ = H4_ACL_PKT;
 351    pkt[len & ~1] = 0;
 352    memcpy(pkt, data, len);
 353
 354    csrhci_fifo_wake(s);
 355}
 356
 357static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg)
 358{
 359    QEMUSerialSetParams *ssp;
 360    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
 361    int prev_state = s->modem_state;
 362
 363    switch (cmd) {
 364    case CHR_IOCTL_SERIAL_SET_PARAMS:
 365        ssp = (QEMUSerialSetParams *) arg;
 366        s->baud_delay = NANOSECONDS_PER_SECOND / ssp->speed;
 367        /* Moments later... (but shorter than 100ms) */
 368        s->modem_state |= CHR_TIOCM_CTS;
 369        break;
 370
 371    case CHR_IOCTL_SERIAL_GET_TIOCM:
 372        *(int *) arg = s->modem_state;
 373        break;
 374
 375    case CHR_IOCTL_SERIAL_SET_TIOCM:
 376        s->modem_state = *(int *) arg;
 377        if (~s->modem_state & prev_state & CHR_TIOCM_RTS)
 378            s->modem_state &= ~CHR_TIOCM_CTS;
 379        break;
 380
 381    default:
 382        return -ENOTSUP;
 383    }
 384    return 0;
 385}
 386
 387static void csrhci_reset(struct csrhci_s *s)
 388{
 389    s->out_len = 0;
 390    s->out_size = FIFO_LEN;
 391    s->in_len = 0;
 392    s->baud_delay = NANOSECONDS_PER_SECOND;
 393    s->enable = 0;
 394    s->in_hdr = INT_MAX;
 395    s->in_data = INT_MAX;
 396
 397    s->modem_state = 0;
 398    /* After a while... (but sooner than 10ms) */
 399    s->modem_state |= CHR_TIOCM_CTS;
 400
 401    memset(&s->bd_addr, 0, sizeof(bdaddr_t));
 402}
 403
 404static void csrhci_out_tick(void *opaque)
 405{
 406    csrhci_fifo_wake((struct csrhci_s *) opaque);
 407}
 408
 409static void csrhci_pins(void *opaque, int line, int level)
 410{
 411    struct csrhci_s *s = (struct csrhci_s *) opaque;
 412    int state = s->pin_state;
 413
 414    s->pin_state &= ~(1 << line);
 415    s->pin_state |= (!!level) << line;
 416
 417    if ((state & ~s->pin_state) & (1 << csrhci_pin_reset)) {
 418        /* TODO: Disappear from lower layers */
 419        csrhci_reset(s);
 420    }
 421
 422    if (s->pin_state == 3 && state != 3) {
 423        s->enable = 1;
 424        /* TODO: Wake lower layers up */
 425    }
 426}
 427
 428qemu_irq *csrhci_pins_get(CharDriverState *chr)
 429{
 430    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
 431
 432    return s->pins;
 433}
 434
 435CharDriverState *uart_hci_init(qemu_irq wakeup)
 436{
 437    struct csrhci_s *s = (struct csrhci_s *)
 438            g_malloc0(sizeof(struct csrhci_s));
 439
 440    s->chr.opaque = s;
 441    s->chr.chr_write = csrhci_write;
 442    s->chr.chr_ioctl = csrhci_ioctl;
 443    s->chr.avail_connections = 1;
 444
 445    s->hci = qemu_next_hci();
 446    s->hci->opaque = s;
 447    s->hci->evt_recv = csrhci_out_hci_packet_event;
 448    s->hci->acl_recv = csrhci_out_hci_packet_acl;
 449
 450    s->out_tm = timer_new_ns(QEMU_CLOCK_VIRTUAL, csrhci_out_tick, s);
 451    s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins);
 452    csrhci_reset(s);
 453
 454    return &s->chr;
 455}
 456