Direct-BT  2.3.1
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 extern "C" {
42  #include <inttypes.h>
43  #include <unistd.h>
44  #include <sys/param.h>
45  #include <sys/uio.h>
46  #include <sys/types.h>
47  #include <sys/ioctl.h>
48  #include <sys/socket.h>
49  #include <poll.h>
50 }
51 
52 namespace direct_bt {
53 
54 int HCIComm::hci_open_dev(const uint16_t dev_id, const uint16_t channel) noexcept
55 {
56  static_assert( sizeof(struct sockaddr) > sizeof(sockaddr_hci), "Requirement sizeof(struct sockaddr) > sizeof(sockaddr_hci)" );
57  sockaddr addr_holder; // sizeof(struct sockaddr) > sizeof(sockaddr_hci), silent valgrind.
58  sockaddr_hci * ptr_hci_addr = (sockaddr_hci*)&addr_holder;
59  int fd, err;
60 
61  /**
62  * dev_id is unsigned and hence always >= 0
63  if ( 0 > dev_id ) {
64  errno = ENODEV;
65  ERR_PRINT("hci_open_dev: invalid dev_id");
66  return -1;
67  } */
68 
69  // Create a loose HCI socket
70  fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
71  if (0 > fd ) {
72  ERR_PRINT("HCIComm::hci_open_dev: socket failed");
73  return fd;
74  }
75 
76  // Bind socket to the HCI device
77  bzero(&addr_holder, sizeof(addr_holder));
78  ptr_hci_addr->hci_family = AF_BLUETOOTH;
79  ptr_hci_addr->hci_dev = dev_id;
80  ptr_hci_addr->hci_channel = channel;
81  if (bind(fd, &addr_holder, sizeof(sockaddr_hci)) < 0) {
82  ERR_PRINT("hci_open_dev: bind failed");
83  goto failed;
84  }
85 
86  return fd;
87 
88 failed:
89  err = errno;
90  ::close(fd);
91  errno = err;
92 
93  return -1;
94 }
95 
96 int HCIComm::hci_close_dev(int dd) noexcept
97 {
98  return ::close(dd);
99 }
100 
101 // *************************************************
102 // *************************************************
103 // *************************************************
104 
105 HCIComm::HCIComm(const uint16_t _dev_id, const uint16_t _channel) noexcept
106 : dev_id( _dev_id ), channel( _channel ),
107  socket_descriptor( hci_open_dev(_dev_id, _channel) ), interrupt_flag(false), tid_read(0)
108 {
109 }
110 
111 void HCIComm::close() noexcept {
112  const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
113  if( 0 > socket_descriptor ) {
114  DBG_PRINT("HCIComm::close: Not opened: dd %d", socket_descriptor.load());
115  return;
116  }
117  DBG_PRINT("HCIComm::close: Start: dd %d", socket_descriptor.load());
118  PERF_TS_T0();
119  // interrupt ::read(..) and , avoiding prolonged hang
120  interrupt_flag = true;
121  {
122  pthread_t _tid_read = tid_read;
123  tid_read = 0;
124  if( 0 != _tid_read ) {
125  pthread_t tid_self = pthread_self();
126  if( tid_self != _tid_read ) {
127  int kerr;
128  if( 0 != ( kerr = pthread_kill(_tid_read, SIGALRM) ) ) {
129  ERR_PRINT("HCIComm::close: pthread_kill read %p FAILED: %d", (void*)_tid_read, kerr);
130  }
131  }
132  }
133  }
134  hci_close_dev(socket_descriptor);
135  socket_descriptor = -1;
136  interrupt_flag = false;
137  PERF_TS_TD("HCIComm::close");
138  DBG_PRINT("HCIComm::close: End: dd %d", socket_descriptor.load());
139 }
140 
141 jau::snsize_t HCIComm::read(uint8_t* buffer, const jau::nsize_t capacity, const int32_t timeoutMS) noexcept {
142  jau::snsize_t len = 0;
143  if( 0 > socket_descriptor ) {
144  goto errout;
145  }
146  if( 0 == capacity ) {
147  goto done;
148  }
149 
150  if( timeoutMS ) {
151  struct pollfd p;
152  int n;
153 
154  p.fd = socket_descriptor; p.events = POLLIN;
155  while ( !interrupt_flag && (n = poll(&p, 1, timeoutMS)) < 0 ) {
156  if ( !interrupt_flag && ( errno == EAGAIN || errno == EINTR ) ) {
157  // cont temp unavail or interruption
158  continue;
159  }
160  goto errout;
161  }
162  if (!n) {
163  errno = ETIMEDOUT;
164  goto errout;
165  }
166  }
167 
168  while ((len = ::read(socket_descriptor, buffer, capacity)) < 0) {
169  if (errno == EAGAIN || errno == EINTR ) {
170  // cont temp unavail or interruption
171  continue;
172  }
173  goto errout;
174  }
175 
176 done:
177  return len;
178 
179 errout:
180  return -1;
181 }
182 
183 jau::snsize_t HCIComm::write(const uint8_t* buffer, const jau::nsize_t size) noexcept {
184  const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
185  jau::snsize_t len = 0;
186  if( 0 > socket_descriptor ) {
187  goto errout;
188  }
189  if( 0 == size ) {
190  goto done;
191  }
192 
193  while( ( len = ::write(socket_descriptor, buffer, size) ) < 0 ) {
194  if( EAGAIN == errno || EINTR == errno ) {
195  continue;
196  }
197  goto errout;
198  }
199 
200 done:
201  return len;
202 
203 errout:
204  return -1;
205 }
206 
207 } /* namespace direct_bt */
direct_bt::HCIComm::HCIComm
HCIComm(const uint16_t dev_id, const uint16_t channel) noexcept
Constructing a newly opened HCI communication channel instance.
Definition: HCIComm.cpp:105
direct_bt
Definition: ATTPDUTypes.hpp:171
debug.hpp
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
ERR_PRINT
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE: '.
Definition: debug.hpp:132
direct_bt::HCIComm::close
void close() noexcept
Closing the HCI channel, locking mutex_write().
Definition: HCIComm.cpp:111
direct_bt::HCIComm::write
jau::snsize_t write(const uint8_t *buffer, const jau::nsize_t size) noexcept
Generic write, locking mutex_write().
Definition: HCIComm.cpp:183
HCIComm.hpp
PERF_TS_TD
#define PERF_TS_TD(m)
Definition: debug.hpp:103
DBG_PRINT
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:78
PERF_TS_T0
#define PERF_TS_T0()
Definition: debug.hpp:102
direct_bt::HCIComm::read
jau::snsize_t read(uint8_t *buffer, const jau::nsize_t capacity, const int32_t timeoutMS) noexcept
Generic read w/ own timeoutMS, w/o locking suitable for a unique ringbuffer sink.
Definition: HCIComm.cpp:141