//
//  bt_l2cap_cb_link.c
//
//  Bluetooth Protocol Stack - BT controller link management control block operation
//  Copyright (C) 2013-2017 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/>
//
//
//  Update history
//  ---------- ----- -------------------------------------------
//  2013.01.21 v0.0  First cording
//  2013.04.21 v0.1  Commit
//  2013.05.08 v0.1  Code reduction, used a malloc()
//  2015.02.03 v0.3  Supported the PIC32
//  2016.02.20 v0.3c+ Bug fix, bt_l2capLinkGetNext()
//  2017.01.03 v0.3e Added a API (bt_l2capLinkFindByDevice,bt_l2capLinkAddByDevice)
//
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include "bt_l2cap_cb_link.h"
#include "bt_l2cap_cb_channel.h"

#ifdef BT_L2CAP_LINK_LED_ENABLE         // ADD START 2015.02.03
    #ifdef USE_STM32F4_DISCOVERY
        #include "stm32f4_discovery.h"
        #define BT_L2CAP_LINK_LED_ON()      STM_EVAL_LEDOn(LED4)    // Green
        #define BT_L2CAP_LINK_LED_OFF()     STM_EVAL_LEDOff(LED4)
    #else
        #include "bsp_config.h"
        #define BT_L2CAP_LINK_LED_ON()      BSP_LEDOn(BSP_LED_1)
        #define BT_L2CAP_LINK_LED_OFF()     BSP_LEDOff(BSP_LED_1)
    #endif
#else
    #define BT_L2CAP_LINK_LED_ON()
    #define BT_L2CAP_LINK_LED_OFF()
#endif                                  // ADD END 2015.02.03

//
//    Static area
//
struct bt_l2capLink *bt_l2capLinkRoot = NULL;

//
//    Sequential access
//
struct bt_l2capLink *bt_l2capLinkGetNext(struct bt_l2capLink **id) {
    struct bt_l2capLink *p;

#ifdef UPD20160220                      // DEL START 2016.02.20
    if (*id == NULL) {
        p = bt_l2capLinkRoot;
    } else {
        p = (*id)->next;
    }
#else                                   // DEL END ADD START 2016.02.20
    p = bt_l2capLinkRoot;
    if ((id != NULL) && (*id != NULL)) {
        for(; p != NULL; p = p->next) {
            if (p == *id) {
                p = (*id)->next;
                break;
            }
        }
    }
#endif                                  // ADD END 2016.02.20
    *id = p;
    return(p);
}

//
// Search entry
//
struct bt_l2capLink *bt_l2capLinkFind(unsigned char *bdAddr, unsigned short handle) {
    struct bt_l2capLink *p;

    for(p = bt_l2capLinkRoot; p != NULL; p = p->next) {
        if ((bdAddr != NULL) && (p->device != NULL)) {
            if (memcmp(p->device->bdAddr, bdAddr, sizeof(p->device->bdAddr)) != 0) {
                continue;
            }
        }
        if ((handle != 0) && (p->handle != handle)) {
            continue;
        }
        break;
    }
    return(p);
}
                                        // ADD START 2017.01.03
struct bt_l2capLink *bt_l2capLinkFindByDevice(struct bt_l2capDevice *device, unsigned short handle) {
    struct bt_l2capLink *p;

    for(p = bt_l2capLinkRoot; p != NULL; p = p->next) {
        if ((device != NULL) && (p->device != device)) {
            continue;
        }
        if ((handle != 0) && (p->handle != handle)) {
            continue;
        }
        break;
    }
    return(p);
}
                                        // ADD END 2017.01.03

//
// Add entry
//
struct bt_l2capLink *bt_l2capLinkAdd(unsigned char *bdAddr, unsigned short handle) {
    struct bt_l2capLink    *p;

    p = bt_l2capLinkFind(bdAddr, handle);
    if (p == NULL) {
        if ((p = calloc(1, sizeof(struct bt_l2capLink))) != NULL) {
            p->handle        = handle;
            p->device        = bt_l2capDeviceFind(bdAddr);
            p->next          = bt_l2capLinkRoot;
            bt_l2capLinkRoot = p;
            BT_L2CAP_LINK_LED_ON();
        }
    }
    return(p);
}
                                        // ADD START 2017.01.03
struct bt_l2capLink *bt_l2capLinkAddByDevice(struct bt_l2capDevice *device, unsigned short handle) {
    struct bt_l2capLink    *p;

    p = bt_l2capLinkFindByDevice(device, handle);
    if (p == NULL) {
        if ((p = calloc(1, sizeof(struct bt_l2capLink))) != NULL) {
            p->handle        = handle;
            p->device        = device;
            p->next          = bt_l2capLinkRoot;
            bt_l2capLinkRoot = p;
            BT_L2CAP_LINK_LED_ON();
        }
    }
    return(p);
}
                                        // ADD END 2017.01.03

//
// Delete entry
//
void bt_l2capLinkDelete(struct bt_l2capLink *entry) {
    struct bt_l2capLink *p;
    struct bt_l2capLink *z;

    z = NULL;
    for(p = bt_l2capLinkRoot; p != NULL; p = p->next) {
        if (p == entry) {
            struct bt_l2capChannel    *c;

//          while((c = bt_l2capChannelFind(p->handle, 0, 0)) != NULL) { // DEL 2017.01.03
            while((c = bt_l2capChannelFindByLink(p, 0, 0)) != NULL) {   // ADD 2017.01.03
                bt_l2capChannelDelete(c);
            }
            if (z == NULL) {
                bt_l2capLinkRoot = p->next;
            } else {
                z->next = p->next;
            }
            free(p);
            break;
        }
        z = p;
    }
    if (bt_l2capLinkRoot == NULL) {
        BT_L2CAP_LINK_LED_OFF();
    }
    return;
}

//
// Delete all entry
//
void bt_l2capLinkClear(void) {
    struct bt_l2capLink        *p;
    struct bt_l2capLink        *i;

    i = NULL;
    while((p = bt_l2capLinkGetNext(&i)) != NULL) {
        bt_l2capLinkDelete(p);
    }
    return;
}
