Implement a bit further the sockets interface

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33320 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Oliver Ruiz Dorantes
2009-09-27 19:07:24 +00:00
parent ae29b1c3c2
commit a58b3b3235
6 changed files with 207 additions and 114 deletions
@@ -1,9 +1,6 @@
/*
* 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
/*
* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "L2capEndpoint.h"
#include "l2cap_address.h"
@@ -15,6 +12,9 @@
#include <bluetooth/L2CAP/btL2CAP.h>
#define BT_DEBUG_THIS_MODULE
#define MODULE_NAME "l2cap"
#define SUBMODULE_NAME "Endpoint"
#define SUBMODULE_COLOR 32
#include <btDebug.h>
@@ -32,10 +32,12 @@ absolute_timeout(bigtime_t timeout)
L2capEndpoint::L2capEndpoint(net_socket* socket)
:
ProtocolSocket(socket),
fConfigurationSet(false),
fPeerEndpoint(NULL),
fAcceptSemaphore(-1)
fAcceptSemaphore(-1),
fChannel(NULL)
{
debugf("[%ld] %p->L2capEndpoint::L2capEndpoint()\n", find_thread(NULL), this);
debugf("[%ld] %p\n", find_thread(NULL), this);
/* Set MTU and flow control settings to defaults */
configuration.imtu = L2CAP_MTU_DEFAULT;
@@ -47,16 +49,13 @@ L2capEndpoint::L2capEndpoint(net_socket* socket)
configuration.flush_timo = L2CAP_FLUSH_TIMO_DEFAULT;
configuration.link_timo = L2CAP_LINK_TIMO_DEFAULT;
configurationSet = false;
gStackModule->init_fifo(&fReceivingFifo, "l2cap recvfifo", L2CAP_MTU_DEFAULT);
}
L2capEndpoint::~L2capEndpoint()
{
debugf("[%ld] %p->L2capEndpoint::~L2capEndpoint()\n", find_thread(NULL), this);
debugf("[%ld] %p\n", find_thread(NULL), this);
}
@@ -64,7 +63,7 @@ L2capEndpoint::~L2capEndpoint()
status_t
L2capEndpoint::Init()
{
debugf("[%ld] %p->L2capEndpoint::Init()\n", find_thread(NULL), this);
debugf("[%ld] %p\n", find_thread(NULL), this);
return B_OK;
}
@@ -73,7 +72,7 @@ L2capEndpoint::Init()
void
L2capEndpoint::Uninit()
{
debugf("[%ld] %p->L2capEndpoint::Uninit()\n", find_thread(NULL), this);
debugf("[%ld] %p\n", find_thread(NULL), this);
}
@@ -81,7 +80,7 @@ L2capEndpoint::Uninit()
status_t
L2capEndpoint::Open()
{
debugf("[%ld] %p->L2capEndpoint::Open()\n", find_thread(NULL), this);
debugf("[%ld] %p\n", find_thread(NULL), this);
status_t error = ProtocolSocket::Open();
if (error != B_OK)
@@ -94,7 +93,7 @@ L2capEndpoint::Open()
status_t
L2capEndpoint::Close()
{
debugf("[%ld] %p->L2capEndpoint::Close()\n", find_thread(NULL), this);
debugf("[%ld] %p\n", find_thread(NULL), this);
return B_OK;
}
@@ -103,7 +102,7 @@ L2capEndpoint::Close()
status_t
L2capEndpoint::Free()
{
debugf("[%ld] %p->L2capEndpoint::Free()\n", find_thread(NULL), this);
debugf("[%ld] %p\n", find_thread(NULL), this);
return B_OK;
}
@@ -137,7 +136,7 @@ L2capEndpoint::Bind(const struct sockaddr *_address)
memcpy(&socket->address, _address, sizeof(struct sockaddr_l2cap));
socket->address.ss_len = sizeof(struct sockaddr_l2cap);
fState = LISTEN;
fState = CLOSED;
return B_OK;
@@ -147,7 +146,7 @@ L2capEndpoint::Bind(const struct sockaddr *_address)
status_t
L2capEndpoint::Unbind()
{
debugf("[%ld] %p->L2capEndpoint::Unbind()\n", find_thread(NULL), this);
debugf("[%ld] %p\n", find_thread(NULL), this);
return B_OK;
}
@@ -156,12 +155,20 @@ L2capEndpoint::Unbind()
status_t
L2capEndpoint::Listen(int backlog)
{
if (fState != CLOSED)
debugf("[%ld] %p\n", find_thread(NULL), this);
if (fState != CLOSED) {
flowf("Invalid State\n");
return B_BAD_VALUE;
}
fAcceptSemaphore = create_sem(0, "tcp accept");
if (fAcceptSemaphore < B_OK)
fAcceptSemaphore = create_sem(0, "l2cap serv accept");
if (fAcceptSemaphore < B_OK) {
flowf("Semaphore could not be created\n");
return ENOBUFS;
}
gSocketModule->set_max_backlog(socket, backlog);
fState = LISTEN;
@@ -190,12 +197,42 @@ L2capEndpoint::Connect(const struct sockaddr *_address)
status_t
L2capEndpoint::Accept(net_socket **_acceptedSocket)
{
debugf("[%ld] %p->L2capEndpoint::Accept()\n", find_thread(NULL), this);
debugf("[%ld]\n", find_thread(NULL));
bigtime_t timeout = absolute_timeout(socket->receive.timeout);
// MutexLocker locker(fLock);
TOUCH(timeout);
return B_OK;
status_t status;
bigtime_t timeout = absolute_timeout(300*1000*1000);
do {
// locker.Unlock();
status = acquire_sem_etc(fAcceptSemaphore, 1, B_ABSOLUTE_TIMEOUT
| B_CAN_INTERRUPT, timeout);
if (status != B_OK)
return status;
// locker.Lock();
status = gSocketModule->dequeue_connected(socket, _acceptedSocket);
if (status != B_OK)
debugf("Could not dequeue socket %s\n", strerror(status));
//TODO: Establish relationship with the negotiated channel by the parent endpoint
((L2capEndpoint*)socket)->fChannel = fChannel;
// point parent
((L2capEndpoint*)socket)->fPeerEndpoint = this;
// unassign any channel for the parent endpoint
fChannel = NULL;
fState = LISTEN;
} while (status != B_OK);
return status;
}
@@ -203,7 +240,7 @@ ssize_t
L2capEndpoint::Send(const iovec *vecs, size_t vecCount,
ancillary_data_container *ancillaryData)
{
debugf("[%ld] %p->L2capEndpoint::Send(%p, %ld, %p)\n", find_thread(NULL),
debugf("[%ld] %p Send(%p, %ld, %p)\n", find_thread(NULL),
this, vecs, vecCount, ancillaryData);
return B_OK;
@@ -215,7 +252,7 @@ L2capEndpoint::Receive(const iovec *vecs, size_t vecCount,
ancillary_data_container **_ancillaryData, struct sockaddr *_address,
socklen_t *_addressLength)
{
debugf("[%ld] %p->L2capEndpoint::Receive(%p, %ld)\n", find_thread(NULL),
debugf("[%ld] %p Receive(%p, %ld)\n", find_thread(NULL),
this, vecs, vecCount);
@@ -227,15 +264,15 @@ ssize_t
L2capEndpoint::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer)
{
return gStackModule->fifo_dequeue_buffer(&fReceivingFifo, flags, B_INFINITE_TIMEOUT, _buffer);
return gStackModule->fifo_dequeue_buffer(&fReceivingFifo, flags,
B_INFINITE_TIMEOUT, _buffer);
}
ssize_t
L2capEndpoint::Sendable()
{
debugf("[%ld] %p->L2capEndpoint::Sendable()\n", find_thread(NULL), this);
debugf("[%ld] %p\n", find_thread(NULL), this);
return 0;
}
@@ -244,7 +281,7 @@ L2capEndpoint::Sendable()
ssize_t
L2capEndpoint::Receivable()
{
debugf("[%ld] %p->L2capEndpoint::Receivable()\n", find_thread(NULL), this);
debugf("[%ld] %p\n", find_thread(NULL), this);
return 0;
}
@@ -253,8 +290,7 @@ L2capEndpoint::Receivable()
L2capEndpoint*
L2capEndpoint::ForPsm(uint16 psm)
{
L2capEndpoint* endpoint;
L2capEndpoint* endpoint;
DoublyLinkedList<L2capEndpoint>::Iterator iterator = EndpointList.GetIterator();
@@ -275,8 +311,29 @@ L2capEndpoint::ForPsm(uint16 psm)
void
L2capEndpoint::BindToChannel(L2capChannel* channel)
{
this->channel = channel;
channel->configuration = &configuration;
fChannel = channel;
fChannel->endpoint = this;
fChannel->configuration = &configuration;
debugf("Endpoint %p for psm %d, schannel %x dchannel %x\n", this,
channel->psm, channel->scid, channel->dcid);
}
status_t
L2capEndpoint::MarkEstablished()
{
debugf("Endpoint %p for psm %d, schannel %x dchannel %x\n", this,
fChannel->psm, fChannel->scid, fChannel->dcid);
net_socket* newSocket;
status_t error = gSocketModule->spawn_pending_socket(socket, &newSocket);
gSocketModule->set_connected(newSocket);
release_sem(fAcceptSemaphore);
return error;
}
@@ -1,9 +1,6 @@
/*
* 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
/*
* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef L2CAP_ENDPOINT_H
#define L2CAP_ENDPOINT_H
@@ -47,17 +44,18 @@ public:
mutex_unlock(&fLock);
}
status_t Bind(const struct sockaddr *_address);
status_t Unbind();
status_t Listen(int backlog);
status_t Connect(const struct sockaddr *address);
status_t Accept(net_socket **_acceptedSocket);
status_t Bind(const struct sockaddr *_address);
status_t Unbind();
status_t Listen(int backlog);
status_t Connect(const struct sockaddr *address);
status_t Accept(net_socket **_acceptedSocket);
ssize_t Send(const iovec *vecs, size_t vecCount,
ancillary_data_container *ancillaryData);
ssize_t Receive(const iovec *vecs, size_t vecCount,
ancillary_data_container **_ancillaryData,
struct sockaddr *_address, socklen_t *_addressLength);
ssize_t Send(const iovec *vecs, size_t vecCount,
ancillary_data_container *ancillaryData);
ssize_t Receive(const iovec *vecs, size_t vecCount,
ancillary_data_container **_ancillaryData, struct sockaddr *_address,
socklen_t *_addressLength);
ssize_t ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer);
ssize_t Sendable();
ssize_t Receivable();
@@ -67,15 +65,19 @@ public:
status_t Shutdown(int direction);
void BindToChannel(L2capChannel* channel);
static L2capEndpoint* ForPsm(uint16 psm);
ChannelConfiguration configuration;
L2capChannel* channel;
bool configurationSet;
net_fifo fReceivingFifo;
void BindToChannel(L2capChannel* channel);
status_t MarkEstablished();
static L2capEndpoint* ForPsm(uint16 psm);
bool RequiresConfiguration()
{
return fConfigurationSet;
}
ChannelConfiguration configuration;
net_fifo fReceivingFifo;
bool fConfigurationSet;
private:
typedef enum {
@@ -101,7 +103,7 @@ private:
State fState;
L2capEndpoint* fPeerEndpoint;
sem_id fAcceptSemaphore;
L2capChannel* fChannel;
};
@@ -126,9 +126,7 @@ l2cap_connect(net_protocol* protocol, const struct sockaddr* address)
status_t
l2cap_accept(net_protocol* protocol, struct net_socket** _acceptedSocket)
{
flowf("\n");
return EOPNOTSUPP;
return ((L2capEndpoint*)protocol)->Accept(_acceptedSocket);
}
@@ -160,7 +158,7 @@ l2cap_setsockopt(net_protocol* protocol, int level, int option,
{
flowf("\n");
((L2capEndpoint*)protocol)->configurationSet = true;
((L2capEndpoint*)protocol)->fConfigurationSet = true;
/* return protocol->next->module->setsockopt(protocol->next, level, option, value, length); */
return EOPNOTSUPP;
@@ -100,7 +100,8 @@ l2cap_process_cmd_rej(HciConnection* conn, uint8 ident, net_buffer* buffer);
status_t
l2cap_process_signal_cmd(HciConnection* conn, net_buffer* buffer)
{
net_buffer* m = buffer;
net_buffer* m = buffer;
debugf("Signal size=%ld\n", buffer->size);
while (m != NULL) {
@@ -212,8 +213,8 @@ l2cap_process_signal_cmd(HciConnection* conn, net_buffer* buffer)
static status_t
l2cap_process_con_req(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capChannel* ch;
uint16 dcid, psm;
L2capChannel* ch;
uint16 dcid, psm;
/* Get con req data */
NetBufferHeaderReader<l2cap_con_req_cp> command(buffer);
@@ -262,9 +263,9 @@ 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)
{
L2capFrame* cmd = NULL;
uint16 scid, dcid, result, status;
status_t error = 0;
L2capFrame* cmd = NULL;
uint16 scid, dcid, result, status;
status_t error = 0;
/* Get command parameters */
NetBufferHeaderReader<l2cap_con_rsp_cp> command(buffer);
@@ -359,7 +360,7 @@ reject:
static option_status
getNextSignalOption(net_buffer* nbuf, size_t* off, l2cap_cfg_opt_t* hdr, l2cap_cfg_opt_val_t* val)
{
int hint;
int hint;
size_t len = nbuf->size - (*off);
if (len == 0)
@@ -425,16 +426,16 @@ getNextSignalOption(net_buffer* nbuf, size_t* off, l2cap_cfg_opt_t* hdr, l2cap_c
static status_t
l2cap_process_cfg_req(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capChannel* channel = NULL;
uint16 dcid;
uint16 respond;
uint16 result;
L2capChannel* channel = NULL;
uint16 dcid;
uint16 respond;
uint16 result;
l2cap_cfg_opt_t hdr;
l2cap_cfg_opt_t hdr;
l2cap_cfg_opt_val_t val;
size_t off;
status_t error = 0;
size_t off;
status_t error = 0;
debugf("configuration=%ld\n", buffer->size);
@@ -554,14 +555,14 @@ reject:
static status_t
l2cap_process_cfg_rsp(HciConnection *conn, uint8 ident, net_buffer *buffer)
{
L2capFrame *cmd = NULL;
uint16 scid, cflag, result;
L2capFrame *cmd = NULL;
uint16 scid, cflag, result;
l2cap_cfg_opt_t hdr;
l2cap_cfg_opt_val_t val;
l2cap_cfg_opt_t hdr;
l2cap_cfg_opt_val_t val;
size_t off;
status_t error = 0;
size_t off;
status_t error = 0;
NetBufferHeaderReader<l2cap_cfg_rsp_cp> command(buffer);
status_t status = command.Status();
@@ -655,7 +656,7 @@ l2cap_process_cfg_rsp(HciConnection *conn, uint8 ident, net_buffer *buffer)
btCoreData->TimeoutSignal(cmd, bluetooth_l2cap_rtx_timeout);
else {
/* Send L2CA_Config response to the upper layer protocol */
//INDICATION error = ng_l2cap_l2ca_cfg_rsp(cmd->channel, cmd->token, result);
error = l2cap_upper_cfg_rsp(cmd->channel /*, cmd->token, result*/);
if (error != 0) {
/*
* XXX FIXME what to do here? we were not able to send
@@ -668,10 +669,7 @@ l2cap_process_cfg_rsp(HciConnection *conn, uint8 ident, net_buffer *buffer)
btCoreData->RemoveChannel(conn, cmd->channel->scid);
}
cmd->channel->cfgState |= L2CAP_CFG_OUT;
if ((cmd->channel->cfgState & L2CAP_CFG_BOTH) == L2CAP_CFG_BOTH) {
cmd->channel->state = L2CAP_CHAN_OPEN;
}
btCoreData->AcknowledgeSignal(cmd);
}
@@ -770,7 +768,7 @@ reject:
static status_t
l2cap_process_discon_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
{
L2capFrame *cmd = NULL;
L2capFrame* cmd = NULL;
int16 scid, dcid;
status_t error = 0;
@@ -827,7 +825,7 @@ out:
static status_t
l2cap_process_echo_req(HciConnection *conn, uint8 ident, net_buffer *buffer)
{
L2capFrame *cmd = NULL;
L2capFrame* cmd = NULL;
cmd = btCoreData->SpawnSignal(conn, NULL, l2cap_echo_req(ident, NULL, 0), ident, L2CAP_ECHO_RSP);
if (cmd == NULL) {
@@ -917,8 +915,8 @@ 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;
L2capFrame* cmd = NULL;
status_t error = B_OK;
/* Get command parameters */
NetBufferHeaderReader<l2cap_info_rsp_cp> command(buffer);
@@ -950,8 +948,7 @@ l2cap_process_info_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
switch (command->type) {
case L2CAP_CONNLESS_MTU:
/*
// TODO: Check specs ??
/* TODO: Check specs ??
if (conn->rx_pkt->m_pkthdr.len == sizeof(uint16)) {
*mtod(conn->rx_pkt, uint16 *) = le16toh(*mtod(conn->rx_pkt,uint16 *));
} else {
@@ -1,7 +1,6 @@
/*
* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*
*/
#include <KernelExport.h>
#include <NetBufferUtilities.h>
@@ -31,17 +30,17 @@ l2cap_l2ca_con_ind(L2capChannel* channel)
}
// Pair Channel with endpoint
channel->endpoint = endpoint;
endpoint->BindToChannel(channel);
debugf("Endpoint %p bound for psm %d, schannel %x dchannel %x\n",endpoint, channel->psm, channel->scid, channel->dcid);
net_buffer* buf = l2cap_con_rsp(channel->ident, channel->scid, channel->dcid, L2CAP_SUCCESS, L2CAP_NO_INFO);
L2capFrame* cmd = btCoreData->SpawnSignal(channel->conn, channel, buf, channel->ident, L2CAP_CON_RSP);
net_buffer* buf = l2cap_con_rsp(channel->ident, channel->scid, channel->dcid,
L2CAP_SUCCESS, L2CAP_NO_INFO);
L2capFrame* cmd = btCoreData->SpawnSignal(channel->conn, channel, buf,
channel->ident, L2CAP_CON_RSP);
if (cmd == NULL) {
gBufferModule->free(buf);
return ENOMEM;
}
// we can move to configuration...
channel->state = L2CAP_CHAN_CONFIG;
@@ -54,12 +53,16 @@ l2cap_l2ca_con_ind(L2capChannel* channel)
status_t
l2cap_l2ca_cfg_rsp_ind(L2capChannel* channel)
{
// if our configuration has not been yet send ..
// if our configuration has not been yet sent ...
if(!(channel->cfgState & L2CAP_CFG_OUT_SENT)) {
// send config_rsp
net_buffer* buf = l2cap_cfg_rsp(channel->ident, channel->dcid, 0, L2CAP_SUCCESS, NULL);
L2capFrame* cmd = btCoreData->SpawnSignal(channel->conn, channel, buf, channel->ident, L2CAP_CFG_RSP);
// TODO: check if we can handle this conf
// send config_rsp
net_buffer* buf = l2cap_cfg_rsp(channel->ident, channel->dcid, 0,
L2CAP_SUCCESS, NULL);
L2capFrame* cmd = btCoreData->SpawnSignal(channel->conn, channel, buf,
channel->ident, L2CAP_CFG_RSP);
if (cmd == NULL) {
gBufferModule->free(buf);
channel->state = L2CAP_CHAN_CLOSED;
@@ -67,7 +70,7 @@ l2cap_l2ca_cfg_rsp_ind(L2capChannel* channel)
}
flowf("Sending cfg resp\n");
/* Link command to the queue */
// Link command to the queue
SchedConnectionPurgeThread(channel->conn);
// set status
@@ -82,22 +85,26 @@ l2cap_l2ca_cfg_rsp_ind(L2capChannel* channel)
if ((channel->cfgState & L2CAP_CFG_BOTH) == L2CAP_CFG_BOTH) {
// Channel can be declared open
channel->state = L2CAP_CHAN_OPEN;
channel->endpoint->MarkEstablished();
} else {
// send configuration Request by our side
if (channel->endpoint->configurationSet) {
if (channel->endpoint->RequiresConfiguration()) {
// TODO: define complete configuration packet
} else {
// nothing special requested
net_buffer* buf = l2cap_cfg_req(channel->ident, channel->dcid, 0, NULL);
L2capFrame* cmd = btCoreData->SpawnSignal(channel->conn, channel, buf, channel->ident, L2CAP_CFG_REQ);
L2capFrame* cmd = btCoreData->SpawnSignal(channel->conn, channel, buf,
channel->ident, L2CAP_CFG_REQ);
if (cmd == NULL) {
gBufferModule->free(buf);
channel->state = L2CAP_CHAN_CLOSED;
return ENOMEM;
}
flowf("Sending cfg req\n");
// Link command to the queue
SchedConnectionPurgeThread(channel->conn);
@@ -109,6 +116,19 @@ l2cap_l2ca_cfg_rsp_ind(L2capChannel* channel)
}
status_t
l2cap_upper_cfg_rsp(L2capChannel* channel)
{
channel->cfgState |= L2CAP_CFG_OUT;
if ((channel->cfgState & L2CAP_CFG_BOTH) == L2CAP_CFG_BOTH) {
channel->state = L2CAP_CHAN_OPEN;
return channel->endpoint->MarkEstablished();
}
return B_OK;
}
status_t
l2cap_upper_con_rsp(HciConnection* conn, L2capChannel* channel)
{
@@ -121,6 +141,7 @@ l2cap_upper_con_rsp(HciConnection* conn, L2capChannel* channel)
status_t
l2cap_co_receive(HciConnection* conn, net_buffer* buffer, uint16 dcid)
{
debugf("Handle %d To dcid %d\n", conn->handle, dcid);
L2capChannel* channel = btCoreData->ChannelBySourceID(conn, dcid);
@@ -134,7 +155,22 @@ l2cap_co_receive(HciConnection* conn, net_buffer* buffer, uint16 dcid)
return B_ERROR;
}
flowf("Enqueue to fifo\n");
return gStackModule->fifo_enqueue_buffer(&channel->endpoint->fReceivingFifo, buffer);
}
status_t
l2cap_cl_receive(HciConnection* conn, net_buffer* buffer, uint16 psm)
{
L2capEndpoint* endpoint = L2capEndpoint::ForPsm(psm);
if (endpoint == NULL) {
debugf("no enpoint bound with psm %d\n", psm);
return B_ERROR;
}
flowf("Enqueue to fifo\n");
return gStackModule->fifo_enqueue_buffer(&endpoint->fReceivingFifo, buffer);
}
@@ -8,10 +8,13 @@
#include "l2cap_internal.h"
status_t l2cap_l2ca_con_ind(L2capChannel* channel);
status_t l2cap_upper_con_rsp(HciConnection* conn, L2capChannel* channel);
status_t l2cap_l2ca_cfg_rsp_ind(L2capChannel* channel);
status_t l2cap_upper_con_rsp(HciConnection* conn, L2capChannel* channel);
status_t l2cap_upper_cfg_rsp(L2capChannel* channel);
status_t l2cap_co_receive(HciConnection* conn, net_buffer* buffer, uint16 dcid);
status_t l2cap_cl_receive(HciConnection* conn, net_buffer* buffer, uint16 psm);
#endif