Direct-BT  2.3.1
Direct-BT - Direct Bluetooth Programming.
L2CAPComm.hpp
Go to the documentation of this file.
1 /*
2  * Author: Sven Gothel <sgothel@jausoft.com>
3  * Copyright (c) 2020 Gothel Software e.K.
4  * Copyright (c) 2020 ZAFENA AB
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 
26 #ifndef L2CAP_COMM_HPP_
27 #define L2CAP_COMM_HPP_
28 
29 #include <cstring>
30 #include <string>
31 #include <memory>
32 #include <cstdint>
33 
34 #include <mutex>
35 #include <atomic>
36 
37 #include <jau/environment.hpp>
38 
39 #include "UUID.hpp"
40 #include "BTTypes0.hpp"
41 
42 /**
43  * - - - - - - - - - - - - - - -
44  *
45  * Module L2CAPComm:
46  *
47  * - BT Core Spec v5.2: Vol 3, Part A: BT Logical Link Control and Adaption Protocol (L2CAP)
48  */
49 namespace direct_bt {
50 
51  class BTDevice; // forward
52 
53  /**
54  * L2CAP Singleton runtime environment properties
55  * <p>
56  * Also see {@link DBTEnv::getExplodingProperties(const std::string & prefixDomain)}.
57  * </p>
58  */
60  private:
61  L2CAPEnv() noexcept;
62 
63  const bool exploding; // just to trigger exploding properties
64 
65  public:
66  /**
67  * L2CAP poll timeout for reading, defaults to 10s.
68  * <p>
69  * Environment variable is 'direct_bt.l2cap.reader.timeout'.
70  * </p>
71  */
73 
74  /**
75  * Debugging facility: L2CAP restart count on transmission errors, defaults to 5 attempts.
76  * <p>
77  * If negative, L2CAPComm will abort() the program.
78  * </p>
79  * <p>
80  * Environment variable is 'direct_bt.l2cap.restart.count'.
81  * </p>
82  */
84 
85  /**
86  * Debug all GATT Data communication
87  * <p>
88  * Environment variable is 'direct_bt.debug.l2cap.data'.
89  * </p>
90  */
91  const bool DEBUG_DATA;
92 
93  public:
94  static L2CAPEnv& get() noexcept {
95  /**
96  * Thread safe starting with C++11 6.7:
97  *
98  * If control enters the declaration concurrently while the variable is being initialized,
99  * the concurrent execution shall wait for completion of the initialization.
100  *
101  * (Magic Statics)
102  *
103  * Avoiding non-working double checked locking.
104  */
105  static L2CAPEnv e;
106  return e;
107  }
108  };
109 
110  /**
111  * Read/Write L2CAP communication channel.
112  */
113  class L2CAPComm {
114  public:
115  enum class Defaults : int {
117  };
118  static constexpr int number(const Defaults d) { return static_cast<int>(d); }
119 
120  static std::string getStateString(bool isConnected, bool hasIOError) {
121  return "State[open "+std::to_string(isConnected)+", ioError "+std::to_string(hasIOError)+"]";
122  }
123 
124  private:
125  static int l2cap_open_dev(const BDAddressAndType & adapterAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid);
126  static int l2cap_close_dev(int dd);
127 
128  const L2CAPEnv & env;
129  const BDAddressAndType adapterAddressAndType;
130  const L2CAP_PSM psm;
131  const L2CAP_CID cid;
132 
133  std::recursive_mutex mtx_write;
134  BDAddressAndType deviceAddressAndType;
135  std::atomic<int> socket_descriptor; // the l2cap socket
136  std::atomic<bool> is_open; // reflects state
137  std::atomic<bool> has_ioerror; // reflects state
138  std::atomic<bool> interrupt_flag; // for forced disconnect
139  std::atomic<pthread_t> tid_connect;
140  std::atomic<pthread_t> tid_read;
141 
142  bool setBTSecurityLevelImpl(const BTSecurityLevel sec_level);
143  BTSecurityLevel getBTSecurityLevelImpl();
144 
145  public:
146  /**
147  * Constructing a non connected L2CAP channel instance for the pre-defined PSM and CID.
148  */
149  L2CAPComm(const BDAddressAndType& adapterAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid);
150 
151  L2CAPComm(const L2CAPComm&) = delete;
152  void operator=(const L2CAPComm&) = delete;
153 
154  /** Destructor closing the L2CAP channel, see {@link #disconnect()}. */
155  ~L2CAPComm() noexcept { close(); }
156 
157  /**
158  * Opens and connects the L2CAP channel, locking {@link #mutex_write()}.
159  * <p>
160  * BT Core Spec v5.2: Vol 3, Part A: L2CAP_CONNECTION_REQ
161  * </p>
162  *
163  * @param device the remote device to establish this L2CAP connection
164  * @param sec_level sec_level < BTSecurityLevel::NONE will not set security level
165  * @return true if connection has been established, otherwise false
166  */
167  bool open(const BTDevice& device, const BTSecurityLevel sec_level=BTSecurityLevel::NONE);
168 
169  bool isOpen() const { return is_open; }
170 
171  /** Closing the L2CAP channel, locking {@link #mutex_write()}. */
172  bool close() noexcept;
173 
174  /** Return this L2CAP socket descriptor. */
175  inline int getSocketDescriptor() const noexcept { return socket_descriptor; }
176 
177  bool hasIOError() const { return has_ioerror; }
178  std::string getStateString() const { return getStateString(is_open, has_ioerror); }
179 
180  /** Return the recursive write mutex for multithreading access. */
181  std::recursive_mutex & mutex_write() { return mtx_write; }
182 
183  /**
184  * If sec_level > ::BTSecurityLevel::UNSET, sets the BlueZ's L2CAP socket BT_SECURITY sec_level, determining the SMP security mode per connection.
185  * <p>
186  * To unset security, the L2CAP socket should be closed and opened again.
187  * </p>
188  *
189  * @param sec_level sec_level < ::BTSecurityLevel::NONE will not set security level and returns false.
190  * @return true if a security level > ::BTSecurityLevel::UNSET has been set successfully, false if no security level has been set or if it failed.
191  */
192  bool setBTSecurityLevel(const BTSecurityLevel sec_level);
193 
194  /**
195  * Fetches the current BlueZ's L2CAP socket BT_SECURITY sec_level.
196  *
197  * @return ::BTSecurityLevel sec_level value, ::BTSecurityLevel::UNSET if failure
198  */
200 
201  /** Generic read, w/o locking suitable for a unique ringbuffer sink. Using L2CAPEnv::L2CAP_READER_POLL_TIMEOUT.*/
202  jau::snsize_t read(uint8_t* buffer, const jau::nsize_t capacity);
203 
204  /** Generic write, locking {@link #mutex_write()}. */
205  jau::snsize_t write(const uint8_t *buffer, const jau::nsize_t length);
206  };
207 
208 } // namespace direct_bt
209 
210 #endif /* L2CAP_COMM_HPP_ */
direct_bt::L2CAPComm::number
static constexpr int number(const Defaults d)
Definition: L2CAPComm.hpp:118
direct_bt::L2CAPComm::getStateString
static std::string getStateString(bool isConnected, bool hasIOError)
Definition: L2CAPComm.hpp:120
direct_bt::L2CAPComm::L2CAPComm
L2CAPComm(const L2CAPComm &)=delete
direct_bt::L2CAPEnv::get
static L2CAPEnv & get() noexcept
Definition: L2CAPComm.hpp:94
direct_bt::L2CAP_CID
L2CAP_CID
Definition: BTTypes0.hpp:432
jau::root_environment
Base jau environment class, merely to tag all environment settings by inheritance and hence documenta...
Definition: environment.hpp:51
direct_bt::L2CAPEnv
L2CAP Singleton runtime environment properties.
Definition: L2CAPComm.hpp:59
direct_bt
Definition: ATTPDUTypes.hpp:171
direct_bt::L2CAPComm::getBTSecurityLevel
BTSecurityLevel getBTSecurityLevel()
Fetches the current BlueZ's L2CAP socket BT_SECURITY sec_level.
Definition: L2CAPComm.cpp:336
direct_bt::L2CAPComm::~L2CAPComm
~L2CAPComm() noexcept
Destructor closing the L2CAP channel, see disconnect().
Definition: L2CAPComm.hpp:155
direct_bt::L2CAPEnv::L2CAP_READER_POLL_TIMEOUT
const int32_t L2CAP_READER_POLL_TIMEOUT
L2CAP poll timeout for reading, defaults to 10s.
Definition: L2CAPComm.hpp:72
direct_bt::L2CAPComm
Read/Write L2CAP communication channel.
Definition: L2CAPComm.hpp:113
direct_bt::BTSecurityLevel::NONE
@ NONE
No encryption and no authentication.
direct_bt::L2CAPComm::Defaults::L2CAP_CONNECT_MAX_RETRY
@ L2CAP_CONNECT_MAX_RETRY
jau::to_string
PRAGMA_DISABLE_WARNING_POP constexpr_cxx20 std::string to_string(const endian &v) noexcept
Return std::string representation of the given jau::endian.
Definition: byte_util.hpp:198
direct_bt::BTSecurityLevel
BTSecurityLevel
Bluetooth Security Level.
Definition: BTTypes0.hpp:211
direct_bt::L2CAPComm::getStateString
std::string getStateString() const
Definition: L2CAPComm.hpp:178
direct_bt::L2CAPComm::hasIOError
bool hasIOError() const
Definition: L2CAPComm.hpp:177
direct_bt::L2CAPEnv::DEBUG_DATA
const bool DEBUG_DATA
Debug all GATT Data communication.
Definition: L2CAPComm.hpp:91
direct_bt::L2CAPComm::L2CAPComm
L2CAPComm(const BDAddressAndType &adapterAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid)
Constructing a non connected L2CAP channel instance for the pre-defined PSM and CID.
Definition: L2CAPComm.cpp:109
direct_bt::L2CAPComm::read
jau::snsize_t read(uint8_t *buffer, const jau::nsize_t capacity)
Generic read, w/o locking suitable for a unique ringbuffer sink.
Definition: L2CAPComm.cpp:374
direct_bt::L2CAP_PSM
L2CAP_PSM
Protocol Service Multiplexers (PSM) Assigned numbers https://www.bluetooth.com/specifications/assigne...
Definition: BTTypes0.hpp:458
BTTypes0.hpp
UUID.hpp
direct_bt::L2CAPComm::write
jau::snsize_t write(const uint8_t *buffer, const jau::nsize_t length)
Generic write, locking mutex_write().
Definition: L2CAPComm.cpp:441
direct_bt::L2CAPComm::open
bool open(const BTDevice &device, const BTSecurityLevel sec_level=BTSecurityLevel::NONE)
Opens and connects the L2CAP channel, locking mutex_write().
Definition: L2CAPComm.cpp:130
direct_bt::L2CAPComm::getSocketDescriptor
int getSocketDescriptor() const noexcept
Return this L2CAP socket descriptor.
Definition: L2CAPComm.hpp:175
jau::snsize_t
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
Definition: int_types.hpp:56
jau::nsize_t
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:44
direct_bt::L2CAPComm::Defaults
Defaults
Definition: L2CAPComm.hpp:115
direct_bt::L2CAPComm::isOpen
bool isOpen() const
Definition: L2CAPComm.hpp:169
direct_bt::L2CAPEnv::L2CAP_RESTART_COUNT_ON_ERROR
const int32_t L2CAP_RESTART_COUNT_ON_ERROR
Debugging facility: L2CAP restart count on transmission errors, defaults to 5 attempts.
Definition: L2CAPComm.hpp:83
direct_bt::L2CAPComm::operator=
void operator=(const L2CAPComm &)=delete
direct_bt::L2CAPComm::setBTSecurityLevel
bool setBTSecurityLevel(const BTSecurityLevel sec_level)
If sec_level > BTSecurityLevel::UNSET, sets the BlueZ's L2CAP socket BT_SECURITY sec_level,...
Definition: L2CAPComm.cpp:289
direct_bt::BDAddressAndType
Unique Bluetooth EUI48 address and BDAddressType tuple.
Definition: BTAddress.hpp:417
direct_bt::BTDevice
Definition: BTDevice.hpp:57
direct_bt::L2CAPComm::close
bool close() noexcept
Closing the L2CAP channel, locking mutex_write().
Definition: L2CAPComm.cpp:241
direct_bt::L2CAPComm::mutex_write
std::recursive_mutex & mutex_write()
Return the recursive write mutex for multithreading access.
Definition: L2CAPComm.hpp:181
environment.hpp