Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
HCIComm.cpp
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#include <cstring>
27#include <string>
28#include <memory>
29#include <cstdint>
30#include <vector>
31#include <cstdio>
32
33#include <algorithm>
34#include <type_traits>
35
36// #define PERF_PRINT_ON 1
37#include <jau/debug.hpp>
38
39#include "HCIComm.hpp"
40
41#include "BTIoctl.hpp"
42
43extern "C" {
44 #include <inttypes.h>
45 #include <unistd.h>
46 #include <sys/param.h>
47 #include <sys/uio.h>
48 #include <sys/types.h>
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
51 #include <poll.h>
52 #include <pthread.h>
53 #include <signal.h>
54}
55
56namespace direct_bt {
57
58int HCIComm::hci_open_dev(const uint16_t dev_id, const uint16_t channel) noexcept
59{
60 static_assert( sizeof(struct sockaddr) > sizeof(sockaddr_hci), "Requirement sizeof(struct sockaddr) > sizeof(sockaddr_hci)" );
61 sockaddr addr_holder; // sizeof(struct sockaddr) > sizeof(sockaddr_hci), silent valgrind.
62 sockaddr_hci * ptr_hci_addr = (sockaddr_hci*)&addr_holder;
63 int fd, err;
64
65#if defined(__linux__)
66 // OK, tested
67#elif defined(__FreeBSD__)
68 // #warning add implementation
69 ABORT("add implementation for FreeBSD");
70#else
71 #warning add implementation
72 ABORT("add implementation");
73#endif
74
75 // Create a loose HCI socket
76 fd = ::socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
77 if (0 > fd ) {
78 ERR_PRINT("HCIComm::hci_open_dev: socket failed");
79 return fd;
80 }
81
82 // Bind socket to the HCI device
83 bzero(&addr_holder, sizeof(addr_holder));
84 ptr_hci_addr->hci_family = AF_BLUETOOTH;
85 ptr_hci_addr->hci_dev = dev_id;
86 ptr_hci_addr->hci_channel = channel;
87 if (::bind(fd, &addr_holder, sizeof(sockaddr_hci)) < 0) {
88 ERR_PRINT("hci_open_dev: bind failed");
89 goto failed;
90 }
91
92 return fd;
93
94failed:
95 err = errno;
96 ::close(fd);
97 errno = err;
98
99 return -1;
100}
101
102int HCIComm::hci_close_dev(int dd) noexcept
103{
104 return ::close(dd);
105}
106
107// *************************************************
108// *************************************************
109// *************************************************
110
111HCIComm::HCIComm(const uint16_t _dev_id, const uint16_t _channel) noexcept
112: dev_id( _dev_id ), channel( _channel ),
113 socket_descriptor( hci_open_dev(_dev_id, _channel) ),
114 interrupted_intern(false), is_interrupted_extern(/* Null Type */), tid_read(0)
115{
116}
117
118void HCIComm::close() noexcept {
119 const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
120 if( 0 > socket_descriptor ) {
121 DBG_PRINT("HCIComm::close: Not opened: dd %d", socket_descriptor.load());
122 return;
123 }
124 DBG_PRINT("HCIComm::close: Start: dd %d", socket_descriptor.load());
125 PERF_TS_T0();
126 // interrupt ::read(..) and , avoiding prolonged hang
127 interrupted_intern = true;
128 {
129 ::pthread_t _tid_read = tid_read;
130 tid_read = 0;
131 if( 0 != _tid_read ) {
132 ::pthread_t tid_self = ::pthread_self();
133 if( tid_self != _tid_read ) {
134 int kerr;
135 if( 0 != ( kerr = ::pthread_kill(_tid_read, SIGALRM) ) ) {
136 ERR_PRINT("HCIComm::close: pthread_kill read %p FAILED: %d", (void*)_tid_read, kerr); // NOLINT(performance-no-int-to-ptr)
137 }
138 }
139 }
140 }
141 hci_close_dev(socket_descriptor);
142 socket_descriptor = -1;
143 interrupted_intern = false;
144 PERF_TS_TD("HCIComm::close");
145 DBG_PRINT("HCIComm::close: End: dd %d", socket_descriptor.load());
146}
147
148jau::snsize_t HCIComm::read(uint8_t* buffer, const jau::nsize_t capacity, const jau::fraction_i64& timeout) noexcept {
149 jau::snsize_t len = 0;
150 if( 0 > socket_descriptor ) {
151 goto errout;
152 }
153 if( 0 == capacity ) {
154 goto done;
155 }
156
157 if( !timeout.is_zero() ) {
158 struct pollfd p;
159 int n = 1;
160 const int32_t timeoutMS = (int32_t) timeout.to_num_of(jau::fractions_i64::milli);
161
162 p.fd = socket_descriptor; p.events = POLLIN;
163 while ( !interrupted() && (n = ::poll(&p, 1, timeoutMS)) < 0 ) {
164 if ( !interrupted() && ( errno == EAGAIN || errno == EINTR ) ) {
165 // cont temp unavail or interruption
166 continue;
167 }
168 goto errout;
169 }
170 if (!n) {
171 errno = ETIMEDOUT;
172 goto errout;
173 }
174 }
175
176 while ((len = ::read(socket_descriptor, buffer, capacity)) < 0) {
177 if (errno == EAGAIN || errno == EINTR ) {
178 // cont temp unavail or interruption
179 continue;
180 }
181 goto errout;
182 }
183
184done:
185 return len;
186
187errout:
188 return -1;
189}
190
191jau::snsize_t HCIComm::write(const uint8_t* buffer, const jau::nsize_t size) noexcept {
192 const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
193 jau::snsize_t len = 0;
194 if( 0 > socket_descriptor ) {
195 goto errout;
196 }
197 if( 0 == size ) {
198 goto done;
199 }
200
201 while( ( len = ::write(socket_descriptor, buffer, size) ) < 0 ) {
202 if( EAGAIN == errno || EINTR == errno ) {
203 continue;
204 }
205 goto errout;
206 }
207
208done:
209 return len;
210
211errout:
212 return -1;
213}
214
215} /* namespace direct_bt */
HCIComm(const uint16_t dev_id, const uint16_t channel) noexcept
Constructing a newly opened HCI communication channel instance.
Definition: HCIComm.cpp:111
void close() noexcept
Closing the HCI channel, locking mutex_write().
Definition: HCIComm.cpp:118
jau::snsize_t read(uint8_t *buffer, const jau::nsize_t capacity, const jau::fraction_i64 &timeout) noexcept
Generic read w/ own timeout, w/o locking suitable for a unique ringbuffer sink.
Definition: HCIComm.cpp:148
jau::snsize_t write(const uint8_t *buffer, const jau::nsize_t size) noexcept
Generic write, locking mutex_write().
Definition: HCIComm.cpp:191
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:109
#define ABORT(...)
Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ file:line fu...
Definition: debug.hpp:101
#define PERF_TS_T0()
Definition: debug.hpp:79
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:52
#define PERF_TS_TD(m)
Definition: debug.hpp:80
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
Definition: int_types.hpp:65
constexpr const jau::fraction_i64 milli(1l, 1 '000lu)
milli is 10^-3
CXX_ALWAYS_INLINE _Tp load() const noexcept