L2CAP: Major refactor of the whole component.

(And surrounding portions of the "btCoreData" module.)

 * Rewrote the main "l2cap.h" header representing protocol constants
   and structures. Now conforms to general Haiku naming conventions
   rather than BSD ones. Some more constants added/removed based
   on the most recent Bluetooth specification.

 * Rewrote all code derived from the BSDs to match Haiku conventions
   and structures in the driver.

 * Dropped the "channel" and "frame" structures from "btCoreData".
   Channels are now managed by L2capEndpoints, and "frames" are
   now just plain net_buffers without surrounding structures.
   This also makes state management much simpler.

 * Made it so that actual net_buffers are passed through to the
   l2cap_receive function rather than another data structure.
   A fake interface address is used to communicate connection
   information. (This probably ought to be changed, though.)

 * Get rid of l2cap_lower and l2cap_upper abstractions.
   Everything related to channel/endpoint management is now
   done in L2capEndpoint, while buffer reception is handled
   directly in l2cap_receive and elsewhere, same as other drivers.

 * Wire up more hooks and fix module flags (needed to be able to
   get the module loaded and opening sockets at all.)

 * Implement an actual locking strategy in L2capEndpoint
   and HciConnection. There's still problems with lifetime
   management, but at least thread-safety is mostly handled.

 * Create an L2capEndpointManager and use it to manage
   the endpoints, rather than having a single (unsafe)
   linked-list.

And plenty of other refactorings and cleanups besides.
There's still more to be done for Bluetooth overall, though:

 * The "btCoreData" and "hci" modules also badly need a major
   overhaul, and should be merged into a single "bluetooth"
   bus_manager. They also shouldn't be passing around pointers
   to other modules like this.

 * There's a number of TODOs/FIXMEs in the L2CAP module, most
   notably around timeouts (especially command timeouts) and
   parameter validation/specification.

Tested by myself with kallisti5's help. Incoming connections
(on the PSM for SDP) get all the way to the latter half
of the Configuration step before hanging.
This commit is contained in:
Augustin Cavalier 2024-04-26 16:22:39 -04:00
parent d3e2c76409
commit bb83316a58
29 changed files with 2027 additions and 3466 deletions

View File

@ -106,6 +106,7 @@ SYSTEM_SERVERS += [ FFilterByBuildFeatures
] ;
# Bluetooth stack + drivers
#SYSTEM_NETWORK_PROTOCOLS += l2cap ;
SYSTEM_BT_STACK = hci btCoreData ;
SYSTEM_ADD_ONS_DRIVERS_BT_H2 +=
h2generic

View File

@ -161,6 +161,5 @@ enum {
#define BT_USERLAND_PORT_NAME "BT Kernel-User Event"
#define BT_RX_PORT_NAME "BT Kernel RX assembly"
#define BLUETOOTH_CONNECTION_PORT "bluetooth connection port"
#define BLUETOOTH_CONNECTION_SCHED_PORT "bluetooth con sched port"
#endif // _BTHCI_TRANSPORT_H_

View File

@ -9,6 +9,10 @@
#include <module.h>
#include <lock.h>
#include <util/DoublyLinkedList.h>
#include <util/VectorMap.h>
#include <net_datalink.h>
#include <net/if_dl.h>
#include <net_buffer.h>
#include <net_device.h>
@ -22,10 +26,6 @@
#define BT_CORE_DATA_MODULE_NAME "bluetooth/btCoreData/v1"
struct L2capChannel;
struct L2capFrame;
struct L2capEndpoint;
typedef enum _connection_status {
HCI_CONN_CLOSED,
HCI_CONN_OPEN,
@ -35,25 +35,30 @@ typedef enum _connection_status {
#ifdef __cplusplus
struct HciConnection : DoublyLinkedListLinkImpl<HciConnection> {
HciConnection();
HciConnection(hci_id hid);
virtual ~HciConnection();
hci_id Hid;
bluetooth_device* ndevice;
net_buffer* currentRxPacket;
ssize_t currentRxExpectedLength;
bdaddr_t destination;
uint16 handle;
int type;
uint16 mtu;
connection_status status;
uint16 lastCid;
uint8 lastIdent;
DoublyLinkedList<L2capChannel> ChannelList;
DoublyLinkedList<L2capFrame> ExpectedResponses;
DoublyLinkedList<L2capFrame> OutGoingFrames;
mutex fLock;
mutex fLockExpected;
net_buffer* currentRxPacket;
ssize_t currentRxExpectedLength;
struct net_interface_address interface_address;
struct sockaddr_dl address_dl;
struct sockaddr_storage address_dest;
void (*disconnect_hook)(HciConnection*);
public:
mutex fLock;
uint8 fNextIdent;
VectorMap<uint8, void*> fInUseIdents;
};
#else
@ -62,84 +67,14 @@ struct HciConnection;
#endif
typedef enum _channel_status {
L2CAP_CHAN_CLOSED, /* channel closed */
L2CAP_CHAN_W4_L2CAP_CON_RSP, /* wait for L2CAP resp. */
L2CAP_CHAN_W4_L2CA_CON_RSP, /* wait for upper resp. */
L2CAP_CHAN_CONFIG, /* L2CAP configuration */
L2CAP_CHAN_OPEN, /* channel open */
L2CAP_CHAN_W4_L2CAP_DISCON_RSP, /* wait for L2CAP discon. */
L2CAP_CHAN_W4_L2CA_DISCON_RSP /* wait for upper discon. */
} channel_status;
#ifdef __cplusplus
typedef struct _ChannelConfiguration {
uint16 imtu; /* incoming channel MTU */
l2cap_flow_t iflow; /* incoming flow control */
uint16 omtu; /* outgoing channel MTU */
l2cap_flow_t oflow; /* outgoing flow control */
uint16 flush_timo; /* flush timeout */
uint16 link_timo; /* link timeout */
} ChannelConfiguration;
struct L2capChannel : DoublyLinkedListLinkImpl<L2capChannel> {
HciConnection* conn;
uint16 scid;
uint16 dcid;
uint16 psm;
uint8 ident;
uint8 cfgState;
channel_status state;
ChannelConfiguration* configuration;
L2capEndpoint* endpoint;
};
#endif
typedef enum _frametype {
L2CAP_C_FRAME, // signals
L2CAP_G_FRAME, // CL packets
L2CAP_B_FRAME, // CO packets
L2CAP_I_FRAME,
L2CAP_S_FRAME
} frame_type;
#ifdef __cplusplus
struct L2capFrame : DoublyLinkedListLinkImpl<L2capFrame> {
HciConnection* conn;
L2capChannel* channel;
uint16 flags; /* command flags */
#define L2CAP_CMD_PENDING (1 << 0) /* command is pending */
uint8 code; /* L2CAP command opcode */
uint8 ident; /* L2CAP command ident */
frame_type type;
net_buffer* buffer; // contains 1 l2cap / mutliple acls
// TODO :struct callout timo; /* RTX/ERTX timeout */
};
#endif
struct bluetooth_core_data_module_info {
module_info info;
status_t (*PostEvent)(bluetooth_device* ndev, void* event,
size_t size);
// FIXME: We really shouldn't be passing out connection pointers at all...
struct HciConnection* (*AddConnection)(uint16 handle, int type,
const bdaddr_t& dst, hci_id hid);
@ -148,42 +83,13 @@ struct bluetooth_core_data_module_info {
hci_id (*RouteConnection)(const bdaddr_t& destination);
void (*SetAclBuffer)(struct HciConnection* conn,
net_buffer* nbuf);
void (*SetAclExpectedSize)(struct HciConnection* conn,
size_t size);
void (*AclPutting)(struct HciConnection* conn,
size_t size);
bool (*AclComplete)(struct HciConnection* conn);
bool (*AclOverFlowed)(struct HciConnection* conn);
struct HciConnection* (*ConnectionByHandle)(uint16 handle, hci_id hid);
struct HciConnection* (*ConnectionByDestination)(
const bdaddr_t& destination, hci_id hid);
struct L2capChannel* (*AddChannel)(struct HciConnection* conn,
uint16 psm);
void (*RemoveChannel)(struct HciConnection* conn,
uint16 scid);
struct L2capChannel* (*ChannelBySourceID)(struct HciConnection* conn,
uint16 sid);
uint16 (*ChannelAllocateCid)(struct HciConnection* conn);
uint16 (*ChannelAllocateIdent)(struct HciConnection* conn);
struct L2capFrame* (*SignalByIdent)(struct HciConnection* conn,
uint8 ident);
status_t (*TimeoutSignal)(struct L2capFrame* frame,
uint32 timeo);
status_t (*UnTimeoutSignal)(struct L2capFrame* frame);
struct L2capFrame* (*SpawnFrame)(struct HciConnection* conn,
struct L2capChannel* channel,
net_buffer* buffer, frame_type frame);
struct L2capFrame* (*SpawnSignal)(struct HciConnection* conn,
struct L2capChannel* channel,
net_buffer* buffer, uint8 ident, uint8 code);
status_t (*AcknowledgeSignal)(struct L2capFrame* frame);
status_t (*QueueSignal)(struct L2capFrame* frame);
uint8 (*allocate_command_ident)(struct HciConnection* conn, void* associated);
void* (*lookup_command_ident)(struct HciConnection* conn, uint8 ident);
void (*free_command_ident)(struct HciConnection* conn, uint8 ident);
};

View File

@ -1,311 +1,228 @@
/*
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*
* Copyright 2024, Haiku, Inc. 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.
*/
/**************************************************************************
**************************************************************************
** Common defines and types (L2CAP)
**************************************************************************
**************************************************************************/
#ifndef _L2CAP_
#define _L2CAP_
#ifndef _L2CAP_H_
#define _L2CAP_H_
#include <bluetooth/bluetooth.h>
// TODO: from BSD compatibility layer
#define htole16(x) (x)
#define le16toh(x) (x)
#define le32toh(x) (x)
#define htole32(x) (x)
#define HZ 1000000 // us per second TODO: move somewhere more generic
#define bluetooth_l2cap_ertx_timeout (60 * HZ)
#define bluetooth_l2cap_rtx_timeout (300 * HZ)
/*
* Channel IDs are assigned relative to the instance of L2CAP node, i.e.
* relative to the unit. So the total number of channels that unit can have
* open at the same time is 0xffff - 0x0040 = 0xffbf (65471). This number
* does not depend on number of connections.
*/
#define L2CAP_NULL_CID 0x0000 /* DO NOT USE THIS CID */
#define L2CAP_SIGNAL_CID 0x0001 /* signaling channel ID */
#define L2CAP_CLT_CID 0x0002 /* connectionless channel ID */
/* 0x0003 - 0x003f Reserved */
#define L2CAP_FIRST_CID 0x0040 /* dynamically alloc. (start) */
#define L2CAP_LAST_CID 0xffff /* dynamically alloc. (end) */
/* Channel IDs */
/*! These are unique for a unit. Thus the total number of channels that a unit
* can have open simultaneously is (L2CAP_LAST_CID - L2CAP_FIRST_CID) = 65471.
* (This does not depend on the number of connections.) */
#define L2CAP_NULL_CID 0x0000
#define L2CAP_SIGNALING_CID 0x0001
#define L2CAP_CONNECTIONLESS_CID 0x0002
/* 0x0003-0x003f: reserved */
#define L2CAP_FIRST_CID 0x0040
#define L2CAP_LAST_CID 0xffff
/*
* L2CAP signaling command ident's are assigned relative to the connection,
* because there is only one signaling channel (cid == 0x01) for every
* connection. So up to 254 (0xff - 0x01) L2CAP commands can be pending at the
* same time for the same connection.
*/
#define L2CAP_NULL_IDENT 0x00 /* DO NOT USE THIS IDENT */
#define L2CAP_FIRST_IDENT 0x01 /* dynamically alloc. (start) */
#define L2CAP_LAST_IDENT 0xff /* dynamically alloc. (end) */
/* Idents */
/*! Command idents are unique within a connection, since there is only one
* L2CAP_SIGNALING_CID. Thus only (L2CAP_LAST_IDENT - L2CAP_FIRST_IDENT),
* i.e. 254, commands can be pending simultaneously for a connection. */
#define L2CAP_NULL_IDENT 0x00
#define L2CAP_FIRST_IDENT 0x01
#define L2CAP_LAST_IDENT 0xff
/* L2CAP MTU */
/* MTU */
#define L2CAP_MTU_MINIMUM 48
#define L2CAP_MTU_DEFAULT 672
#define L2CAP_MTU_MAXIMUM 0xffff
/* L2CAP flush and link timeouts */
#define L2CAP_FLUSH_TIMO_DEFAULT 0xffff /* always retransmit */
#define L2CAP_LINK_TIMO_DEFAULT 0xffff
/* L2CAP Command Reject reasons */
#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000
#define L2CAP_REJ_MTU_EXCEEDED 0x0001
#define L2CAP_REJ_INVALID_CID 0x0002
/* 0x0003 - 0xffff - reserved for future use */
/* Timeouts */
#define L2CAP_FLUSH_TIMEOUT_DEFAULT 0xffff /* always retransmit */
#define L2CAP_LINK_TIMEOUT_DEFAULT 0xffff
/* Protocol/Service Multioplexor (PSM) values */
/* Protocol/Service Multiplexer (PSM) values */
#define L2CAP_PSM_ANY 0x0000 /* Any/Invalid PSM */
#define L2CAP_PSM_SDP 0x0001 /* Service Discovery Protocol */
#define L2CAP_PSM_RFCOMM 0x0003 /* RFCOMM protocol */
#define L2CAP_PSM_TCP 0x0005 /* Telephony Control Protocol */
#define L2CAP_PSM_TCS 0x0007 /* TCS cordless */
#define L2CAP_PSM_TCS_BIN 0x0005 /* Telephony Control Protocol */
#define L2CAP_PSM_TCS_BIN_CORDLESS 0x0007 /* TCS cordless */
#define L2CAP_PSM_BNEP 0x000F /* BNEP */
#define L2CAP_PSM_HID_CTRL 0x0011 /* HID Control */
#define L2CAP_PSM_HID_INT 0x0013 /* HID Interrupt */
#define L2CAP_PSM_HID_CTRL 0x0011 /* HID control */
#define L2CAP_PSM_HID_INT 0x0013 /* HID interrupt */
#define L2CAP_PSM_UPnP 0x0015 /* UPnP (ESDP) */
#define L2CAP_PSM_AVCTP 0x0017 /* AVCTP */
#define L2CAP_PSM_AVDTP 0x0019 /* AVDTP */
/* < 0x1000 - reserved for future use */
/* 0x1001 < x < 0xFFFF dinamically assigned */
/* L2CAP Connection response command result codes */
#define L2CAP_SUCCESS 0x0000
#define L2CAP_PENDING 0x0001
#define L2CAP_PSM_NOT_SUPPORTED 0x0002
#define L2CAP_SEQUIRY_BLOCK 0x0003
#define L2CAP_NO_RESOURCES 0x0004
#define L2CAP_TIMEOUT 0xeeee
#define L2CAP_UNKNOWN 0xffff
/* 0x0005 - 0xffff - reserved for future use */
/* L2CAP Connection response status codes */
#define L2CAP_NO_INFO 0x0000
#define L2CAP_AUTH_PENDING 0x0001
#define L2CAP_AUTZ_PENDING 0x0002
/* 0x0003 - 0xffff - reserved for future use */
/* L2CAP Configuration response result codes */
#define L2CAP_UNACCEPTABLE_PARAMS 0x0001
#define L2CAP_REJECT 0x0002
#define L2CAP_UNKNOWN_OPTION 0x0003
/* 0x0003 - 0xffff - reserved for future use */
/* L2CAP Configuration options */
#define L2CAP_OPT_CFLAG_BIT 0x0001
#define L2CAP_OPT_CFLAG(flags) ((flags) & L2CAP_OPT_CFLAG_BIT)
#define L2CAP_OPT_HINT_BIT 0x80
#define L2CAP_OPT_HINT(type) ((type) & L2CAP_OPT_HINT_BIT)
#define L2CAP_OPT_HINT_MASK 0x7f
#define L2CAP_OPT_MTU 0x01
#define L2CAP_OPT_MTU_SIZE sizeof(uint16)
#define L2CAP_OPT_FLUSH_TIMO 0x02
#define L2CAP_OPT_FLUSH_TIMO_SIZE sizeof(uint16)
#define L2CAP_OPT_QOS 0x03
#define L2CAP_OPT_QOS_SIZE sizeof(l2cap_flow_t)
/* 0x4 - 0xff - reserved for future use */
#define L2CAP_CFG_IN (1 << 0) /* incoming path done */
#define L2CAP_CFG_OUT (1 << 1) /* outgoing path done */
#define L2CAP_CFG_BOTH (L2CAP_CFG_IN | L2CAP_CFG_OUT)
#define L2CAP_CFG_IN_SENT (1 << 2) /* L2CAP ConfigReq sent */
#define L2CAP_CFG_OUT_SENT (1 << 3) /* ---/--- */
/* L2CAP Information request type codes */
#define L2CAP_CONNLESS_MTU 0x0001
#define L2CAP_EXTENDED_MASK 0x0002
/* 0x0003 - 0xffff - reserved for future use */
/* L2CAP Information response codes */
#define L2CAP_NOT_SUPPORTED 0x0001
/* 0x0002 - 0xffff - reserved for future use */
/* L2CAP flow (QoS) */
typedef struct {
uint8 flags; /* reserved for future use */
uint8 service_type; /* service type */
uint32 token_rate; /* bytes per second */
uint32 token_bucket_size; /* bytes */
uint32 peak_bandwidth; /* bytes per second */
uint32 latency; /* microseconds */
uint32 delay_variation; /* microseconds */
} __attribute__ ((packed)) l2cap_flow_t;
/* < 0x1000: reserved */
/* >= 0x1000: dynamically assigned */
/**************************************************************************
**************************************************************************
** Link level defines, headers and types
**************************************************************************
**************************************************************************/
/* L2CAP header */
typedef struct {
uint16 length; /* payload size */
uint16 dcid; /* destination channel ID */
} __attribute__ ((packed)) l2cap_hdr_t;
} _PACKED l2cap_basic_header;
/* L2CAP ConnectionLess Traffic (CLT) (if destination cid == 0x2) */
/* Connectionless traffic ("CLT") */
typedef struct {
uint16 psm; /* Protocol/Service Multiplexor */
} __attribute__ ((packed)) l2cap_clt_hdr_t;
/* dcid == L2CAP_CONNECTIONLESS_CID (0x2) */
uint16 psm;
} _PACKED l2cap_connectionless_header;
#define L2CAP_CONNECTIONLESS_MTU_MAXIMUM (L2CAP_MTU_MAXIMUM - sizeof(l2cap_connectionless_header))
#define L2CAP_CLT_MTU_MAXIMUM (L2CAP_MTU_MAXIMUM - sizeof(l2cap_clt_hdr_t))
/* L2CAP command header */
typedef struct {
uint8 code; /* command OpCode */
uint8 code; /* command opcode */
#define L2CAP_IS_SIGNAL_REQ(code) (((code) & 1) == 0)
#define L2CAP_IS_SIGNAL_RSP(code) (((code) & 1) == 1)
uint8 ident; /* identifier to match request and response */
uint16 length; /* command parameters length */
} __attribute__ ((packed)) l2cap_cmd_hdr_t;
} _PACKED l2cap_command_header;
/* L2CAP Command Reject */
#define L2CAP_CMD_REJ 0x01
#define L2CAP_COMMAND_REJECT_RSP 0x01
typedef struct {
uint16 reason; /* reason to reject command */
/* uint8 data[]; -- optional data (depends on reason) */
} __attribute__ ((packed)) l2cap_cmd_rej_cp;
enum : uint16 {
REJECTED_NOT_UNDERSTOOD = 0x0000,
REJECTED_MTU_EXCEEDED = 0x0001,
REJECTED_INVALID_CID = 0x0002,
/* 0x0003-0xffff: reserved */
}; uint16 reason;
/* data may follow */
} _PACKED l2cap_command_reject;
/* CommandReject data */
typedef union {
/* L2CAP_REJ_MTU_EXCEEDED */
struct {
uint16 mtu; /* actual signaling MTU */
} __attribute__ ((packed)) mtu;
/* L2CAP_REJ_INVALID_CID */
} _PACKED mtu_exceeded;
struct {
uint16 scid; /* local CID */
uint16 dcid; /* remote CID */
} __attribute__ ((packed)) cid;
} l2cap_cmd_rej_data_t;
typedef l2cap_cmd_rej_data_t * l2cap_cmd_rej_data_p;
uint16 scid; /* source (local) CID */
uint16 dcid; /* destination (remote) CID */
} _PACKED invalid_cid;
} l2cap_command_reject_data;
/* L2CAP Connection Request */
#define L2CAP_CON_REQ 0x02
#define L2CAP_CONNECTION_REQ 0x02
typedef struct {
uint16 psm; /* Protocol/Service Multiplexor (PSM) */
uint16 psm;
uint16 scid; /* source channel ID */
} __attribute__ ((packed)) l2cap_con_req_cp;
} _PACKED l2cap_connection_req;
/* L2CAP Connection Response */
#define L2CAP_CON_RSP 0x03
#define L2CAP_CONNECTION_RSP 0x03
typedef struct {
uint16 dcid; /* destination channel ID */
uint16 scid; /* source channel ID */
uint16 result; /* 0x00 - success */
uint16 status; /* more info if result != 0x00 */
} __attribute__ ((packed)) l2cap_con_rsp_cp;
enum : uint16 {
RESULT_SUCCESS = 0x0000,
RESULT_PENDING = 0x0001,
RESULT_PSM_NOT_SUPPORTED = 0x0002,
RESULT_SECURITY_BLOCK = 0x0003,
RESULT_NO_RESOURCES = 0x0004,
RESULT_INVALID_SCID = 0x0005,
RESULT_SCID_ALREADY_ALLOCATED = 0x0006,
/* 0x0007-0xffff: reserved */
}; uint16 result;
enum : uint16 {
NO_STATUS_INFO = 0x0000,
STATUS_AUTHENTICATION_PENDING = 0x0001,
STATUS_AUTHORIZATION_PENDING = 0x0002,
/* 0x0003-0xffff: reserved */
}; uint16 status; /* only defined if result = pending */
} _PACKED l2cap_connection_rsp;
/* L2CAP Configuration Request */
#define L2CAP_CFG_REQ 0x04
#define L2CAP_CONFIGURATION_REQ 0x04
typedef struct {
uint16 dcid; /* destination channel ID */
uint16 flags; /* flags */
/* uint8 options[] -- options */
} __attribute__ ((packed)) l2cap_cfg_req_cp;
uint16 flags;
/* options may follow */
} _PACKED l2cap_configuration_req;
/* L2CAP Configuration Response */
#define L2CAP_CFG_RSP 0x05
#define L2CAP_CONFIGURATION_RSP 0x05
typedef struct {
uint16 scid; /* source channel ID */
uint16 flags; /* flags */
uint16 result; /* 0x00 - success */
/* uint8 options[] -- options */
} __attribute__ ((packed)) l2cap_cfg_rsp_cp;
uint16 flags;
#define L2CAP_CFG_FLAG_CONTINUATION 0x0001
enum : uint16 {
RESULT_SUCCESS = 0x0000,
RESULT_UNACCEPTABLE_PARAMS = 0x0001,
RESULT_REJECTED = 0x0002,
RESULT_UNKNOWN_OPTION = 0x0003,
RESULT_PENDING = 0x0004,
RESULT_FLOW_SPEC_REJECTED = 0x0005,
/* 0x0006-0xffff: reserved */
}; uint16 result;
/* options may follow */
} _PACKED l2cap_configuration_rsp;
/* L2CAP configuration option */
typedef struct {
uint8 type;
uint8 length;
/* uint8 value[] -- option value (depends on type) */
} __attribute__ ((packed)) l2cap_cfg_opt_t;
typedef l2cap_cfg_opt_t * l2cap_cfg_opt_p;
enum : uint8 {
OPTION_MTU = 0x01,
OPTION_FLUSH_TIMEOUT = 0x02,
OPTION_QOS = 0x03,
OPTION_HINT_BIT = 0x80,
}; uint8 type;
uint8 length;
/* value follows */
} _PACKED l2cap_configuration_option;
typedef struct {
uint8 flags; /* reserved for future use */
uint8 service_type; /* 1 = best effort */
uint32 token_rate; /* average bytes per second */
uint32 token_bucket_size; /* max burst bytes */
uint32 peak_bandwidth; /* bytes per second */
uint32 access_latency; /* microseconds */
uint32 delay_variation; /* microseconds */
} _PACKED l2cap_qos;
/* L2CAP configuration option value */
typedef union {
uint16 mtu; /* L2CAP_OPT_MTU */
uint16 flush_timo; /* L2CAP_OPT_FLUSH_TIMO */
l2cap_flow_t flow; /* L2CAP_OPT_QOS */
} l2cap_cfg_opt_val_t;
typedef l2cap_cfg_opt_val_t * l2cap_cfg_opt_val_p;
uint16 mtu;
uint16 flush_timeout;
l2cap_qos qos;
} l2cap_configuration_option_value;
/* L2CAP Disconnect Request */
#define L2CAP_DISCON_REQ 0x06
#define L2CAP_DISCONNECTION_REQ 0x06
typedef struct {
uint16 dcid; /* destination channel ID */
uint16 scid; /* source channel ID */
} __attribute__ ((packed)) l2cap_discon_req_cp;
} _PACKED l2cap_disconnection_req;
#define L2CAP_DISCONNECTION_RSP 0x07
typedef l2cap_disconnection_req l2cap_disconnection_rsp;
/* L2CAP Disconnect Response */
#define L2CAP_DISCON_RSP 0x07
typedef l2cap_discon_req_cp l2cap_discon_rsp_cp;
/* L2CAP Echo Request */
#define L2CAP_ECHO_REQ 0x08
/* No command parameters, only optional data */
/* L2CAP Echo Response */
#define L2CAP_ECHO_RSP 0x09
#define L2CAP_MAX_ECHO_SIZE \
(L2CAP_MTU_MAXIMUM - sizeof(l2cap_cmd_hdr_t))
/* No command parameters, only optional data */
(L2CAP_MTU_MAXIMUM - sizeof(l2cap_command_header))
/* L2CAP Information Request */
#define L2CAP_INFO_REQ 0x0a
#define L2CAP_INFORMATION_REQ 0x0a
typedef struct {
uint16 type; /* requested information type */
} __attribute__ ((packed)) l2cap_info_req_cp;
enum : uint16 {
TYPE_CONNECTIONLESS_MTU = 0x0001,
TYPE_EXTENDED_FEATURES = 0x0002,
TYPE_FIXED_CHANNELS = 0x0003,
/* 0x0004-0xffff: reserved */
}; uint16 type;
} _PACKED l2cap_information_req;
/* L2CAP Information Response */
#define L2CAP_INFO_RSP 0x0b
#define L2CAP_INFORMATION_RSP 0x0b
typedef struct {
uint16 type; /* requested information type */
uint16 result; /* 0x00 - success */
/* uint8 info[] -- info data (depends on type)
*
* L2CAP_CONNLESS_MTU - 2 bytes connectionless MTU
*/
} __attribute__ ((packed)) l2cap_info_rsp_cp;
#define IS_SIGNAL_REQ(code) ((code & 1) == 0)
#define IS_SIGNAL_RSP(code) ((code & 1) == 1)
uint16 type;
enum : uint16 {
RESULT_SUCCESS = 0x0000,
RESULT_NOT_SUPPORTED = 0x0001,
}; uint16 result;
/* data may follow */
} _PACKED l2cap_information_rsp;
typedef union {
/* L2CAP_CONNLESS_MTU */
struct {
uint16 mtu;
} __attribute__ ((packed)) mtu;
} l2cap_info_rsp_data_t;
typedef l2cap_info_rsp_data_t * l2cap_info_rsp_data_p;
uint16 mtu;
uint32 extended_features;
} _PACKED l2cap_information_rsp_data;
#endif
#endif /* _L2CAP_H_ */

View File

@ -3,8 +3,6 @@
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ConnectionInterface.h"
#include "ChannelInterface.h"
#include "FrameInterface.h"
#include <bluetooth/bdaddrUtils.h>
@ -18,6 +16,7 @@
int32 api_version = B_CUR_DRIVER_API_VERSION;
mutex sConnectionListLock = MUTEX_INITIALIZER("bt connection list");
DoublyLinkedList<HciConnection> sConnectionList;
net_buffer_module_info* gBufferModule = NULL;
@ -36,64 +35,6 @@ ExistConnectionByHandle(uint16 handle, hci_id hid)
}
static int
DumpHciConnections(int argc, char** argv)
{
HciConnection* conn;
L2capChannel* chan;
L2capFrame* frame;
DoublyLinkedList<HciConnection>::Iterator iterator
= sConnectionList.GetIterator();
while (iterator.HasNext()) {
conn = iterator.Next();
/*
kprintf("LocalDevice=0x%" B_PRIx32 " Destination=%s handle=%#x type=%d"
"outqueue=%" B_PRId32 " expected=%" B_PRId32 "\n", conn->Hid,
bdaddrUtils::ToString(conn->destination).String(), conn->handle,
conn->type, conn->OutGoingFrames.Count(),
conn->ExpectedResponses.Count());
*/
// each channel
kprintf("\tChannels\n");
DoublyLinkedList<L2capChannel>::Iterator channelIterator
= conn->ChannelList.GetIterator();
while (channelIterator.HasNext()) {
chan = channelIterator.Next();
kprintf("\t\tscid=%x dcid=%x state=%x cfg=%x\n", chan->scid,
chan->dcid, chan->state, chan->cfgState);
}
// Each outgoing
kprintf("\n\tOutGoingFrames\n");
DoublyLinkedList<L2capFrame>::Iterator frameIterator
= conn->OutGoingFrames.GetIterator();
while (frameIterator.HasNext()) {
frame = frameIterator.Next();
kprintf("\t\tscid=%x code=%x ident=%x type=%x, buffer=%p\n",
frame->channel->scid, frame->code, frame->ident,
frame->type, frame->buffer);
}
// Each expected
kprintf("\n\tExpectedFrames\n");
DoublyLinkedList<L2capFrame>::Iterator frameExpectedIterator
= conn->ExpectedResponses.GetIterator();
while (frameExpectedIterator.HasNext()) {
frame = frameExpectedIterator.Next();
kprintf("\t\tscid=%x code=%x ident=%x type=%x, buffer=%p\n",
frame->channel->scid, frame->code, frame->ident,
frame->type, frame->buffer);
}
}
return 0;
}
status_t
PostEvent(bluetooth_device* ndev, void* event, size_t size)
{
@ -161,25 +102,17 @@ bcd_std_ops(int32 op, ...)
switch (op) {
case B_MODULE_INIT:
new (&sConnectionList) DoublyLinkedList<HciConnection>;
add_debugger_command("btConnections", &DumpHciConnections,
"Lists Bluetooth Connections with RemoteDevices & channels");
status = get_module(NET_BUFFER_MODULE_NAME,
(module_info **)&gBufferModule);
if (status < B_OK)
if (status != B_OK)
return status;
return B_OK;
break;
case B_MODULE_UNINIT:
remove_debugger_command("btConnections", &DumpHciConnections);
put_module(NET_BUFFER_MODULE_NAME);
return B_OK;
break;
}
return B_ERROR;
@ -199,29 +132,12 @@ bluetooth_core_data_module_info sBCDModule = {
RouteConnection,
SetAclBuffer,
SetAclExpectedSize,
AclPutting,
AclComplete,
AclOverFlowed,
ConnectionByHandle,
ConnectionByDestination,
AddChannel,
RemoveChannel,
ChannelBySourceID,
ChannelAllocateCid,
ChannelAllocateIdent,
SignalByIdent,
TimeoutSignal,
unTimeoutSignal,
SpawmFrame,
SpawmSignal,
AcknowledgeSignal,
QueueSignal,
allocate_command_ident,
lookup_command_ident,
free_command_ident,
};

View File

@ -1,120 +0,0 @@
/*
* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <string.h>
#include <l2cap.h>
#include <bluetooth/HCI/btHCI_command.h>
#include <btDebug.h>
#include "ChannelInterface.h"
#include "FrameInterface.h"
L2capChannel*
ChannelBySourceID(HciConnection *conn, uint16 scid)
{
L2capChannel* channel = NULL;
DoublyLinkedList<L2capChannel>::Iterator iterator = conn->ChannelList.GetIterator();
while (iterator.HasNext()) {
channel = iterator.Next();
if (channel->scid == scid)
return channel;
}
return NULL;
}
uint16
ChannelAllocateCid(HciConnection* conn)
{
uint16 cid = conn->lastCid;
TRACE("%s: Starting search cid %d\n", __func__, cid);
do {
cid = (cid == L2CAP_LAST_CID) ? L2CAP_FIRST_CID : cid + 1;
if (ChannelBySourceID(conn, cid) == NULL) {
conn->lastCid = cid;
return cid;
}
} while (cid != conn->lastCid);
return L2CAP_NULL_CID;
}
uint16
ChannelAllocateIdent(HciConnection* conn)
{
uint8 ident = conn->lastIdent + 1;
if (ident < L2CAP_FIRST_IDENT)
ident = L2CAP_FIRST_IDENT;
while (ident != conn->lastIdent) {
if (SignalByIdent(conn, ident) == NULL) {
conn->lastIdent = ident;
return ident;
}
ident++;
if (ident < L2CAP_FIRST_IDENT)
ident = L2CAP_FIRST_IDENT;
}
return L2CAP_NULL_IDENT;
}
L2capChannel*
AddChannel(HciConnection* conn, uint16 psm)
{
L2capChannel* channel = new (std::nothrow) L2capChannel;
if (channel == NULL) {
ERROR("%s: Unable to allocate memory for channel!\n", __func__);
return NULL;
}
/* Get a new Source CID */
channel->scid = ChannelAllocateCid(conn);
if (channel->scid != L2CAP_NULL_CID) {
/* Initialize channel */
channel->psm = psm;
channel->conn = conn;
channel->state = L2CAP_CHAN_CLOSED;
channel->cfgState = 0;
channel->endpoint = NULL;
// the last assigned CID should be the last in the list
// Think if keeping an ordered list will improve the search method
// as it is called in every reception
conn->ChannelList.Add(channel);
//conn->num_channels++;
// Any constance of the new channel created ...? ng_l2cap_con_ref(con);
} else {
ERROR("%s: no CID available\n", __func__);
delete channel;
channel = NULL;
}
return channel;
}
void
RemoveChannel(HciConnection* conn, uint16 scid)
{
//TODO make it safer
conn->ChannelList.Remove(ChannelBySourceID(conn, scid));
}

View File

@ -1,21 +0,0 @@
/*
* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _CHANNEL_INTERFACE_H
#define _CHANNEL_INTERFACE_H
#include <util/DoublyLinkedList.h>
#include <l2cap.h>
#include <btCoreData.h>
L2capChannel* AddChannel(HciConnection* conn, uint16 psm);
void RemoveChannel(HciConnection* conn, uint16 scid);
L2capChannel* ChannelBySourceID(HciConnection* conn, uint16 scid);
uint16 ChannelAllocateCid(HciConnection* conn);
uint16 ChannelAllocateIdent(HciConnection* conn);
#endif // _CHANNELINTERFACE_H

View File

@ -4,33 +4,59 @@
*/
#include <util/DoublyLinkedList.h>
#include <util/AutoLock.h>
#include <KernelExport.h>
#include <net_protocol.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/bdaddrUtils.h>
#include <bluetooth/L2CAP/btL2CAP.h>
#include <btDebug.h>
#include <btModules.h>
#include <l2cap.h>
#include "ConnectionInterface.h"
void PurgeChannels(HciConnection* conn);
struct net_protocol_module_info* L2cap = NULL;
extern net_buffer_module_info* gBufferModule;
HciConnection::HciConnection()
HciConnection::HciConnection(hci_id hid)
{
mutex_init(&fLock, "conn outgoing");
mutex_init(&fLockExpected, "frame expected");
mutex_init(&fLock, "HciConnection");
Hid = hid;
fNextIdent = L2CAP_FIRST_CID;
// TODO: This doesn't really belong here...
interface_address = {};
address_dl = {};
interface_address.local = (struct sockaddr*)&address_dl;
interface_address.destination = (struct sockaddr*)&address_dest;
address_dl.sdl_index = Hid;
}
HciConnection::~HciConnection()
{
if (L2cap == NULL)
if (get_module(NET_BLUETOOTH_L2CAP_NAME, (module_info**)&L2cap) != B_OK) {
ERROR("%s: cannot get module \"%s\"\n", __func__,
NET_BLUETOOTH_L2CAP_NAME);
} // TODO: someone put it
// Inform the L2CAP module this connection is about to be gone.
if (L2cap != NULL) {
net_buffer* error = gBufferModule->create(128);
error->interface_address = &interface_address;
if (L2cap->error_received(B_NET_ERROR_UNREACH_HOST, error) != B_OK) {
error->interface_address = NULL;
gBufferModule->free(error);
}
}
mutex_destroy(&fLock);
mutex_destroy(&fLockExpected);
}
@ -43,7 +69,7 @@ AddConnection(uint16 handle, int type, const bdaddr_t& dst, hci_id hid)
if (conn != NULL)
goto update;
conn = new (std::nothrow) HciConnection;
conn = new (std::nothrow) HciConnection(hid);
if (conn == NULL)
goto bail;
@ -54,15 +80,21 @@ AddConnection(uint16 handle, int type, const bdaddr_t& dst, hci_id hid)
update:
// fill values
bdaddrUtils::Copy(conn->destination, dst);
{
sockaddr_l2cap* destination = (sockaddr_l2cap*)&conn->address_dest;
destination->l2cap_len = sizeof(sockaddr_l2cap);
destination->l2cap_family = AF_BLUETOOTH;
destination->l2cap_bdaddr = dst;
}
conn->type = type;
conn->handle = handle;
conn->Hid = hid;
conn->status = HCI_CONN_OPEN;
conn->mtu = L2CAP_MTU_MINIMUM; // TODO: give the mtu to the connection
conn->lastCid = L2CAP_FIRST_CID;
conn->lastIdent = L2CAP_FIRST_IDENT;
{
MutexLocker _(&sConnectionListLock);
sConnectionList.Add(conn);
}
bail:
return conn;
@ -72,6 +104,7 @@ bail:
status_t
RemoveConnection(const bdaddr_t& destination, hci_id hid)
{
MutexLocker locker(&sConnectionListLock);
HciConnection* conn;
DoublyLinkedList<HciConnection>::Iterator iterator
@ -89,6 +122,7 @@ RemoveConnection(const bdaddr_t& destination, hci_id hid)
|| conn == sConnectionList.Head()) {
sConnectionList.Remove(conn);
locker.Unlock();
delete conn;
return B_OK;
}
@ -101,6 +135,7 @@ RemoveConnection(const bdaddr_t& destination, hci_id hid)
status_t
RemoveConnection(uint16 handle, hci_id hid)
{
MutexLocker locker(&sConnectionListLock);
HciConnection* conn;
DoublyLinkedList<HciConnection>::Iterator iterator
@ -116,7 +151,7 @@ RemoveConnection(uint16 handle, hci_id hid)
|| conn == sConnectionList.Head()) {
sConnectionList.Remove(conn);
PurgeChannels(conn);
locker.Unlock();
delete conn;
return B_OK;
}
@ -127,8 +162,9 @@ RemoveConnection(uint16 handle, hci_id hid)
hci_id
RouteConnection(const bdaddr_t& destination) {
RouteConnection(const bdaddr_t& destination)
{
MutexLocker _(&sConnectionListLock);
HciConnection* conn;
DoublyLinkedList<HciConnection>::Iterator iterator
@ -148,6 +184,7 @@ RouteConnection(const bdaddr_t& destination) {
HciConnection*
ConnectionByHandle(uint16 handle, hci_id hid)
{
MutexLocker _(&sConnectionListLock);
HciConnection* conn;
DoublyLinkedList<HciConnection>::Iterator iterator
@ -167,13 +204,13 @@ ConnectionByHandle(uint16 handle, hci_id hid)
HciConnection*
ConnectionByDestination(const bdaddr_t& destination, hci_id hid)
{
HciConnection* conn;
MutexLocker _(&sConnectionListLock);
DoublyLinkedList<HciConnection>::Iterator iterator
= sConnectionList.GetIterator();
while (iterator.HasNext()) {
conn = iterator.Next();
HciConnection* conn = iterator.Next();
if (conn->Hid == hid
&& bdaddrUtils::Compare(conn->destination, destination)) {
return conn;
@ -184,51 +221,47 @@ ConnectionByDestination(const bdaddr_t& destination, hci_id hid)
}
#if 0
#pragma mark - ACL helper funcs
#endif
void
SetAclBuffer(HciConnection* conn, net_buffer* nbuf)
uint8
allocate_command_ident(HciConnection* conn, void* pointer)
{
conn->currentRxPacket = nbuf;
MutexLocker _(&conn->fLock);
uint8 ident = conn->fNextIdent + 1;
if (ident < L2CAP_FIRST_IDENT)
ident = L2CAP_FIRST_IDENT;
while (ident != conn->fNextIdent) {
if (conn->fInUseIdents.Find(ident) == conn->fInUseIdents.End()) {
conn->fInUseIdents.Insert(ident, pointer);
return ident;
}
ident++;
if (ident < L2CAP_FIRST_IDENT)
ident = L2CAP_FIRST_IDENT;
}
return L2CAP_NULL_IDENT;
}
void*
lookup_command_ident(HciConnection* conn, uint8 ident)
{
MutexLocker _(&conn->fLock);
auto iter = conn->fInUseIdents.Find(ident);
if (iter == conn->fInUseIdents.End())
return NULL;
return iter->Value();
}
void
SetAclExpectedSize(HciConnection* conn, size_t size)
free_command_ident(HciConnection* conn, uint8 ident)
{
conn->currentRxExpectedLength = size;
}
void
AclPutting(HciConnection* conn, size_t size)
{
conn->currentRxExpectedLength -= size;
}
bool
AclComplete(HciConnection* conn)
{
return conn->currentRxExpectedLength == 0;
}
bool
AclOverFlowed(HciConnection* conn)
{
return conn->currentRxExpectedLength < 0;
}
#if 0
#pragma mark - private funcs
#endif
void
PurgeChannels(HciConnection* conn)
{
MutexLocker _(&conn->fLock);
conn->fInUseIdents.Remove(ident);
}

View File

@ -12,6 +12,7 @@
#include <btCoreData.h>
extern mutex sConnectionListLock;
extern DoublyLinkedList<HciConnection> sConnectionList;
HciConnection* ConnectionByHandle(uint16 handle, hci_id hid);
@ -26,11 +27,8 @@ status_t RemoveConnection(uint16 handle, hci_id hid);
hci_id RouteConnection(const bdaddr_t& destination);
void SetAclBuffer(HciConnection* conn, net_buffer* nbuf);
void SetAclExpectedSize(HciConnection* conn, size_t size);
void AclPutting(HciConnection* conn, size_t size);
bool AclComplete(HciConnection* conn);
bool AclOverFlowed(HciConnection* conn);
uint8 allocate_command_ident(HciConnection* conn, void* pointer);
void* lookup_command_ident(HciConnection* conn, uint8 ident);
void free_command_ident(HciConnection* conn, uint8 ident);
#endif // _CONNECTION_INTERFACE_H

View File

@ -1,149 +0,0 @@
/*
* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "FrameInterface.h"
#include <btDebug.h>
#include <lock.h>
L2capFrame*
SignalByIdent(HciConnection* conn, uint8 ident)
{
L2capFrame* frame;
mutex_lock(&conn->fLockExpected);
DoublyLinkedList<L2capFrame>::Iterator iterator
= conn->ExpectedResponses.GetIterator();
while (iterator.HasNext()) {
frame = iterator.Next();
if (frame->type == L2CAP_C_FRAME && frame->ident == ident) {
mutex_unlock(&frame->conn->fLockExpected);
return frame;
}
}
mutex_unlock(&conn->fLockExpected);
return NULL;
}
status_t
TimeoutSignal(L2capFrame* frame, uint32 timeo)
{
if (frame != NULL)
return B_OK;
return B_ERROR;
}
status_t
unTimeoutSignal(L2capFrame* frame)
{
if (frame != NULL)
return B_OK;
return B_ERROR;
}
L2capFrame*
SpawmFrame(HciConnection* conn, L2capChannel* channel, net_buffer* buffer,
frame_type type)
{
if (buffer == NULL)
panic("Null Buffer to outgoing queue");
L2capFrame* frame = new (std::nothrow) L2capFrame;
frame->conn = conn;
frame->channel = channel; // TODO: maybe only scid needed
frame->buffer = buffer;
frame->type = type;
mutex_lock(&conn->fLock);
conn->OutGoingFrames.Add(frame, true);
mutex_unlock(&conn->fLock);
return frame;
}
L2capFrame*
SpawmSignal(HciConnection* conn, L2capChannel* channel, net_buffer* buffer,
uint8 ident, uint8 code)
{
if (buffer == NULL)
panic("Null Buffer to outgoing queue");
L2capFrame* frame = new (std::nothrow) L2capFrame;
frame->conn = conn;
frame->channel = channel; // TODO: not specific descriptor should be required
frame->buffer = buffer;
frame->type = L2CAP_C_FRAME;
frame->ident = ident;
frame->code = code;
mutex_lock(&conn->fLock);
conn->OutGoingFrames.Add(frame, true);
mutex_unlock(&conn->fLock);
return frame;
}
status_t
AcknowledgeSignal(L2capFrame* frame)
{
if (frame != NULL) {
if (frame->type == L2CAP_C_FRAME) {
HciConnection* connection = frame->conn;
unTimeoutSignal(frame);
mutex_lock(&connection->fLockExpected);
connection->ExpectedResponses.Remove(frame);
mutex_unlock(&connection->fLockExpected);
}
// NO! This will be deleted by lower layers while being sent!
// gBufferModule->free(frame->buffer);
delete frame;
return B_OK;
}
return B_ERROR;
}
status_t
QueueSignal(L2capFrame* frame)
{
if (frame != NULL) {
if (frame->type == L2CAP_C_FRAME) {
mutex_lock(&frame->conn->fLockExpected);
frame->conn->ExpectedResponses.Add(frame);
mutex_unlock(&frame->conn->fLockExpected);
return B_OK;
}
}
return B_ERROR;
}

View File

@ -1,31 +0,0 @@
#ifndef _FRAME_INTERFACE_H
#define _FRAME_INTERFACE_H
#include <net_buffer.h>
#include <btCoreData.h>
extern net_buffer_module_info* gBufferModule;
L2capFrame*
SignalByIdent(HciConnection* conn, uint8 ident);
status_t
TimeoutSignal(L2capFrame* frame, uint32 timeo);
status_t
unTimeoutSignal(L2capFrame* frame);
L2capFrame*
SpawmFrame(HciConnection* conn, L2capChannel* channel, net_buffer* buffer, frame_type frame);
L2capFrame*
SpawmSignal(HciConnection* conn, L2capChannel* channel, net_buffer* buffer, uint8 ident, uint8 code);
status_t
AcknowledgeSignal(L2capFrame* frame);
status_t
QueueSignal(L2capFrame* frame);
#endif

View File

@ -1,19 +1,9 @@
SubDir HAIKU_TOP src add-ons kernel bluetooth btCoreData ;
UsePrivateHeaders kernel net bluetooth ;
UsePrivateHeaders [ FDirName kernel arch $(TARGET_KERNEL_ARCH_DIR) ] ;
#UsePrivateHeaders [ FDirName kernel boot platform $(TARGET_KERNEL_PLATFORM) ] ;
# disable debug output, if debugging is disabled
if $(DEBUG) = 0 {
SubDirCcFlags [ FDefines DEBUG_MAX_LEVEL_FLOW=0 DEBUG_MAX_LEVEL_INFO=0 ] ;
}
UsePrivateKernelHeaders ;
UsePrivateHeaders net bluetooth ;
KernelAddon btCoreData :
ConnectionInterface.cpp
ChannelInterface.cpp
FrameInterface.cpp
BTCoreData.cpp
;

View File

@ -51,13 +51,12 @@ AclAssembly(net_buffer* nbuf, hci_id hid)
return ENOBUFS;
}
// Get ACL connection handle, PB flag and payload length
aclHeader->handle = le16toh(aclHeader->handle);
aclHeader->handle = B_LENDIAN_TO_HOST_INT16(aclHeader->handle);
uint16 con_handle = get_acl_handle(aclHeader->handle);
uint16 pb = get_acl_pb_flag(aclHeader->handle);
uint16 length = le16toh(aclHeader->alen);
uint16 length = B_LENDIAN_TO_HOST_INT16(aclHeader->alen);
aclHeader.Remove();
@ -95,32 +94,26 @@ AclAssembly(net_buffer* nbuf, hci_id hid)
}
// Get L2CAP header, ACL header was dimissed
if (nbuf->size < sizeof(l2cap_hdr_t)) {
if (nbuf->size < sizeof(l2cap_basic_header)) {
TRACE("%s: Invalid L2CAP start fragment, small, length=%" B_PRIu32
"\n", __func__, nbuf->size);
gBufferModule->free(nbuf);
return (EMSGSIZE);
}
NetBufferHeaderReader<l2cap_hdr_t> l2capHeader(nbuf);
status_t status = l2capHeader.Status();
if (status < B_OK) {
NetBufferHeaderReader<l2cap_basic_header> l2capHeader(nbuf);
if (l2capHeader.Status() != B_OK) {
gBufferModule->free(nbuf);
return ENOBUFS;
}
l2capHeader->length = le16toh(l2capHeader->length);
l2capHeader->dcid = le16toh(l2capHeader->dcid);
TRACE("%s: New L2CAP, handle=%#x length=%d\n", __func__, con_handle,
le16toh(l2capHeader->length));
// Start new L2CAP packet
conn->currentRxPacket = nbuf;
conn->currentRxExpectedLength = l2capHeader->length + sizeof(l2cap_hdr_t);
conn->currentRxExpectedLength = B_LENDIAN_TO_HOST_INT16(l2capHeader->length)
+ sizeof(l2cap_basic_header);
} else if (pb == HCI_ACL_PACKET_FRAGMENT) {
if (conn->currentRxPacket == NULL) {
gBufferModule->free(nbuf);
@ -129,7 +122,6 @@ AclAssembly(net_buffer* nbuf, hci_id hid)
// Add fragment to the L2CAP packet
gBufferModule->merge(conn->currentRxPacket, nbuf, true);
} else {
ERROR("%s: invalid ACL data packet. Invalid PB flag=%#x\n", __func__,
pb);
@ -153,6 +145,10 @@ AclAssembly(net_buffer* nbuf, hci_id hid)
// OK, we have got complete L2CAP packet, so process it
TRACE("%s: L2cap packet ready %" B_PRIu32 " bytes\n", __func__,
conn->currentRxPacket->size);
memcpy(conn->currentRxPacket->source, &conn->address_dest, sizeof(sockaddr_storage));
conn->currentRxPacket->interface_address = &conn->interface_address;
error = PostToUpper(conn, conn->currentRxPacket);
// clean
conn->currentRxPacket = NULL;
@ -177,6 +173,5 @@ PostToUpper(HciConnection* conn, net_buffer* buf)
return B_ERROR;
} // TODO: someone put it
return L2cap->receive_data((net_buffer*)conn);// XXX pass handle in type
return L2cap->receive_data(buf);
}

View File

@ -8,12 +8,6 @@ KernelAddon l2cap :
l2cap_address.cpp
l2cap_command.cpp
l2cap_signal.cpp
l2cap_lower.cpp
l2cap_upper.cpp
# Stack subModules
L2capEndpoint.cpp
L2capEndpoint.cpp
L2capEndpointManager.cpp
;
# Installation
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/protocols
: l2cap ;

File diff suppressed because it is too large Load Diff

View File

@ -1,116 +1,124 @@
/*
* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
* Copyright 2008, Oliver Ruiz Dorantes. All rights reserved.
* Copyright 2024, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef L2CAP_ENDPOINT_H
#define L2CAP_ENDPOINT_H
#include <sys/stat.h>
#include <lock.h>
#include <util/DoublyLinkedList.h>
#include <util/OpenHashTable.h>
#include <condition_variable.h>
#include <util/AVLTree.h>
#include <net_protocol.h>
#include <net_socket.h>
#include <ProtocolUtilities.h>
#include <bluetooth/L2CAP/btL2CAP.h>
#include "l2cap_internal.h"
extern net_stack_module_info* gStackModule;
class L2capEndpoint : public net_protocol,
public ProtocolSocket,
public DoublyLinkedListLinkImpl<L2capEndpoint> {
class L2capEndpoint;
class L2capEndpoint : public net_protocol, public ProtocolSocket, public AVLTreeNode {
public:
L2capEndpoint(net_socket* socket);
virtual ~L2capEndpoint();
status_t Open();
status_t Shutdown();
status_t Close();
status_t Free();
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);
uint16 ChannelID() const { return fChannelID; }
ssize_t ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer);
ssize_t SendData(net_buffer* buffer);
status_t ReceiveData(net_buffer* buffer);
ssize_t Sendable();
ssize_t Receivable();
public:
L2capEndpoint(net_socket* socket);
virtual ~L2capEndpoint();
status_t Init();
void Uninit();
status_t Open();
status_t Close();
status_t Free();
bool Lock()
{
return mutex_lock(&fLock) == B_OK;
}
void Unlock()
{
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);
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 SendData(net_buffer* buffer);
ssize_t Sendable();
ssize_t Receivable();
status_t SetReceiveBufferSize(size_t size);
status_t GetPeerCredentials(ucred* credentials);
status_t Shutdown(int direction);
void BindNewEnpointToChannel(L2capChannel* channel);
void BindToChannel(L2capChannel* channel);
status_t MarkEstablished();
status_t MarkClosed();
static L2capEndpoint* ForPsm(uint16 psm);
bool RequiresConfiguration()
{
return fConfigurationSet;
}
ChannelConfiguration fConfiguration;
bool fConfigurationSet;
net_fifo fReceivingFifo;
void _HandleCommandRejected(uint8 ident, uint16 reason,
const l2cap_command_reject_data& data);
void _HandleConnectionReq(HciConnection* connection,
uint8 ident, uint16 psm, uint16 scid);
void _HandleConnectionRsp(uint8 ident, const l2cap_connection_rsp& response);
void _HandleConfigurationReq(uint8 ident, uint16 flags,
uint16* mtu, uint16* flush_timeout, l2cap_qos* flow);
void _HandleConfigurationRsp(uint8 ident, uint16 scid, uint16 flags,
uint16 result, uint16* mtu, uint16* flush_timeout, l2cap_qos* flow);
void _HandleDisconnectionReq(uint8 ident, uint16 scid);
void _HandleDisconnectionRsp(uint8 ident, uint16 dcid, uint16 scid);
private:
typedef enum {
// establishing a connection
inline bool
_IsEstablishing()
{
return fState > LISTEN && fState < OPEN;
}
status_t _WaitForStateChange(bigtime_t absoluteTimeout);
static void _SendTimer(net_timer* timer, void* _endpoint);
void _SendQueued();
void _SendChannelConfig();
status_t _MarkEstablished();
void _MarkClosed();
private:
friend class L2capEndpointManager;
sem_id fAcceptSemaphore;
mutex fLock;
enum channel_status {
CLOSED,
BOUND,
LISTEN,
CONNECTING,
ESTABLISHED,
// peer closes the connection
FINISH_RECEIVED,
WAIT_FOR_FINISH_ACKNOWLEDGE,
WAIT_FOR_CONNECTION_RSP,
CONFIGURATION,
OPEN,
WAIT_FOR_DISCONNECTION_RSP,
RECEIVED_DISCONNECTION_REQ,
} fState;
// we close the connection
FINISH_SENT,
FINISH_ACKNOWLEDGED,
CLOSING,
TIME_WAIT
} State;
HciConnection* fConnection;
mutex fLock;
State fState;
sem_id fEstablishSemaphore;
L2capEndpoint* fPeerEndpoint;
L2capChannel* fChannel;
ConditionVariable fCommandWait;
uint16 fChannelID, fDestinationChannelID;
net_fifo fReceiveQueue, fSendQueue;
net_timer fSendTimer;
struct ConfigState {
enum : uint8 {
NONE,
SENT,
DONE,
} in, out;
} fConfigState;
struct ChannelConfiguration {
uint16 incoming_mtu;
l2cap_qos incoming_flow;
uint16 outgoing_mtu;
l2cap_qos outgoing_flow;
uint16 flush_timeout;
uint16 link_timeout;
} fChannelConfig;
};
extern DoublyLinkedList<L2capEndpoint> EndpointList;
#endif // L2CAP_ENDPOINT_H

View File

@ -0,0 +1,137 @@
/*
* Copyright 2024, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "L2capEndpointManager.h"
#include <AutoDeleter.h>
#include <bluetooth/bdaddrUtils.h>
L2capEndpointManager gL2capEndpointManager;
L2capEndpointManager::L2capEndpointManager()
:
fNextChannelID(L2CAP_FIRST_CID)
{
rw_lock_init(&fBoundEndpointsLock, "l2cap bound endpoints");
rw_lock_init(&fChannelEndpointsLock, "l2cap channel endpoints");
}
L2capEndpointManager::~L2capEndpointManager()
{
rw_lock_destroy(&fBoundEndpointsLock);
rw_lock_destroy(&fChannelEndpointsLock);
}
status_t
L2capEndpointManager::Bind(L2capEndpoint* endpoint, const sockaddr_l2cap& address)
{
// TODO: Support binding to specific addresses?
if (!Bluetooth::bdaddrUtils::Compare(address.l2cap_bdaddr, BDADDR_ANY))
return EINVAL;
// PSM values must be odd.
if ((address.l2cap_psm & 1) == 0)
return EINVAL;
WriteLocker _(fBoundEndpointsLock);
if (fBoundEndpoints.Find(address.l2cap_psm) != NULL)
return EADDRINUSE;
memcpy(*endpoint->LocalAddress(), &address, sizeof(struct sockaddr_l2cap));
fBoundEndpoints.Insert(endpoint);
gSocketModule->acquire_socket(endpoint->socket);
return B_OK;
}
status_t
L2capEndpointManager::Unbind(L2capEndpoint* endpoint)
{
WriteLocker _(fBoundEndpointsLock);
fBoundEndpoints.Remove(endpoint);
(*endpoint->LocalAddress())->sa_len = 0;
gSocketModule->release_socket(endpoint->socket);
return B_OK;
}
L2capEndpoint*
L2capEndpointManager::ForPSM(uint16 psm)
{
ReadLocker _(fBoundEndpointsLock);
// TODO: Acquire reference?
return fBoundEndpoints.Find(psm);
}
status_t
L2capEndpointManager::BindToChannel(L2capEndpoint* endpoint)
{
WriteLocker _(fChannelEndpointsLock);
for (uint16 i = 0; i < (L2CAP_LAST_CID - L2CAP_FIRST_CID); i++) {
const uint16 cid = fNextChannelID;
fNextChannelID++;
if (fNextChannelID < L2CAP_FIRST_CID)
fNextChannelID = L2CAP_FIRST_CID;
if (fChannelEndpoints.Find(cid) != NULL)
continue;
endpoint->fChannelID = cid;
fChannelEndpoints.Insert(endpoint);
gSocketModule->acquire_socket(endpoint->socket);
return B_OK;
}
return EADDRINUSE;
}
status_t
L2capEndpointManager::UnbindFromChannel(L2capEndpoint* endpoint)
{
WriteLocker _(fChannelEndpointsLock);
fChannelEndpoints.Remove(endpoint);
endpoint->fChannelID = 0;
gSocketModule->release_socket(endpoint->socket);
return B_OK;
}
L2capEndpoint*
L2capEndpointManager::ForChannel(uint16 cid)
{
ReadLocker _(fChannelEndpointsLock);
// TODO: Acquire reference?
return fChannelEndpoints.Find(cid);
}
void
L2capEndpointManager::Disconnected(HciConnection* connection)
{
ReadLocker _(fChannelEndpointsLock);
auto iter = fChannelEndpoints.GetIterator();
while (iter.HasNext()) {
L2capEndpoint* endpoint = iter.Next();
if (endpoint->fConnection != connection)
continue;
endpoint->fConnection = NULL;
endpoint->fState = L2capEndpoint::CLOSED;
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2024, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef L2CAP_ENDPOINT_MANAGER_H
#define L2CAP_ENDPOINT_MANAGER_H
#include <util/AVLTree.h>
#include "L2capEndpoint.h"
class L2capEndpointManager {
public:
L2capEndpointManager();
~L2capEndpointManager();
status_t Bind(L2capEndpoint* endpoint, const sockaddr_l2cap& address);
status_t Unbind(L2capEndpoint* endpoint);
L2capEndpoint* ForPSM(uint16 psm);
status_t BindToChannel(L2capEndpoint* endpoint);
status_t UnbindFromChannel(L2capEndpoint* endpoint);
L2capEndpoint* ForChannel(uint16 cid);
void Disconnected(HciConnection* connection);
private:
struct EndpointTreeDefinitionBase {
typedef uint16 Key;
typedef L2capEndpoint Value;
AVLTreeNode* GetAVLTreeNode(Value* value) const
{
return value;
}
Value* GetValue(AVLTreeNode* node) const
{
return static_cast<Value*>(node);
}
};
struct EndpointBindTreeDefinition : public EndpointTreeDefinitionBase {
int Compare(const Key& a, const Value* b) const
{
return a - ((sockaddr_l2cap*)*b->LocalAddress())->l2cap_psm;
}
int Compare(const Value* a, const Value* b) const
{
return Compare(((sockaddr_l2cap*)*a->LocalAddress())->l2cap_psm, b);
}
};
struct EndpointChannelTreeDefinition : public EndpointTreeDefinitionBase {
int Compare(const Key& a, const Value* b) const
{
return a - b->ChannelID();
}
int Compare(const Value* a, const Value* b) const
{
return Compare(a->ChannelID(), b);
}
};
rw_lock fBoundEndpointsLock;
AVLTree<EndpointBindTreeDefinition> fBoundEndpoints;
rw_lock fChannelEndpointsLock;
uint16 fNextChannelID;
AVLTree<EndpointChannelTreeDefinition> fChannelEndpoints;
};
extern L2capEndpointManager gL2capEndpointManager;
#endif // L2CAP_ENDPOINT_MANAGER_H

View File

@ -1,56 +1,33 @@
/*
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*
* Copyright 2007, Oliver Ruiz Dorantes. All rights reserved.
* Copyright 2024, Haiku, Inc. 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 <net_datalink.h>
#include <net_protocol.h>
#include <net_stack.h>
#include <NetBufferUtilities.h>
#include <KernelExport.h>
#include <util/list.h>
#include <util/DoublyLinkedList.h>
#include <new>
#include <stdlib.h>
#include <string.h>
#include "l2cap_address.h"
#include "l2cap_command.h"
#include "l2cap_internal.h"
#include "l2cap_lower.h"
#include "l2cap_signal.h"
#include "L2capEndpoint.h"
#include "L2capEndpointManager.h"
#include <bluetooth/HCI/btHCI_acl.h>
#include <btModules.h>
#include <btDebug.h>
typedef NetBufferField<uint16, offsetof(hci_acl_header, alen)> AclLenField;
DoublyLinkedList<L2capEndpoint> EndpointList;
extern net_protocol_module_info gL2CAPModule;
// module references
bluetooth_core_data_module_info* btCoreData;
bt_hci_module_info* btDevices;
net_buffer_module_info* gBufferModule;
net_stack_module_info* gStackModule;
@ -58,6 +35,7 @@ net_socket_module_info* gSocketModule;
static struct net_domain* sDomain;
net_protocol*
l2cap_init_protocol(net_socket* socket)
{
@ -67,8 +45,6 @@ l2cap_init_protocol(net_socket* socket)
if (protocol == NULL)
return NULL;
EndpointList.Add(protocol);
return protocol;
}
@ -79,10 +55,6 @@ l2cap_uninit_protocol(net_protocol* protocol)
CALLED();
L2capEndpoint* endpoint = static_cast<L2capEndpoint*>(protocol);
// TODO: Some more checkins / uninit
EndpointList.Remove(endpoint);
delete endpoint;
return B_OK;
@ -92,53 +64,34 @@ l2cap_uninit_protocol(net_protocol* protocol)
status_t
l2cap_open(net_protocol* protocol)
{
CALLED();
return B_OK;
return ((L2capEndpoint*)protocol)->Open();
}
status_t
l2cap_close(net_protocol* protocol)
{
L2capEndpoint* endpoint = static_cast<L2capEndpoint*>(protocol);
CALLED();
endpoint->Close();
return B_OK;
return ((L2capEndpoint*)protocol)->Close();
}
status_t
l2cap_free(net_protocol* protocol)
{
CALLED();
return B_OK;
return ((L2capEndpoint*)protocol)->Free();
}
status_t
l2cap_connect(net_protocol* protocol, const struct sockaddr* address)
{
CALLED();
if (address == NULL)
return EINVAL;
if (address->sa_family != AF_BLUETOOTH)
return EAFNOSUPPORT;
return ((L2capEndpoint*)protocol)->Connect(address);;
return ((L2capEndpoint*)protocol)->Connect(address);
}
status_t
l2cap_accept(net_protocol* protocol, struct net_socket** _acceptedSocket)
{
CALLED();
return ((L2capEndpoint*)protocol)->Accept(_acceptedSocket);
}
@ -154,27 +107,27 @@ l2cap_control(net_protocol* protocol, int level, int option, void* value,
status_t
l2cap_getsockopt(net_protocol* protocol, int level, int option,
void* value, int* length)
void* value, int* _length)
{
CALLED();
return B_OK;
return gSocketModule->get_option(protocol->socket, level, option, value,
_length);
}
status_t
l2cap_setsockopt(net_protocol* protocol, int level, int option,
const void* value, int length)
const void* _value, int length)
{
CALLED();
((L2capEndpoint*)protocol)->fConfigurationSet = true;
return B_OK;
return gSocketModule->set_option(protocol->socket, level, option,
_value, length);
}
status_t
l2cap_bind(net_protocol* protocol, const struct sockaddr* address)
{
CALLED();
return ((L2capEndpoint*)protocol)->Bind(address);
}
@ -182,15 +135,13 @@ l2cap_bind(net_protocol* protocol, const struct sockaddr* address)
status_t
l2cap_unbind(net_protocol* protocol, struct sockaddr* address)
{
CALLED();
return B_ERROR;
return ((L2capEndpoint*)protocol)->Unbind();
}
status_t
l2cap_listen(net_protocol* protocol, int count)
{
CALLED();
return ((L2capEndpoint*)protocol)->Listen(count);
}
@ -198,19 +149,15 @@ l2cap_listen(net_protocol* protocol, int count)
status_t
l2cap_shutdown(net_protocol* protocol, int direction)
{
CALLED();
return EOPNOTSUPP;
if (direction != SHUT_RDWR)
return EOPNOTSUPP;
return ((L2capEndpoint*)protocol)->Shutdown();
}
status_t
l2cap_send_data(net_protocol* protocol, net_buffer* buffer)
{
CALLED();
if (buffer == NULL)
return ENOBUFS;
return ((L2capEndpoint*)protocol)->SendData(buffer);
}
@ -227,8 +174,7 @@ l2cap_send_routed_data(net_protocol* protocol, struct net_route* route,
ssize_t
l2cap_send_avail(net_protocol* protocol)
{
CALLED();
return B_ERROR;
return ((L2capEndpoint*)protocol)->Sendable();
}
@ -236,7 +182,6 @@ status_t
l2cap_read_data(net_protocol* protocol, size_t numBytes, uint32 flags,
net_buffer** _buffer)
{
CALLED();
return ((L2capEndpoint*)protocol)->ReadData(numBytes, flags, _buffer);
}
@ -244,15 +189,13 @@ l2cap_read_data(net_protocol* protocol, size_t numBytes, uint32 flags,
ssize_t
l2cap_read_avail(net_protocol* protocol)
{
CALLED();
return B_ERROR;
return ((L2capEndpoint*)protocol)->Receivable();
}
struct net_domain*
l2cap_get_domain(net_protocol* protocol)
{
CALLED();
return sDomain;
}
@ -265,16 +208,90 @@ l2cap_get_mtu(net_protocol* protocol, const struct sockaddr* address)
}
static HciConnection*
connection_for(net_buffer* buffer)
{
const sockaddr_l2cap* l2capAddr = (sockaddr_l2cap*)buffer->source;
const sockaddr_dl* interfaceAddr = (sockaddr_dl*)buffer->interface_address->local;
struct HciConnection* connection = btCoreData->ConnectionByDestination(
l2capAddr->l2cap_bdaddr, interfaceAddr->sdl_index);
buffer->interface_address = NULL;
// This isn't a real interface_address; it could confuse the buffer module.
// FIXME: We probably should have an alternate interface for passing along data.
return connection;
}
status_t
l2cap_receive_data(net_buffer* buffer)
{
HciConnection* conn = (HciConnection*)buffer;
TRACE("%s: received some data, buffer length %" B_PRIu32 "\n", __func__,
conn->currentRxPacket->size);
if (buffer->size < sizeof(l2cap_basic_header)) {
ERROR("%s: invalid L2CAP packet: too small. len=%" B_PRIu32 "\n",
__func__, buffer->size);
gBufferModule->free(buffer);
return EMSGSIZE;
}
l2cap_receive(conn, conn->currentRxPacket);
NetBufferHeaderReader<l2cap_basic_header> bufferHeader(buffer);
if (bufferHeader.Status() != B_OK)
return ENOBUFS;
return B_OK;
uint16 length = le16toh(bufferHeader->length);
uint16 dcid = le16toh(bufferHeader->dcid);
TRACE("%s: len=%d cid=%x\n", __func__, length, dcid);
bufferHeader.Remove();
if (length != buffer->size) {
ERROR("l2cap: payload length mismatch, packetlen=%d, bufferlen=%" B_PRIu32 "\n",
length, buffer->size);
return EMSGSIZE;
}
status_t status = B_ERROR;
switch (dcid) {
case L2CAP_SIGNALING_CID:
{
// We need to find the connection this packet is associated with.
struct HciConnection* connection = connection_for(buffer);
if (connection == NULL) {
panic("no connection for received L2CAP command");
return ENOTCONN;
}
status = l2cap_handle_signaling_command(connection, buffer);
break;
}
case L2CAP_CONNECTIONLESS_CID:
{
NetBufferHeaderReader<l2cap_connectionless_header> connlessHeader(buffer);
const uint16 psm = le16toh(connlessHeader->psm);
L2capEndpoint* endpoint = gL2capEndpointManager.ForPSM(psm);
if (endpoint == NULL)
return ECONNRESET;
connlessHeader.Remove();
buffer->interface_address = NULL;
status = endpoint->ReceiveData(buffer);
break;
}
default:
{
L2capEndpoint* endpoint = gL2capEndpointManager.ForChannel(dcid);
if (endpoint == NULL)
return ECONNRESET;
buffer->interface_address = NULL;
status = endpoint->ReceiveData(buffer);
break;
}
}
return status;
}
@ -283,6 +300,18 @@ l2cap_error_received(net_error error, net_buffer* data)
{
CALLED();
if (error == B_NET_ERROR_UNREACH_HOST) {
struct HciConnection* connection = connection_for(data);
if (connection == NULL)
return ENOTCONN;
// Disconnect all connections with this HciConnection.
gL2capEndpointManager.Disconnected(connection);
gBufferModule->free(data);
return B_OK;
}
return B_ERROR;
}
@ -292,7 +321,6 @@ l2cap_error_reply(net_protocol* protocol, net_buffer* cause, net_error error,
net_error_data* errorData)
{
CALLED();
return B_ERROR;
}
@ -310,37 +338,25 @@ l2cap_std_ops(int32 op, ...)
switch (op) {
case B_MODULE_INIT:
{
new(&gL2capEndpointManager) L2capEndpointManager;
error = gStackModule->register_domain_protocols(AF_BLUETOOTH,
SOCK_STREAM, BLUETOOTH_PROTO_L2CAP,
"network/protocols/l2cap/v1",
NULL);
if (error != B_OK)
return error;
error = gStackModule->register_domain_receiving_protocol(
AF_BLUETOOTH,
BLUETOOTH_PROTO_L2CAP,
"network/protocols/l2cap/v1");
NET_BLUETOOTH_L2CAP_NAME, NULL);
if (error != B_OK)
return error;
error = gStackModule->register_domain(AF_BLUETOOTH, "l2cap",
&gL2CAPModule, &gL2cap4AddressModule, &sDomain);
&gL2CAPModule, &gL2capAddressModule, &sDomain);
if (error != B_OK)
return error;
new (&EndpointList) DoublyLinkedList<L2capEndpoint>;
error = InitializeConnectionPurgeThread();
return B_OK;
}
case B_MODULE_UNINIT:
error = QuitConnectionPurgeThread();
gL2capEndpointManager.~L2capEndpointManager();
gStackModule->unregister_domain(sDomain);
return B_OK;
default:
@ -352,7 +368,7 @@ l2cap_std_ops(int32 op, ...)
net_protocol_module_info gL2CAPModule = {
{
NET_BLUETOOTH_L2CAP_NAME,
B_KEEP_LOADED,
0,
l2cap_std_ops
},
NET_PROTOCOL_ATOMIC_MESSAGES,
@ -392,8 +408,9 @@ net_protocol_module_info gL2CAPModule = {
module_dependency module_dependencies[] = {
{NET_STACK_MODULE_NAME, (module_info**)&gStackModule},
{NET_BUFFER_MODULE_NAME, (module_info**)&gBufferModule},
{BT_CORE_DATA_MODULE_NAME, (module_info**)&btCoreData},
{NET_SOCKET_MODULE_NAME, (module_info**)&gSocketModule},
{BT_CORE_DATA_MODULE_NAME, (module_info**)&btCoreData},
{BT_HCI_MODULE_NAME, (module_info**)&btDevices},
{}
};

View File

@ -1,503 +1,256 @@
/*
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*
* Copyright 2007 Oliver Ruiz Dorantes. All rights reserved.
* Copyright 2024, Haiku, Inc. 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 "l2cap_command.h"
#include <NetBufferUtilities.h>
#include "l2cap_command.h"
/*
* Note: All L2CAP implementations are required to support minimal signaling
* MTU of 48 bytes. In order to simplify things we will send one command
* per one L2CAP packet. Given evrything above we can assume that one
* signaling packet will fit into single mbuf.
*/
/* Private types */
struct _cmd_rej {
l2cap_cmd_hdr_t hdr;
l2cap_cmd_rej_cp param;
l2cap_cmd_rej_data_t data;
} __attribute__ ((packed)) ;
struct _con_req {
l2cap_cmd_hdr_t hdr;
l2cap_con_req_cp param;
} __attribute__ ((packed));
struct _con_rsp {
l2cap_cmd_hdr_t hdr;
l2cap_con_rsp_cp param;
} __attribute__ ((packed));
struct _cfg_req {
l2cap_cmd_hdr_t hdr;
l2cap_cfg_req_cp param;
} __attribute__ ((packed));
struct _cfg_rsp {
l2cap_cmd_hdr_t hdr;
l2cap_cfg_rsp_cp param;
} __attribute__ ((packed));
struct _discon_req {
l2cap_cmd_hdr_t hdr;
l2cap_discon_req_cp param;
} __attribute__ ((packed));
struct _discon_rsp {
l2cap_cmd_hdr_t hdr;
l2cap_discon_rsp_cp param;
} __attribute__ ((packed));
struct _info_req {
l2cap_cmd_hdr_t hdr;
l2cap_info_req_cp param;
} __attribute__ ((packed));
struct _info_rsp {
l2cap_cmd_hdr_t hdr;
l2cap_info_rsp_cp param;
l2cap_info_rsp_data_t data;
} __attribute__ ((packed));
// Configuration options
struct _cfg_opt_flow {
l2cap_cfg_opt_t hdr;
l2cap_flow_t val;
} __attribute__ ((packed));
struct _cfg_opt_flush {
l2cap_cfg_opt_t hdr;
uint16 val;
} __attribute__ ((packed));
struct _cfg_opt_mtu {
l2cap_cfg_opt_t hdr;
uint16 val;
} __attribute__ ((packed));
/* L2CAP_CommandRej */
net_buffer*
l2cap_cmd_rej(uint8 _ident, uint16 _reason, uint16 _mtu, uint16 _scid, uint16 _dcid)
make_l2cap_command_reject(uint8& code, uint16 reason, uint16 mtu, uint16 scid, uint16 dcid)
{
net_buffer* _m = gBufferModule->create(sizeof(struct _cmd_rej));
if ((_m) == NULL)
NetBufferDeleter<> buffer(gBufferModule->create(128));
if (!buffer.IsSet())
return NULL;
NetBufferPrepend<struct _cmd_rej> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
// free the buffer
if (reason == l2cap_command_reject::REJECTED_MTU_EXCEEDED) {
NetBufferPrepend<uint16> data(buffer.Get());
*data = htole16(mtu);
} else if (reason == l2cap_command_reject::REJECTED_INVALID_CID) {
NetBufferPrepend<l2cap_command_reject_data> data(buffer.Get());
data->invalid_cid.scid = htole16(scid);
data->invalid_cid.dcid = htole16(dcid);
}
NetBufferPrepend<l2cap_command_reject> command(buffer.Get());
if (command.Status() != B_OK)
return NULL;
}
bufferHeader->hdr.code = L2CAP_CMD_REJ;
bufferHeader->hdr.ident = (_ident);
bufferHeader->hdr.length = sizeof(bufferHeader->param);
code = L2CAP_COMMAND_REJECT_RSP;
command->reason = (uint16)htole16(reason);
bufferHeader->param.reason = htole16((_reason));
if ((_reason) == L2CAP_REJ_MTU_EXCEEDED) {
bufferHeader->data.mtu.mtu = htole16((_mtu));
bufferHeader->hdr.length += sizeof(bufferHeader->data.mtu);
} else if ((_reason) == L2CAP_REJ_INVALID_CID) {
bufferHeader->data.cid.scid = htole16((_scid));
bufferHeader->data.cid.dcid = htole16((_dcid));
bufferHeader->hdr.length += sizeof(bufferHeader->data.cid);
}
bufferHeader->hdr.length = htole16(bufferHeader->hdr.length);
bufferHeader.Sync();
return _m;
return buffer.Detach();
}
/* L2CAP_ConnectReq */
net_buffer*
l2cap_con_req(uint8 _ident, uint16 _psm, uint16 _scid)
make_l2cap_connection_req(uint8& code, uint16 psm, uint16 scid)
{
net_buffer* _m = gBufferModule->create(sizeof(struct _con_req));
if ((_m) == NULL)
NetBufferDeleter<> buffer(gBufferModule->create(128));
if (!buffer.IsSet())
return NULL;
NetBufferPrepend<struct _con_req> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
/* TODO free the buffer */
NetBufferPrepend<l2cap_connection_req> command(buffer.Get());
if (command.Status() != B_OK)
return NULL;
}
bufferHeader->hdr.code = L2CAP_CON_REQ;
bufferHeader->hdr.ident = (_ident);
bufferHeader->hdr.length = htole16(sizeof(bufferHeader->param));
code = L2CAP_CONNECTION_REQ;
command->psm = htole16(psm);
command->scid = htole16(scid);
bufferHeader->param.psm = htole16((_psm));
bufferHeader->param.scid = htole16((_scid));
bufferHeader.Sync();
return _m;
return buffer.Detach();
}
/* L2CAP_ConnectRsp */
net_buffer*
l2cap_con_rsp(uint8 _ident, uint16 _dcid, uint16 _scid, uint16 _result, uint16 _status)
make_l2cap_connection_rsp(uint8& code, uint16 dcid, uint16 scid, uint16 result, uint16 status)
{
net_buffer* _m = gBufferModule->create(sizeof(struct _con_rsp));
if ((_m) == NULL)
NetBufferDeleter<> buffer(gBufferModule->create(128));
if (!buffer.IsSet())
return NULL;
NetBufferPrepend<struct _con_rsp> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
/* TODO free the buffer */
NetBufferPrepend<l2cap_connection_rsp> command(buffer.Get());
if (command.Status() != B_OK)
return NULL;
}
bufferHeader->hdr.code = L2CAP_CON_RSP;
bufferHeader->hdr.ident = (_ident);
bufferHeader->hdr.length = htole16(sizeof(bufferHeader->param));
code = L2CAP_CONNECTION_RSP;
command->dcid = htole16(dcid);
command->scid = htole16(scid);
command->result = htole16(result);
command->status = htole16(status);
bufferHeader->param.dcid = htole16((_dcid));
bufferHeader->param.scid = htole16((_scid));
bufferHeader->param.result = htole16((_result));
bufferHeader->param.status = htole16((_status)); /* reason */
bufferHeader.Sync();
return _m;
return buffer.Detach();
}
/* L2CAP_ConfigReq */
net_buffer*
l2cap_cfg_req(uint8 _ident, uint16 _dcid, uint16 _flags, net_buffer* _data)
make_l2cap_configuration_req(uint8& code, uint16 dcid, uint16 flags,
uint16* mtu, uint16* flush_timeout, l2cap_qos* flow)
{
net_buffer* _m = gBufferModule->create(sizeof(struct _cfg_req));
if ((_m) == NULL){
/* TODO free the _data buffer? */
NetBufferDeleter<> buffer(gBufferModule->create(128));
if (!buffer.IsSet())
return NULL;
if (mtu != NULL) {
struct config_option_mtu {
l2cap_configuration_option header;
uint16 value;
} _PACKED;
NetBufferPrepend<config_option_mtu> option(buffer.Get());
if (option.Status() != B_OK)
return NULL;
option->header.type = l2cap_configuration_option::OPTION_MTU;
option->header.length = sizeof(option->value);
option->value = htole16(*mtu);
}
NetBufferPrepend<struct _cfg_req> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
/* TODO free the buffer */
return NULL;
if (flush_timeout != NULL) {
struct config_option_flush_timeout {
l2cap_configuration_option header;
uint16 value;
} _PACKED;
NetBufferPrepend<config_option_flush_timeout> option(buffer.Get());
if (option.Status() != B_OK)
return NULL;
option->header.type = l2cap_configuration_option::OPTION_FLUSH_TIMEOUT;
option->header.length = sizeof(option->value);
option->value = htole16(*flush_timeout);
}
bufferHeader->hdr.code = L2CAP_CFG_REQ;
bufferHeader->hdr.ident = (_ident);
bufferHeader->hdr.length = htole16(sizeof(bufferHeader->param));
if (flow != NULL) {
struct config_option_flow {
l2cap_configuration_option header;
l2cap_qos value;
} _PACKED;
NetBufferPrepend<config_option_flow> option(buffer.Get());
if (option.Status() != B_OK)
return NULL;
bufferHeader->param.dcid = htole16((_dcid));
bufferHeader->param.flags = htole16((_flags));
option->header.type = l2cap_configuration_option::OPTION_QOS;
option->header.length = sizeof(option->value);
option->value.flags = flow->flags;
option->value.service_type = flow->service_type;
option->value.token_rate = htole32(flow->token_rate);
option->value.token_bucket_size = htole32(flow->token_bucket_size);
option->value.peak_bandwidth = htole32(flow->peak_bandwidth);
option->value.access_latency = htole32(flow->access_latency);
option->value.delay_variation = htole32(flow->delay_variation);
}
bufferHeader.Sync();
NetBufferPrepend<l2cap_configuration_req> command(buffer.Get());
if (command.Status() != B_OK)
return NULL;
/* Add the given data */
// TODO: given data can be freed... merge does it?
if (_data != NULL)
gBufferModule->merge(_m, _data, true);
code = L2CAP_CONFIGURATION_REQ;
command->dcid = htole16(dcid);
command->flags = htole16(flags);
return _m;
return buffer.Detach();
}
/* L2CAP_ConfigRsp */
net_buffer*
l2cap_cfg_rsp(uint8 _ident, uint16 _scid, uint16 _flags, uint16 _result, net_buffer* _data)
make_l2cap_configuration_rsp(uint8& code, uint16 scid, uint16 flags,
uint16 result, net_buffer* opt)
{
net_buffer* _m = gBufferModule->create(sizeof(struct _cfg_rsp));
if ((_m) == NULL){
/* TODO free the _data buffer */
NetBufferDeleter<> buffer(gBufferModule->create(128));
if (!buffer.IsSet())
return NULL;
NetBufferPrepend<l2cap_configuration_rsp> command(buffer.Get());
if (command.Status() != B_OK)
return NULL;
code = L2CAP_CONFIGURATION_RSP;
command->scid = htole16(scid);
command->flags = htole16(flags);
command->result = htole16(result);
if (opt != NULL) {
if (gBufferModule->append_cloned(buffer.Get(), opt, 0, opt->size) != B_OK)
return NULL;
}
NetBufferPrepend<struct _cfg_rsp> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
/* TODO free the buffer */
return NULL;
}
bufferHeader->hdr.code = L2CAP_CFG_RSP;
bufferHeader->hdr.ident = (_ident);
bufferHeader->hdr.length = htole16(sizeof(bufferHeader->param));
bufferHeader->param.scid = htole16((_scid));
bufferHeader->param.flags = htole16((_flags));
bufferHeader->param.result = htole16((_result));
bufferHeader.Sync();
if (_data != NULL)
gBufferModule->merge(_m, _data, true);
return _m;
return buffer.Detach();
}
/* L2CAP_DisconnectReq */
net_buffer*
l2cap_discon_req(uint8 _ident, uint16 _dcid, uint16 _scid)
make_l2cap_disconnection_req(uint8& code, uint16 dcid, uint16 scid)
{
net_buffer* _m = gBufferModule->create(sizeof(struct _discon_req));
if ((_m) == NULL){
NetBufferDeleter<> buffer(gBufferModule->create(128));
if (!buffer.IsSet())
return NULL;
}
NetBufferPrepend<struct _discon_req> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
/* TODO free the buffer */
NetBufferPrepend<l2cap_disconnection_req> command(buffer.Get());
if (command.Status() != B_OK)
return NULL;
}
bufferHeader->hdr.code = L2CAP_DISCON_REQ;
bufferHeader->hdr.ident = (_ident);
bufferHeader->hdr.length = htole16(sizeof(bufferHeader->param));
code = L2CAP_DISCONNECTION_REQ;
command->dcid = htole16(dcid);
command->scid = htole16(scid);
bufferHeader->param.dcid = htole16((_dcid));
bufferHeader->param.scid = htole16((_scid));
bufferHeader.Sync();
return _m;
return buffer.Detach();
}
/* L2CA_DisconnectRsp */
net_buffer*
l2cap_discon_rsp(uint8 _ident, uint16 _dcid, uint16 _scid)
make_l2cap_disconnection_rsp(uint8& code, uint16 dcid, uint16 scid)
{
net_buffer* _m = gBufferModule->create(sizeof(struct _discon_rsp));
if ((_m) == NULL){
NetBufferDeleter<> buffer(gBufferModule->create(128));
if (!buffer.IsSet())
return NULL;
}
NetBufferPrepend<struct _discon_rsp> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
/* TODO free the buffer */
NetBufferPrepend<l2cap_disconnection_rsp> command(buffer.Get());
if (command.Status() != B_OK)
return NULL;
}
bufferHeader->hdr.code = L2CAP_DISCON_RSP;
bufferHeader->hdr.ident = (_ident);
bufferHeader->hdr.length = htole16(sizeof(bufferHeader->param));
code = L2CAP_DISCONNECTION_RSP;
command->dcid = htole16(dcid);
command->scid = htole16(scid);
bufferHeader->param.dcid = htole16((_dcid));
bufferHeader->param.scid = htole16((_scid));
bufferHeader.Sync();
return _m;
return buffer.Detach();
}
/* L2CAP_EchoReq */
net_buffer*
l2cap_echo_req(uint8 _ident, void* _data, size_t _size)
make_l2cap_information_req(uint8& code, uint16 type)
{
net_buffer* _m = gBufferModule->create(sizeof(l2cap_cmd_hdr_t));
if ((_m) == NULL){
/* TODO free the _data buffer */
NetBufferDeleter<> buffer(gBufferModule->create(128));
if (!buffer.IsSet())
return NULL;
}
NetBufferPrepend<l2cap_information_req> command(buffer.Get());
if (command.Status() != B_OK)
return NULL;
code = L2CAP_INFORMATION_REQ;
if ((_data) != NULL) {
gBufferModule->append(_m, _data, _size);
}
command->type = htole16(type);
return _m;
return buffer.Detach();
}
/* L2CAP_InfoReq */
net_buffer*
l2cap_info_req(uint8 _ident, uint16 _type)
make_l2cap_information_rsp(uint8& code, uint16 type, uint16 result, uint16 _mtu)
{
net_buffer* _m = gBufferModule->create(sizeof(struct _info_req));
if ((_m) == NULL){
NetBufferDeleter<> buffer(gBufferModule->create(128));
if (!buffer.IsSet())
return NULL;
}
NetBufferPrepend<struct _info_req> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
/* TODO free the buffer */
NetBufferPrepend<l2cap_information_rsp> command(buffer.Get());
if (command.Status() != B_OK)
return NULL;
}
bufferHeader->hdr.code = L2CAP_INFO_REQ;
bufferHeader->hdr.ident = (_ident);
bufferHeader->hdr.length = htole16(sizeof(bufferHeader->param));
code = L2CAP_INFORMATION_RSP;
bufferHeader->param.type = htole16((_type));
command->type = htole16(type);
command->result = htole16(result);
bufferHeader.Sync();
return _m;
}
/* L2CAP_InfoRsp */
net_buffer*
l2cap_info_rsp(uint8 _ident, uint16 _type, uint16 _result, uint16 _mtu)
{
net_buffer* _m = gBufferModule->create(sizeof(struct _info_rsp));
if ((_m) == NULL){
return NULL;
}
NetBufferPrepend<struct _info_rsp> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
/* TODO free the buffer */
return NULL;
}
bufferHeader->hdr.code = L2CAP_INFO_REQ;
bufferHeader->hdr.ident = (_ident);
bufferHeader->hdr.length = sizeof(bufferHeader->param);
bufferHeader->param.type = htole16((_type));
bufferHeader->param.result = htole16((_result));
if ((_result) == L2CAP_SUCCESS) {
switch ((_type)) {
case L2CAP_CONNLESS_MTU:
bufferHeader->data.mtu.mtu = htole16((_mtu));
bufferHeader->hdr.length += sizeof((bufferHeader->data.mtu.mtu));
if (result == l2cap_information_rsp::RESULT_SUCCESS) {
switch (type) {
case l2cap_information_req::TYPE_CONNECTIONLESS_MTU: {
uint16 mtu = htole16(_mtu);
gBufferModule->append(buffer.Get(), &mtu, sizeof(mtu));
break;
}
}
}
bufferHeader->hdr.length = htole16(bufferHeader->hdr.length);
bufferHeader.Sync();
return _m;
}
#if 0
#pragma mark -
#endif
/* Build configuration options */
net_buffer*
l2cap_build_cfg_options(uint16* _mtu, uint16* _flush_timo, l2cap_flow_t* _flow)
{
size_t requestedSize = 0;
if (_mtu != NULL)
requestedSize+=sizeof(*_mtu);
if (_flush_timo != NULL)
requestedSize+=sizeof(*_flush_timo);
if (_flow != NULL)
requestedSize+=sizeof(*_flow);
net_buffer* _m = gBufferModule->create(sizeof(requestedSize));
if (_m == NULL)
return NULL;
if (_mtu != NULL) {
NetBufferPrepend<struct _cfg_opt_mtu> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
/* TODO free the buffer ?? */
return NULL;
}
bufferHeader->hdr.type = L2CAP_OPT_MTU;
bufferHeader->hdr.length = sizeof(bufferHeader->val);
bufferHeader->val = htole16(*(uint16 *)(_mtu));
bufferHeader.Sync();
}
if (_flush_timo != NULL) {
NetBufferPrepend<struct _cfg_opt_flush> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
/* TODO free the buffer ?? */
return NULL;
}
bufferHeader->hdr.type = L2CAP_OPT_FLUSH_TIMO;
bufferHeader->hdr.length = sizeof(bufferHeader->val);
bufferHeader->val = htole16(*(int16 *)(_flush_timo));
bufferHeader.Sync();
}
if (_flow != NULL) {
NetBufferPrepend<struct _cfg_opt_flow> bufferHeader(_m);
status_t status = bufferHeader.Status();
if (status < B_OK) {
/* TODO free the buffer ?? */
return NULL;
}
bufferHeader->hdr.type = L2CAP_OPT_QOS;
bufferHeader->hdr.length = sizeof(bufferHeader->val);
bufferHeader->val.flags = _flow->flags;
bufferHeader->val.service_type = _flow->service_type;
bufferHeader->val.token_rate = htole32(_flow->token_rate);
bufferHeader->val.token_bucket_size = htole32(_flow->token_bucket_size);
bufferHeader->val.peak_bandwidth = htole32(_flow->peak_bandwidth);
bufferHeader->val.latency = htole32(_flow->latency);
bufferHeader->val.delay_variation = htole32(_flow->delay_variation);
bufferHeader.Sync();
}
return _m;
return buffer.Detach();
}

View File

@ -1,48 +1,39 @@
/*
* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
* Copyright 2024, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _L2CAP_CMDS_H_
#define _L2CAP_CMDS_H_
#ifndef _L2CAP_COMMAND_H_
#define _L2CAP_COMMAND_H_
#include <ByteOrder.h>
#include <NetBufferUtilities.h>
#include <l2cap.h>
#include <bluetooth/l2cap.h>
extern net_buffer_module_info *gBufferModule;
#define htole16(x) B_HOST_TO_LENDIAN_INT16(x)
#define le16toh(x) B_LENDIAN_TO_HOST_INT16(x)
#define htole32(x) B_HOST_TO_LENDIAN_INT16(x)
#define le32toh(x) B_LENDIAN_TO_HOST_INT32(x)
net_buffer*
l2cap_cmd_rej(uint8 _ident, uint16 _reason, uint16 _mtu, uint16 _scid, uint16 _dcid);
net_buffer*
l2cap_con_req(uint8 _ident, uint16 _psm, uint16 _scid);
net_buffer* make_l2cap_command_reject(uint8& code,
uint16 reason, uint16 mtu, uint16 scid, uint16 dcid);
net_buffer*
l2cap_con_rsp(uint8 _ident, uint16 _dcid, uint16 _scid, uint16 _result, uint16 _status);
net_buffer* make_l2cap_connection_req(uint8& code, uint16 psm, uint16 scid);
net_buffer* make_l2cap_connection_rsp(uint8& code,
uint16 dcid, uint16 scid, uint16 result, uint16 status);
net_buffer*
l2cap_cfg_req(uint8 _ident, uint16 _dcid, uint16 _flags, net_buffer* _data);
net_buffer* make_l2cap_configuration_req(uint8& code, uint16 dcid, uint16 flags,
uint16* mtu, uint16* flush_timeout, l2cap_qos* flow);
net_buffer* make_l2cap_configuration_rsp(uint8& code, uint16 scid, uint16 flags,
uint16 result, net_buffer* opt);
net_buffer*
l2cap_cfg_rsp(uint8 _ident, uint16 _scid, uint16 _flags, uint16 _result, net_buffer* _data);
net_buffer* make_l2cap_disconnection_req(uint8& code, uint16 dcid, uint16 scid);
net_buffer* make_l2cap_disconnection_rsp(uint8& code, uint16 dcid, uint16 scid);
net_buffer*
l2cap_build_cfg_options(uint16* _mtu, uint16* _flush_timo, l2cap_flow_t* _flow);
net_buffer* make_l2cap_information_req(uint8& code, uint16 type);
net_buffer* make_l2cap_information_rsp(uint8& code, uint16 type, uint16 result, uint16 mtu);
net_buffer*
l2cap_discon_req(uint8 _ident, uint16 _dcid, uint16 _scid);
net_buffer*
l2cap_discon_rsp(uint8 _ident, uint16 _dcid, uint16 _scid);
net_buffer*
l2cap_echo_req(uint8 _ident, void* _data, size_t _size);
net_buffer*
l2cap_info_req(uint8 _ident, uint16 _type);
net_buffer*
l2cap_info_rsp(uint8 _ident, uint16 _type, uint16 _result, uint16 _mtu);
#endif
#endif /* _L2CAP_COMMAND_H_ */

View File

@ -1,19 +1,22 @@
/*
* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
* Copyright 2008 Oliver Ruiz Dorantes. All rights reserved.
* Copyright 2024, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef L2CAP_INTERNAL_H
#define L2CAP_INTERNAL_H
#include <btCoreData.h>
#include <net_protocol.h>
#include <net_stack.h>
extern l2cap_flow_t default_qos;
extern bluetooth_core_data_module_info *btCoreData;
extern bt_hci_module_info* btDevices;
extern net_buffer_module_info *gBufferModule;
extern net_stack_module_info *gStackModule;
extern net_socket_module_info *gSocketModule;
#endif // L2CAP_ADDRESS_H
#endif // L2CAP_INTERNAL_H

View File

@ -1,291 +0,0 @@
/*
* 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 <lock.h>
#include <NetBufferUtilities.h>
#include <bluetooth/HCI/btHCI_transport.h>
#include <btModules.h>
#include <l2cap.h>
#include "l2cap_internal.h"
#include "l2cap_signal.h"
#include "l2cap_upper.h"
#include <btDebug.h>
status_t
l2cap_receive(HciConnection* conn, net_buffer* buffer)
{
status_t error = B_OK;
uint16 dcid;
uint16 length;
#ifdef DUMP_L2CAP_FRAME
dprintf("DUMP:");
for (uint i = 0; i < buffer->size; i++) {
uint8 c = 0;
gBufferModule->read(buffer, i, &c, 1);
dprintf("[%x]", c);
}
dprintf("\n");
#endif
// Check packet
if (buffer->size < sizeof(l2cap_hdr_t)) {
ERROR("%s: invalid L2CAP packet. Packet too small, len=%" B_PRIu32 "\n",
__func__, buffer->size);
gBufferModule->free(buffer);
return EMSGSIZE;
}
// Get L2CAP header
NetBufferHeaderReader<l2cap_hdr_t> bufferHeader(buffer);
status_t status = bufferHeader.Status();
if (status < B_OK) {
return ENOBUFS;
}
length = bufferHeader->length = le16toh(bufferHeader->length);
dcid = bufferHeader->dcid = le16toh(bufferHeader->dcid);
TRACE("%s: len=%d cid=%x\n", __func__, length, dcid);
bufferHeader.Remove(); // pulling
// Check payload size
if (length != buffer->size ) {
ERROR("%s: Payload length mismatch, packetlen=%d, bufferlen=%" B_PRIu32
"\n", __func__, length, buffer->size);
gBufferModule->free(buffer);
return EMSGSIZE;
}
// Process packet
switch (dcid) {
case L2CAP_SIGNAL_CID: // L2CAP command
error = l2cap_process_signal_cmd(conn, buffer);
break;
case L2CAP_CLT_CID: // Connectionless packet
// error = l2cap_cl_receive(buffer);
TRACE("%s: CL FRAME!!\n", __func__);
break;
default: // Data packet
error = l2cap_co_receive(conn, buffer, dcid);
break;
}
return (error);
}
struct bt_hci_module_info* btDevices = NULL;
#if 0
#pragma mark - thread conn sched -
#endif
static thread_id sConnectionThread;
static void
AddL2capHeader(L2capFrame* frame)
{
NetBufferPrepend<l2cap_hdr_t> bufferHeader(frame->buffer);
status_t status = bufferHeader.Status();
if (status < B_OK) {
ERROR("%s: header could not be prepended! code=%d\n", __func__,
frame->code);
return;
}
// fill
bufferHeader->length = htole16(frame->buffer->size - sizeof(l2cap_hdr_t));
switch (frame->type) {
case L2CAP_C_FRAME:
bufferHeader->dcid = L2CAP_SIGNAL_CID;
break;
case L2CAP_G_FRAME:
bufferHeader->dcid = L2CAP_CLT_CID;
break;
default:
bufferHeader->dcid = frame->channel->dcid;
break;
}
}
void
purge_connection(HciConnection* conn)
{
CALLED();
L2capFrame* frame;
bool containerCanBeDestroyed;
mutex_lock(&conn->fLock);
frame = conn->OutGoingFrames.RemoveHead();
mutex_unlock(&conn->fLock);
// while ( frame != NULL) {
// Here is the place to decide how many l2cap signals we want to have
// per l2cap packet. 1 ATM
if (frame->type == L2CAP_C_FRAME && IS_SIGNAL_REQ(frame->code)) {
btCoreData->TimeoutSignal(frame, bluetooth_l2cap_rtx_timeout);
btCoreData->QueueSignal(frame);
containerCanBeDestroyed = false;
} else
containerCanBeDestroyed = true;
// Add the l2cap header
if (frame->buffer == NULL)
panic("Malformed frame in ongoing queue");
AddL2capHeader(frame);
if (btDevices == NULL)
if (get_module(BT_HCI_MODULE_NAME, (module_info**)&btDevices) != B_OK) {
panic("l2cap: cannot get dev module");
} // TODO: someone put it
TRACE("%s: type=%d, code=%d frame %p tolower\n", __func__, frame->type,
frame->code, frame->buffer);
frame->buffer->type = conn->handle;
btDevices->PostACL(conn->ndevice->index, frame->buffer);
// Only in the case that we need a response the frame container needs
// to be kept: Request C-Frames
if (containerCanBeDestroyed) {
delete frame;
}
// frame = conn->OutGoingFrames.RemoveHead();
// }
}
static status_t
connection_thread(void*)
{
int32 code;
ssize_t ssizePort;
ssize_t ssizeRead;
HciConnection* conn = NULL;
// TODO: Keep this a static var
port_id fPort = find_port(BLUETOOTH_CONNECTION_SCHED_PORT);
if (fPort == B_NAME_NOT_FOUND) {
panic("BT Connection port has been deleted");
}
while ((ssizePort = port_buffer_size(fPort)) != B_BAD_PORT_ID) {
if (ssizePort <= 0) {
ERROR("%s: Error %s\n", __func__, strerror(ssizePort));
snooze(500 * 1000);
continue;
}
if (ssizePort > (ssize_t) sizeof(conn)) {
ERROR("%s: Message too big %ld\n", __func__, ssizePort);
snooze(500 * 1000);
continue;
}
ssizeRead = read_port(fPort, &code, &conn, ssizePort);
if (ssizeRead != ssizePort) {
ERROR("%s: Mismatch size port=%ld read=%ld\n", __func__,
ssizePort, ssizeRead);
snooze(500 * 1000);
continue;
}
purge_connection(conn);
}
return B_OK;
}
status_t
InitializeConnectionPurgeThread()
{
port_id fPort = find_port(BLUETOOTH_CONNECTION_SCHED_PORT);
if (fPort == B_NAME_NOT_FOUND) {
TRACE("%s: Creating connection purge port\n", __func__);
fPort = create_port(16, BLUETOOTH_CONNECTION_SCHED_PORT);
}
// This thread has to catch up connections before first package is sent.
sConnectionThread = spawn_kernel_thread(connection_thread,
"bluetooth connection purge", B_URGENT_DISPLAY_PRIORITY, NULL);
if (sConnectionThread >= B_OK)
return resume_thread(sConnectionThread);
else
return B_ERROR;
}
status_t
QuitConnectionPurgeThread()
{
CALLED();
status_t status;
port_id fPort = find_port(BLUETOOTH_CONNECTION_SCHED_PORT);
if (fPort != B_NAME_NOT_FOUND)
close_port(fPort);
wait_for_thread(sConnectionThread, &status);
return status;
}
void
SchedConnectionPurgeThread(HciConnection* conn)
{
port_id port = find_port(BLUETOOTH_CONNECTION_SCHED_PORT);
HciConnection* temp = conn;
if (port == B_NAME_NOT_FOUND)
panic("BT Connection Port Deleted");
status_t error = write_port(port, (addr_t)conn, &temp, sizeof(conn));
if (error != B_OK)
panic("BT Connection sched failed");
}

View File

@ -1,15 +0,0 @@
/*
* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef L2CAP_LOWER_H
#define L2CAP_LOWER_H
status_t l2cap_receive(HciConnection* conn, net_buffer* buffer);
status_t InitializeConnectionPurgeThread();
status_t QuitConnectionPurgeThread();
void SchedConnectionPurgeThread(HciConnection* conn);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -5,18 +5,25 @@
#ifndef L2CAP_SIGNAL_H
#define L2CAP_SIGNAL_H
// Processing signals
status_t l2cap_process_signal_cmd(HciConnection* conn, net_buffer* buffer);
#include <l2cap.h>
#if 0
#pragma - Signals Responses
#endif
status_t
send_l2cap_reject(HciConnection *conn, uint8 ident, uint16 reason, uint16 mtu, uint16 scid, uint16 dcid);
status_t
send_l2cap_con_rej(HciConnection *conn, uint8 ident, uint16 scid, uint16 dcid, uint16 result);
status_t
send_l2cap_cfg_rsp(HciConnection *conn, uint8 ident, uint16 scid, uint16 result, net_buffer *opt);
status_t l2cap_handle_signaling_command(struct HciConnection* connection, net_buffer* buffer);
#endif
status_t send_l2cap_command(HciConnection* conn, uint8 code, uint8 ident, net_buffer* command);
status_t send_l2cap_command_reject(HciConnection* conn, uint8 ident,
uint16 reason, uint16 mtu, uint16 scid, uint16 dcid);
status_t send_l2cap_configuration_req(HciConnection* conn, uint8 ident, uint16 dcid, uint16 flags,
uint16* mtu, uint16* flush_timeout, l2cap_qos* flow);
status_t send_l2cap_connection_req(HciConnection* conn, uint8 ident, uint16 psm, uint16 scid);
status_t send_l2cap_connection_rsp(HciConnection* conn, uint8 ident,
uint16 dcid, uint16 scid, uint16 result, uint16 status);
status_t send_l2cap_configuration_rsp(HciConnection* conn, uint8 ident,
uint16 scid, uint16 flags, uint16 result, net_buffer *opt);
status_t send_l2cap_disconnection_req(HciConnection* conn, uint8 ident, uint16 dcid, uint16 scid);
status_t send_l2cap_disconnection_rsp(HciConnection* conn, uint8 ident, uint16 dcid, uint16 scid);
#endif /* L2CAP_SIGNAL_H */

View File

@ -1,319 +0,0 @@
/*
* 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>
#include "l2cap_internal.h"
#include "l2cap_signal.h"
#include "l2cap_command.h"
#include "l2cap_lower.h"
#include "L2capEndpoint.h"
#define BT_DEBUG_THIS_MODULE
#define SUBMODULE_NAME "upper"
#define SUBMODULE_COLOR 36
#include <btDebug.h>
#include <l2cap.h>
#if 0
#pragma mark - Signals from the other pair
#endif
status_t
l2cap_l2ca_con_ind(L2capChannel* channel)
{
L2capEndpoint* endpoint = L2capEndpoint::ForPsm(channel->psm);
if (endpoint == NULL) { // TODO: refuse connection no endpoint bound
ERROR("%s: No endpoint bound for psm %d\n", __func__, channel->psm);
return B_ERROR;
}
// Pair Channel with endpoint
endpoint->BindNewEnpointToChannel(channel);
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;
// Link command to the queue
SchedConnectionPurgeThread(channel->conn);
return B_OK;
}
status_t
l2cap_con_rsp_ind(HciConnection* conn, L2capChannel* channel)
{
uint16* flush_timo = NULL;
uint16* mtu = NULL;
l2cap_flow_t* flow = NULL;
CALLED();
// We received a configuration response, connection process
// is a step further but still configuration pending
// Check channel state
if (channel->state != L2CAP_CHAN_OPEN
&& channel->state != L2CAP_CHAN_CONFIG) {
ERROR("%s: unexpected L2CA_Config request message. Invalid channel"
" state, state=%d, lcid=%d\n", __func__, channel->state, channel->scid);
return EINVAL;
}
// Set requested channel configuration options
net_buffer* options = NULL;
if (channel->endpoint->fConfigurationSet) {
// Compare channel settings with defaults
if (channel->configuration->imtu != L2CAP_MTU_DEFAULT)
mtu = &channel->configuration->imtu;
if (channel->configuration->flush_timo != L2CAP_FLUSH_TIMO_DEFAULT)
flush_timo = &channel->configuration->flush_timo;
if (memcmp(&default_qos, &channel->configuration->oflow,
sizeof(channel->configuration->oflow)) != 0)
flow = &channel->configuration->oflow;
// Create configuration options
if (mtu != NULL || flush_timo != NULL || flow!=NULL)
options = l2cap_build_cfg_options(mtu, flush_timo, flow);
if (options == NULL)
return ENOBUFS;
}
// Send Configuration Request
// Create L2CAP command descriptor
channel->ident = btCoreData->ChannelAllocateIdent(conn);
if (channel->ident == L2CAP_NULL_IDENT)
return EIO;
net_buffer* buffer = l2cap_cfg_req(channel->ident, channel->dcid, 0, options);
if (buffer == NULL)
return ENOBUFS;
L2capFrame* command = btCoreData->SpawnSignal(conn, channel, buffer,
channel->ident, L2CAP_CFG_REQ);
if (command == NULL) {
gBufferModule->free(buffer);
channel->state = L2CAP_CHAN_CLOSED;
return ENOMEM;
}
channel->cfgState |= L2CAP_CFG_IN_SENT;
/* Adjust channel state for re-configuration */
if (channel->state == L2CAP_CHAN_OPEN) {
channel->state = L2CAP_CHAN_CONFIG;
channel->cfgState = 0;
}
TRACE("%s: Sending cfg req\n", __func__);
// Link command to the queue
SchedConnectionPurgeThread(channel->conn);
return B_OK;
}
status_t
l2cap_cfg_rsp_ind(L2capChannel* channel)
{
channel->cfgState |= L2CAP_CFG_OUT;
if ((channel->cfgState & L2CAP_CFG_BOTH) == L2CAP_CFG_BOTH) {
return channel->endpoint->MarkEstablished();
}
return B_OK;
}
status_t
l2cap_cfg_req_ind(L2capChannel* channel)
{
// if our configuration has not been yet sent ...
if (!(channel->cfgState & L2CAP_CFG_OUT_SENT)) {
// 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;
return ENOMEM;
}
// Link command to the queue
SchedConnectionPurgeThread(channel->conn);
// set status
channel->cfgState |= L2CAP_CFG_OUT_SENT;
}
if ((channel->cfgState & L2CAP_CFG_BOTH) == L2CAP_CFG_BOTH) {
// Channel can be declared open
channel->endpoint->MarkEstablished();
} else if ((channel->cfgState & L2CAP_CFG_IN_SENT) == 0) {
// send configuration Request by our side
if (channel->endpoint->RequiresConfiguration()) {
// TODO: define complete configuration packet
} else {
// nothing special requested
channel->ident = btCoreData->ChannelAllocateIdent(channel->conn);
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);
if (cmd == NULL) {
gBufferModule->free(buf);
channel->state = L2CAP_CHAN_CLOSED;
return ENOMEM;
}
// Link command to the queue
SchedConnectionPurgeThread(channel->conn);
}
channel->cfgState |= L2CAP_CFG_IN_SENT;
}
return B_OK;
}
status_t
l2cap_discon_req_ind(L2capChannel* channel)
{
return channel->endpoint->MarkClosed();
}
status_t
l2cap_discon_rsp_ind(L2capChannel* channel)
{
if (channel->state == L2CAP_CHAN_W4_L2CA_DISCON_RSP) {
channel->endpoint->MarkClosed();
}
return B_OK;
}
#if 0
#pragma mark - Signals from Upper Layer
#endif
status_t
l2cap_upper_con_req(L2capChannel* channel)
{
channel->ident = btCoreData->ChannelAllocateIdent(channel->conn);
net_buffer* buf = l2cap_con_req(channel->ident, channel->psm, channel->scid);
L2capFrame* cmd = btCoreData->SpawnSignal(channel->conn, channel, buf,
channel->ident, L2CAP_CON_REQ);
if (cmd == NULL) {
gBufferModule->free(buf);
return ENOMEM;
}
channel->state = L2CAP_CHAN_W4_L2CAP_CON_RSP;
// Link command to the queue
SchedConnectionPurgeThread(channel->conn);
return B_OK;
}
status_t
l2cap_upper_dis_req(L2capChannel* channel)
{
channel->ident = btCoreData->ChannelAllocateIdent(channel->conn);
net_buffer* buf = l2cap_discon_req(channel->ident, channel->dcid,
channel->scid);
L2capFrame* cmd = btCoreData->SpawnSignal(channel->conn, channel, buf,
channel->ident, L2CAP_DISCON_REQ);
if (cmd == NULL) {
gBufferModule->free(buf);
return ENOMEM;
}
channel->state = L2CAP_CHAN_W4_L2CA_DISCON_RSP;
// Link command to the queue
SchedConnectionPurgeThread(channel->conn);
return B_OK;
}
#if 0
#pragma mark -
#endif
status_t
l2cap_co_receive(HciConnection* conn, net_buffer* buffer, uint16 dcid)
{
CALLED();
L2capChannel* channel = btCoreData->ChannelBySourceID(conn, dcid);
if (channel == NULL) {
ERROR("%s: dcid %d does not exist for handle %d\n", __func__,
dcid, conn->handle);
return B_ERROR;
}
if (channel->endpoint == NULL) {
ERROR("%s: dcid %d not bound to endpoint\n", __func__, dcid);
return B_ERROR;
}
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) {
ERROR("%s: no endpoint bound with psm %d\n", __func__, psm);
return B_ERROR;
}
return gStackModule->fifo_enqueue_buffer(
&endpoint->fReceivingFifo, buffer);
}

View File

@ -1,26 +0,0 @@
/*
* Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef L2CAP_UPPER_H
#define L2CAP_UPPER_H
#include "l2cap_internal.h"
status_t l2cap_l2ca_con_ind(L2capChannel* channel);
status_t l2cap_cfg_req_ind(L2capChannel* channel);
status_t l2cap_discon_req_ind(L2capChannel* channel);
status_t l2cap_discon_rsp_ind(L2capChannel* channel);
status_t l2cap_con_rsp_ind(HciConnection* conn, L2capChannel* channel);
status_t l2cap_cfg_rsp_ind(L2capChannel* channel);
status_t l2cap_upper_con_req(L2capChannel* channel);
status_t l2cap_upper_dis_req(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

View File

@ -31,7 +31,7 @@ CreateL2capSocket(const bdaddr_t* bdaddr, struct sockaddr_l2cap& l2sa, uint16 ps
}
/* Bind a name to the socket. */
printf("Binding socket ...\n");
//printf("Binding socket ...\n");
size = sizeof(struct sockaddr_l2cap);
@ -40,10 +40,12 @@ CreateL2capSocket(const bdaddr_t* bdaddr, struct sockaddr_l2cap& l2sa, uint16 ps
memcpy(&l2sa.l2cap_bdaddr, bdaddr, sizeof(bdaddr_t));
l2sa.l2cap_psm = psm;
#if 0
if (bind(sock, (struct sockaddr *)&l2sa, size) < 0) {
perror ("bind");
exit (EXIT_FAILURE);
}
#endif
printf("Connecting socket for %s\n", bdaddrUtils::ToString(*bdaddr));
if ((error = connect(sock, (struct sockaddr *)&l2sa, sizeof(l2sa))) < 0) {