//
//  bt_hci_usb.c
//
//  Bluetooth Protocol Stack - HCI Transport Layer Interface for USB
//              (Controller(HCI:USB) -> USB host stack -> "my" -> L2CAP)
//  Copyright (C) 2013-2014 Toyohiko Togashi tog001@nifty.com
//
//
//  This program is free software; you can redistribute it and/or modify it under the terms of the
//  GNU General Public License as published by the Free Software Foundation; either version 3
//  of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
//  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//  See the GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License along with this program.
//  If not, see <http://www.gnu.org/licenses/>
//
//
//  Reference:
//      Bluetooth SIG (www.bluetooth.com)
//          BLUETOOTH SPECIFICATION Version 4.0
//          [Vol 4] Host Controller Interface [Transport Layer]
//          Part B: USB Transport Layer
//      usbh_wcc_bluetooth.[ch]
//
//  Update history
//  ---------- ----- -------------------------------------------
//  2013.01.21 v0.0  First cording
//  2013.04.21 v0.1  Commit
//  2014.03.05 v0.2  Changed the source file name, bt_hci->bt_hci_usb
//
#include <stddef.h>
#include <string.h>
#include "bt_hci.h"

#define MIN(a,b)    ((a)<(b) ? (a):(b))
//#define DEBUG

//
// Debug code
//
#ifdef DEBUG
#include <stdio.h>
#include "bt_spec_hci.h"
void cmd_dump(struct bt_hci_Command_Packet  *p) {
    int             opcode;
    int             ogf;
    int             ocf;
    int             i;
    unsigned char   *c;

    opcode = HCI_UINT16(p->Op_Code);
    ogf = opcode >> HCI_OPCODE_OGF_SHIFT;
    ocf = opcode & HCI_OPCODE_OCF_MASK;
    c = (unsigned char *) &(p->Parameter);
    printf("cmd=0x%02x-%03x", ogf, ocf);
    if (p->Parameter_Total_Length > 0) {
        printf(" 0x");
        for(i = 0; i < p->Parameter_Total_Length; i++) {
            printf("%02x", *c++);
            if ((i % 4) == 3) {
                printf(",");
            }
        }
    }
    printf("\n");
}

void evt_dump(struct bt_hci_Event_Packet *p) {
    unsigned char   *c;
    int             i;

    c = (unsigned char *) &(p->Event_Parameter);
    printf("evt=0x%02x", p->Event_Code);
    if (p->Parameter_Total_Length > 0) {
        printf(" 0x");
        for(i = 0; i < p->Parameter_Total_Length; i++) {
            printf("%02x", *c++);
            if ((i % 4) == 3) {
                printf(",");
            }
        }
    }
    printf("\n");
}

void acl_dump(char *title, struct bt_hci_ACL_Data_Packet *p){
    int i;
    int l;

    printf("%s: <0x%x> 0x", title, HCI_UINT16(p->Connection_Handle));
    l = HCI_UINT16(p->Data_Total_Length);
    for(i = 0; i < l; i++) {
        printf("%02x", p->Data[i]);
        if ((i % 4) == 3) {
            printf(",");
        }
    }
    printf("\n");
}

void bt_dumpHciBuf(struct bt_hciBufCB *cb) {
    if (!strcmp(cb->id, "cmd")) {
        cmd_dump((struct bt_hci_Command_Packet  *)cb->buf);
    } else if (!strcmp(cb->id, "evt")) {
        evt_dump((struct bt_hci_Event_Packet *)cb->buf);
    } else if (!strcmp(cb->id, "out")) {
        acl_dump("out", (struct bt_hci_ACL_Data_Packet *)cb->buf);
    } else {
        acl_dump("in", (struct bt_hci_ACL_Data_Packet *)cb->buf);
    }
    return;
}
#else
#define bt_dumpHciBuf(a)
#endif

//
// Control block entity
//
struct bt_hciBufCB bt_hciBufCB[4] = {
        {"cmd", 2, 1, NULL, 0, BT_HCI_BUFFER_INIT, NULL, 0},
        {"evt", 1, 1, NULL, 0, BT_HCI_BUFFER_INIT, NULL, 0},
        {"out", 2, 2, NULL, 0, BT_HCI_BUFFER_INIT, NULL, 0},
        {"in",  2, 2, NULL, 0, BT_HCI_BUFFER_INIT, NULL, 0},
};

//
// Internal subroutine
//
size_t bt_lenHciBuf(struct bt_hciBufCB *cb) {
    size_t l;

    if (cb->lenSize == 1) {
        l = cb->buf[cb->lenPos] + cb->lenPos + 1;
    } else {
        l = cb->buf[cb->lenPos] + ((cb->buf[cb->lenPos + 1]) << 8) + cb->lenPos + 2;
    }
    return(l);
}

//
// Init
//
void bt_setHciBuffer(struct bt_hciBufCB *cb, unsigned char *buf, size_t bufsize) {
    cb->buf     = buf;
    cb->bufSize = bufsize;
    cb->status  = BT_HCI_BUFFER_INIT;
    cb->dataLen = 0;
    cb->current = cb->buf;
    return;
}

//
// [L2CAP] -> Bluetooth controler(USB dongle) path
//
void bt_sendHciBuffer(struct bt_hciBufCB *cb){
    cb->dataLen = bt_lenHciBuf(cb);
    cb->current = cb->buf;
    cb->status  = BT_HCI_BUFFER_VALID;
    bt_dumpHciBuf(cb);
    return;
}

//
// [L2CAP] <- Bluetooth controler(USB dongle) path
//
int bt_recieveHciBuffer(struct bt_hciBufCB *cb){
    int rc;

    if (cb->status == BT_HCI_BUFFER_VALID) {
        cb->dataLen = 0;
        cb->current = cb->buf;
        cb->status  = BT_HCI_BUFFER_EMPTY;
        rc = 1;
        bt_dumpHciBuf(cb);
    } else {
        rc = 0;
    }
    return(rc);
}

//
// L2CAP -> [Bluetooth controler(USB dongle)] path
//
int bt_getHciBuffer(struct bt_hciBufCB *cb, unsigned char *buf, size_t len){
    int     rc;
    size_t  l;

    if (cb->status == BT_HCI_BUFFER_VALID) {
        l = MIN(len, cb->dataLen);
        memcpy(buf, cb->current, l);
        cb->dataLen -= l;
        cb->current += l;
        if (cb->dataLen == 0) {
            cb->status = BT_HCI_BUFFER_EMPTY;
        }
        rc = l;
    } else {
        rc = -1;                        // EAGAIN
    }
    return(rc);
}


//
// L2CAP <- [Bluetooth controler(USB dongle)] path
//
int bt_putHciBuffer(struct bt_hciBufCB *cb, unsigned char *buf, size_t len){
    int     rc;
    size_t  l;
    size_t  f;

    if (cb->status != BT_HCI_BUFFER_VALID) {
        f = cb->bufSize - cb->dataLen;
        l = MIN(len, f);
        memcpy(cb->current, buf, l);
        cb->dataLen += l;
        cb->current += l;
        if (bt_lenHciBuf(cb) > cb->dataLen) {
            cb->status = BT_HCI_BUFFER_BUSY;
        } else {
            cb->status = BT_HCI_BUFFER_VALID;
        }
        rc = l;
    } else {
        rc = -1;                        // EAGAIN
    }
    return(rc);
}
