* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*/
* Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*/
#include <KernelExport.h>
#include <string.h>
#include <NetBufferUtilities.h>
#include <l2cap.h>
#include "l2cap_internal.h"
#include "l2cap_signal.h"
#include "l2cap_command.h"
#include "l2cap_upper.h"
#include "l2cap_lower.h"
#include <btDebug.h>
#include <bluetooth/HCI/btHCI_command.h>
typedef enum _option_status {
OPTION_NOT_PRESENT = 0,
OPTION_PRESENT = 1,
HEADER_TOO_SHORT = -1,
BAD_OPTION_LENGTH = -2,
OPTION_UNKNOWN = -3
} option_status;
l2cap_flow_t default_qos = {
0x0,
HCI_SERVICE_TYPE_BEST_EFFORT,
0xffffffff,
0xffffffff,
0x00000000,
0xffffffff,
0xffffffff
};
static status_t
l2cap_process_con_req(HciConnection* conn, uint8 ident, net_buffer* buffer);
static status_t
l2cap_process_con_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer);
static status_t
l2cap_process_cfg_req(HciConnection* conn, uint8 ident, net_buffer* buffer);
static status_t
l2cap_process_cfg_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer);
static status_t
l2cap_process_discon_req(HciConnection* conn, uint8 ident, net_buffer* buffer);
static status_t
l2cap_process_discon_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer);
static status_t
l2cap_process_echo_req(HciConnection* conn, uint8 ident, net_buffer* buffer);
static status_t
l2cap_process_echo_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer);
static status_t
l2cap_process_info_req(HciConnection* conn, uint8 ident, net_buffer* buffer);
static status_t
l2cap_process_info_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer);
static status_t
l2cap_process_cmd_rej(HciConnection* conn, uint8 ident, net_buffer* buffer);
* Process L2CAP signaling command. We already know that destination channel ID
* is 0x1 that means we have received signaling command from peer's L2CAP layer.
* So get command header, decode and process it.
*
* XXX do we need to check signaling MTU here?
*/
status_t
l2cap_process_signal_cmd(HciConnection* conn, net_buffer* buffer)
{
net_buffer* m = buffer;
TRACE("%s: Signal size=%" B_PRIu32 "\n", __func__, buffer->size);
while (m != NULL) {
if (buffer->size < sizeof(l2cap_cmd_hdr_t)) {
TRACE("%s: small L2CAP signaling command len=%" B_PRIu32 "\n",
__func__, buffer->size);
gBufferModule->free(buffer);
return EMSGSIZE;
}
NetBufferHeaderReader<l2cap_cmd_hdr_t> commandHeader(buffer);
status_t status = commandHeader.Status();
if (status < B_OK) {
return ENOBUFS;
}
uint8 processingCode = commandHeader->code;
uint8 processingIdent = commandHeader->ident;
uint16 processingLength = le16toh(commandHeader->length);
if (buffer->size < processingLength) {
ERROR("%s: invalid L2CAP signaling command, code=%#x, "
"ident=%d, length=%d, buffer size=%" B_PRIu32 "\n", __func__,
processingCode, processingIdent, processingLength,
buffer->size);
gBufferModule->free(buffer);
return (EMSGSIZE);
}
commandHeader.Remove();
switch (processingCode) {
case L2CAP_CMD_REJ:
l2cap_process_cmd_rej(conn, processingIdent, buffer);
break;
case L2CAP_CON_REQ:
l2cap_process_con_req(conn, processingIdent, buffer);
break;
case L2CAP_CON_RSP:
l2cap_process_con_rsp(conn, processingIdent, buffer);
break;
case L2CAP_CFG_REQ:
l2cap_process_cfg_req(conn, processingIdent, buffer);
break;
case L2CAP_CFG_RSP:
l2cap_process_cfg_rsp(conn, processingIdent, buffer);
break;
case L2CAP_DISCON_REQ:
l2cap_process_discon_req(conn, processingIdent, buffer);
break;
case L2CAP_DISCON_RSP:
l2cap_process_discon_rsp(conn, processingIdent, buffer);
break;
case L2CAP_ECHO_REQ:
l2cap_process_echo_req(conn, processingIdent, buffer);
break;
case L2CAP_ECHO_RSP:
l2cap_process_echo_rsp(conn, processingIdent, buffer);
break;
case L2CAP_INFO_REQ:
l2cap_process_info_req(conn, processingIdent, buffer);
break;
case L2CAP_INFO_RSP:
l2cap_process_info_rsp(conn, processingIdent, buffer);
break;
default:
ERROR("%s: unknown L2CAP signaling command, "
"code=%#x, ident=%d\n", __func__, processingCode,
processingIdent);
gBufferModule->free(m);
break;
}
if (m->size == 0) {
gBufferModule->free(m);
m = NULL;
}
}
return B_OK;
}
#if 0
#pragma mark - Processing Incoming signals
#endif
static status_t
l2cap_process_con_req(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capChannel* channel;
uint16 dcid, psm;
NetBufferHeaderReader<l2cap_con_req_cp> command(buffer);
status_t status = command.Status();
if (status < B_OK) {
return ENOBUFS;
}
psm = le16toh(command->psm);
dcid = le16toh(command->scid);
command.Remove();
channel = btCoreData->AddChannel(conn, psm );
if (channel == NULL) {
TRACE("%s: No resources to create channel\n", __func__);
return (send_l2cap_con_rej(conn, ident, 0, dcid, L2CAP_NO_RESOURCES));
} else {
TRACE("%s: New channel created scid=%d\n", __func__, channel->scid);
}
channel->dcid = dcid;
channel->ident = ident;
status_t indicationStatus = l2cap_l2ca_con_ind(channel);
if ( indicationStatus == B_OK ) {
} else if (indicationStatus == B_NO_MEMORY) {
btCoreData->RemoveChannel(conn, channel->scid);
} else {
send_l2cap_con_rej(conn, ident, channel->scid, dcid, L2CAP_PSM_NOT_SUPPORTED);
btCoreData->RemoveChannel(conn, channel->scid);
}
return (indicationStatus);
}
static status_t
l2cap_process_con_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capFrame* cmd = NULL;
uint16 scid, dcid, result, status;
status_t error = 0;
NetBufferHeaderReader<l2cap_con_rsp_cp> command(buffer);
if (command.Status() < B_OK) {
return ENOBUFS;
}
dcid = le16toh(command->dcid);
scid = le16toh(command->scid);
result = le16toh(command->result);
status = le16toh(command->status);
command.Remove();
TRACE("%s: dcid=%d scid=%d result=%d status%d\n", __func__, dcid, scid,
result, status);
cmd = btCoreData->SignalByIdent(conn, ident);
if (cmd == NULL) {
ERROR("%s: unexpected L2CAP_ConnectRsp command. ident=%d, "
"con_handle=%d\n", __func__, ident, conn->handle);
return ENOENT;
}
if (cmd->channel->state != L2CAP_CHAN_W4_L2CAP_CON_RSP) {
ERROR("%s: unexpected L2CAP_ConnectRsp. Invalid channel state, "
"cid=%d, state=%d\n", __func__, scid, cmd->channel->state);
goto reject;
}
if (cmd->channel->scid != scid) {
ERROR("%s: unexpected L2CAP_ConnectRsp. Channel IDs do not match, "
"scid=%d(%d)\n", __func__, cmd->channel->scid, scid);
goto reject;
}
* Looks good. We got confirmation from our peer. Now process
* it. First disable RTX timer. Then check the result and send
* notification to the upper layer. If command timeout already
* happened then ignore response.
*/
if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0)
return error;
if (result == L2CAP_PENDING) {
* Our peer wants more time to complete connection. We shall
* start ERTX timer and wait. Keep command in the list.
*/
cmd->channel->dcid = dcid;
btCoreData->TimeoutSignal(cmd, bluetooth_l2cap_ertx_timeout);
} else {
if (result == L2CAP_SUCCESS) {
* Channel is open. Complete command and move to CONFIG
* state. Since we have sent positive confirmation we
* expect to receive L2CA_Config request from the upper
* layer protocol.
*/
cmd->channel->dcid = dcid;
cmd->channel->state = L2CAP_CHAN_CONFIG;
}
error = l2cap_con_rsp_ind(conn, cmd->channel);
if (error != 0 || result != L2CAP_SUCCESS) {
ERROR("%s: failed to open L2CAP channel, result=%d, status=%d\n",
__func__, result, status);
btCoreData->RemoveChannel(conn, cmd->channel->scid);
}
btCoreData->AcknowledgeSignal(cmd);
}
return error;
reject:
send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, scid, dcid);
return 0;
}
static option_status
getNextSignalOption(net_buffer* nbuf, size_t* off, l2cap_cfg_opt_t* hdr,
l2cap_cfg_opt_val_t* val)
{
int hint;
size_t len = nbuf->size - (*off);
if (len == 0)
return OPTION_NOT_PRESENT;
if (len < 0 || len < sizeof(*hdr))
return HEADER_TOO_SHORT;
gBufferModule->read(nbuf, *off, hdr, sizeof(*hdr));
*off += sizeof(*hdr);
len -= sizeof(*hdr);
hint = L2CAP_OPT_HINT(hdr->type);
hdr->type &= L2CAP_OPT_HINT_MASK;
switch (hdr->type) {
case L2CAP_OPT_MTU:
if (hdr->length != L2CAP_OPT_MTU_SIZE || len < hdr->length)
return BAD_OPTION_LENGTH;
gBufferModule->read(nbuf, *off, val, L2CAP_OPT_MTU_SIZE);
val->mtu = le16toh(val->mtu);
*off += L2CAP_OPT_MTU_SIZE;
TRACE("%s: mtu %d specified\n", __func__, val->mtu);
break;
case L2CAP_OPT_FLUSH_TIMO:
if (hdr->length != L2CAP_OPT_FLUSH_TIMO_SIZE || len < hdr->length)
return BAD_OPTION_LENGTH;
gBufferModule->read(nbuf, *off, val, L2CAP_OPT_FLUSH_TIMO_SIZE);
val->flush_timo = le16toh(val->flush_timo);
TRACE("%s: flush specified\n", __func__);
*off += L2CAP_OPT_FLUSH_TIMO_SIZE;
break;
case L2CAP_OPT_QOS:
if (hdr->length != L2CAP_OPT_QOS_SIZE || len < hdr->length)
return BAD_OPTION_LENGTH;
gBufferModule->read(nbuf, *off, val, L2CAP_OPT_QOS_SIZE);
val->flow.token_rate = le32toh(val->flow.token_rate);
val->flow.token_bucket_size = le32toh(val->flow.token_bucket_size);
val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth);
val->flow.latency = le32toh(val->flow.latency);
val->flow.delay_variation = le32toh(val->flow.delay_variation);
*off += L2CAP_OPT_QOS_SIZE;
TRACE("%s: qos specified\n", __func__);
break;
default:
if (hint)
*off += hdr->length;
else
return OPTION_UNKNOWN;
break;
}
return OPTION_PRESENT;
}
static status_t
l2cap_process_cfg_req(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capChannel* channel = NULL;
uint16 dcid;
uint16 respond;
uint16 result;
l2cap_cfg_opt_t hdr;
l2cap_cfg_opt_val_t val;
size_t off;
status_t error = 0;
NetBufferHeaderReader<l2cap_cfg_req_cp> command(buffer);
status_t status = command.Status();
if (status < B_OK) {
return ENOBUFS;
}
dcid = le16toh(command->dcid);
respond = L2CAP_OPT_CFLAG(le16toh(command->flags));
command.Remove();
channel = btCoreData->ChannelBySourceID(conn, dcid);
if (channel == NULL) {
ERROR("%s: unexpected L2CAP_ConfigReq command. "
"Channel does not exist, cid=%d\n", __func__, dcid);
goto reject;
}
if (channel->state != L2CAP_CHAN_CONFIG
&& channel->state != L2CAP_CHAN_OPEN) {
ERROR("%s: unexpected L2CAP_ConfigReq. Invalid channel state, "
"cid=%d, state=%d\n", __func__, dcid, channel->state);
goto reject;
}
if (channel->state == L2CAP_CHAN_OPEN) {
channel->cfgState = 0;
channel->state = L2CAP_CHAN_CONFIG;
}
for (result = 0, off = 0; ; ) {
error = getNextSignalOption(buffer, &off, &hdr, &val);
if (error == OPTION_NOT_PRESENT) {
break;
} else if (error > 0) {
switch (hdr.type) {
case L2CAP_OPT_MTU:
channel->configuration->omtu = val.mtu;
break;
case L2CAP_OPT_FLUSH_TIMO:
channel->configuration->flush_timo = val.flush_timo;
break;
case L2CAP_OPT_QOS:
memcpy(&val.flow, &channel->configuration->iflow,
sizeof(channel->configuration->iflow));
break;
default:
break;
}
} else {
respond = 1;
if (error == OPTION_UNKNOWN) {
result = L2CAP_UNKNOWN_OPTION;
} else {
gBufferModule->free(buffer);
result = L2CAP_REJECT;
}
break;
}
}
TRACE("%s: Pulled %ld of configuration fields respond=%d "
"remaining=%" B_PRIu32 "\n", __func__, off, respond, buffer->size);
gBufferModule->remove_header(buffer, off);
* Now check and see if we have to respond. If everything was OK then
* respond contain "C flag" and (if set) we will respond with empty
* packet and will wait for more options.
*
* Other case is that we did not like peer's options and will respond
* with L2CAP_Config response command with Reject error code.
*
* When "respond == 0" than we have received all options and we will
* sent L2CA_ConfigInd event to the upper layer protocol.
*/
if (respond) {
error = send_l2cap_cfg_rsp(conn, ident, channel->dcid, result, buffer);
if (error != 0) {
btCoreData->RemoveChannel(conn, channel->scid);
}
} else {
channel->cfgState |= L2CAP_CFG_IN;
channel->ident = ident;
error = l2cap_cfg_req_ind(channel);
if (error != 0)
btCoreData->RemoveChannel(conn, channel->scid);
}
return error;
reject:
gBufferModule->free(buffer);
send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, 0, dcid);
return B_OK;
}
static status_t
l2cap_process_cfg_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capFrame* cmd = NULL;
uint16 scid, cflag, result;
l2cap_cfg_opt_t hdr;
l2cap_cfg_opt_val_t val;
size_t off;
status_t error = 0;
NetBufferHeaderReader<l2cap_cfg_rsp_cp> command(buffer);
status_t status = command.Status();
if (status < B_OK) {
return ENOBUFS;
}
scid = le16toh(command->scid);
cflag = L2CAP_OPT_CFLAG(le16toh(command->flags));
result = le16toh(command->result);
command.Remove();
TRACE("%s: scid=%d cflag=%d result=%d\n", __func__, scid, cflag, result);
cmd = btCoreData->SignalByIdent(conn, ident);
if (cmd == NULL) {
ERROR("%s: unexpected L2CAP_ConfigRsp command. "
"ident=%d, con_handle=%d\n", __func__, ident, conn->handle);
gBufferModule->free(buffer);
return ENOENT;
}
if (cmd->channel->scid != scid) {
ERROR("%s: unexpected L2CAP_ConfigRsp.Channel ID does not match, "
"scid=%d(%d)\n", __func__, cmd->channel->scid, scid);
goto reject;
}
if (cmd->channel->state != L2CAP_CHAN_CONFIG) {
ERROR("%s: unexpected L2CAP_ConfigRsp. Invalid channel state, scid=%d, "
"state=%d\n", __func__, cmd->channel->scid, cmd->channel->state);
goto reject;
}
* Looks like it is our response, so process it. First parse options,
* then verify C flag. If it is set then we shall expect more
* configuration options from the peer and we will wait. Otherwise we
* have received all options and we will send L2CA_ConfigRsp event to
* the upper layer protocol. If command timeout already happened then
* ignore response.
*/
if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0) {
gBufferModule->free(buffer);
return error;
}
for (off = 0; ; ) {
error = getNextSignalOption(buffer, &off, &hdr, &val);
if (error == OPTION_NOT_PRESENT)
break;
else if (error > 0) {
switch (hdr.type) {
case L2CAP_OPT_MTU:
cmd->channel->configuration->imtu = val.mtu;
break;
case L2CAP_OPT_FLUSH_TIMO:
cmd->channel->configuration->flush_timo = val.flush_timo;
break;
case L2CAP_OPT_QOS:
memcpy(&val.flow, &cmd->channel->configuration->oflow,
sizeof(cmd->channel->configuration->oflow));
break;
default:
break;
}
} else {
* XXX FIXME What to do here?
*
* This is really BAD :( options packet was broken, or
* peer sent us option that we did not understand. Let
* upper layer know and do not wait for more options.
*/
ERROR("%s: fail parsing configuration options\n", __func__);
result = L2CAP_UNKNOWN;
cflag = 0;
break;
}
}
if (cflag)
btCoreData->TimeoutSignal(cmd, bluetooth_l2cap_rtx_timeout);
else {
error = l2cap_cfg_rsp_ind(cmd->channel );
if (error != 0) {
* XXX FIXME what to do here? we were not able to send
* response to the upper layer protocol, so for now
* just close the channel. Send L2CAP_Disconnect to
* remote peer?
*/
ERROR("%s: failed to send L2CA_Config response\n", __func__);
btCoreData->RemoveChannel(conn, cmd->channel->scid);
}
btCoreData->AcknowledgeSignal(cmd);
}
return error;
reject:
gBufferModule->free(buffer);
send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, scid, 0);
return B_OK;
}
static status_t
l2cap_process_discon_req(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capChannel* channel = NULL;
L2capFrame* cmd = NULL;
net_buffer* buff = NULL;
uint16 scid;
uint16 dcid;
NetBufferHeaderReader<l2cap_discon_req_cp> command(buffer);
status_t status = command.Status();
if (status < B_OK) {
return ENOBUFS;
}
dcid = le16toh(command->dcid);
scid = le16toh(command->scid);
command.Remove();
channel = btCoreData->ChannelBySourceID(conn, dcid);
if (channel == NULL) {
ERROR("%s: unexpected L2CAP_DisconnectReq message. "
"Channel does not exist, cid=%x\n", __func__, dcid);
goto reject;
}
if (channel->state != L2CAP_CHAN_OPEN
&& channel->state != L2CAP_CHAN_CONFIG
&& channel->state != L2CAP_CHAN_W4_L2CAP_DISCON_RSP) {
ERROR("%s: unexpected L2CAP_DisconnectReq. Invalid channel state, "
"cid=%d, state=%d\n", __func__, dcid, channel->state);
goto reject;
}
if (channel->dcid != scid || channel->scid != dcid) {
ERROR("%s: unexpected L2CAP_DisconnectReq. Channel IDs does not match, "
"channel: scid=%d, dcid=%d, request: scid=%d, dcid=%d\n", __func__,
channel->scid, channel->dcid, scid, dcid);
goto reject;
}
* Looks good, so notify upper layer protocol that channel is about
* to be disconnected and send L2CA_DisconnectInd message. Then respond
* with L2CAP_DisconnectRsp.
*/
if (channel->state != L2CAP_CHAN_W4_L2CAP_DISCON_RSP) {
l2cap_discon_req_ind(channel);
}
buff = l2cap_discon_rsp(ident, dcid, scid);
cmd = btCoreData->SpawnSignal(conn, channel, buff, ident, L2CAP_DISCON_RSP);
if (cmd == NULL)
return ENOMEM;
SchedConnectionPurgeThread(conn);
return B_OK;
reject:
send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, scid, dcid);
return B_OK;
}
static status_t
l2cap_process_discon_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capFrame* cmd = NULL;
int16 scid, dcid;
status_t error = 0;
NetBufferHeaderReader<l2cap_discon_rsp_cp> command(buffer);
status_t status = command.Status();
if (status < B_OK) {
return ENOBUFS;
}
dcid = le16toh(command->dcid);
scid = le16toh(command->scid);
command.Remove();
cmd = btCoreData->SignalByIdent(conn, ident);
if (cmd == NULL) {
ERROR("%s: unexpected L2CAP_DisconnectRsp command. ident=%d, "
"con_handle=%d\n", __func__, ident, conn->handle);
goto out;
}
if (cmd->channel->state != L2CAP_CHAN_W4_L2CA_DISCON_RSP) {
ERROR("%s: unexpected L2CAP_DisconnectRsp. Invalid state, cid=%d, "
"state=%d\n", __func__, scid, cmd->channel->state);
goto out;
}
if (cmd->channel->scid != scid || cmd->channel->dcid != dcid) {
ERROR("%s: unexpected L2CAP_DisconnectRsp. Channel IDs do not match, "
"scid=%d(%d), dcid=%d(%d)\n", __func__, cmd->channel->scid, scid,
cmd->channel->dcid, dcid);
goto out;
}
* Looks like we have successfuly disconnected channel, so notify
* upper layer. If command timeout already happened then ignore
* response.
*/
if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0)
goto out;
l2cap_discon_rsp_ind(cmd->channel);
btCoreData->RemoveChannel(conn, scid);
out:
return error;
}
static status_t
l2cap_process_echo_req(HciConnection *conn, uint8 ident, net_buffer *buffer)
{
L2capFrame* cmd = NULL;
cmd = btCoreData->SpawnSignal(conn, NULL, l2cap_echo_req(ident, NULL, 0),
ident, L2CAP_ECHO_RSP);
if (cmd == NULL) {
gBufferModule->free(buffer);
return ENOMEM;
}
SchedConnectionPurgeThread(conn);
return B_OK;
}
static status_t
l2cap_process_echo_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capFrame* cmd = NULL;
status_t error = 0;
cmd = btCoreData->SignalByIdent(conn, ident);
if (cmd != NULL) {
if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0) {
return error;
}
btCoreData->AcknowledgeSignal(cmd);
} else {
ERROR("%s: unexpected L2CAP_EchoRsp command. ident does not exist, "
"ident=%d\n", __func__, ident);
gBufferModule->free(buffer);
error = B_ERROR;
}
return error;
}
static status_t
l2cap_process_info_req(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capFrame* cmd = NULL;
net_buffer* buf = NULL;
uint16 type;
NetBufferHeaderReader<l2cap_info_req_cp> command(buffer);
status_t status = command.Status();
if (status < B_OK) {
return ENOBUFS;
}
type = le16toh(command->type);
command.Remove();
switch (type) {
case L2CAP_CONNLESS_MTU:
buf = l2cap_info_rsp(ident, L2CAP_CONNLESS_MTU, L2CAP_SUCCESS,
L2CAP_MTU_DEFAULT);
break;
default:
buf = l2cap_info_rsp(ident, type, L2CAP_NOT_SUPPORTED, 0);
break;
}
cmd = btCoreData->SpawnSignal(conn, NULL, buf, ident, L2CAP_INFO_RSP);
if (cmd == NULL)
return ENOMEM;
SchedConnectionPurgeThread(conn);
return B_OK;
}
static status_t
l2cap_process_info_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
l2cap_info_rsp_cp* cp = NULL;
L2capFrame* cmd = NULL;
status_t error = B_OK;
NetBufferHeaderReader<l2cap_info_rsp_cp> command(buffer);
status_t status = command.Status();
if (status < B_OK) {
return ENOBUFS;
}
command->type = le16toh(command->type);
command->result = le16toh(command->result);
command.Remove();
cmd = btCoreData->SignalByIdent(conn, ident);
if (cmd == NULL) {
ERROR("%s: unexpected L2CAP_InfoRsp command. Requested ident does not "
"exist, ident=%d\n", __func__, ident);
gBufferModule->free(buffer);
return ENOENT;
}
if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0) {
gBufferModule->free(buffer);
return error;
}
if (command->result == L2CAP_SUCCESS) {
switch (command->type) {
case L2CAP_CONNLESS_MTU:
#if 0
if (conn->rx_pkt->m_pkthdr.len == sizeof(uint16)) {
*mtod(conn->rx_pkt, uint16 *)
= le16toh(*mtod(conn->rx_pkt, uint16 *));
} else {
cp->result = L2CAP_UNKNOWN; XXX
ERROR("%s: invalid L2CAP_InfoRsp command. "
"Bad connectionless MTU parameter, len=%d\n", __func__,
conn->rx_pkt->m_pkthdr.len);
}
#endif
break;
default:
ERROR("%s: invalid L2CAP_InfoRsp command. "
"Unknown info type=%d\n", __func__, cp->type);
break;
}
}
btCoreData->AcknowledgeSignal(cmd);
return error;
}
static status_t
l2cap_process_cmd_rej(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capFrame* cmd = NULL;
NetBufferHeaderReader<l2cap_cmd_rej_cp> command(buffer);
status_t status = command.Status();
if (status < B_OK) {
return ENOBUFS;
}
command->reason = le16toh(command->reason);
TRACE("%s: reason=%d\n", __func__, command->reason);
command.Remove();
cmd = btCoreData->SignalByIdent(conn, ident);
if (cmd != NULL) {
if (btCoreData->UnTimeoutSignal(cmd) != 0) {
gBufferModule->free(buffer);
return ETIMEDOUT;
}
switch (cmd->code) {
case L2CAP_CON_REQ:
btCoreData->RemoveChannel(conn, cmd->channel->scid);
break;
case L2CAP_CFG_REQ:
break;
case L2CAP_DISCON_REQ:
btCoreData->RemoveChannel(conn, cmd->channel->scid);
break;
case L2CAP_ECHO_REQ:
break;
case L2CAP_INFO_REQ:
break;
default:
ERROR("%s: unexpected L2CAP_CommandRej. Unexpected opcode=%d\n",
__func__, cmd->code);
break;
}
btCoreData->AcknowledgeSignal(cmd);
} else {
ERROR("%s: unexpected L2CAP_CommandRej command. "
"Requested ident does not exist, ident=%d\n", __func__, ident);
}
return B_OK;
}
#if 0
#pragma mark - Queuing Outgoing signals
#endif
status_t
send_l2cap_reject(HciConnection* conn, uint8 ident, uint16 reason,
uint16 mtu, uint16 scid, uint16 dcid)
{
L2capFrame* cmd = NULL;
cmd = btCoreData->SpawnSignal(conn, NULL, l2cap_cmd_rej(ident, reason,
mtu, scid, dcid), ident, L2CAP_CMD_REJ);
if (cmd == NULL)
return ENOMEM;
SchedConnectionPurgeThread(conn);
return B_OK;
}
status_t
send_l2cap_con_rej(HciConnection* conn, uint8 ident, uint16 scid, uint16 dcid,
uint16 result)
{
L2capFrame* cmd = NULL;
cmd = btCoreData->SpawnSignal(conn, NULL,
l2cap_con_rsp(ident, scid, dcid, result, 0), ident, L2CAP_CON_RSP);
if (cmd == NULL)
return ENOMEM;
SchedConnectionPurgeThread(conn);
return B_OK;
}
status_t
send_l2cap_cfg_rsp(HciConnection* conn, uint8 ident, uint16 scid,
uint16 result, net_buffer* opt)
{
L2capFrame* cmd = NULL;
cmd = btCoreData->SpawnSignal(conn, NULL,
l2cap_cfg_rsp(ident, scid, 0, result, opt), ident, L2CAP_CFG_RSP);
if (cmd == NULL) {
gBufferModule->free(opt);
return ENOMEM;
}
SchedConnectionPurgeThread(conn);
return B_OK;
}