root/src/add-ons/kernel/bluetooth/hci/bluetooth.cpp
/*
 * Copyright 2008, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
 */

#include <new>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <KernelExport.h>
#include <lock.h>
#include <SupportDefs.h>
#include <util/AutoLock.h>
#include <util/DoublyLinkedList.h>

#include <net_buffer.h>
#include <net_device.h>
#include <net_stack.h>
#include <NetBufferUtilities.h>

#include <btDebug.h>
#include <btCoreData.h>
#include <btModules.h>
#include <CodeHandler.h>
#define KERNEL_LAND
#include <PortListener.h>
#undef KERNEL_LAND

#include <bluetooth/HCI/btHCI.h>
#include <bluetooth/HCI/btHCI_acl.h>
#include <bluetooth/HCI/btHCI_command.h>
#include <bluetooth/HCI/btHCI_event.h>
#include <bluetooth/HCI/btHCI_transport.h>
#include <bluetooth/HCI/btHCI_sco.h>
#include <bluetooth/bdaddrUtils.h>

#include "acl.h"


int32 api_version = B_CUR_DRIVER_API_VERSION;


typedef PortListener<void,
        HCI_MAX_FRAME_SIZE, // Event Body can hold max 255 + 2 header
        24                                      // Some devices have sent chunks of 24 events(inquiry result)
        > BluetoothRawDataPort;


// Modules references
net_buffer_module_info* gBufferModule = NULL;
struct bluetooth_core_data_module_info* btCoreData = NULL;

static mutex sListLock;
static sem_id sLinkChangeSemaphore;
static DoublyLinkedList<bluetooth_device> sDeviceList;

BluetoothRawDataPort* BluetoothRXPort;

// forward declarations
status_t HciPacketHandler(void* data, int32 code, size_t size);


bluetooth_device*
FindDeviceByID(hci_id hid)
{
        bluetooth_device* device;

        DoublyLinkedList<bluetooth_device>::Iterator iterator
                = sDeviceList.GetIterator();

        while (iterator.HasNext()) {
                device = iterator.Next();
                if (device->index == hid)
                        return device;
        }

        return NULL;
}


status_t
PostTransportPacket(hci_id hid, bt_packet_t type, void* data, size_t count)
{
        uint32 code = 0;

        Bluetooth::CodeHandler::SetDevice(&code, hid);
        Bluetooth::CodeHandler::SetProtocol(&code, type);

        return BluetoothRXPort->Trigger(code, data, count);
}


status_t
Assemble(bluetooth_device* bluetoothDevice, bt_packet_t type, void* data,
        size_t count)
{
        net_buffer* nbuf = bluetoothDevice->fBuffersRx[type];

        size_t currentPacketLen = 0;

        while (count) {

                if (nbuf == NULL) {
                        // new buffer incoming
                        switch (type) {
                                case BT_EVENT:
                                        if (count >= HCI_EVENT_HDR_SIZE) {
                                                struct hci_event_header* headerPacket
                                                        = (struct hci_event_header*)data;
                                                bluetoothDevice->fExpectedPacketSize[type]
                                                        = HCI_EVENT_HDR_SIZE + headerPacket->elen;

                                                if (count >= bluetoothDevice->fExpectedPacketSize[type]) {
                                                        // the whole packet is here so it can be already posted.
                                                        ERROR("%s: EVENT posted in HCI!!!\n", __func__);
                                                        btCoreData->PostEvent(bluetoothDevice, data,
                                                                bluetoothDevice->fExpectedPacketSize[type]);

                                                } else {
                                                        nbuf = gBufferModule->create(
                                                                bluetoothDevice->fExpectedPacketSize[type]);
                                                        bluetoothDevice->fBuffersRx[type] = nbuf;

                                                        nbuf->protocol = type;
                                                }

                                        } else {
                                                panic("EVENT frame corrupted\n");
                                                return EILSEQ;
                                        }
                                        break;

                                case BT_ACL:
                                        if (count >= HCI_ACL_HDR_SIZE) {
                                                struct hci_acl_header* headerPkt = (struct hci_acl_header*)data;

                                                bluetoothDevice->fExpectedPacketSize[type] = HCI_ACL_HDR_SIZE
                                                        + B_LENDIAN_TO_HOST_INT16(headerPkt->alen);

                                                // Create the buffer -> TODO: this allocation can fail
                                                nbuf = gBufferModule->create(
                                                        bluetoothDevice->fExpectedPacketSize[type]);
                                                bluetoothDevice->fBuffersRx[type] = nbuf;

                                                nbuf->protocol = type;
                                        } else {
                                                panic("ACL frame corrupted\n");
                                                return EILSEQ;
                                        }
                                        break;

                                case BT_SCO:

                                        break;

                                default:
                                        panic("unknown packet type in assembly");
                                        break;
                        }

                        currentPacketLen = bluetoothDevice->fExpectedPacketSize[type];

                } else {
                        // Continuation of a packet
                        currentPacketLen = bluetoothDevice->fExpectedPacketSize[type] - nbuf->size;
                }
                if (nbuf != NULL) {
                        currentPacketLen = min_c(currentPacketLen, count);

                        gBufferModule->append(nbuf, data, currentPacketLen);

                        if ((bluetoothDevice->fExpectedPacketSize[type] - nbuf->size) == 0) {

                                switch (nbuf->protocol) {
                                        case BT_EVENT:
                                                panic("need to send full buffer to btdatacore!\n");
                                                btCoreData->PostEvent(bluetoothDevice, data,
                                                        bluetoothDevice->fExpectedPacketSize[type]);

                                                break;
                                        case BT_ACL:
                                                // TODO: device descriptor has been fetched better not
                                                // pass id again
                                                TRACE("%s: ACL parsed in ACL!\n", __func__);
                                                AclAssembly(nbuf, bluetoothDevice->index);
                                                break;
                                        default:

                                                break;
                                }

                                bluetoothDevice->fBuffersRx[type] = nbuf = NULL;
                                bluetoothDevice->fExpectedPacketSize[type] = 0;
                        } else {
                                if (type == BT_ACL) {
                                        TRACE("%s: ACL Packet not filled size %" B_PRIu32
                                                " expected=%" B_PRIuSIZE "\n", __func__, nbuf->size,
                                                bluetoothDevice->fExpectedPacketSize[type]);
                                }
                        }

                }
                // in case in the pipe there is info about the next buffer
                count -= currentPacketLen;
                data = (void*)((uint8*)data + currentPacketLen);
        }

        return B_OK;
}


status_t
HciPacketHandler(void* data, int32 code, size_t size)
{
        hci_id deviceId = Bluetooth::CodeHandler::Device(code);

        bluetooth_device* bluetoothDevice = FindDeviceByID(deviceId);

        TRACE("%s: to assemble %" B_PRIuSIZE " bytes of 0x%" B_PRIx32 "\n",
                __func__, size, deviceId);

        if (bluetoothDevice != NULL) {
                return Assemble(bluetoothDevice, Bluetooth::CodeHandler::Protocol(code),
                        data, size);
        } else {
                ERROR("%s: Device 0x%" B_PRIx32 " could not be matched\n", __func__,
                        deviceId);
        }

        return B_ERROR;
}


//      #pragma mark -


status_t
RegisterDriver(bt_hci_transport_hooks* hooks, bluetooth_device** _device)
{

        bluetooth_device* device = new (std::nothrow) bluetooth_device;
        if (device == NULL)
                return B_NO_MEMORY;

        for (int index = 0; index < HCI_NUM_PACKET_TYPES; index++) {
                device->fBuffersRx[index] = NULL;
                device->fExpectedPacketSize[index] = 0;
        }

        device->info = NULL; // not yet used
        device->hooks = hooks;
        device->supportedPacketTypes = (HCI_DM1 | HCI_DH1 | HCI_HV1);
        device->linkMode = (HCI_LM_ACCEPT);
        device->mtu = L2CAP_MTU_MINIMUM; // TODO: ensure specs min value

        MutexLocker _(&sListLock);

        if (sDeviceList.IsEmpty())
                device->index = HCI_DEVICE_INDEX_OFFSET; // REVIEW: dev index
        else {
                device->index = (sDeviceList.Tail())->index + 1; // REVIEW!
                TRACE("%s: List not empty\n", __func__);
        }

        sDeviceList.Add(device);

        TRACE("%s: Device %" B_PRIx32 "\n", __func__, device->index);

        *_device = device;

        return B_OK;
}


status_t
UnregisterDriver(hci_id id)
{
        bluetooth_device* device = FindDeviceByID(id);

        if (device == NULL)
                return B_ERROR;

        if (device->GetDoublyLinkedListLink()->next != NULL
                || device->GetDoublyLinkedListLink()->previous != NULL
                || device == sDeviceList.Head())
                sDeviceList.Remove(device);

        delete device;

        return B_OK;
}


// PostACL
status_t
PostACL(hci_id hciId, net_buffer* buffer)
{
        net_buffer* curr_frame = NULL;
        net_buffer* next_frame = buffer;
        uint8 flag = HCI_ACL_PACKET_START;

        if (buffer == NULL)
                panic("passing null buffer");

        uint16 handle = buffer->type; // TODO: CodeHandler

        bluetooth_device* device = FindDeviceByID(hciId);

        if (device == NULL) {
                ERROR("%s: No device 0x%" B_PRIx32 "\n", __func__, hciId);
                return B_ERROR;
        }

        TRACE("%s: index 0x%" B_PRIx32 " try to send bt packet of %" B_PRIu32
                " bytes (flags 0x%" B_PRIx32 "):\n", __func__, device->index,
                buffer->size, buffer->flags);

        // TODO: ATOMIC! any other thread should stop here
        do {
                // Divide packet if big enough
                curr_frame = next_frame;

                if (curr_frame->size > device->mtu) {
                        next_frame = gBufferModule->split(curr_frame, device->mtu);
                } else {
                        next_frame = NULL;
                }

                // Attach acl header
                {
                        NetBufferPrepend<struct hci_acl_header> bufferHeader(curr_frame);
                        status_t status = bufferHeader.Status();
                        if (status < B_OK) {
                                // free the buffer
                                continue;
                        }

                        bufferHeader->handle = pack_acl_handle_flags(handle, flag, 0);
                        bufferHeader->alen = curr_frame->size - sizeof(struct hci_acl_header);
                }

                // Send to driver
                curr_frame->protocol = BT_ACL;

                TRACE("%s: Tolower nbuf %p!\n", __func__, curr_frame);
                // We could pass a cookie and avoid the driver fetch the Id
                device->hooks->SendACL(device->index, curr_frame);
                flag = HCI_ACL_PACKET_FRAGMENT;

        } while (next_frame != NULL);

        return B_OK;
}


status_t
PostSCO(hci_id hciId, net_buffer* buffer)
{
        return B_ERROR;
}


status_t
PostESCO(hci_id hciId, net_buffer* buffer)
{
        return B_ERROR;
}


static int
dump_bluetooth_devices(int argc, char** argv)
{
        bluetooth_device*       device;

        DoublyLinkedList<bluetooth_device>::Iterator iterator
                = sDeviceList.GetIterator();

        while (iterator.HasNext()) {
                device = iterator.Next();
                kprintf("\tindex=%" B_PRIx32 " @%p hooks=%p\n", device->index,
                        device, device->hooks);
        }

        return 0;
}


static status_t
bluetooth_std_ops(int32 op, ...)
{
        switch (op) {
                case B_MODULE_INIT:
                {
                        status_t status;

                        status = get_module(NET_BUFFER_MODULE_NAME,
                                (module_info**)&gBufferModule);

                        if (status < B_OK) {
                                panic("no way Dude we need that!");
                                return status;
                        }

                        status = get_module(BT_CORE_DATA_MODULE_NAME,
                                (module_info**)&btCoreData);
                        if (status < B_OK) {
                                ERROR("%s: problem getting bt core data module\n", __func__);
                                return status;
                        }

                        new (&sDeviceList) DoublyLinkedList<bluetooth_device>;
                                // static C++ objects are not initialized in the module startup

                        BluetoothRXPort = new BluetoothRawDataPort(BT_RX_PORT_NAME,
                                (BluetoothRawDataPort::port_listener_func)&HciPacketHandler);

                        if (BluetoothRXPort->Launch() != B_OK) {
                                ERROR("%s: RX thread creation failed!\n", __func__);
                                // we Cannot do much here ... avoid registering
                        } else {
                                TRACE("%s: RX thread launched!\n", __func__);
                        }

                        sLinkChangeSemaphore = create_sem(0, "bt sem");
                        if (sLinkChangeSemaphore < B_OK) {
                                put_module(NET_STACK_MODULE_NAME);
                                ERROR("%s: Link change semaphore failed\n", __func__);
                                return sLinkChangeSemaphore;
                        }

                        mutex_init(&sListLock, "bluetooth devices");

                        // status = InitializeAclConnectionThread();
                        ERROR("%s: Connection Thread error\n", __func__);

                        add_debugger_command("btLocalDevices", &dump_bluetooth_devices,
                                "Lists Bluetooth LocalDevices registered in the Stack");

                        return B_OK;
                }

                case B_MODULE_UNINIT:
                {
                        delete_sem(sLinkChangeSemaphore);

                        mutex_destroy(&sListLock);
                        put_module(NET_BUFFER_MODULE_NAME);
                        put_module(NET_STACK_MODULE_NAME);
                        put_module(BT_CORE_DATA_MODULE_NAME);
                        remove_debugger_command("btLocalDevices", &dump_bluetooth_devices);
                        // status_t status = QuitAclConnectionThread();

                        return B_OK;
                }

                default:
                        return B_ERROR;
        }
}


bt_hci_module_info sBluetoothModule = {
        {
                BT_HCI_MODULE_NAME,
                B_KEEP_LOADED,
                bluetooth_std_ops
        },
        RegisterDriver,
        UnregisterDriver,
        FindDeviceByID,
        PostTransportPacket,
        PostACL,
        PostSCO,
        PostESCO
};


module_info* modules[] = {
        (module_info*)&sBluetoothModule,
        NULL
};