From bb83316a5811a550c4f850d07fa8e328e7ac0a94 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Fri, 26 Apr 2024 16:22:39 -0400 Subject: [PATCH] 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. --- build/jam/images/definitions/regular | 1 + headers/os/bluetooth/HCI/btHCI_transport.h | 1 - headers/private/bluetooth/btCoreData.h | 142 +- headers/private/bluetooth/l2cap.h | 385 ++--- .../bluetooth/btCoreData/BTCoreData.cpp | 94 +- .../bluetooth/btCoreData/ChannelInterface.cpp | 120 -- .../bluetooth/btCoreData/ChannelInterface.h | 21 - .../btCoreData/ConnectionInterface.cpp | 147 +- .../btCoreData/ConnectionInterface.h | 10 +- .../bluetooth/btCoreData/FrameInterface.cpp | 149 -- .../bluetooth/btCoreData/FrameInterface.h | 31 - .../kernel/bluetooth/btCoreData/Jamfile | 14 +- src/add-ons/kernel/bluetooth/hci/acl.cpp | 29 +- .../kernel/network/protocols/l2cap/Jamfile | 10 +- .../network/protocols/l2cap/L2capEndpoint.cpp | 905 +++++++--- .../network/protocols/l2cap/L2capEndpoint.h | 180 +- .../protocols/l2cap/L2capEndpointManager.cpp | 137 ++ .../protocols/l2cap/L2capEndpointManager.h | 76 + .../kernel/network/protocols/l2cap/l2cap.cpp | 229 +-- .../network/protocols/l2cap/l2cap_command.cpp | 557 ++---- .../network/protocols/l2cap/l2cap_command.h | 57 +- .../network/protocols/l2cap/l2cap_internal.h | 11 +- .../network/protocols/l2cap/l2cap_lower.cpp | 291 ---- .../network/protocols/l2cap/l2cap_lower.h | 15 - .../network/protocols/l2cap/l2cap_signal.cpp | 1501 +++++------------ .../network/protocols/l2cap/l2cap_signal.h | 31 +- .../network/protocols/l2cap/l2cap_upper.cpp | 319 ---- .../network/protocols/l2cap/l2cap_upper.h | 26 - .../bluetooth/l2capClient/l2capClient.cpp | 4 +- 29 files changed, 2027 insertions(+), 3466 deletions(-) delete mode 100644 src/add-ons/kernel/bluetooth/btCoreData/ChannelInterface.cpp delete mode 100644 src/add-ons/kernel/bluetooth/btCoreData/ChannelInterface.h delete mode 100644 src/add-ons/kernel/bluetooth/btCoreData/FrameInterface.cpp delete mode 100644 src/add-ons/kernel/bluetooth/btCoreData/FrameInterface.h create mode 100644 src/add-ons/kernel/network/protocols/l2cap/L2capEndpointManager.cpp create mode 100644 src/add-ons/kernel/network/protocols/l2cap/L2capEndpointManager.h delete mode 100644 src/add-ons/kernel/network/protocols/l2cap/l2cap_lower.cpp delete mode 100644 src/add-ons/kernel/network/protocols/l2cap/l2cap_lower.h delete mode 100644 src/add-ons/kernel/network/protocols/l2cap/l2cap_upper.cpp delete mode 100644 src/add-ons/kernel/network/protocols/l2cap/l2cap_upper.h diff --git a/build/jam/images/definitions/regular b/build/jam/images/definitions/regular index a8e9b85c42..8e7b073f5f 100644 --- a/build/jam/images/definitions/regular +++ b/build/jam/images/definitions/regular @@ -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 diff --git a/headers/os/bluetooth/HCI/btHCI_transport.h b/headers/os/bluetooth/HCI/btHCI_transport.h index 875327797a..2a7b5d69dc 100644 --- a/headers/os/bluetooth/HCI/btHCI_transport.h +++ b/headers/os/bluetooth/HCI/btHCI_transport.h @@ -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_ diff --git a/headers/private/bluetooth/btCoreData.h b/headers/private/bluetooth/btCoreData.h index f56c1ffc5d..c1e4d1b771 100644 --- a/headers/private/bluetooth/btCoreData.h +++ b/headers/private/bluetooth/btCoreData.h @@ -9,6 +9,10 @@ #include #include #include +#include +#include +#include + #include #include @@ -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(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 ChannelList; - DoublyLinkedList ExpectedResponses; - DoublyLinkedList 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 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 { - 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 { - - 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); }; diff --git a/headers/private/bluetooth/l2cap.h b/headers/private/bluetooth/l2cap.h index 39cc446db6..551b18b569 100644 --- a/headers/private/bluetooth/l2cap.h +++ b/headers/private/bluetooth/l2cap.h @@ -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 - * 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 -// 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_ */ diff --git a/src/add-ons/kernel/bluetooth/btCoreData/BTCoreData.cpp b/src/add-ons/kernel/bluetooth/btCoreData/BTCoreData.cpp index 7049f6f08b..0d8a572813 100644 --- a/src/add-ons/kernel/bluetooth/btCoreData/BTCoreData.cpp +++ b/src/add-ons/kernel/bluetooth/btCoreData/BTCoreData.cpp @@ -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 @@ -18,6 +16,7 @@ int32 api_version = B_CUR_DRIVER_API_VERSION; +mutex sConnectionListLock = MUTEX_INITIALIZER("bt connection list"); DoublyLinkedList 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::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::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::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::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; - 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, }; diff --git a/src/add-ons/kernel/bluetooth/btCoreData/ChannelInterface.cpp b/src/add-ons/kernel/bluetooth/btCoreData/ChannelInterface.cpp deleted file mode 100644 index 3a18aed3bd..0000000000 --- a/src/add-ons/kernel/bluetooth/btCoreData/ChannelInterface.cpp +++ /dev/null @@ -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 - -#include -#include - -#include - -#include "ChannelInterface.h" -#include "FrameInterface.h" - - -L2capChannel* -ChannelBySourceID(HciConnection *conn, uint16 scid) -{ - L2capChannel* channel = NULL; - - DoublyLinkedList::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)); -} diff --git a/src/add-ons/kernel/bluetooth/btCoreData/ChannelInterface.h b/src/add-ons/kernel/bluetooth/btCoreData/ChannelInterface.h deleted file mode 100644 index c329f96eb1..0000000000 --- a/src/add-ons/kernel/bluetooth/btCoreData/ChannelInterface.h +++ /dev/null @@ -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 - -#include -#include - -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 diff --git a/src/add-ons/kernel/bluetooth/btCoreData/ConnectionInterface.cpp b/src/add-ons/kernel/bluetooth/btCoreData/ConnectionInterface.cpp index e25b537a00..8c8dff29ee 100644 --- a/src/add-ons/kernel/bluetooth/btCoreData/ConnectionInterface.cpp +++ b/src/add-ons/kernel/bluetooth/btCoreData/ConnectionInterface.cpp @@ -4,33 +4,59 @@ */ #include +#include -#include +#include #include #include +#include #include +#include #include #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::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::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::Iterator iterator @@ -148,6 +184,7 @@ RouteConnection(const bdaddr_t& destination) { HciConnection* ConnectionByHandle(uint16 handle, hci_id hid) { + MutexLocker _(&sConnectionListLock); HciConnection* conn; DoublyLinkedList::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::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); } diff --git a/src/add-ons/kernel/bluetooth/btCoreData/ConnectionInterface.h b/src/add-ons/kernel/bluetooth/btCoreData/ConnectionInterface.h index feadf44726..a672b1a27e 100644 --- a/src/add-ons/kernel/bluetooth/btCoreData/ConnectionInterface.h +++ b/src/add-ons/kernel/bluetooth/btCoreData/ConnectionInterface.h @@ -12,6 +12,7 @@ #include +extern mutex sConnectionListLock; extern DoublyLinkedList 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 diff --git a/src/add-ons/kernel/bluetooth/btCoreData/FrameInterface.cpp b/src/add-ons/kernel/bluetooth/btCoreData/FrameInterface.cpp deleted file mode 100644 index 343952cf16..0000000000 --- a/src/add-ons/kernel/bluetooth/btCoreData/FrameInterface.cpp +++ /dev/null @@ -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 - -#include - - -L2capFrame* -SignalByIdent(HciConnection* conn, uint8 ident) -{ - L2capFrame* frame; - - mutex_lock(&conn->fLockExpected); - DoublyLinkedList::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; -} diff --git a/src/add-ons/kernel/bluetooth/btCoreData/FrameInterface.h b/src/add-ons/kernel/bluetooth/btCoreData/FrameInterface.h deleted file mode 100644 index 18701c4230..0000000000 --- a/src/add-ons/kernel/bluetooth/btCoreData/FrameInterface.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _FRAME_INTERFACE_H -#define _FRAME_INTERFACE_H - -#include - -#include - -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 diff --git a/src/add-ons/kernel/bluetooth/btCoreData/Jamfile b/src/add-ons/kernel/bluetooth/btCoreData/Jamfile index 62c839977e..49cac5e8f9 100644 --- a/src/add-ons/kernel/bluetooth/btCoreData/Jamfile +++ b/src/add-ons/kernel/bluetooth/btCoreData/Jamfile @@ -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 ; - diff --git a/src/add-ons/kernel/bluetooth/hci/acl.cpp b/src/add-ons/kernel/bluetooth/hci/acl.cpp index 230ba65db5..2d99a2dc16 100644 --- a/src/add-ons/kernel/bluetooth/hci/acl.cpp +++ b/src/add-ons/kernel/bluetooth/hci/acl.cpp @@ -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 l2capHeader(nbuf); - status_t status = l2capHeader.Status(); - if (status < B_OK) { + NetBufferHeaderReader 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); } diff --git a/src/add-ons/kernel/network/protocols/l2cap/Jamfile b/src/add-ons/kernel/network/protocols/l2cap/Jamfile index 26f6a4cf68..228850319a 100644 --- a/src/add-ons/kernel/network/protocols/l2cap/Jamfile +++ b/src/add-ons/kernel/network/protocols/l2cap/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/network/protocols/l2cap/L2capEndpoint.cpp b/src/add-ons/kernel/network/protocols/l2cap/L2capEndpoint.cpp index a6c89f1650..149b0aaa9f 100644 --- a/src/add-ons/kernel/network/protocols/l2cap/L2capEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/l2cap/L2capEndpoint.cpp @@ -1,20 +1,30 @@ /* - * 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. */ #include "L2capEndpoint.h" -#include "l2cap_address.h" -#include "l2cap_upper.h" -#include "l2cap_lower.h" #include #include -#include -#include -#include +#include #include +#include "L2capEndpointManager.h" +#include "l2cap_address.h" +#include "l2cap_signal.h" + + +static l2cap_qos sDefaultQOS = { + .flags = 0x0, + .service_type = 1, + .token_rate = 0xffffffff, /* maximum */ + .token_bucket_size = 0xffffffff, /* maximum */ + .peak_bandwidth = 0x00000000, /* maximum */ + .access_latency = 0xffffffff, /* don't care */ + .delay_variation = 0xffffffff /* don't care */ +}; static inline bigtime_t @@ -23,33 +33,52 @@ absolute_timeout(bigtime_t timeout) if (timeout == 0 || timeout == B_INFINITE_TIMEOUT) return timeout; - // TODO: Make overflow safe! return timeout + system_time(); } +static inline status_t +posix_error(status_t error) +{ + if (error == B_TIMED_OUT) + return B_WOULD_BLOCK; + + return error; +} + + +// #pragma mark - endpoint + + L2capEndpoint::L2capEndpoint(net_socket* socket) : ProtocolSocket(socket), - fConfigurationSet(false), - fEstablishSemaphore(-1), - fPeerEndpoint(NULL), - fChannel(NULL) + fAcceptSemaphore(-1), + fState(CLOSED), + fConnection(NULL), + fChannelID(L2CAP_NULL_CID), + fDestinationChannelID(L2CAP_NULL_CID) { CALLED(); - /* Set MTU and flow control settings to defaults */ - fConfiguration.imtu = L2CAP_MTU_DEFAULT; - memcpy(&fConfiguration.iflow, &default_qos , sizeof(l2cap_flow_t) ); + mutex_init(&fLock, "l2cap endpoint"); + fCommandWait.Init(this, "l2cap endpoint command"); - fConfiguration.omtu = L2CAP_MTU_DEFAULT; - memcpy(&fConfiguration.oflow, &default_qos , sizeof(l2cap_flow_t) ); + // Set MTU and flow control settings to defaults + fChannelConfig.incoming_mtu = L2CAP_MTU_DEFAULT; + memcpy(&fChannelConfig.incoming_flow, &sDefaultQOS, sizeof(l2cap_qos)); - fConfiguration.flush_timo = L2CAP_FLUSH_TIMO_DEFAULT; - fConfiguration.link_timo = L2CAP_LINK_TIMO_DEFAULT; + fChannelConfig.outgoing_mtu = L2CAP_MTU_DEFAULT; + memcpy(&fChannelConfig.outgoing_flow, &sDefaultQOS, sizeof(l2cap_qos)); - // TODO: XXX not for listening endpoints, imtu should be known first - gStackModule->init_fifo(&fReceivingFifo, "l2cap recvfifo", L2CAP_MTU_DEFAULT); + fChannelConfig.flush_timeout = L2CAP_FLUSH_TIMEOUT_DEFAULT; + fChannelConfig.link_timeout = L2CAP_LINK_TIMEOUT_DEFAULT; + + fConfigState = {}; + + gStackModule->init_fifo(&fReceiveQueue, "l2cap recv", L2CAP_MTU_MAXIMUM); + gStackModule->init_fifo(&fSendQueue, "l2cap send", L2CAP_MTU_MAXIMUM); + gStackModule->init_timer(&fSendTimer, L2capEndpoint::_SendTimer, this); } @@ -57,35 +86,94 @@ L2capEndpoint::~L2capEndpoint() { CALLED(); - gStackModule->uninit_fifo(&fReceivingFifo); + mutex_lock(&fLock); + mutex_destroy(&fLock); + + ASSERT(fState == CLOSED); + + fCommandWait.NotifyAll(B_ERROR); + + gStackModule->uninit_fifo(&fReceiveQueue); + gStackModule->uninit_fifo(&fSendQueue); + gStackModule->wait_for_timer(&fSendTimer); } status_t -L2capEndpoint::Init() +L2capEndpoint::_WaitForStateChange(bigtime_t absoluteTimeout) { - CALLED(); + channel_status state = fState; + while (fState == state) { + status_t status = fCommandWait.Wait(&fLock, + B_ABSOLUTE_TIMEOUT | B_CAN_INTERRUPT, absoluteTimeout); + if (status != B_OK) + return posix_error(status); + } return B_OK; } -void -L2capEndpoint::Uninit() -{ - CALLED(); - -} - - status_t L2capEndpoint::Open() { CALLED(); + return ProtocolSocket::Open(); +} - status_t error = ProtocolSocket::Open(); - if (error != B_OK) - return error; + +status_t +L2capEndpoint::Shutdown() +{ + CALLED(); + MutexLocker locker(fLock); + + if (fState == CLOSED) { + // Nothing to do. + return B_OK; + } + if (fState == LISTEN) { + delete_sem(fAcceptSemaphore); + fAcceptSemaphore = -1; + gSocketModule->set_max_backlog(socket, 0); + fState = BOUND; + return B_OK; + } + + status_t status; + bigtime_t timeout = absolute_timeout(socket->receive.timeout); + if (gStackModule->is_restarted_syscall()) + timeout = gStackModule->restore_syscall_restart_timeout(); + else + gStackModule->store_syscall_restart_timeout(timeout); + + // FIXME: If we are currently waiting for a connection or configuration, + // we need to wait for that command to return (and free its ident on timeout.) + + while (fState > OPEN) { + status = _WaitForStateChange(timeout); + if (status != B_OK) + return status; + } + if (fState == CLOSED) + return B_OK; + + uint8 ident = btCoreData->allocate_command_ident(fConnection, this); + if (ident == L2CAP_NULL_IDENT) + return ENOBUFS; + + status = send_l2cap_disconnection_req(fConnection, ident, + fDestinationChannelID, fChannelID); + if (status != B_OK) + return status; + + fState = WAIT_FOR_DISCONNECTION_RSP; + + while (fState != CLOSED) { + status = _WaitForStateChange(timeout); + if (status != B_OK) + return status; + } return B_OK; } @@ -94,37 +182,7 @@ L2capEndpoint::Open() status_t L2capEndpoint::Close() { - CALLED(); - - if (fChannel == NULL) { - // TODO: Parent socket - - } else { - // Child Socket - if (fState == CLOSED) { - // TODO: Clean needed stuff - return B_OK; - } else { - // Issue Disconnection request over the channel - MarkClosed(); - - bigtime_t timeout = absolute_timeout(300 * 1000 * 1000); - - status_t error = l2cap_upper_dis_req(fChannel); - - if (error != B_OK) - return error; - - return acquire_sem_etc(fEstablishSemaphore, 1, - B_ABSOLUTE_TIMEOUT | B_CAN_INTERRUPT, timeout); - } - } - - if (fEstablishSemaphore != -1) { - delete_sem(fEstablishSemaphore); - } - - return B_OK; + return Shutdown(); } @@ -142,33 +200,24 @@ L2capEndpoint::Bind(const struct sockaddr* _address) { const sockaddr_l2cap* address = reinterpret_cast(_address); - - if (_address == NULL) - return B_ERROR; - - if (address->l2cap_family != AF_BLUETOOTH ) + if (AddressModule()->is_empty_address(_address, true)) + return B_OK; // We don't need to bind to empty. + if (!AddressModule()->is_same_family(_address)) return EAFNOSUPPORT; - if (address->l2cap_len != sizeof(struct sockaddr_l2cap)) return EAFNOSUPPORT; - // TODO: Check if that PSM is already bound - // return EADDRINUSE; + CALLED(); + MutexLocker _(fLock); - // TODO: Check if the PSM is valid, check assigned numbers document for valid - // psm available to applications. - // All PSM values shall be ODD, that is, the least significant bit of the least - // significant octet must be ’1’. Also, all PSM values shall have the least - // significant bit of the most significant octet equal to ’0’. This allows - // the PSM field to be extended beyond 16 bits. - if ((address->l2cap_psm & 1) == 0) - return B_ERROR; + if (fState != CLOSED) + return EISCONN; - memcpy(&socket->address, _address, sizeof(struct sockaddr_l2cap)); - socket->address.ss_len = sizeof(struct sockaddr_l2cap); + status_t status = gL2capEndpointManager.Bind(this, *address); + if (status != B_OK) + return status; fState = BOUND; - return B_OK; } @@ -177,7 +226,17 @@ status_t L2capEndpoint::Unbind() { CALLED(); + MutexLocker _(fLock); + if (LocalAddress().IsEmpty(true)) + return EINVAL; + + status_t status = gL2capEndpointManager.Unbind(this); + if (status != B_OK) + return status; + + if (fState == BOUND) + fState = CLOSED; return B_OK; } @@ -186,14 +245,13 @@ status_t L2capEndpoint::Listen(int backlog) { CALLED(); + MutexLocker _(fLock); - if (fState != BOUND) { - ERROR("%s: Invalid State\n", __func__); + if (fState != BOUND) return B_BAD_VALUE; - } - fEstablishSemaphore = create_sem(0, "l2cap serv accept"); - if (fEstablishSemaphore < B_OK) { + fAcceptSemaphore = create_sem(0, "l2cap accept"); + if (fAcceptSemaphore < B_OK) { ERROR("%s: Semaphore could not be created\n", __func__); return ENOBUFS; } @@ -201,7 +259,6 @@ L2capEndpoint::Listen(int backlog) gSocketModule->set_max_backlog(socket, backlog); fState = LISTEN; - return B_OK; } @@ -211,66 +268,75 @@ L2capEndpoint::Connect(const struct sockaddr* _address) { const sockaddr_l2cap* address = reinterpret_cast(_address); + if (!AddressModule()->is_same_family(_address)) + return EAFNOSUPPORT; + if (address->l2cap_len != sizeof(struct sockaddr_l2cap)) + return EAFNOSUPPORT; - if (address->l2cap_len != sizeof(*address)) - return EINVAL; + TRACE("l2cap: connect(\"%s\")\n", + ConstSocketAddress(&gL2capAddressModule, _address).AsString().Data()); + MutexLocker _(fLock); - // Check for any specific status? - if (fState == CONNECTING) - return EINPROGRESS; + status_t status; + bigtime_t timeout = absolute_timeout(socket->send.timeout); + if (gStackModule->is_restarted_syscall()) { + timeout = gStackModule->restore_syscall_restart_timeout(); - // TODO: should not be in the BOUND status first? - - #if 0 - TRACE("%s: [%ld] %p->L2capEndpoint::Connect(\"%s\")\n", __func__, - find_thread(NULL), this, - ConstSocketAddress(&gL2cap4AddressModule, _address) - .AsString().Data()); - #endif - - // TODO: If we were bound to a specific source address - - // Route, we must find a Connection descriptor with address->l2cap_address - hci_id hid = btCoreData->RouteConnection(address->l2cap_bdaddr); - - #if 0 - TRACE("%s: %" B_PRId32 " for route %s\n", __func__, hid, - bdaddrUtils::ToString(address->l2cap_bdaddr).String()); - #endif - - if (hid > 0) { - HciConnection* connection = btCoreData->ConnectionByDestination( - address->l2cap_bdaddr, hid); - - L2capChannel* channel = btCoreData->AddChannel(connection, - address->l2cap_psm); - - if (channel == NULL) - return ENOMEM; - - // Send connection request - if (l2cap_upper_con_req(channel) == B_OK) { - fState = CONNECTING; - - BindToChannel(channel); - - fEstablishSemaphore = create_sem(0, "l2cap client"); - if (fEstablishSemaphore < B_OK) { - ERROR("%s: Semaphore could not be created\n", __func__); - return ENOBUFS; - } - - bigtime_t timeout = absolute_timeout(300 * 1000 * 1000); - - return acquire_sem_etc(fEstablishSemaphore, 1, - B_ABSOLUTE_TIMEOUT | B_CAN_INTERRUPT, timeout); - - } else { - return ECONNREFUSED; + while (fState != CLOSED && fState != OPEN) { + status = _WaitForStateChange(timeout); + if (status != B_OK) + return status; } + return (fState == OPEN) ? B_OK : ECONNREFUSED; + } else { + gStackModule->store_syscall_restart_timeout(timeout); } - return ENETUNREACH; + if (fState == LISTEN) + return EINVAL; + if (fState == OPEN) + return EISCONN; + if (fState != CLOSED) + return EALREADY; + + // Set up route. + hci_id hid = btCoreData->RouteConnection(address->l2cap_bdaddr); + if (hid <= 0) + return ENETUNREACH; + + TRACE("l2cap: %" B_PRId32 " for route %s\n", hid, + bdaddrUtils::ToString(address->l2cap_bdaddr).String()); + + fConnection = btCoreData->ConnectionByDestination( + address->l2cap_bdaddr, hid); + if (fConnection == NULL) + return EHOSTUNREACH; + + memcpy(&socket->peer, _address, sizeof(struct sockaddr_l2cap)); + + status = gL2capEndpointManager.BindToChannel(this); + if (status != B_OK) + return status; + + fConfigState = {}; + + uint8 ident = btCoreData->allocate_command_ident(fConnection, this); + if (ident == L2CAP_NULL_IDENT) + return ENOBUFS; + + status = send_l2cap_connection_req(fConnection, ident, + address->l2cap_psm, fChannelID); + if (status != B_OK) + return status; + + fState = WAIT_FOR_CONNECTION_RSP; + + while (fState != CLOSED && fState != OPEN) { + status = _WaitForStateChange(timeout); + if (status != B_OK) + return status; + } + return (fState == OPEN) ? B_OK : ECONNREFUSED; } @@ -278,80 +344,56 @@ status_t L2capEndpoint::Accept(net_socket** _acceptedSocket) { CALLED(); - - // MutexLocker locker(fLock); + MutexLocker locker(fLock); status_t status; - bigtime_t timeout = absolute_timeout(300 * 1000 * 1000); + bigtime_t timeout = absolute_timeout(socket->receive.timeout); + if (gStackModule->is_restarted_syscall()) + timeout = gStackModule->restore_syscall_restart_timeout(); + else + gStackModule->store_syscall_restart_timeout(timeout); do { - // locker.Unlock(); + locker.Unlock(); - status = acquire_sem_etc(fEstablishSemaphore, 1, B_ABSOLUTE_TIMEOUT + status = acquire_sem_etc(fAcceptSemaphore, 1, B_ABSOLUTE_TIMEOUT | B_CAN_INTERRUPT, timeout); - - if (status != B_OK) - return status; - - // locker.Lock(); - status = gSocketModule->dequeue_connected(socket, _acceptedSocket); - if (status != B_OK) { - ERROR("%s: Could not dequeue socket %s\n", __func__, - strerror(status)); - } else { + if (status == B_TIMED_OUT && socket->receive.timeout == 0) + return B_WOULD_BLOCK; - ((L2capEndpoint*)((*_acceptedSocket)->first_protocol))->fState = ESTABLISHED; - // unassign any channel for the parent endpoint - fChannel = NULL; - // we are listening again - fState = LISTEN; + return status; } + locker.Lock(); + status = gSocketModule->dequeue_connected(socket, _acceptedSocket); } while (status != B_OK); return status; } -ssize_t -L2capEndpoint::Send(const iovec* vecs, size_t vecCount, - ancillary_data_container* ancillaryData) -{ - CALLED(); - - return B_OK; -} - - -ssize_t -L2capEndpoint::Receive(const iovec* vecs, size_t vecCount, - ancillary_data_container** _ancillaryData, struct sockaddr* _address, - socklen_t* _addressLength) -{ - CALLED(); - - if (fState != ESTABLISHED) { - ERROR("%s: Invalid State %p\n", __func__, this); - return B_BAD_VALUE; - } - - return B_OK; -} - - ssize_t L2capEndpoint::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer) { CALLED(); + MutexLocker locker(fLock); - if (fState != ESTABLISHED) { - ERROR("%s: Invalid State %p\n", __func__, this); - return B_BAD_VALUE; + *_buffer = NULL; + + bigtime_t timeout = 0; + if ((flags & MSG_DONTWAIT) == 0) { + timeout = absolute_timeout(socket->receive.timeout); + if (gStackModule->is_restarted_syscall()) + timeout = gStackModule->restore_syscall_restart_timeout(); + else + gStackModule->store_syscall_restart_timeout(timeout); } - return gStackModule->fifo_dequeue_buffer(&fReceivingFifo, flags, - B_INFINITE_TIMEOUT, _buffer); + if (fState == CLOSED) + flags |= MSG_DONTWAIT; + + return gStackModule->fifo_dequeue_buffer(&fReceiveQueue, flags, timeout, _buffer); } @@ -359,18 +401,92 @@ ssize_t L2capEndpoint::SendData(net_buffer* buffer) { CALLED(); + MutexLocker locker(fLock); - if (fState != ESTABLISHED) { - ERROR("%s: Invalid State %p\n", __func__, this); - return B_BAD_VALUE; + if (buffer == NULL) + return ENOBUFS; + + if (fState != OPEN) + return ENOTCONN; + + ssize_t sent = 0; + while (buffer != NULL) { + net_buffer* current = buffer; + buffer = NULL; + if (current->size > fChannelConfig.outgoing_mtu) { + // Break up into MTU-sized chunks. + buffer = gBufferModule->split(current, fChannelConfig.outgoing_mtu); + if (buffer == NULL) { + if (sent > 0) { + gBufferModule->free(current); + return sent; + } + return ENOMEM; + } + } + + const size_t bufferSize = current->size; + status_t status = gStackModule->fifo_enqueue_buffer(&fSendQueue, current); + if (status != B_OK) { + gBufferModule->free(current); + return sent; + } + + sent += bufferSize; } - btCoreData->SpawnFrame(fChannel->conn, fChannel, buffer, L2CAP_B_FRAME); + if (!gStackModule->is_timer_active(&fSendTimer)) + gStackModule->set_timer(&fSendTimer, 0); - SchedConnectionPurgeThread(fChannel->conn); + return sent; +} - // TODO: Report bytes sent? - return B_OK; + +status_t +L2capEndpoint::ReceiveData(net_buffer* buffer) +{ + // FIXME: Check address specified in net_buffer! + return gStackModule->fifo_enqueue_buffer(&fReceiveQueue, buffer); +} + + +/*static*/ void +L2capEndpoint::_SendTimer(net_timer* timer, void* _endpoint) +{ + L2capEndpoint* endpoint = (L2capEndpoint*)_endpoint; + + MutexLocker locker(endpoint->fLock); + if (!locker.IsLocked() || gStackModule->is_timer_active(timer)) + return; + + endpoint->_SendQueued(); +} + + +void +L2capEndpoint::_SendQueued() +{ + CALLED(); + ASSERT_LOCKED_MUTEX(&fLock); + + if (fState != OPEN) + return; + + net_buffer* buffer; + while (gStackModule->fifo_dequeue_buffer(&fSendQueue, MSG_DONTWAIT, 0, &buffer) >= 0) { + NetBufferPrepend header(buffer); + if (header.Status() != B_OK) { + ERROR("%s: header could not be prepended!\n", __func__); + gBufferModule->free(buffer); + continue; + } + + header->length = B_HOST_TO_LENDIAN_INT16(buffer->size - sizeof(l2cap_basic_header)); + header->dcid = B_HOST_TO_LENDIAN_INT16(fDestinationChannelID); + + buffer->type = fConnection->handle; + btDevices->PostACL(fConnection->ndevice->index, buffer); + } } @@ -378,7 +494,16 @@ ssize_t L2capEndpoint::Sendable() { CALLED(); - return B_OK; + MutexLocker locker(fLock); + + if (fState != OPEN) { + if (_IsEstablishing()) + return 0; + return EPIPE; + } + + MutexLocker fifoLocker(fSendQueue.lock); + return (fSendQueue.max_bytes - fSendQueue.current_bytes); } @@ -386,112 +511,360 @@ ssize_t L2capEndpoint::Receivable() { CALLED(); - return 0; -} + MutexLocker locker(fLock); - -L2capEndpoint* -L2capEndpoint::ForPsm(uint16 psm) -{ - L2capEndpoint* endpoint; - - DoublyLinkedList::Iterator iterator - = EndpointList.GetIterator(); - - while (iterator.HasNext()) { - - endpoint = iterator.Next(); - if (((struct sockaddr_l2cap*)&endpoint->socket->address)->l2cap_psm == psm - && endpoint->fState == LISTEN) { - // TODO endpoint ocupied, lock it! define a channel for it - return endpoint; - } - } - - return NULL; + MutexLocker fifoLocker(fReceiveQueue.lock); + return fReceiveQueue.current_bytes; } void -L2capEndpoint::BindNewEnpointToChannel(L2capChannel* channel) +L2capEndpoint::_HandleCommandRejected(uint8 ident, uint16 reason, + const l2cap_command_reject_data& data) { + CALLED(); + MutexLocker locker(fLock); + + switch (fState) { + case WAIT_FOR_CONNECTION_RSP: + // Connection request was rejected. Reset state. + fState = CLOSED; + socket->error = ECONNREFUSED; + break; + + case CONFIGURATION: + // TODO: Adjust and resend configuration request. + break; + + default: + ERROR("l2cap: unknown command unexpectedly rejected (ident %d)\n", ident); + break; + } + + fCommandWait.NotifyAll(); +} + + +void +L2capEndpoint::_HandleConnectionReq(HciConnection* connection, + uint8 ident, uint16 psm, uint16 scid) +{ + MutexLocker locker(fLock); + if (fState != LISTEN) { + send_l2cap_connection_rsp(connection, ident, 0, scid, + l2cap_connection_rsp::RESULT_PSM_NOT_SUPPORTED, 0); + return; + } + locker.Unlock(); + net_socket* newSocket; - status_t error = gSocketModule->spawn_pending_socket(socket, &newSocket); - if (error != B_OK) { - ERROR("%s: Could not spawn child for Endpoint %p\n", __func__, this); - // TODO: Handle situation + status_t status = gSocketModule->spawn_pending_socket(socket, &newSocket); + if (status != B_OK) { + ERROR("l2cap: could not spawn child for endpoint: %s\n", strerror(status)); + send_l2cap_connection_rsp(connection, ident, 0, scid, + l2cap_connection_rsp::RESULT_NO_RESOURCES, 0); return; } L2capEndpoint* endpoint = (L2capEndpoint*)newSocket->first_protocol; + MutexLocker newEndpointLocker(endpoint->fLock); - endpoint->fChannel = channel; - endpoint->fPeerEndpoint = this; + status = gL2capEndpointManager.BindToChannel(endpoint); + if (status != B_OK) { + ERROR("l2cap: could not allocate channel for endpoint: %s\n", strerror(status)); + send_l2cap_connection_rsp(connection, ident, 0, scid, + l2cap_connection_rsp::RESULT_NO_RESOURCES, 0); + return; + } - channel->endpoint = endpoint; + endpoint->fAcceptSemaphore = fAcceptSemaphore; - //debugf("new socket %p/e->%p from parent %p/e->%p\n", - // newSocket, endpoint, socket, this); + endpoint->fConnection = connection; + endpoint->fState = CONFIGURATION; - // Provide the channel the configuration set by the user socket - channel->configuration = &fConfiguration; + endpoint->fDestinationChannelID = scid; - // It might be used keep the last negotiated channel - // fChannel = channel; - - //debugf("New endpoint %p for psm %d, schannel %x dchannel %x\n", endpoint, - // channel->psm, channel->scid, channel->dcid); + send_l2cap_connection_rsp(connection, ident, endpoint->fChannelID, scid, + l2cap_connection_rsp::RESULT_SUCCESS, 0); } void -L2capEndpoint::BindToChannel(L2capChannel* channel) +L2capEndpoint::_HandleConnectionRsp(uint8 ident, const l2cap_connection_rsp& response) { - this->fChannel = channel; - channel->endpoint = this; + CALLED(); + MutexLocker locker(fLock); + fCommandWait.NotifyAll(); - // Provide the channel the configuration set by the user socket - channel->configuration = &fConfiguration; + if (fState != WAIT_FOR_CONNECTION_RSP) { + ERROR("l2cap: unexpected connection response, scid=%d, state=%d\n", + response.scid, fState); + send_l2cap_command_reject(fConnection, ident, + l2cap_command_reject::REJECTED_INVALID_CID, 0, response.scid, response.dcid); + return; + } - // no parent to give feedback - fPeerEndpoint = NULL; + if (fChannelID != response.scid) { + ERROR("l2cap: invalid connection response, mismatched SCIDs (%d, %d)\n", + fChannelID, response.scid); + send_l2cap_command_reject(fConnection, ident, + l2cap_command_reject::REJECTED_INVALID_CID, 0, response.scid, response.dcid); + return; + } + + if (response.result == l2cap_connection_rsp::RESULT_PENDING) { + // The connection is still pending on the remote end. + // We will receive another CONNECTION_RSP later. + + // TODO: Increase/reset timeout? (We don't have any timeouts presently.) + return; + } else if (response.result != l2cap_connection_rsp::RESULT_SUCCESS) { + // Some error response. + // TODO: Translate `result` if possible? + socket->error = ECONNREFUSED; + + fState = CLOSED; + fCommandWait.NotifyAll(); + } + + // Success: channel is now open for configuration. + fState = CONFIGURATION; + fDestinationChannelID = response.dcid; + + _SendChannelConfig(); +} + + +void +L2capEndpoint::_SendChannelConfig() +{ + uint16* mtu = NULL; + if (fChannelConfig.incoming_mtu != L2CAP_MTU_DEFAULT) + mtu = &fChannelConfig.incoming_mtu; + + uint16* flush_timeout = NULL; + if (fChannelConfig.flush_timeout != L2CAP_FLUSH_TIMEOUT_DEFAULT) + flush_timeout = &fChannelConfig.flush_timeout; + + l2cap_qos* flow = NULL; + if (memcmp(&sDefaultQOS, &fChannelConfig.outgoing_flow, + sizeof(fChannelConfig.outgoing_flow)) != 0) { + flow = &fChannelConfig.outgoing_flow; + } + + uint8 ident = btCoreData->allocate_command_ident(fConnection, this); + if (ident == L2CAP_NULL_IDENT) { + // TODO: Retry later? + return; + } + + status_t status = send_l2cap_configuration_req(fConnection, ident, + fDestinationChannelID, 0, flush_timeout, mtu, flow); + if (status != B_OK) { + socket->error = status; + return; + } + + fConfigState.out = ConfigState::SENT; +} + + +void +L2capEndpoint::_HandleConfigurationReq(uint8 ident, uint16 flags, + uint16* mtu, uint16* flush_timeout, l2cap_qos* flow) +{ + CALLED(); + MutexLocker locker(fLock); + fCommandWait.NotifyAll(); + + if (fState != CONFIGURATION && fState != OPEN) { + ERROR("l2cap: unexpected configuration req: invalid channel state (cid=%d, state=%d)\n", + fChannelID, fState); + send_l2cap_configuration_rsp(fConnection, ident, fChannelID, 0, + l2cap_configuration_rsp::RESULT_REJECTED, NULL); + return; + } + + if (fState == OPEN) { + // Re-configuration. + fConfigState = {}; + fState = CONFIGURATION; + } + + // Process options. + // TODO: Validate parameters! + if (mtu != NULL && *mtu != fChannelConfig.outgoing_mtu) + fChannelConfig.outgoing_mtu = *mtu; + if (flush_timeout != NULL && *flush_timeout != fChannelConfig.flush_timeout) + fChannelConfig.flush_timeout = *flush_timeout; + if (flow != NULL) + fChannelConfig.incoming_flow = *flow; + + send_l2cap_configuration_rsp(fConnection, ident, fChannelID, 0, + l2cap_configuration_rsp::RESULT_SUCCESS, NULL); + + if ((flags & L2CAP_CFG_FLAG_CONTINUATION) != 0) { + // More options are coming, just keep waiting. + return; + } + + // We now have all options. + fConfigState.in = ConfigState::DONE; + + if (fConfigState.out < ConfigState::SENT) + _SendChannelConfig(); + else if (fConfigState.out == ConfigState::DONE) + _MarkEstablished(); +} + + +void +L2capEndpoint::_HandleConfigurationRsp(uint8 ident, uint16 scid, uint16 flags, + uint16 result, uint16* mtu, uint16* flush_timeout, l2cap_qos* flow) +{ + CALLED(); + MutexLocker locker(fLock); + fCommandWait.NotifyAll(); + + if (fState != CONFIGURATION) { + ERROR("l2cap: unexpected configuration rsp: invalid channel state (cid=%d, state=%d)\n", + fChannelID, fState); + send_l2cap_command_reject(fConnection, ident, + l2cap_command_reject::REJECTED_INVALID_CID, 0, scid, fChannelID); + return; + } + if (scid != fDestinationChannelID) { + ERROR("l2cap: unexpected configuration rsp: invalid source channel (cid=%d, scid=%d)\n", + fChannelID, scid); + send_l2cap_command_reject(fConnection, ident, + l2cap_command_reject::REJECTED_INVALID_CID, 0, scid, fChannelID); + return; + } + + // TODO: Validate parameters! + if (result == l2cap_configuration_rsp::RESULT_PENDING) { + // We will receive another CONFIGURATION_RSP later. + return; + } else if (result == l2cap_configuration_rsp::RESULT_UNACCEPTABLE_PARAMS) { + // The acceptable parameters are specified in options. + if (mtu != NULL && *mtu != fChannelConfig.incoming_mtu) + fChannelConfig.incoming_mtu = *mtu; + if (flush_timeout != NULL && *flush_timeout != fChannelConfig.flush_timeout) + fChannelConfig.flush_timeout = *flush_timeout; + } else if (result == l2cap_configuration_rsp::RESULT_FLOW_SPEC_REJECTED) { + if (flow != NULL) + fChannelConfig.outgoing_flow = *flow; + } else if (result != l2cap_configuration_rsp::RESULT_SUCCESS) { + ERROR("l2cap: unhandled configuration response! (result=%d)\n", + result); + return; + } + + if ((flags & L2CAP_CFG_FLAG_CONTINUATION) != 0) { + // More options are coming, just keep waiting. + return; + } + + if (result != l2cap_configuration_rsp::RESULT_SUCCESS) { + // Resend configuration request to try again. + _SendChannelConfig(); + return; + } + + // We now have all options. + fConfigState.out = ConfigState::DONE; + + if (fConfigState.in == ConfigState::DONE) + _MarkEstablished(); } status_t -L2capEndpoint::MarkEstablished() +L2capEndpoint::_MarkEstablished() { CALLED(); + ASSERT_LOCKED_MUTEX(&fLock); + + fState = OPEN; + fCommandWait.NotifyAll(); status_t error = B_OK; - fChannel->state = L2CAP_CHAN_OPEN; - fState = ESTABLISHED; - - if (fPeerEndpoint != NULL) { - + if (gSocketModule->has_parent(socket)) { error = gSocketModule->set_connected(socket); if (error == B_OK) { - release_sem(fPeerEndpoint->fEstablishSemaphore); + release_sem(fAcceptSemaphore); + fAcceptSemaphore = -1; } else { - ERROR("%s: Could not set child Endpoint %p %s\n", __func__, this, + ERROR("%s: could not set endpoint %p connected: %s\n", __func__, this, strerror(error)); } - } else - release_sem(fEstablishSemaphore); + } return error; } -status_t -L2capEndpoint::MarkClosed() +void +L2capEndpoint::_HandleDisconnectionReq(uint8 ident, uint16 scid) { CALLED(); + MutexLocker locker(fLock); + fCommandWait.NotifyAll(); - if (fState == CLOSED) - release_sem(fEstablishSemaphore); + if (scid != fDestinationChannelID) { + ERROR("l2cap: unexpected disconnection req: invalid source channel (cid=%d, scid=%d)\n", + fChannelID, scid); + send_l2cap_command_reject(fConnection, ident, + l2cap_command_reject::REJECTED_INVALID_CID, 0, scid, fChannelID); + return; + } + + if (fState != WAIT_FOR_DISCONNECTION_RSP) + fState = RECEIVED_DISCONNECTION_REQ; + + // The dcid/scid are the same as in the REQ command. + status_t status = send_l2cap_disconnection_rsp(fConnection, ident, fChannelID, scid); + if (status != B_OK) { + // TODO? + return; + } + + _MarkClosed(); +} + + +void +L2capEndpoint::_HandleDisconnectionRsp(uint8 ident, uint16 dcid, uint16 scid) +{ + CALLED(); + MutexLocker locker(fLock); + fCommandWait.NotifyAll(); + + if (fState != WAIT_FOR_DISCONNECTION_RSP) { + ERROR("l2cap: unexpected disconnection rsp (cid=%d, scid=%d)\n", + fChannelID, scid); + send_l2cap_command_reject(fConnection, ident, + l2cap_command_reject::REJECTED_INVALID_CID, 0, scid, fChannelID); + return; + } + + if (dcid != fDestinationChannelID && scid != fChannelID) { + ERROR("l2cap: unexpected disconnection rsp: mismatched CIDs (dcid=%d, scid=%d)\n", + dcid, scid); + return; + } + + _MarkClosed(); +} + + +void +L2capEndpoint::_MarkClosed() +{ + CALLED(); + ASSERT_LOCKED_MUTEX(&fLock); fState = CLOSED; - return B_OK; + gL2capEndpointManager.UnbindFromChannel(this); } diff --git a/src/add-ons/kernel/network/protocols/l2cap/L2capEndpoint.h b/src/add-ons/kernel/network/protocols/l2cap/L2capEndpoint.h index ddc30243d1..8f6266c96d 100644 --- a/src/add-ons/kernel/network/protocols/l2cap/L2capEndpoint.h +++ b/src/add-ons/kernel/network/protocols/l2cap/L2capEndpoint.h @@ -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 #include -#include -#include +#include +#include #include #include #include +#include #include "l2cap_internal.h" -extern net_stack_module_info* gStackModule; -class L2capEndpoint : public net_protocol, - public ProtocolSocket, - public DoublyLinkedListLinkImpl { +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 EndpointList; - #endif // L2CAP_ENDPOINT_H diff --git a/src/add-ons/kernel/network/protocols/l2cap/L2capEndpointManager.cpp b/src/add-ons/kernel/network/protocols/l2cap/L2capEndpointManager.cpp new file mode 100644 index 0000000000..dfecebbc3e --- /dev/null +++ b/src/add-ons/kernel/network/protocols/l2cap/L2capEndpointManager.cpp @@ -0,0 +1,137 @@ +/* + * Copyright 2024, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#include "L2capEndpointManager.h" + +#include + +#include + + +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; + } +} + diff --git a/src/add-ons/kernel/network/protocols/l2cap/L2capEndpointManager.h b/src/add-ons/kernel/network/protocols/l2cap/L2capEndpointManager.h new file mode 100644 index 0000000000..d59e280021 --- /dev/null +++ b/src/add-ons/kernel/network/protocols/l2cap/L2capEndpointManager.h @@ -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 + +#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(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 fBoundEndpoints; + + rw_lock fChannelEndpointsLock; + uint16 fNextChannelID; + AVLTree fChannelEndpoints; +}; + +extern L2capEndpointManager gL2capEndpointManager; + + +#endif // L2CAP_ENDPOINT_MANAGER_H diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap.cpp b/src/add-ons/kernel/network/protocols/l2cap/l2cap.cpp index 6b29da5418..5c7882aa82 100644 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap.cpp +++ b/src/add-ons/kernel/network/protocols/l2cap/l2cap.cpp @@ -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 - * 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 #include #include #include -#include -#include -#include - #include -#include -#include #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 #include - - #include -typedef NetBufferField AclLenField; -DoublyLinkedList 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(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(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 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 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; - - 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}, {} }; diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap_command.cpp b/src/add-ons/kernel/network/protocols/l2cap/l2cap_command.cpp index 601e4cdaff..2d92b40d69 100644 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap_command.cpp +++ b/src/add-ons/kernel/network/protocols/l2cap/l2cap_command.cpp @@ -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 - * 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 -#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 bufferHeader(_m); - status_t status = bufferHeader.Status(); - if (status < B_OK) { - // free the buffer + if (reason == l2cap_command_reject::REJECTED_MTU_EXCEEDED) { + NetBufferPrepend data(buffer.Get()); + *data = htole16(mtu); + } else if (reason == l2cap_command_reject::REJECTED_INVALID_CID) { + NetBufferPrepend data(buffer.Get()); + data->invalid_cid.scid = htole16(scid); + data->invalid_cid.dcid = htole16(dcid); + } + + NetBufferPrepend 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 bufferHeader(_m); - status_t status = bufferHeader.Status(); - if (status < B_OK) { - /* TODO free the buffer */ + NetBufferPrepend 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 bufferHeader(_m); - status_t status = bufferHeader.Status(); - if (status < B_OK) { - /* TODO free the buffer */ + NetBufferPrepend 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 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 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 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 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 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 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 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 bufferHeader(_m); - status_t status = bufferHeader.Status(); - if (status < B_OK) { - /* TODO free the buffer */ + NetBufferPrepend 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 bufferHeader(_m); - status_t status = bufferHeader.Status(); - if (status < B_OK) { - /* TODO free the buffer */ + NetBufferPrepend 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 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 bufferHeader(_m); - status_t status = bufferHeader.Status(); - if (status < B_OK) { - /* TODO free the buffer */ + NetBufferPrepend 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 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 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 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 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(); } diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap_command.h b/src/add-ons/kernel/network/protocols/l2cap/l2cap_command.h index db47d8017d..7bb467418d 100644 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap_command.h +++ b/src/add-ons/kernel/network/protocols/l2cap/l2cap_command.h @@ -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 #include -#include +#include -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_ */ diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap_internal.h b/src/add-ons/kernel/network/protocols/l2cap/l2cap_internal.h index 53de22f093..3cfd4849c2 100644 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap_internal.h +++ b/src/add-ons/kernel/network/protocols/l2cap/l2cap_internal.h @@ -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 #include #include -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 diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap_lower.cpp b/src/add-ons/kernel/network/protocols/l2cap/l2cap_lower.cpp deleted file mode 100644 index 6c74c32987..0000000000 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap_lower.cpp +++ /dev/null @@ -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 - * 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 -#include -#include - -#include - -#include - -#include -#include - -#include "l2cap_internal.h" -#include "l2cap_signal.h" -#include "l2cap_upper.h" - -#include - - -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 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 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"); - -} diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap_lower.h b/src/add-ons/kernel/network/protocols/l2cap/l2cap_lower.h deleted file mode 100644 index f86c9a3ddb..0000000000 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap_lower.h +++ /dev/null @@ -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 diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap_signal.cpp b/src/add-ons/kernel/network/protocols/l2cap/l2cap_signal.cpp index 60488af8e7..39d706c7bc 100644 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap_signal.cpp +++ b/src/add-ons/kernel/network/protocols/l2cap/l2cap_signal.cpp @@ -2,1128 +2,545 @@ * 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 - * 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 #include +#include +#include #include - #include +#include "L2capEndpoint.h" +#include "L2capEndpointManager.h" #include "l2cap_internal.h" #include "l2cap_signal.h" #include "l2cap_command.h" -#include "l2cap_upper.h" -#include "l2cap_lower.h" #include +#include #include -typedef enum _option_status { - OPTION_NOT_PRESENT = 0, - OPTION_PRESENT = 1, - HEADER_TOO_SHORT = -1, - BAD_OPTION_LENGTH = -2, - OPTION_UNKNOWN = -3 -} option_status; - -l2cap_flow_t default_qos = { - /* flags */ 0x0, - /* service_type */ HCI_SERVICE_TYPE_BEST_EFFORT, - /* token_rate */ 0xffffffff, /* maximum */ - /* token_bucket_size */ 0xffffffff, /* maximum */ - /* peak_bandwidth */ 0x00000000, /* maximum */ - /* latency */ 0xffffffff, /* don't care */ - /* delay_variation */ 0xffffffff /* don't care */ +struct l2cap_config_options { + uint16 mtu; + bool mtu_set; + uint16 flush_timeout; + bool flush_timeout_set; + l2cap_qos qos; + bool qos_set; + net_buffer* rejected; }; -static status_t -l2cap_process_con_req(HciConnection* conn, uint8 ident, net_buffer* buffer); +// #pragma mark - incoming signals + static status_t -l2cap_process_con_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer); +l2cap_handle_connection_req(HciConnection* conn, uint8 ident, net_buffer* buffer) +{ + NetBufferHeaderReader command(buffer); + if (command.Status() != B_OK) + return ENOBUFS; + + const uint16 psm = le16toh(command->psm); + const uint16 scid = le16toh(command->scid); + + L2capEndpoint* endpoint = gL2capEndpointManager.ForPSM(psm); + if (endpoint == NULL) { + // Refuse connection. + send_l2cap_connection_rsp(conn, ident, 0, scid, + l2cap_connection_rsp::RESULT_PSM_NOT_SUPPORTED, 0); + return B_OK; + } + + endpoint->_HandleConnectionReq(conn, ident, psm, scid); + return B_OK; +} + + +static void +l2cap_handle_connection_rsp(L2capEndpoint* endpoint, uint8 ident, net_buffer* buffer) +{ + NetBufferHeaderReader command(buffer); + if (command.Status() != B_OK) + return; + + l2cap_connection_rsp response; + response.dcid = le16toh(command->dcid); + response.scid = le16toh(command->scid); + response.result = le16toh(command->result); + response.status = le16toh(command->status); + + TRACE("%s: dcid=%d scid=%d result=%d status%d\n", + __func__, response.dcid, response.scid, response.result, response.status); + + endpoint->_HandleConnectionRsp(ident, response); +} + + +static void +parse_configuration_options(net_buffer* buffer, uint16 length, l2cap_config_options& options) +{ + for (size_t offset = sizeof(l2cap_configuration_req); offset < length; ) { + l2cap_configuration_option option; + if (gBufferModule->read(buffer, offset, &option, sizeof(option)) != B_OK) + break; + + l2cap_configuration_option_value value; + if (gBufferModule->read(buffer, offset + sizeof(option), &value, + min_c(sizeof(value), option.length)) != B_OK) { + break; + } + + const bool hint = (option.type & l2cap_configuration_option::OPTION_HINT_BIT); + option.type &= ~l2cap_configuration_option::OPTION_HINT_BIT; + + switch (option.type) { + case l2cap_configuration_option::OPTION_MTU: + options.mtu = le16toh(value.mtu); + options.mtu_set = true; + break; + + case l2cap_configuration_option::OPTION_FLUSH_TIMEOUT: + options.flush_timeout = le16toh(value.flush_timeout); + options.flush_timeout_set = true; + break; + + case l2cap_configuration_option::OPTION_QOS: + options.qos.flags = value.qos.flags; + options.qos.service_type = value.qos.service_type; + options.qos.token_rate = le32toh(value.qos.token_rate); + options.qos.token_bucket_size = le32toh(value.qos.token_bucket_size); + options.qos.peak_bandwidth = le32toh(value.qos.peak_bandwidth); + options.qos.access_latency = le32toh(value.qos.access_latency); + options.qos.delay_variation = le32toh(value.qos.delay_variation); + options.qos_set = true; + break; + + default: { + if (hint) + break; + + // Unknown option: we need to reject it. + if (options.rejected == NULL) + options.rejected = gBufferModule->create(128); + gBufferModule->append_cloned(options.rejected, buffer, offset, + sizeof(option) + option.length); + } + } + + offset += sizeof(option) + option.length; + } +} + static status_t -l2cap_process_cfg_req(HciConnection* conn, uint8 ident, net_buffer* buffer); +l2cap_handle_configuration_req(HciConnection* conn, uint8 ident, net_buffer* buffer, uint16 length) +{ + NetBufferHeaderReader command(buffer); + if (command.Status() != B_OK) + return ENOBUFS; + + const uint16 dcid = le16toh(command->dcid); + L2capEndpoint* endpoint = gL2capEndpointManager.ForChannel(dcid); + if (endpoint == NULL) { + ERROR("l2cap: unexpected configuration req: channel does not exist (cid=%d)\n", dcid); + send_l2cap_command_reject(conn, ident, + l2cap_command_reject::REJECTED_INVALID_CID, 0, 0, 0); + return B_ERROR; + } + + const uint16 flags = le16toh(command->flags); + + // Read options (if any). + l2cap_config_options options = {}; + parse_configuration_options(buffer, length, options); + + if (options.rejected != NULL) { + // Reject without doing anything else. + send_l2cap_configuration_rsp(conn, ident, dcid, 0, + l2cap_configuration_rsp::RESULT_UNKNOWN_OPTION, options.rejected); + return B_OK; + } + + endpoint->_HandleConfigurationReq(ident, flags, + options.mtu_set ? &options.mtu : NULL, + options.flush_timeout_set ? &options.flush_timeout : NULL, + options.qos_set ? &options.qos : NULL); + return B_OK; +} + static status_t -l2cap_process_cfg_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer); +l2cap_handle_configuration_rsp(HciConnection* conn, L2capEndpoint* endpoint, + uint8 ident, net_buffer* buffer, uint16 length, bool& releaseIdent) +{ + if (endpoint == NULL) { + // The endpoint seems to be gone. + return B_ERROR; + } + + NetBufferHeaderReader command(buffer); + if (command.Status() != B_OK) + return ENOBUFS; + + const uint16 scid = le16toh(command->scid); + const uint16 flags = le16toh(command->flags); + const uint16 result = le16toh(command->result); + releaseIdent = (flags & L2CAP_CFG_FLAG_CONTINUATION) == 0; + + // Read options (if any). + l2cap_config_options options = {}; + parse_configuration_options(buffer, length, options); + + if (options.rejected != NULL) { + // Reject without doing anything else. + send_l2cap_command_reject(conn, ident, + l2cap_command_reject::REJECTED_NOT_UNDERSTOOD, 0, 0, 0); + return B_OK; + } + + endpoint->_HandleConfigurationRsp(ident, scid, flags, result, + options.mtu_set ? &options.mtu : NULL, + options.flush_timeout_set ? &options.flush_timeout : NULL, + options.qos_set ? &options.qos : NULL); + return B_OK; +} + static status_t -l2cap_process_discon_req(HciConnection* conn, uint8 ident, net_buffer* buffer); +l2cap_handle_disconnection_req(HciConnection* conn, uint8 ident, net_buffer* buffer) +{ + NetBufferHeaderReader command(buffer); + if (command.Status() != B_OK) + return ENOBUFS; + + const uint16 dcid = le16toh(command->dcid); + L2capEndpoint* endpoint = gL2capEndpointManager.ForChannel(dcid); + if (endpoint == NULL) { + ERROR("l2cap: unexpected disconnection req: channel does not exist (cid=%d)\n", dcid); + send_l2cap_command_reject(conn, ident, + l2cap_command_reject::REJECTED_INVALID_CID, 0, 0, 0); + return B_ERROR; + } + + const uint16 scid = le16toh(command->scid); + + endpoint->_HandleDisconnectionReq(ident, scid); + return B_OK; +} + static status_t -l2cap_process_discon_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer); +l2cap_handle_disconnection_rsp(L2capEndpoint* endpoint, uint8 ident, net_buffer* buffer) +{ + NetBufferHeaderReader command(buffer); + if (command.Status() != B_OK) + return ENOBUFS; + + const uint16 dcid = le16toh(command->dcid); + const uint16 scid = le16toh(command->scid); + + endpoint->_HandleDisconnectionRsp(ident, dcid, scid); + return B_OK; +} + static status_t -l2cap_process_echo_req(HciConnection* conn, uint8 ident, net_buffer* buffer); +l2cap_handle_echo_req(HciConnection *conn, uint8 ident, net_buffer* buffer, uint16 length) +{ + net_buffer* reply = gBufferModule->clone(buffer, false); + if (reply == NULL) + return ENOMEM; + + gBufferModule->trim(reply, length); + return send_l2cap_command(conn, L2CAP_ECHO_RSP, ident, reply); +} + static status_t -l2cap_process_echo_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer); +l2cap_handle_echo_rsp(L2capEndpoint* endpoint, uint8 ident, net_buffer* buffer, uint16 length) +{ + // Not currently triggered by this module. + return B_OK; +} + static status_t -l2cap_process_info_req(HciConnection* conn, uint8 ident, net_buffer* buffer); +l2cap_handle_info_req(HciConnection* conn, uint8 ident, net_buffer* buffer) +{ + NetBufferHeaderReader command(buffer); + if (command.Status() != B_OK) + return ENOBUFS; + + const uint16 type = le16toh(command->type); + + net_buffer* reply = NULL; + uint8 replyCode = 0; + switch (type) { + case l2cap_information_req::TYPE_CONNECTIONLESS_MTU: + reply = make_l2cap_information_rsp(replyCode, type, + l2cap_information_rsp::RESULT_SUCCESS, L2CAP_MTU_DEFAULT); + break; + + default: + ERROR("l2cap: unhandled information request type %d\n", type); + reply = make_l2cap_information_rsp(replyCode, type, + l2cap_information_rsp::RESULT_NOT_SUPPORTED, 0); + break; + } + + return send_l2cap_command(conn, replyCode, ident, reply); +} + static status_t -l2cap_process_info_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer); +l2cap_handle_info_rsp(L2capEndpoint* endpoint, uint8 ident, net_buffer* buffer) +{ + // Not currently triggered by this module. + return B_OK; +} + static status_t -l2cap_process_cmd_rej(HciConnection* conn, uint8 ident, net_buffer* buffer); +l2cap_handle_command_reject(L2capEndpoint* endpoint, uint8 ident, net_buffer* buffer) +{ + if (endpoint == NULL) { + ERROR("l2cap: unexpected command rejected: ident %d unknown\n", ident); + return B_ERROR; + } + + NetBufferHeaderReader command(buffer); + if (command.Status() != B_OK) + return ENOBUFS; + + const uint16 reason = le16toh(command->reason); + TRACE("%s: reason=%d\n", __func__, command->reason); + + l2cap_command_reject_data data = {}; + gBufferModule->read(buffer, sizeof(l2cap_command_reject), + &data, min_c(buffer->size, sizeof(l2cap_command_reject_data))); + + endpoint->_HandleCommandRejected(ident, reason, data); + + return B_OK; +} -/* - * Process L2CAP signaling command. We already know that destination channel ID - * is 0x1 that means we have received signaling command from peer's L2CAP layer. - * So get command header, decode and process it. - * - * XXX do we need to check signaling MTU here? - */ +// #pragma mark - outgoing signals + status_t -l2cap_process_signal_cmd(HciConnection* conn, net_buffer* buffer) +send_l2cap_command(HciConnection* conn, uint8 code, uint8 ident, net_buffer* command) { - net_buffer* m = buffer; + // TODO: Command timeouts (probably in L2capEndpoint.) + { + NetBufferPrepend header(command); + header->code = code; + header->ident = ident; + header->length = htole16(command->size - sizeof(l2cap_command_header)); + } { + NetBufferPrepend basicHeader(command); + basicHeader->length = htole16(command->size - sizeof(l2cap_basic_header)); + basicHeader->dcid = htole16(L2CAP_SIGNALING_CID); + } + command->type = conn->handle; + status_t status = btDevices->PostACL(conn->Hid, command); + if (status != B_OK) + gBufferModule->free(command); + return status; +} - TRACE("%s: Signal size=%" B_PRIu32 "\n", __func__, buffer->size); - while (m != NULL) { +status_t +send_l2cap_command_reject(HciConnection* conn, uint8 ident, uint16 reason, + uint16 mtu, uint16 scid, uint16 dcid) +{ + uint8 code = 0; + net_buffer* buffer = make_l2cap_command_reject(code, reason, mtu, scid, dcid); + if (buffer == NULL) + return ENOMEM; - /* Verify packet length */ - if (buffer->size < sizeof(l2cap_cmd_hdr_t)) { - TRACE("%s: small L2CAP signaling command len=%" B_PRIu32 "\n", - __func__, buffer->size); - gBufferModule->free(buffer); + return send_l2cap_command(conn, code, ident, buffer); +} + + +status_t +send_l2cap_configuration_req(HciConnection* conn, uint8 ident, uint16 dcid, uint16 flags, + uint16* mtu, uint16* flush_timeout, l2cap_qos* flow) +{ + uint8 code = 0; + net_buffer* buffer = make_l2cap_configuration_req(code, dcid, flags, mtu, flush_timeout, flow); + if (buffer == NULL) + return ENOMEM; + + return send_l2cap_command(conn, code, ident, buffer); +} + + +status_t +send_l2cap_connection_req(HciConnection* conn, uint8 ident, uint16 psm, uint16 scid) +{ + uint8 code = 0; + net_buffer* buffer = make_l2cap_connection_req(code, psm, scid); + if (buffer == NULL) + return ENOMEM; + + return send_l2cap_command(conn, code, ident, buffer); +} + + +status_t +send_l2cap_connection_rsp(HciConnection* conn, uint8 ident, uint16 dcid, uint16 scid, + uint16 result, uint16 status) +{ + uint8 code = 0; + net_buffer* buffer = make_l2cap_connection_rsp(code, dcid, scid, result, status); + if (buffer == NULL) + return ENOMEM; + + return send_l2cap_command(conn, code, ident, buffer); +} + + +status_t +send_l2cap_configuration_rsp(HciConnection* conn, uint8 ident, uint16 scid, uint16 flags, + uint16 result, net_buffer* opt) +{ + uint8 code = 0; + net_buffer* buffer = make_l2cap_configuration_rsp(code, scid, flags, result, opt); + if (buffer == NULL) + return ENOMEM; + + return send_l2cap_command(conn, code, ident, buffer); +} + + +status_t +send_l2cap_disconnection_req(HciConnection* conn, uint8 ident, uint16 dcid, uint16 scid) +{ + uint8 code = 0; + net_buffer* buffer = make_l2cap_disconnection_req(code, dcid, scid); + if (buffer == NULL) + return ENOMEM; + + return send_l2cap_command(conn, code, ident, buffer); +} + + +status_t +send_l2cap_disconnection_rsp(HciConnection* conn, uint8 ident, uint16 dcid, uint16 scid) +{ + uint8 code = 0; + net_buffer* buffer = make_l2cap_disconnection_rsp(code, dcid, scid); + if (buffer == NULL) + return ENOMEM; + + return send_l2cap_command(conn, code, ident, buffer); +} + + +// #pragma mark - dispatcher + + +status_t +l2cap_handle_signaling_command(HciConnection* connection, net_buffer* buffer) +{ + TRACE("%s: signal size=%" B_PRIu32 "\n", __func__, buffer->size); + + // TODO: If there are multiple commands in a packet, we could accumulate the + // responses into a single packet also... + + while (buffer->size != 0) { + NetBufferHeaderReader commandHeader(buffer); + if (commandHeader.Status() != B_OK) + return ENOBUFS; + + const uint8 code = commandHeader->code; + const uint8 ident = commandHeader->ident; + const uint16 length = le16toh(commandHeader->length); + + L2capEndpoint* endpoint = NULL; + bool releaseIdent = false; + if (L2CAP_IS_SIGNAL_RSP(code)) { + endpoint = static_cast( + btCoreData->lookup_command_ident(connection, ident)); + releaseIdent = true; + } + + if (buffer->size < length) { + ERROR("%s: invalid L2CAP signaling command packet, code=%#x, " + "ident=%d, length=%d, buffer size=%" B_PRIu32 "\n", __func__, + code, ident, length,buffer->size); return EMSGSIZE; } - /* Get COMMAND header */ - NetBufferHeaderReader commandHeader(buffer); - status_t status = commandHeader.Status(); - if (status < B_OK) { - return ENOBUFS; - } + commandHeader.Remove(); - uint8 processingCode = commandHeader->code; - uint8 processingIdent = commandHeader->ident; - uint16 processingLength = le16toh(commandHeader->length); - - /* Verify command length */ - if (buffer->size < processingLength) { - ERROR("%s: invalid L2CAP signaling command, code=%#x, " - "ident=%d, length=%d, buffer size=%" B_PRIu32 "\n", __func__, - processingCode, processingIdent, processingLength, - buffer->size); - gBufferModule->free(buffer); - return (EMSGSIZE); - } - - - commandHeader.Remove(); // pulling the header of the command - - /* Process command processors responsible to delete the command*/ - switch (processingCode) { - case L2CAP_CMD_REJ: - l2cap_process_cmd_rej(conn, processingIdent, buffer); + switch (code) { + case L2CAP_COMMAND_REJECT_RSP: + l2cap_handle_command_reject(endpoint, ident, buffer); break; - case L2CAP_CON_REQ: - l2cap_process_con_req(conn, processingIdent, buffer); + case L2CAP_CONNECTION_REQ: + l2cap_handle_connection_req(connection, ident, buffer); break; - case L2CAP_CON_RSP: - l2cap_process_con_rsp(conn, processingIdent, buffer); + case L2CAP_CONNECTION_RSP: + l2cap_handle_connection_rsp(endpoint, ident, buffer); break; - case L2CAP_CFG_REQ: - l2cap_process_cfg_req(conn, processingIdent, buffer); + case L2CAP_CONFIGURATION_REQ: + l2cap_handle_configuration_req(connection, ident, + buffer, length); break; - case L2CAP_CFG_RSP: - l2cap_process_cfg_rsp(conn, processingIdent, buffer); + case L2CAP_CONFIGURATION_RSP: + l2cap_handle_configuration_rsp(connection, endpoint, ident, + buffer, length, releaseIdent); break; - case L2CAP_DISCON_REQ: - l2cap_process_discon_req(conn, processingIdent, buffer); + case L2CAP_DISCONNECTION_REQ: + l2cap_handle_disconnection_req(connection, ident, buffer); break; - case L2CAP_DISCON_RSP: - l2cap_process_discon_rsp(conn, processingIdent, buffer); + case L2CAP_DISCONNECTION_RSP: + l2cap_handle_disconnection_rsp(endpoint, ident, buffer); break; case L2CAP_ECHO_REQ: - l2cap_process_echo_req(conn, processingIdent, buffer); + l2cap_handle_echo_req(connection, ident, buffer, length); break; case L2CAP_ECHO_RSP: - l2cap_process_echo_rsp(conn, processingIdent, buffer); + l2cap_handle_echo_rsp(endpoint, ident, buffer, length); break; - case L2CAP_INFO_REQ: - l2cap_process_info_req(conn, processingIdent, buffer); + case L2CAP_INFORMATION_REQ: + l2cap_handle_info_req(connection, ident, buffer); break; - case L2CAP_INFO_RSP: - l2cap_process_info_rsp(conn, processingIdent, buffer); + case L2CAP_INFORMATION_RSP: + l2cap_handle_info_rsp(endpoint, ident, buffer); break; default: - ERROR("%s: unknown L2CAP signaling command, " - "code=%#x, ident=%d\n", __func__, processingCode, - processingIdent); - - // Send L2CAP_CommandRej. Do not really care about the result - // ORD: Remiaining commands in the same packet are also to - // be rejected? - // send_l2cap_reject(con, processingIdent, - // L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0); - gBufferModule->free(m); + ERROR("l2cap: unknown L2CAP signaling command, " + "code=%#x\n", code); + send_l2cap_command_reject(connection, ident, + l2cap_command_reject::REJECTED_NOT_UNDERSTOOD, 0, 0, 0); break; } - // Is there still remaining size? processors should have pulled its content... - if (m->size == 0) { - // free the buffer - gBufferModule->free(m); - m = NULL; - } + // Only release the ident if no more signals with it are expected. + if (releaseIdent) + btCoreData->free_command_ident(connection, ident); + + // Advance to the next command (if any.) + gBufferModule->remove_header(buffer, length); } - return B_OK; -} - - -#if 0 -#pragma mark - Processing Incoming signals -#endif - - -/* Process L2CAP_ConnectReq command */ -static status_t -l2cap_process_con_req(HciConnection* conn, uint8 ident, net_buffer* buffer) -{ - L2capChannel* channel; - uint16 dcid, psm; - - /* Get con req data */ - NetBufferHeaderReader command(buffer); - status_t status = command.Status(); - if (status < B_OK) { - return ENOBUFS; - } - - psm = le16toh(command->psm); - dcid = le16toh(command->scid); - - command.Remove(); // pull the command body - - // Create new channel and send L2CA_ConnectInd - // notification to the upper layer protocol. - channel = btCoreData->AddChannel(conn, psm /*, dcid, ident*/); - if (channel == NULL) { - TRACE("%s: No resources to create channel\n", __func__); - return (send_l2cap_con_rej(conn, ident, 0, dcid, L2CAP_NO_RESOURCES)); - } else { - TRACE("%s: New channel created scid=%d\n", __func__, channel->scid); - } - - channel->dcid = dcid; - channel->ident = ident; - - status_t indicationStatus = l2cap_l2ca_con_ind(channel); - - if ( indicationStatus == B_OK ) { - // channel->state = L2CAP_CHAN_W4_L2CA_CON_RSP; - - } else if (indicationStatus == B_NO_MEMORY) { - /* send_l2cap_con_rej(con, ident, channel->scid, dcid, L2CAP_NO_RESOURCES);*/ - btCoreData->RemoveChannel(conn, channel->scid); - - } else { - send_l2cap_con_rej(conn, ident, channel->scid, dcid, L2CAP_PSM_NOT_SUPPORTED); - btCoreData->RemoveChannel(conn, channel->scid); - } - - return (indicationStatus); -} - - -static status_t -l2cap_process_con_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer) -{ - L2capFrame* cmd = NULL; - uint16 scid, dcid, result, status; - status_t error = 0; - - /* Get command parameters */ - NetBufferHeaderReader command(buffer); - if (command.Status() < B_OK) { - return ENOBUFS; - } - - dcid = le16toh(command->dcid); - scid = le16toh(command->scid); - result = le16toh(command->result); - status = le16toh(command->status); - - command.Remove(); // pull the command body - - TRACE("%s: dcid=%d scid=%d result=%d status%d\n", __func__, dcid, scid, - result, status); - - /* Check if we have pending command descriptor */ - cmd = btCoreData->SignalByIdent(conn, ident); - if (cmd == NULL) { - ERROR("%s: unexpected L2CAP_ConnectRsp command. ident=%d, " - "con_handle=%d\n", __func__, ident, conn->handle); - return ENOENT; - } - - /* Verify channel state, if invalid - do nothing */ - if (cmd->channel->state != L2CAP_CHAN_W4_L2CAP_CON_RSP) { - ERROR("%s: unexpected L2CAP_ConnectRsp. Invalid channel state, " - "cid=%d, state=%d\n", __func__, scid, cmd->channel->state); - goto reject; - } - - /* Verify CIDs and send reject if does not match */ - if (cmd->channel->scid != scid) { - ERROR("%s: unexpected L2CAP_ConnectRsp. Channel IDs do not match, " - "scid=%d(%d)\n", __func__, cmd->channel->scid, scid); - goto reject; - } - - /* - * Looks good. We got confirmation from our peer. Now process - * it. First disable RTX timer. Then check the result and send - * notification to the upper layer. If command timeout already - * happened then ignore response. - */ - - if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0) - return error; - - if (result == L2CAP_PENDING) { - /* - * Our peer wants more time to complete connection. We shall - * start ERTX timer and wait. Keep command in the list. - */ - - cmd->channel->dcid = dcid; - btCoreData->TimeoutSignal(cmd, bluetooth_l2cap_ertx_timeout); - - // TODO: - // INDICATION error = ng_l2cap_l2ca_con_rsp(cmd->channel, cmd->token, - // result, status); - // if (error != B_OK) - // btCoreData->RemoveChannel(conn, cmd->channel->scid); - - } else { - - if (result == L2CAP_SUCCESS) { - /* - * Channel is open. Complete command and move to CONFIG - * state. Since we have sent positive confirmation we - * expect to receive L2CA_Config request from the upper - * layer protocol. - */ - - cmd->channel->dcid = dcid; - cmd->channel->state = L2CAP_CHAN_CONFIG; - } - - error = l2cap_con_rsp_ind(conn, cmd->channel); - - /* XXX do we have to remove the channel on error? */ - if (error != 0 || result != L2CAP_SUCCESS) { - ERROR("%s: failed to open L2CAP channel, result=%d, status=%d\n", - __func__, result, status); - btCoreData->RemoveChannel(conn, cmd->channel->scid); - } - - btCoreData->AcknowledgeSignal(cmd); - } - - return error; - -reject: - /* Send reject. Do not really care about the result */ - send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, scid, dcid); - - return 0; -} - - -static option_status -getNextSignalOption(net_buffer* nbuf, size_t* off, l2cap_cfg_opt_t* hdr, - l2cap_cfg_opt_val_t* val) -{ - int hint; - size_t len = nbuf->size - (*off); - - if (len == 0) - return OPTION_NOT_PRESENT; - if (len < 0 || len < sizeof(*hdr)) - return HEADER_TOO_SHORT; - - gBufferModule->read(nbuf, *off, hdr, sizeof(*hdr)); - *off += sizeof(*hdr); - len -= sizeof(*hdr); - - hint = L2CAP_OPT_HINT(hdr->type); - hdr->type &= L2CAP_OPT_HINT_MASK; - - switch (hdr->type) { - case L2CAP_OPT_MTU: - if (hdr->length != L2CAP_OPT_MTU_SIZE || len < hdr->length) - return BAD_OPTION_LENGTH; - - gBufferModule->read(nbuf, *off, val, L2CAP_OPT_MTU_SIZE); - val->mtu = le16toh(val->mtu); - *off += L2CAP_OPT_MTU_SIZE; - TRACE("%s: mtu %d specified\n", __func__, val->mtu); - break; - - case L2CAP_OPT_FLUSH_TIMO: - if (hdr->length != L2CAP_OPT_FLUSH_TIMO_SIZE || len < hdr->length) - return BAD_OPTION_LENGTH; - - gBufferModule->read(nbuf, *off, val, L2CAP_OPT_FLUSH_TIMO_SIZE); - val->flush_timo = le16toh(val->flush_timo); - TRACE("%s: flush specified\n", __func__); - *off += L2CAP_OPT_FLUSH_TIMO_SIZE; - break; - - case L2CAP_OPT_QOS: - if (hdr->length != L2CAP_OPT_QOS_SIZE || len < hdr->length) - return BAD_OPTION_LENGTH; - - gBufferModule->read(nbuf, *off, val, L2CAP_OPT_QOS_SIZE); - val->flow.token_rate = le32toh(val->flow.token_rate); - val->flow.token_bucket_size = le32toh(val->flow.token_bucket_size); - val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth); - val->flow.latency = le32toh(val->flow.latency); - val->flow.delay_variation = le32toh(val->flow.delay_variation); - *off += L2CAP_OPT_QOS_SIZE; - TRACE("%s: qos specified\n", __func__); - break; - - default: - if (hint) - *off += hdr->length; - else - return OPTION_UNKNOWN; - break; - } - - return OPTION_PRESENT; -} - - -static status_t -l2cap_process_cfg_req(HciConnection* conn, uint8 ident, net_buffer* buffer) -{ - L2capChannel* channel = NULL; - uint16 dcid; - uint16 respond; - uint16 result; - - l2cap_cfg_opt_t hdr; - l2cap_cfg_opt_val_t val; - - size_t off; - status_t error = 0; - - /* Get command parameters */ - NetBufferHeaderReader command(buffer); - status_t status = command.Status(); - if (status < B_OK) { - return ENOBUFS; - } - - dcid = le16toh(command->dcid); - respond = L2CAP_OPT_CFLAG(le16toh(command->flags)); - - command.Remove(); // pull configuration header - - /* Check if we have this channel and it is in valid state */ - channel = btCoreData->ChannelBySourceID(conn, dcid); - if (channel == NULL) { - ERROR("%s: unexpected L2CAP_ConfigReq command. " - "Channel does not exist, cid=%d\n", __func__, dcid); - goto reject; - } - - /* Verify channel state */ - if (channel->state != L2CAP_CHAN_CONFIG - && channel->state != L2CAP_CHAN_OPEN) { - ERROR("%s: unexpected L2CAP_ConfigReq. Invalid channel state, " - "cid=%d, state=%d\n", __func__, dcid, channel->state); - goto reject; - } - - if (channel->state == L2CAP_CHAN_OPEN) { /* Re-configuration */ - channel->cfgState = 0; // Reset configuration state - channel->state = L2CAP_CHAN_CONFIG; - } - - for (result = 0, off = 0; ; ) { - error = getNextSignalOption(buffer, &off, &hdr, &val); - - if (error == OPTION_NOT_PRESENT) { /* We done with this packet */ - // TODO: configurations should have been pulled - break; - } else if (error > 0) { /* Got option */ - switch (hdr.type) { - case L2CAP_OPT_MTU: - channel->configuration->omtu = val.mtu; - break; - - case L2CAP_OPT_FLUSH_TIMO: - channel->configuration->flush_timo = val.flush_timo; - break; - - case L2CAP_OPT_QOS: - memcpy(&val.flow, &channel->configuration->iflow, - sizeof(channel->configuration->iflow)); - break; - - default: /* Ignore unknown hint option */ - break; - } - } else { /* Oops, something is wrong */ - respond = 1; - if (error == OPTION_UNKNOWN) { - // TODO: Remote to get the next possible option - result = L2CAP_UNKNOWN_OPTION; - } else { - /* XXX FIXME Send other reject codes? */ - gBufferModule->free(buffer); - result = L2CAP_REJECT; - } - - break; - } - } - - TRACE("%s: Pulled %ld of configuration fields respond=%d " - "remaining=%" B_PRIu32 "\n", __func__, off, respond, buffer->size); - gBufferModule->remove_header(buffer, off); - - - /* - * Now check and see if we have to respond. If everything was OK then - * respond contain "C flag" and (if set) we will respond with empty - * packet and will wait for more options. - * - * Other case is that we did not like peer's options and will respond - * with L2CAP_Config response command with Reject error code. - * - * When "respond == 0" than we have received all options and we will - * sent L2CA_ConfigInd event to the upper layer protocol. - */ - - if (respond) { - error = send_l2cap_cfg_rsp(conn, ident, channel->dcid, result, buffer); - if (error != 0) { - // TODO: - // INDICATION ng_l2cap_l2ca_discon_ind(ch); - btCoreData->RemoveChannel(conn, channel->scid); - } - } else { - /* Send L2CA_ConfigInd event to the upper layer protocol */ - channel->cfgState |= L2CAP_CFG_IN; - channel->ident = ident; // sent ident to reply - error = l2cap_cfg_req_ind(channel); - if (error != 0) - btCoreData->RemoveChannel(conn, channel->scid); - } - - return error; - -reject: - /* Send reject. Do not really care about the result */ gBufferModule->free(buffer); - - send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, 0, dcid); - - return B_OK; -} - - -/* Process L2CAP_ConfigRsp command */ -static status_t -l2cap_process_cfg_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer) -{ - L2capFrame* cmd = NULL; - uint16 scid, cflag, result; - - l2cap_cfg_opt_t hdr; - l2cap_cfg_opt_val_t val; - - size_t off; - status_t error = 0; - - NetBufferHeaderReader command(buffer); - status_t status = command.Status(); - if (status < B_OK) { - return ENOBUFS; - } - - scid = le16toh(command->scid); - cflag = L2CAP_OPT_CFLAG(le16toh(command->flags)); - result = le16toh(command->result); - - command.Remove(); - - TRACE("%s: scid=%d cflag=%d result=%d\n", __func__, scid, cflag, result); - - /* Check if we have this command */ - cmd = btCoreData->SignalByIdent(conn, ident); - if (cmd == NULL) { - ERROR("%s: unexpected L2CAP_ConfigRsp command. " - "ident=%d, con_handle=%d\n", __func__, ident, conn->handle); - gBufferModule->free(buffer); - return ENOENT; - } - - /* Verify CIDs and send reject if does not match */ - if (cmd->channel->scid != scid) { - ERROR("%s: unexpected L2CAP_ConfigRsp.Channel ID does not match, " - "scid=%d(%d)\n", __func__, cmd->channel->scid, scid); - goto reject; - } - - /* Verify channel state and reject if invalid */ - if (cmd->channel->state != L2CAP_CHAN_CONFIG) { - ERROR("%s: unexpected L2CAP_ConfigRsp. Invalid channel state, scid=%d, " - "state=%d\n", __func__, cmd->channel->scid, cmd->channel->state); - goto reject; - } - - /* - * Looks like it is our response, so process it. First parse options, - * then verify C flag. If it is set then we shall expect more - * configuration options from the peer and we will wait. Otherwise we - * have received all options and we will send L2CA_ConfigRsp event to - * the upper layer protocol. If command timeout already happened then - * ignore response. - */ - - if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0) { - gBufferModule->free(buffer); - return error; - } - - for (off = 0; ; ) { - error = getNextSignalOption(buffer, &off, &hdr, &val); - // TODO: pull the option - - if (error == OPTION_NOT_PRESENT) /* We done with this packet */ - break; - else if (error > 0) { /* Got option */ - switch (hdr.type) { - case L2CAP_OPT_MTU: - cmd->channel->configuration->imtu = val.mtu; - break; - - case L2CAP_OPT_FLUSH_TIMO: - cmd->channel->configuration->flush_timo = val.flush_timo; - break; - - case L2CAP_OPT_QOS: - memcpy(&val.flow, &cmd->channel->configuration->oflow, - sizeof(cmd->channel->configuration->oflow)); - break; - - default: /* Ignore unknown hint option */ - break; - } - } else { - /* - * XXX FIXME What to do here? - * - * This is really BAD :( options packet was broken, or - * peer sent us option that we did not understand. Let - * upper layer know and do not wait for more options. - */ - - ERROR("%s: fail parsing configuration options\n", __func__); - - // INDICATION - result = L2CAP_UNKNOWN; - cflag = 0; - - break; - } - } - - if (cflag) /* Restart timer and wait for more options */ - btCoreData->TimeoutSignal(cmd, bluetooth_l2cap_rtx_timeout); - else { - /* Send L2CA_Config response to the upper layer protocol */ - error = l2cap_cfg_rsp_ind(cmd->channel /*, cmd->token, result*/); - if (error != 0) { - /* - * XXX FIXME what to do here? we were not able to send - * response to the upper layer protocol, so for now - * just close the channel. Send L2CAP_Disconnect to - * remote peer? - */ - - ERROR("%s: failed to send L2CA_Config response\n", __func__); - - btCoreData->RemoveChannel(conn, cmd->channel->scid); - } - - btCoreData->AcknowledgeSignal(cmd); - } - - return error; - -reject: - /* Send reject. Do not really care about the result */ - gBufferModule->free(buffer); - send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, scid, 0); - - return B_OK; -} - - -/* Process L2CAP_DisconnectReq command */ -static status_t -l2cap_process_discon_req(HciConnection* conn, uint8 ident, net_buffer* buffer) -{ - L2capChannel* channel = NULL; - L2capFrame* cmd = NULL; - net_buffer* buff = NULL; - uint16 scid; - uint16 dcid; - - NetBufferHeaderReader command(buffer); - status_t status = command.Status(); - if (status < B_OK) { - return ENOBUFS; - } - - dcid = le16toh(command->dcid); - scid = le16toh(command->scid); - - command.Remove(); - - /* Check if we have this channel and it is in valid state */ - channel = btCoreData->ChannelBySourceID(conn, dcid); - if (channel == NULL) { - ERROR("%s: unexpected L2CAP_DisconnectReq message. " - "Channel does not exist, cid=%x\n", __func__, dcid); - goto reject; - } - - /* XXX Verify channel state and reject if invalid -- is that true? */ - if (channel->state != L2CAP_CHAN_OPEN - && channel->state != L2CAP_CHAN_CONFIG - && channel->state != L2CAP_CHAN_W4_L2CAP_DISCON_RSP) { - ERROR("%s: unexpected L2CAP_DisconnectReq. Invalid channel state, " - "cid=%d, state=%d\n", __func__, dcid, channel->state); - goto reject; - } - - /* Match destination channel ID */ - if (channel->dcid != scid || channel->scid != dcid) { - ERROR("%s: unexpected L2CAP_DisconnectReq. Channel IDs does not match, " - "channel: scid=%d, dcid=%d, request: scid=%d, dcid=%d\n", __func__, - channel->scid, channel->dcid, scid, dcid); - goto reject; - } - - /* - * Looks good, so notify upper layer protocol that channel is about - * to be disconnected and send L2CA_DisconnectInd message. Then respond - * with L2CAP_DisconnectRsp. - */ - - // inform upper if we were not actually already waiting - if (channel->state != L2CAP_CHAN_W4_L2CAP_DISCON_RSP) { - l2cap_discon_req_ind(channel); // do not care about result - } - - /* Send L2CAP_DisconnectRsp */ - buff = l2cap_discon_rsp(ident, dcid, scid); - cmd = btCoreData->SpawnSignal(conn, channel, buff, ident, L2CAP_DISCON_RSP); - if (cmd == NULL) - return ENOMEM; - - /* Link command to the queue */ - SchedConnectionPurgeThread(conn); - - return B_OK; - -reject: - /* Send reject. Do not really care about the result */ - send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, scid, dcid); - - return B_OK; -} - - -/* Process L2CAP_DisconnectRsp command */ -static status_t -l2cap_process_discon_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer) -{ - L2capFrame* cmd = NULL; - int16 scid, dcid; - status_t error = 0; - - /* Get command parameters */ - NetBufferHeaderReader command(buffer); - status_t status = command.Status(); - if (status < B_OK) { - return ENOBUFS; - } - - dcid = le16toh(command->dcid); - scid = le16toh(command->scid); - - command.Remove(); - //? NG_FREE_M(con->rx_pkt); - - /* Check if we have pending command descriptor */ - cmd = btCoreData->SignalByIdent(conn, ident); - if (cmd == NULL) { - ERROR("%s: unexpected L2CAP_DisconnectRsp command. ident=%d, " - "con_handle=%d\n", __func__, ident, conn->handle); - goto out; - } - - /* Verify channel state, do nothing if invalid */ - if (cmd->channel->state != L2CAP_CHAN_W4_L2CA_DISCON_RSP) { - ERROR("%s: unexpected L2CAP_DisconnectRsp. Invalid state, cid=%d, " - "state=%d\n", __func__, scid, cmd->channel->state); - goto out; - } - - /* Verify CIDs and send reject if does not match */ - if (cmd->channel->scid != scid || cmd->channel->dcid != dcid) { - ERROR("%s: unexpected L2CAP_DisconnectRsp. Channel IDs do not match, " - "scid=%d(%d), dcid=%d(%d)\n", __func__, cmd->channel->scid, scid, - cmd->channel->dcid, dcid); - goto out; - } - - /* - * Looks like we have successfuly disconnected channel, so notify - * upper layer. If command timeout already happened then ignore - * response. - */ - - if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0) - goto out; - - l2cap_discon_rsp_ind(cmd->channel/* results? */); - btCoreData->RemoveChannel(conn, scid); /* this will free commands too */ - -out: - return error; -} - - -static status_t -l2cap_process_echo_req(HciConnection *conn, uint8 ident, net_buffer *buffer) -{ - L2capFrame* cmd = NULL; - - cmd = btCoreData->SpawnSignal(conn, NULL, l2cap_echo_req(ident, NULL, 0), - ident, L2CAP_ECHO_RSP); - if (cmd == NULL) { - gBufferModule->free(buffer); - return ENOMEM; - } - - /* Attach data and link command to the queue */ - SchedConnectionPurgeThread(conn); - - return B_OK; -} - - -/* Process L2CAP_EchoRsp command */ -static status_t -l2cap_process_echo_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer) -{ - L2capFrame* cmd = NULL; - status_t error = 0; - - /* Check if we have this command */ - cmd = btCoreData->SignalByIdent(conn, ident); - if (cmd != NULL) { - /* If command timeout already happened then ignore response */ - if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0) { - return error; - } - - // INDICATION error = ng_l2cap_l2ca_ping_rsp(cmd->conn, cmd->token, - // L2CAP_SUCCESS, conn->rx_pkt); - btCoreData->AcknowledgeSignal(cmd); - - } else { - ERROR("%s: unexpected L2CAP_EchoRsp command. ident does not exist, " - "ident=%d\n", __func__, ident); - gBufferModule->free(buffer); - error = B_ERROR; - } - - return error; -} - - -/* Process L2CAP_InfoReq command */ -static status_t -l2cap_process_info_req(HciConnection* conn, uint8 ident, net_buffer* buffer) -{ - L2capFrame* cmd = NULL; - net_buffer* buf = NULL; - uint16 type; - - /* Get command parameters */ - NetBufferHeaderReader command(buffer); - status_t status = command.Status(); - if (status < B_OK) { - return ENOBUFS; - } - - // ?? - // command->type = le16toh(mtod(conn->rx_pkt, - // ng_l2cap_info_req_cp *)->type); - type = le16toh(command->type); - - command.Remove(); - - switch (type) { - case L2CAP_CONNLESS_MTU: - buf = l2cap_info_rsp(ident, L2CAP_CONNLESS_MTU, L2CAP_SUCCESS, - L2CAP_MTU_DEFAULT); - break; - - default: - buf = l2cap_info_rsp(ident, type, L2CAP_NOT_SUPPORTED, 0); - break; - } - - cmd = btCoreData->SpawnSignal(conn, NULL, buf, ident, L2CAP_INFO_RSP); - if (cmd == NULL) - return ENOMEM; - - /* Link command to the queue */ - SchedConnectionPurgeThread(conn); - - return B_OK; -} - - -/* Process L2CAP_InfoRsp command */ -static status_t -l2cap_process_info_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer) -{ - l2cap_info_rsp_cp* cp = NULL; - L2capFrame* cmd = NULL; - status_t error = B_OK; - - /* Get command parameters */ - NetBufferHeaderReader command(buffer); - status_t status = command.Status(); - if (status < B_OK) { - return ENOBUFS; - } - - command->type = le16toh(command->type); - command->result = le16toh(command->result); - - command.Remove(); - - /* Check if we have pending command descriptor */ - cmd = btCoreData->SignalByIdent(conn, ident); - if (cmd == NULL) { - ERROR("%s: unexpected L2CAP_InfoRsp command. Requested ident does not " - "exist, ident=%d\n", __func__, ident); - gBufferModule->free(buffer); - return ENOENT; - } - - /* If command timeout already happened then ignore response */ - if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0) { - gBufferModule->free(buffer); - return error; - } - - if (command->result == L2CAP_SUCCESS) { - switch (command->type) { - case L2CAP_CONNLESS_MTU: - #if 0 - /* TODO: Check specs ?? */ - if (conn->rx_pkt->m_pkthdr.len == sizeof(uint16)) { - *mtod(conn->rx_pkt, uint16 *) - = le16toh(*mtod(conn->rx_pkt, uint16 *)); - } else { - cp->result = L2CAP_UNKNOWN; XXX - ERROR("%s: invalid L2CAP_InfoRsp command. " - "Bad connectionless MTU parameter, len=%d\n", __func__, - conn->rx_pkt->m_pkthdr.len); - } - #endif - break; - - default: - ERROR("%s: invalid L2CAP_InfoRsp command. " - "Unknown info type=%d\n", __func__, cp->type); - break; - } - } - - //INDICATION error = ng_l2cap_l2ca_get_info_rsp(cmd->conn, cmd->token, cp->result, conn->rx_pkt); - btCoreData->AcknowledgeSignal(cmd); - - return error; -} - - -static status_t -l2cap_process_cmd_rej(HciConnection* conn, uint8 ident, net_buffer* buffer) -{ - L2capFrame* cmd = NULL; - - /* TODO: review this command... Get command data */ - NetBufferHeaderReader command(buffer); - status_t status = command.Status(); - if (status < B_OK) { - return ENOBUFS; - } - - command->reason = le16toh(command->reason); - - TRACE("%s: reason=%d\n", __func__, command->reason); - - command.Remove(); - - /* Check if we have pending command descriptor */ - cmd = btCoreData->SignalByIdent(conn, ident); - if (cmd != NULL) { - /* If command timeout already happened then ignore reject */ - if (btCoreData->UnTimeoutSignal(cmd) != 0) { - gBufferModule->free(buffer); - return ETIMEDOUT; - } - - - switch (cmd->code) { - case L2CAP_CON_REQ: - //INDICATION l2cap_l2ca_con_rsp(cmd->channel, cmd->token, cp->reason, 0); - btCoreData->RemoveChannel(conn, cmd->channel->scid); - break; - - case L2CAP_CFG_REQ: - //INDICATION l2cap_l2ca_cfg_rsp(cmd->channel, cmd->token, cp->reason); - break; - - case L2CAP_DISCON_REQ: - //INDICATION l2cap_l2ca_discon_rsp(cmd->channel, cmd->token, cp->reason); - btCoreData->RemoveChannel(conn, cmd->channel->scid); - break; - - case L2CAP_ECHO_REQ: - //INDICATION l2cap_l2ca_ping_rsp(cmd->con, cmd->token, cp->reason, NULL); - break; - - case L2CAP_INFO_REQ: - //INDICATION l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, cp->reason, NULL); - break; - - default: - ERROR("%s: unexpected L2CAP_CommandRej. Unexpected opcode=%d\n", - __func__, cmd->code); - break; - } - - btCoreData->AcknowledgeSignal(cmd); - - } else { - ERROR("%s: unexpected L2CAP_CommandRej command. " - "Requested ident does not exist, ident=%d\n", __func__, ident); - } - - return B_OK; -} - - -#if 0 -#pragma mark - Queuing Outgoing signals -#endif - - -status_t -send_l2cap_reject(HciConnection* conn, uint8 ident, uint16 reason, - uint16 mtu, uint16 scid, uint16 dcid) -{ - L2capFrame* cmd = NULL; - - cmd = btCoreData->SpawnSignal(conn, NULL, l2cap_cmd_rej(ident, reason, - mtu, scid, dcid), ident, L2CAP_CMD_REJ); - if (cmd == NULL) - return ENOMEM; - - /* Link command to the queue */ - SchedConnectionPurgeThread(conn); - - return B_OK; -} - - -status_t -send_l2cap_con_rej(HciConnection* conn, uint8 ident, uint16 scid, uint16 dcid, - uint16 result) -{ - L2capFrame* cmd = NULL; - - cmd = btCoreData->SpawnSignal(conn, NULL, - l2cap_con_rsp(ident, scid, dcid, result, 0), ident, L2CAP_CON_RSP); - if (cmd == NULL) - return ENOMEM; - - /* Link command to the queue */ - SchedConnectionPurgeThread(conn); - - return B_OK; -} - - -status_t -send_l2cap_cfg_rsp(HciConnection* conn, uint8 ident, uint16 scid, - uint16 result, net_buffer* opt) -{ - L2capFrame* cmd = NULL; - - cmd = btCoreData->SpawnSignal(conn, NULL, - l2cap_cfg_rsp(ident, scid, 0, result, opt), ident, L2CAP_CFG_RSP); - if (cmd == NULL) { - gBufferModule->free(opt); - return ENOMEM; - } - - /* Link command to the queue */ - SchedConnectionPurgeThread(conn); - return B_OK; } diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap_signal.h b/src/add-ons/kernel/network/protocols/l2cap/l2cap_signal.h index 15e3da72af..c69ccd61dd 100644 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap_signal.h +++ b/src/add-ons/kernel/network/protocols/l2cap/l2cap_signal.h @@ -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 -#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 */ diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap_upper.cpp b/src/add-ons/kernel/network/protocols/l2cap/l2cap_upper.cpp deleted file mode 100644 index 80ef5a9cc9..0000000000 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap_upper.cpp +++ /dev/null @@ -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 -#include - -#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 -#include - - -#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); -} - - - diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap_upper.h b/src/add-ons/kernel/network/protocols/l2cap/l2cap_upper.h deleted file mode 100644 index c1c555b3a8..0000000000 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap_upper.h +++ /dev/null @@ -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 diff --git a/src/tests/kits/bluetooth/l2capClient/l2capClient.cpp b/src/tests/kits/bluetooth/l2capClient/l2capClient.cpp index 778e600f6c..3029972ce8 100644 --- a/src/tests/kits/bluetooth/l2capClient/l2capClient.cpp +++ b/src/tests/kits/bluetooth/l2capClient/l2capClient.cpp @@ -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) {