//
//  bt_att_server.c
//
//  Bluetooth Protocol Stack - Attribute Server
//  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/>
//
//
//  Reference
//      Bluetooth SIG (www.bluetooth.com)
//          BLUETOOTH SPECIFICATION Version 4.0
//          Volume 3: Core System Package [Host volume]
//          PartF: Attribute Protocol (ATT)
//
//  Update history
//  ---------- ----- -------------------------------------------
//  2013.03.25 v0.0  First cording
//  2013.04.21 v0.1  Commit
//  2013.04.25 v0.1  WRITE,NOTIFICATION,INDICATION support
//  2013.11.04 v0.1a ATT DB manager API move to main()
//  2013.12.12 v0.1a Rejecting a notify at repeat
//  2015.02.20 v0.3b Bug fix (Compile warning problem)
//  2017.01.03 v0.3e argument handle -> cb_channel
//  2017.01.18 v0.3f Appended a authentication function
//  2017.02.06 v0.3g Supported a 16bit UUID
//
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "bt_spec_att.h"
#include "bt_att_db.h"
#include "bt_l2cap_cb_channel.h"        // Channel manage table     ADD 2017.01.03

//#define DEBUG
#define MAX_HANDLE_PAIRES       5
#define MAX_NOTIFICATION_QUEUE  10

//
//  Debug
//
#ifdef DEBUG
#include <stdio.h>                      // Debug
#define DEBUGP(...)     printf(__VA_ARGS__)
void dump99(char *t, unsigned char *s, int l){
    printf(t);
    for(; l > 0; --l) {
        printf("%02x ", *s++);
    }
    printf("\n");
}
#else
#define DEBUGP(...)
#define dump99(a,b,c)
#endif

//
//  Global area
//
unsigned short  bt_att_serverNotificationHandleQueue[MAX_NOTIFICATION_QUEUE];
int             bt_att_serverNotificationHandleQueueLock;

//
//  Internal subroutine
//
void bt_att_serverNotificationQueueAdd(unsigned short handle){
    int i;

    for(i = 0; i < MAX_NOTIFICATION_QUEUE; i++) {
        if (bt_att_serverNotificationHandleQueue[i] == 0) {
            bt_att_serverNotificationHandleQueue[i] = handle;
            break;
        } else if (bt_att_serverNotificationHandleQueue[i] == handle) { // ADD 2013.12.12
            break;                                                      // ADD 2013.12.12
        }
    }
    return;
}

unsigned short  bt_att_serverNotificationQueueGet(void){
    unsigned short  h;

    if (bt_att_serverNotificationHandleQueueLock) {
        h = 0;
    } else {
        h = bt_att_serverNotificationHandleQueue[0];
        if (h != 0) {
            bt_att_serverNotificationHandleQueueLock = 1;
        }
    }
    return(h);
}

void bt_att_serverNotificationQueueDelete(void){
    int             i;
    unsigned short  *q;

    q = bt_att_serverNotificationHandleQueue;
    for(i = 0; i < (MAX_NOTIFICATION_QUEUE-1); i++) {
        *q = *(q + 1);
        q++;
    }
    *q = 0;
    bt_att_serverNotificationHandleQueueLock = 0;
    return;
}

size_t  bt_attServerError(struct bt_att_PDU_Format *out, unsigned char errorCode, unsigned char opCode, unsigned short handle){
    out->Attribute_Opcode                                                         = ATT_Opcode_Error_Response;
    out->Attribute_Parameters.Error_Response.Error_Code                           = errorCode;
    out->Attribute_Parameters.Error_Response.Request_Opcode_In_Error              = opCode;
    ATT_STORE16(out->Attribute_Parameters.Error_Response.Attribute_Handle_In_Error, handle);
    return(ATT_PDU_Format_HeadSize + sizeof(out->Attribute_Parameters.Error_Response));
}

//
//  Server
//
//size_t bt_attServer(unsigned short  hciHandle, unsigned char *inBuff, size_t inLen, unsigned char *outBuff, size_t maxSize) { // DEL 2017.01.03
size_t bt_attServer(struct bt_l2capChannel *ch, unsigned char *inBuff, size_t inLen, unsigned char *outBuff, size_t maxSize) {  // ADD 2017.01.03
    struct bt_att_PDU_Format    *in;
    struct bt_att_PDU_Format    *out;
    size_t                      outLen;
    const unsigned char         uuid16[16] = {ATT_UUID16TO128_DATA(0)}; // ADD 2017.02.06

    in     = (struct bt_att_PDU_Format *)inBuff;
    out    = (struct bt_att_PDU_Format *)outBuff;
    outLen = 0;

    // Received
    if (in != NULL) {
        DEBUGP("bt_sttServer: opcode=0x%02x\n", in->Attribute_Opcode);
        switch(in->Attribute_Opcode) {
        case ATT_Opcode_Exchange_MTU_Request:   // ADD START 2017.01.18
        {
            size_t  mtu;
            
            mtu = ATT_UINT16(in->Attribute_Parameters.Exchange_MTU_Request.Client_Rx_MTU);
            if (mtu > maxSize) {
                mtu = maxSize;
            }
            out->Attribute_Opcode = ATT_Opcode_Exchange_MTU_Response;
            ATT_STORE16(out->Attribute_Parameters.Exchange_MTU_Response.Server_Rx_MTU, mtu);
            outLen = ATT_PDU_Format_HeadSize + sizeof(out->Attribute_Parameters.Exchange_MTU_Response);
            break;
        }                                       // ADD END 2017.01.18
        case ATT_Opcode_Find_Information_Request:
        {
            unsigned short          s;
            unsigned short          e;
            int                     i;
            int                     j;
            struct bt_attDbEntry    *p;
            int                     f;                                      // ADD 2017.02.06

            s = ATT_UINT16(in->Attribute_Parameters.Find_Information_Request.Starting_Handle);
            e = ATT_UINT16(in->Attribute_Parameters.Find_Information_Request.Ending_Handle);
#ifdef DELETE                           // DEL START 2017.02.06
            j = 0;
            i = 0;
            while((p = bt_attDbGetNext(&i)) != NULL) {
                if ((p->handle >= s) && (p->handle <= e)) {
                    ATT_STORE16(out->Attribute_Parameters.Find_Information_Response.Information_Data[j].Format2.Handle, p->handle);
                    memcpy(out->Attribute_Parameters.Find_Information_Response.Information_Data[j].Format2.UUID128,
                            p->types,
                           sizeof(out->Attribute_Parameters.Find_Information_Response.Information_Data[j].Format2.UUID128));
                    j++;
                }
            }
            if (j > 0) {
                out->Attribute_Opcode                                      = ATT_Opcode_Find_Information_Response;
                out->Attribute_Parameters.Find_Information_Response.Format = ATT_Format_Handles_and_128bit_UUIDs;
                outLen = ATT_PDU_Format_HeadSize + sizeof(out->Attribute_Parameters.Find_Information_Response) + sizeof(out->Attribute_Parameters.Find_Information_Response.Information_Data[0]) * j;
            } else {
                outLen = bt_attServerError(out, ATT_Error_Code_Attribute_Not_Found, ATT_Opcode_Find_Information_Request, s);
            }
#else                                   // DEL END - ADD START 2017.02.06
            f = ATT_Format_Handles_and_16bit_Bluetooth_UUIDs;
            j = 0;
            i = 0;
            while((p = bt_attDbGetNext(&i)) != NULL) {
                if ((p->handle >= s) && (p->handle <= e)) {
                    if (memcmp(p->types, uuid16, 12) != 0) {
                        f = ATT_Format_Handles_and_128bit_UUIDs;
                    }
                    j++;
                }
            }
            if (j > 0) {
                outLen = ATT_PDU_Format_HeadSize + sizeof(out->Attribute_Parameters.Find_Information_Response);
                out->Attribute_Opcode                                      = ATT_Opcode_Find_Information_Response;
                out->Attribute_Parameters.Find_Information_Response.Format = f;
                j = 0;
                i = 0;
                while((p = bt_attDbGetNext(&i)) != NULL) {
                    if ((p->handle >= s) && (p->handle <= e)) {
                    	if (f == ATT_Format_Handles_and_16bit_Bluetooth_UUIDs) {
                            ATT_STORE16(out->Attribute_Parameters.Find_Information_Response.Format1[j].Handle, p->handle);
                            out->Attribute_Parameters.Find_Information_Response.Format1[j].UUID16[0] = p->types[12];
                            out->Attribute_Parameters.Find_Information_Response.Format1[j].UUID16[1] = p->types[13];
                            outLen += sizeof(out->Attribute_Parameters.Find_Information_Response.Format1[j]);
                    	} else {
                            ATT_STORE16(out->Attribute_Parameters.Find_Information_Response.Format2[j].Handle, p->handle);
                            memcpy(out->Attribute_Parameters.Find_Information_Response.Format2[j].UUID128, p->types,
                                   sizeof(out->Attribute_Parameters.Find_Information_Response.Format2[j].UUID128));
                            outLen += sizeof(out->Attribute_Parameters.Find_Information_Response.Format2[j]);
                    	}
                        j++;
                    }
                }
            } else {
                outLen = bt_attServerError(out, ATT_Error_Code_Attribute_Not_Found, ATT_Opcode_Find_Information_Request, s);
            }
#endif                                  // ADD END 2017.02.06
            break;
        }
        case ATT_Opcode_Find_By_Type_Value_Request:
        {
            struct bt_attDbEntry    *p;
            unsigned short          s;
            unsigned short          e;
            uint8_t                 t[sizeof(p->types)];
            int                     i;
            int                     j;
            unsigned short          g1;
            unsigned short          g2;
            size_t                  l;
            unsigned short          handle_s[MAX_HANDLE_PAIRES];
            unsigned short          handle_e[MAX_HANDLE_PAIRES];

            s = ATT_UINT16(in->Attribute_Parameters.Find_By_Type_Value_Request.Starting_Handle);
            e = ATT_UINT16(in->Attribute_Parameters.Find_By_Type_Value_Request.Ending_Handle);
            ATT_STOREUUID16TO128(t, in->Attribute_Parameters.Find_By_Type_Value_Request.Attribute_Type);
            l = inLen - (ATT_PDU_Format_HeadSize + sizeof(in->Attribute_Parameters.Find_By_Type_Value_Request));
            g1 = 0;
            g2 = 0;                     // ADD 2015.02.20
            j  = 0;
            i  = 0;
            while((p = bt_attDbGetNext(&i)) != NULL) {
                if ((p->handle >= s) && (p->handle <= e)) {
                    if (memcmp(p->types, t, sizeof(t)) == 0) {
                        if (memcmp(p->value, in->Attribute_Parameters.Find_By_Type_Value_Request.Attribute_Value, l) == 0) {
                            g1 = p->handle;
                        } else if (g1 > 0) {
                            handle_s[j] = g1;
                            handle_e[j] = g2;
                            j++;
                            g1 = 0;
                        }
                    }
                    g2 = p->handle;
                }
            }
            if (g1 > 0) {
                handle_s[j] = g1;
                handle_e[j] = g2;
                j++;
            }
            if (j > 0) {
                out->Attribute_Opcode = ATT_Opcode_Find_By_Type_Value_Response;
                for(i = 0; i < j; i++) {
                    ATT_STORE16(out->Attribute_Parameters.Find_By_Type_Value_Response.Handles_Information_List[i].Found_Attribute_Handle,   handle_s[i]);
                    ATT_STORE16(out->Attribute_Parameters.Find_By_Type_Value_Response.Handles_Information_List[i].Group_End_Handle,         handle_e[i]);
                }
                outLen = ATT_PDU_Format_HeadSize + sizeof(out->Attribute_Parameters.Find_By_Type_Value_Response.Handles_Information_List) * j;
            } else {
                outLen = bt_attServerError(out, ATT_Error_Code_Attribute_Not_Found, ATT_Opcode_Find_By_Type_Value_Request, s);
            }
            break;
        }
        case ATT_Opcode_Read_By_Type_Request:
        {
            struct bt_attDbEntry    *p;
            unsigned short          s;
            unsigned short          e;
            uint8_t                 t[sizeof(p->types)];

            s = ATT_UINT16(in->Attribute_Parameters.Read_By_Type_Request.Starting_Handle);
            e = ATT_UINT16(in->Attribute_Parameters.Read_By_Type_Request.Ending_Handle);
            if (inLen < (ATT_PDU_Format_HeadSize + sizeof(in->Attribute_Parameters.Read_By_Type_Request))) {
                ATT_STOREUUID16TO128(t, in->Attribute_Parameters.Read_By_Type_Request.Attribute_Type);
            } else {
                memcpy(t, in->Attribute_Parameters.Read_By_Type_Request.Attribute_Type, sizeof(t));
            }
            if ((p = bt_attDbFind(s, e, t, NULL, 0)) != NULL) {
//              if (p->properties & ATT_DB_PROPERTIES_READ) {                                                                       // DEL 2017.01.18
                if ((p->properties & ATT_DB_PROPERTIES_AUTHENTICATION) && !(ch->link->authentication)) {                            // ADD 2017.01.18
                    outLen = bt_attServerError(out, ATT_Error_Code_Insufficient_Authentication, ATT_Opcode_Read_By_Type_Request, s);// ADD 2017.01.18
                } else if ((p->properties & ATT_DB_PROPERTIES_ENCRYPTION) && !(ch->link->encryption)) {                             // ADD 2017.01.18
                    outLen = bt_attServerError(out, ATT_Error_Code_Insufficient_Encryption, ATT_Opcode_Read_By_Type_Request, s);    // ADD 2017.01.18
                } else if (p->properties & ATT_DB_PROPERTIES_READ) {                                                                // ADD 2017.01.18
                    out->Attribute_Opcode                                  = ATT_Opcode_Read_By_Type_Response;
                    out->Attribute_Parameters.Read_By_Type_Response.Length = sizeof(out->Attribute_Parameters.Read_By_Type_Response.Attribute_Data_List) + p->valueLen;
                    ATT_STORE16(out->Attribute_Parameters.Read_By_Type_Response.Attribute_Data_List.Attribute_Handle, p->handle);
                    memcpy(out->Attribute_Parameters.Read_By_Type_Response.Attribute_Data_List.Attribute_Value, p->value, p->valueLen);
                    outLen = ATT_PDU_Format_HeadSize + sizeof(out->Attribute_Parameters.Read_By_Type_Response) + p->valueLen;
                } else {
                    outLen = bt_attServerError(out, ATT_Error_Code_Read_Not_Permitted, ATT_Opcode_Read_By_Type_Request, s);
                }
            } else {
                outLen = bt_attServerError(out, ATT_Error_Code_Attribute_Not_Found, ATT_Opcode_Read_By_Type_Request, s);
            }
            break;
        }
        case ATT_Opcode_Read_Request:
        {
            unsigned short          h;
            struct bt_attDbEntry    *p;

            h = ATT_UINT16(in->Attribute_Parameters.Read_Request.Attribute_Handle);
            if ((p = bt_attDbFind(h, h, NULL, NULL, 0)) != NULL) {
//              if (p->properties & ATT_DB_PROPERTIES_READ) {                                                               // DEL 2017.01.18
                if ((p->properties & ATT_DB_PROPERTIES_AUTHENTICATION) && !(ch->link->authentication)) {                    // ADD 2017.01.18
                    outLen = bt_attServerError(out, ATT_Error_Code_Insufficient_Authentication, ATT_Opcode_Read_Request, h);// ADD 2017.01.18
                } else if ((p->properties & ATT_DB_PROPERTIES_ENCRYPTION) && !(ch->link->encryption)) {                     // ADD 2017.01.18
                    outLen = bt_attServerError(out, ATT_Error_Code_Insufficient_Encryption, ATT_Opcode_Read_Request, h);    // ADD 2017.01.18
                } else if (p->properties & ATT_DB_PROPERTIES_READ) {                                                        // ADD 2017.01.18
                    out->Attribute_Opcode = ATT_Opcode_Read_Response;
                    memcpy(out->Attribute_Parameters.Read_Response.Attribute_Value, p->value, p->valueLen);
                    outLen = ATT_PDU_Format_HeadSize + sizeof(out->Attribute_Parameters.Read_Response) + p->valueLen;
                } else {
                    outLen = bt_attServerError(out, ATT_Error_Code_Read_Not_Permitted, ATT_Opcode_Read_Request, h);
                }
            } else {
                outLen = bt_attServerError(out, ATT_Error_Code_Attribute_Not_Found, ATT_Opcode_Read_Request, h);
            }
            break;
        }
        case ATT_Opcode_Read_Blob_Request:
        {
            unsigned short  h;
            size_t          o;
            struct bt_attDbEntry    *p;

            h = ATT_UINT16(in->Attribute_Parameters.Read_Blob_Request.Attribute_Handle);
            o = ATT_UINT16(in->Attribute_Parameters.Read_Blob_Request.Value_Offset);
            if ((p = bt_attDbFind(h, h, NULL, NULL, 0)) != NULL) {
//              if (p->properties & ATT_DB_PROPERTIES_READ) {                                                                       // DEL 2017.01.18
                if ((p->properties & ATT_DB_PROPERTIES_AUTHENTICATION) && !(ch->link->authentication)) {                            // ADD 2017.01.18
                    outLen = bt_attServerError(out, ATT_Error_Code_Insufficient_Authentication, ATT_Opcode_Read_Blob_Request, h);   // ADD 2017.01.18
                } else if ((p->properties & ATT_DB_PROPERTIES_ENCRYPTION) && !(ch->link->encryption)) {                             // ADD 2017.01.18
                    outLen = bt_attServerError(out, ATT_Error_Code_Insufficient_Encryption, ATT_Opcode_Read_Blob_Request, h);       // ADD 2017.01.18
                } else if (p->properties & ATT_DB_PROPERTIES_READ) {                                                                // ADD 2017.01.18
                    if (p->valueLen > o) {
                        size_t  l;

                        l = p->valueLen - o;
                        out->Attribute_Opcode = ATT_Opcode_Read_Response;
                        memcpy(out->Attribute_Parameters.Read_Blob_Response.Part_Attribute_Value, &(p->value[o]), l);
                        outLen = ATT_PDU_Format_HeadSize + sizeof(out->Attribute_Parameters.Read_Blob_Response) + l;
                    } else {
                        outLen = bt_attServerError(out, ATT_Error_Code_Invalid_Offset, ATT_Opcode_Read_Blob_Request, h);
                    }
                } else {
                    outLen = bt_attServerError(out, ATT_Error_Code_Read_Not_Permitted, ATT_Opcode_Read_Blob_Request, h);
                }
            } else {
                outLen = bt_attServerError(out, ATT_Error_Code_Attribute_Not_Found, ATT_Opcode_Read_Blob_Request, h);
            }
            break;
        }
//      case ATT_Opcode_Read_Multiple_Request:      // No support
        case ATT_Opcode_Read_by_Group_Type_Request:
        {
            struct bt_attDbEntry    *p;
            unsigned short          s;
            unsigned short          e;
            int                     i;
            uint8_t                 t[sizeof(p->types)];
            struct bt_attDbEntry    *found;
            struct bt_attDbEntry    *last;

            s = ATT_UINT16(in->Attribute_Parameters.Read_by_Group_Type_Request.Starting_Handle);
            e = ATT_UINT16(in->Attribute_Parameters.Read_by_Group_Type_Request.Ending_Handle);
            if (inLen < (sizeof(in->Attribute_Parameters.Read_by_Group_Type_Request) + ATT_PDU_Format_HeadSize)) {
                ATT_STOREUUID16TO128(t, in->Attribute_Parameters.Read_by_Group_Type_Request.Attribute_Group_Type);
            } else {
                memcpy(t, in->Attribute_Parameters.Read_by_Group_Type_Request.Attribute_Group_Type, sizeof(t));
            }
            found = NULL;
            last  = NULL;
            i = 0;
            while((p = bt_attDbGetNext(&i)) != NULL) {
                if ((p->handle >= s) && (p->handle <= e)) {
                    if (memcmp(t, p->types, sizeof(t)) == 0) {
                        if (found != NULL) {
                            break;
                        }
                        found = p;
                    }
                    last = p;
                }
            }
            if (found != NULL) {
//              if (found->properties & ATT_DB_PROPERTIES_READ) {                                                                           // DEL 2017.01.18
                if ((found->properties & ATT_DB_PROPERTIES_AUTHENTICATION) && !(ch->link->authentication)) {                                // ADD 2017.01.18
                    outLen = bt_attServerError(out, ATT_Error_Code_Insufficient_Authentication, ATT_Opcode_Read_by_Group_Type_Request, s);  // ADD 2017.01.18
                } else if ((found->properties & ATT_DB_PROPERTIES_ENCRYPTION) && !(ch->link->encryption)) {                                 // ADD 2017.01.18
                    outLen = bt_attServerError(out, ATT_Error_Code_Insufficient_Encryption, ATT_Opcode_Read_by_Group_Type_Request, s);      // ADD 2017.01.18
                } else if (found->properties & ATT_DB_PROPERTIES_READ) {                                                                    // ADD 2017.01.18
                    out->Attribute_Opcode = ATT_Opcode_Read_by_Group_Type_Response;
                    out->Attribute_Parameters.Read_by_Group_Type_Response.Length = sizeof(out->Attribute_Parameters.Read_by_Group_Type_Response.Attribute_Data_List) + found->valueLen;
                    ATT_STORE16(out->Attribute_Parameters.Read_by_Group_Type_Response.Attribute_Data_List.Attribute_Handle, found->handle);
                    ATT_STORE16(out->Attribute_Parameters.Read_by_Group_Type_Response.Attribute_Data_List.End_Group_Handle, last->handle);
                    memcpy(out->Attribute_Parameters.Read_by_Group_Type_Response.Attribute_Data_List.Attribute_Value, found->value, found->valueLen);
                    outLen = ATT_PDU_Format_HeadSize + sizeof(out->Attribute_Parameters.Read_by_Group_Type_Response) + found->valueLen;
                } else {
                    outLen = bt_attServerError(out, ATT_Error_Code_Read_Not_Permitted, ATT_Opcode_Read_by_Group_Type_Request, s);
                }
            } else {
                outLen = bt_attServerError(out, ATT_Error_Code_Attribute_Not_Found, ATT_Opcode_Read_by_Group_Type_Request, s);
            }
            break;
        }
        case ATT_Opcode_Write_Request:
        {
            struct bt_attDbEntry    *p;
            unsigned short          h;
            size_t                  l;

            h = ATT_UINT16(in->Attribute_Parameters.Write_Request.Attribute_Handle);
            l = inLen - ATT_PDU_Format_HeadSize - sizeof(out->Attribute_Parameters.Write_Request);
            if ((p = bt_attDbFind(h, h, NULL, NULL, 0)) != NULL) {
//              if (p->properties & ATT_DB_PROPERTIES_WRITE) {                                                                  // DEL 2017.01.18
                if ((p->properties & ATT_DB_PROPERTIES_AUTHENTICATION)    && !(ch->link->authentication)) {                     // ADD 2017.01.18
                    outLen = bt_attServerError(out, ATT_Error_Code_Insufficient_Authentication, ATT_Opcode_Write_Request, h);   // ADD 2017.01.18
                } else if ((p->properties & ATT_DB_PROPERTIES_ENCRYPTION) && !(ch->link->encryption)) {                         // ADD 2017.01.18
                    outLen = bt_attServerError(out, ATT_Error_Code_Insufficient_Encryption,     ATT_Opcode_Write_Request, h);   // ADD 2017.01.18
                } else if (p->properties & ATT_DB_PROPERTIES_WRITE) {                                                           // ADD 2017.01.18
                    bt_attDbUpdate(h, in->Attribute_Parameters.Write_Command.Attribute_Value, l);
                    out->Attribute_Opcode = ATT_Opcode_Write_Response;
                    outLen = ATT_PDU_Format_HeadSize;
                } else {
                    outLen = bt_attServerError(out, ATT_Error_Code_Write_Not_Permitted, ATT_Opcode_Write_Request, h);
                }
            } else {
                outLen = bt_attServerError(out, ATT_Error_Code_Attribute_Not_Found, ATT_Opcode_Write_Request, h);
            }
            break;
        }
        case ATT_Opcode_Write_Command:
        case ATT_Opcode_Signed_Write_Command:
        {
            struct bt_attDbEntry    *p;
            unsigned short          h;
            size_t                  l;

            h = ATT_UINT16(in->Attribute_Parameters.Write_Command.Attribute_Handle);
            l = inLen - ATT_PDU_Format_HeadSize - sizeof(out->Attribute_Parameters.Write_Command);
            if ((p = bt_attDbFind(h, h, NULL, NULL, 0)) != NULL) {
//              if (p->properties & ATT_DB_PROPERTIES_WRITE) {                                              // DEL 2017.01.18
                if ((!(p->properties & ATT_DB_PROPERTIES_AUTHENTICATION) || (ch->link->authentication)) &&  // ADD 2017.01.18
                    (!(p->properties & ATT_DB_PROPERTIES_ENCRYPTION)     || (ch->link->encryption    )) &&  // ADD 2017.01.18
                      (p->properties & ATT_DB_PROPERTIES_WRITE)) {                                          // ADD 2017.01.18
                    bt_attDbUpdate(h, in->Attribute_Parameters.Write_Command.Attribute_Value, l);
                }
            }
            outLen = 0;
            break;
        }
//      case ATT_Opcode_Prepare_Write_Request:      // No support
//      case ATT_Opcode_Execute_Write_Request:      // No support
        case ATT_Opcode_Handle_Value_Confirmation:
            bt_att_serverNotificationQueueDelete();
            outLen = 0;
            break;
        default:
            outLen = bt_attServerError(out, ATT_Error_Code_Request_Not_Supported, in->Attribute_Opcode, 0);
            break;
        }

    // Idle
    } else {
        unsigned short          h;
        struct bt_attDbEntry    *p;

        // Notification and Indication
        if ((h = bt_att_serverNotificationQueueGet()) != 0) {
            if ((p = bt_attDbFind(h, h, NULL, NULL, 0)) != NULL) {
                if (p->properties & ATT_DB_PROPERTIES_NOTIFY) {
                    DEBUGP("bt_sttServer: notify=0x%04x\n", h);
                    out->Attribute_Opcode = ATT_Opcode_Handle_Value_Notification;
                    ATT_STORE16(out->Attribute_Parameters.Handle_Value_Notification.Attribute_Handle, p->handle);
                    memcpy(out->Attribute_Parameters.Handle_Value_Notification.Attribute_Value, p->value, p->valueLen);
                    outLen = ATT_PDU_Format_HeadSize + sizeof(out->Attribute_Parameters.Handle_Value_Notification) + p->valueLen;
                    bt_att_serverNotificationQueueDelete();
                } else if (p->properties & ATT_DB_PROPERTIES_INDICATE) {
                    DEBUGP("bt_sttServer: indicate=0x%04x\n", h);
                    out->Attribute_Opcode = ATT_Opcode_Handle_Value_Indication;
                    ATT_STORE16(out->Attribute_Parameters.Handle_Value_Indication.Attribute_Handle, p->handle);
                    memcpy(out->Attribute_Parameters.Handle_Value_Indication.Attribute_Value, p->value, p->valueLen);
                    outLen = ATT_PDU_Format_HeadSize + sizeof(out->Attribute_Parameters.Handle_Value_Indication) + p->valueLen;
                } else {
                    bt_att_serverNotificationQueueDelete();
                }
            } else {
                bt_att_serverNotificationQueueDelete();
            }
        }
    }
    return(outLen);
}

#ifdef DEL                              // DEL START 2013.11.04
//
// ATT DB manager Callback
//
void bt_attDbNotification(unsigned short handle, unsigned char *value, size_t valueLen){
    bt_att_serverNotificationQueueAdd(handle);
    return;
}
#endif                                  // DEL END 2013.11.04


//                                      // ADD START 2017.01.18
// User own code (default)
//
void _bt_attDbNotification(unsigned short handle, void *value, size_t valueLen) {
    bt_att_serverNotificationQueueAdd(handle);
    return;
}
void bt_attDbNotification(unsigned short handle, void *value, size_t valueLen)
        __attribute__ ((weak, alias ("_bt_attDbNotification")));
                                        // ADD END 2017.01.18
