Statistics
| Branch: | Revision:

root / hw / bt-hci-csr.c @ f3e3aa8c

History | View | Annotate | Download (12.1 kB)

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, write to the Free Software Foundation, Inc.,
19
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
 */
21

    
22
#include "qemu-common.h"
23
#include "qemu-char.h"
24
#include "qemu-timer.h"
25
#include "irq.h"
26
#include "sysemu.h"
27
#include "net.h"
28
#include "bt.h"
29

    
30
struct csrhci_s {
31
    int enable;
32
    qemu_irq *pins;
33
    int pin_state;
34
    int modem_state;
35
    CharDriverState chr;
36
#define FIFO_LEN        4096
37
    int out_start;
38
    int out_len;
39
    int out_size;
40
    uint8_t outfifo[FIFO_LEN * 2];
41
    uint8_t inpkt[FIFO_LEN];
42
    int in_len;
43
    int in_hdr;
44
    int in_data;
45
    QEMUTimer *out_tm;
46
    int64_t baud_delay;
47

    
48
    bdaddr_t bd_addr;
49
    struct HCIInfo *hci;
50
};
51

    
52
/* H4+ packet types */
53
enum {
54
    H4_CMD_PKT   = 1,
55
    H4_ACL_PKT   = 2,
56
    H4_SCO_PKT   = 3,
57
    H4_EVT_PKT   = 4,
58
    H4_NEG_PKT   = 6,
59
    H4_ALIVE_PKT = 7,
60
};
61

    
62
/* CSR41814 negotiation start magic packet */
63
static const uint8_t csrhci_neg_packet[] = {
64
    H4_NEG_PKT, 10,
65
    0x00, 0xa0, 0x01, 0x00, 0x00,
66
    0x4c, 0x00, 0x96, 0x00, 0x00,
67
};
68

    
69
/* CSR41814 vendor-specific command OCFs */
70
enum {
71
    OCF_CSR_SEND_FIRMWARE = 0x000,
72
};
73

    
74
static inline void csrhci_fifo_wake(struct csrhci_s *s)
75
{
76
    if (!s->enable || !s->out_len)
77
        return;
78

    
79
    /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */
80
    if (s->chr.chr_can_read && s->chr.chr_can_read(s->chr.handler_opaque) &&
81
                    s->chr.chr_read) {
82
        s->chr.chr_read(s->chr.handler_opaque,
83
                        s->outfifo + s->out_start ++, 1);
84
        s->out_len --;
85
        if (s->out_start >= s->out_size) {
86
            s->out_start = 0;
87
            s->out_size = FIFO_LEN;
88
        }
89
    }
90

    
91
    if (s->out_len)
92
        qemu_mod_timer(s->out_tm, qemu_get_clock(vm_clock) + s->baud_delay);
93
}
94

    
95
#define csrhci_out_packetz(s, len) memset(csrhci_out_packet(s, len), 0, len)
96
static uint8_t *csrhci_out_packet(struct csrhci_s *s, int len)
97
{
98
    int off = s->out_start + s->out_len;
99

    
100
    /* TODO: do the padding here, i.e. align len */
101
    s->out_len += len;
102

    
103
    if (off < FIFO_LEN) {
104
        if (off + len > FIFO_LEN && (s->out_size = off + len) > FIFO_LEN * 2) {
105
            fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
106
            exit(-1);
107
        }
108
        return s->outfifo + off;
109
    }
110

    
111
    if (s->out_len > s->out_size) {
112
        fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
113
        exit(-1);
114
    }
115

    
116
    return s->outfifo + off - s->out_size;
117
}
118

    
119
static inline uint8_t *csrhci_out_packet_csr(struct csrhci_s *s,
120
                int type, int len)
121
{
122
    uint8_t *ret = csrhci_out_packetz(s, len + 2);
123

    
124
    *ret ++ = type;
125
    *ret ++ = len;
126

    
127
    return ret;
128
}
129

    
130
static inline uint8_t *csrhci_out_packet_event(struct csrhci_s *s,
131
                int evt, int len)
132
{
133
    uint8_t *ret = csrhci_out_packetz(s,
134
                    len + 1 + sizeof(struct hci_event_hdr));
135

    
136
    *ret ++ = H4_EVT_PKT;
137
    ((struct hci_event_hdr *) ret)->evt = evt;
138
    ((struct hci_event_hdr *) ret)->plen = len;
139

    
140
    return ret + sizeof(struct hci_event_hdr);
141
}
142

    
143
static void csrhci_in_packet_vendor(struct csrhci_s *s, int ocf,
144
                uint8_t *data, int len)
145
{
146
    int offset;
147
    uint8_t *rpkt;
148

    
149
    switch (ocf) {
150
    case OCF_CSR_SEND_FIRMWARE:
151
        /* Check if this is the bd_address packet */
152
        if (len >= 18 + 8 && data[12] == 0x01 && data[13] == 0x00) {
153
            offset = 18;
154
            s->bd_addr.b[0] = data[offset + 7];        /* Beyond cmd packet end(!?) */
155
            s->bd_addr.b[1] = data[offset + 6];
156
            s->bd_addr.b[2] = data[offset + 4];
157
            s->bd_addr.b[3] = data[offset + 0];
158
            s->bd_addr.b[4] = data[offset + 3];
159
            s->bd_addr.b[5] = data[offset + 2];
160

    
161
            s->hci->bdaddr_set(s->hci, s->bd_addr.b);
162
            fprintf(stderr, "%s: bd_address loaded from firmware: "
163
                            "%02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__,
164
                            s->bd_addr.b[0], s->bd_addr.b[1], s->bd_addr.b[2],
165
                            s->bd_addr.b[3], s->bd_addr.b[4], s->bd_addr.b[5]);
166
        }
167

    
168
        rpkt = csrhci_out_packet_event(s, EVT_VENDOR, 11);
169
        /* Status bytes: no error */
170
        rpkt[9] = 0x00;
171
        rpkt[10] = 0x00;
172
        break;
173

    
174
    default:
175
        fprintf(stderr, "%s: got a bad CMD packet\n", __FUNCTION__);
176
        return;
177
    }
178

    
179
    csrhci_fifo_wake(s);
180
}
181

    
182
static void csrhci_in_packet(struct csrhci_s *s, uint8_t *pkt)
183
{
184
    uint8_t *rpkt;
185
    int opc;
186

    
187
    switch (*pkt ++) {
188
    case H4_CMD_PKT:
189
        opc = le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode);
190
        if (cmd_opcode_ogf(opc) == OGF_VENDOR_CMD) {
191
            csrhci_in_packet_vendor(s, cmd_opcode_ocf(opc),
192
                            pkt + sizeof(struct hci_command_hdr),
193
                            s->in_len - sizeof(struct hci_command_hdr) - 1);
194
            return;
195
        }
196

    
197
        /* TODO: if the command is OCF_READ_LOCAL_COMMANDS or the likes,
198
         * we need to send it to the HCI layer and then add our supported
199
         * commands to the returned mask (such as OGF_VENDOR_CMD).  With
200
         * bt-hci.c we could just have hooks for this kind of commands but
201
         * we can't with bt-host.c.  */
202

    
203
        s->hci->cmd_send(s->hci, pkt, s->in_len - 1);
204
        break;
205

    
206
    case H4_EVT_PKT:
207
        goto bad_pkt;
208

    
209
    case H4_ACL_PKT:
210
        s->hci->acl_send(s->hci, pkt, s->in_len - 1);
211
        break;
212

    
213
    case H4_SCO_PKT:
214
        s->hci->sco_send(s->hci, pkt, s->in_len - 1);
215
        break;
216

    
217
    case H4_NEG_PKT:
218
        if (s->in_hdr != sizeof(csrhci_neg_packet) ||
219
                        memcmp(pkt - 1, csrhci_neg_packet, s->in_hdr)) {
220
            fprintf(stderr, "%s: got a bad NEG packet\n", __FUNCTION__);
221
            return;
222
        }
223
        pkt += 2;
224

    
225
        rpkt = csrhci_out_packet_csr(s, H4_NEG_PKT, 10);
226

    
227
        *rpkt ++ = 0x20;        /* Operational settings negotation Ok */
228
        memcpy(rpkt, pkt, 7); rpkt += 7;
229
        *rpkt ++ = 0xff;
230
        *rpkt ++ = 0xff;
231
        break;
232

    
233
    case H4_ALIVE_PKT:
234
        if (s->in_hdr != 4 || pkt[1] != 0x55 || pkt[2] != 0x00) {
235
            fprintf(stderr, "%s: got a bad ALIVE packet\n", __FUNCTION__);
236
            return;
237
        }
238

    
239
        rpkt = csrhci_out_packet_csr(s, H4_ALIVE_PKT, 2);
240

    
241
        *rpkt ++ = 0xcc;
242
        *rpkt ++ = 0x00;
243
        break;
244

    
245
    default:
246
    bad_pkt:
247
        /* TODO: error out */
248
        fprintf(stderr, "%s: got a bad packet\n", __FUNCTION__);
249
        break;
250
    }
251

    
252
    csrhci_fifo_wake(s);
253
}
254

    
255
static int csrhci_header_len(const uint8_t *pkt)
256
{
257
    switch (pkt[0]) {
258
    case H4_CMD_PKT:
259
        return HCI_COMMAND_HDR_SIZE;
260
    case H4_EVT_PKT:
261
        return HCI_EVENT_HDR_SIZE;
262
    case H4_ACL_PKT:
263
        return HCI_ACL_HDR_SIZE;
264
    case H4_SCO_PKT:
265
        return HCI_SCO_HDR_SIZE;
266
    case H4_NEG_PKT:
267
        return pkt[1] + 1;
268
    case H4_ALIVE_PKT:
269
        return 3;
270
    }
271

    
272
    exit(-1);
273
}
274

    
275
static int csrhci_data_len(const uint8_t *pkt)
276
{
277
    switch (*pkt ++) {
278
    case H4_CMD_PKT:
279
        /* It seems that vendor-specific command packets for H4+ are all
280
         * one byte longer than indicated in the standard header.  */
281
        if (le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode) == 0xfc00)
282
            return (((struct hci_command_hdr *) pkt)->plen + 1) & ~1;
283

    
284
        return ((struct hci_command_hdr *) pkt)->plen;
285
    case H4_EVT_PKT:
286
        return ((struct hci_event_hdr *) pkt)->plen;
287
    case H4_ACL_PKT:
288
        return le16_to_cpu(((struct hci_acl_hdr *) pkt)->dlen);
289
    case H4_SCO_PKT:
290
        return ((struct hci_sco_hdr *) pkt)->dlen;
291
    case H4_NEG_PKT:
292
    case H4_ALIVE_PKT:
293
        return 0;
294
    }
295

    
296
    exit(-1);
297
}
298

    
299
static int csrhci_write(struct CharDriverState *chr,
300
                const uint8_t *buf, int len)
301
{
302
    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
303
    int plen = s->in_len;
304

    
305
    if (!s->enable)
306
        return 0;
307

    
308
    s->in_len += len;
309
    memcpy(s->inpkt + plen, buf, len);
310

    
311
    while (1) {
312
        if (s->in_len >= 2 && plen < 2)
313
            s->in_hdr = csrhci_header_len(s->inpkt) + 1;
314

    
315
        if (s->in_len >= s->in_hdr && plen < s->in_hdr)
316
            s->in_data = csrhci_data_len(s->inpkt) + s->in_hdr;
317

    
318
        if (s->in_len >= s->in_data) {
319
            csrhci_in_packet(s, s->inpkt);
320

    
321
            memmove(s->inpkt, s->inpkt + s->in_len, s->in_len - s->in_data);
322
            s->in_len -= s->in_data;
323
            s->in_hdr = INT_MAX;
324
            s->in_data = INT_MAX;
325
            plen = 0;
326
        } else
327
            break;
328
    }
329

    
330
    return len;
331
}
332

    
333
static void csrhci_out_hci_packet_event(void *opaque,
334
                const uint8_t *data, int len)
335
{
336
    struct csrhci_s *s = (struct csrhci_s *) opaque;
337
    uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1);        /* Align */
338

    
339
    *pkt ++ = H4_EVT_PKT;
340
    memcpy(pkt, data, len);
341

    
342
    csrhci_fifo_wake(s);
343
}
344

    
345
static void csrhci_out_hci_packet_acl(void *opaque,
346
                const uint8_t *data, int len)
347
{
348
    struct csrhci_s *s = (struct csrhci_s *) opaque;
349
    uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1);        /* Align */
350

    
351
    *pkt ++ = H4_ACL_PKT;
352
    pkt[len & ~1] = 0;
353
    memcpy(pkt, data, len);
354

    
355
    csrhci_fifo_wake(s);
356
}
357

    
358
static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg)
359
{
360
    QEMUSerialSetParams *ssp;
361
    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
362
    int prev_state = s->modem_state;
363

    
364
    switch (cmd) {
365
    case CHR_IOCTL_SERIAL_SET_PARAMS:
366
        ssp = (QEMUSerialSetParams *) arg;
367
        s->baud_delay = ticks_per_sec / ssp->speed;
368
        /* Moments later... (but shorter than 100ms) */
369
        s->modem_state |= CHR_TIOCM_CTS;
370
        break;
371

    
372
    case CHR_IOCTL_SERIAL_GET_TIOCM:
373
        *(int *) arg = s->modem_state;
374
        break;
375

    
376
    case CHR_IOCTL_SERIAL_SET_TIOCM:
377
        s->modem_state = *(int *) arg;
378
        if (~s->modem_state & prev_state & CHR_TIOCM_RTS)
379
            s->modem_state &= ~CHR_TIOCM_CTS;
380
        break;
381

    
382
    default:
383
        return -ENOTSUP;
384
    }
385
    return 0;
386
}
387

    
388
static void csrhci_reset(struct csrhci_s *s)
389
{
390
    s->out_len = 0;
391
    s->out_size = FIFO_LEN;
392
    s->in_len = 0;
393
    s->baud_delay = ticks_per_sec;
394
    s->enable = 0;
395
    s->in_hdr = INT_MAX;
396
    s->in_data = INT_MAX;
397

    
398
    s->modem_state = 0;
399
    /* After a while... (but sooner than 10ms) */
400
    s->modem_state |= CHR_TIOCM_CTS;
401

    
402
    memset(&s->bd_addr, 0, sizeof(bdaddr_t));
403
}
404

    
405
static void csrhci_out_tick(void *opaque)
406
{
407
    csrhci_fifo_wake((struct csrhci_s *) opaque);
408
}
409

    
410
static void csrhci_pins(void *opaque, int line, int level)
411
{
412
    struct csrhci_s *s = (struct csrhci_s *) opaque;
413
    int state = s->pin_state;
414

    
415
    s->pin_state &= ~(1 << line);
416
    s->pin_state |= (!!level) << line;
417

    
418
    if ((state & ~s->pin_state) & (1 << csrhci_pin_reset)) {
419
        /* TODO: Disappear from lower layers */
420
        csrhci_reset(s);
421
    }
422

    
423
    if (s->pin_state == 3 && state != 3) {
424
        s->enable = 1;
425
        /* TODO: Wake lower layers up */
426
    }
427
}
428

    
429
qemu_irq *csrhci_pins_get(CharDriverState *chr)
430
{
431
    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
432

    
433
    return s->pins;
434
}
435

    
436
CharDriverState *uart_hci_init(qemu_irq wakeup)
437
{
438
    struct csrhci_s *s = (struct csrhci_s *)
439
            qemu_mallocz(sizeof(struct csrhci_s));
440

    
441
    s->chr.opaque = s;
442
    s->chr.chr_write = csrhci_write;
443
    s->chr.chr_ioctl = csrhci_ioctl;
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 = qemu_new_timer(vm_clock, 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
}