blob: 91ad36e8ca0db1eead11a4b930f0a5e578321606 [file] [log] [blame]
jacquelinecchuang617fd262024-04-19 03:11:14 +08001/*
2 * BRLTTY - A background process providing access to the console screen (when in
3 * text mode) for a blind person using a refreshable braille display.
4 *
5 * Copyright (C) 1995-2023 by The BRLTTY Developers.
6 *
7 * BRLTTY comes with ABSOLUTELY NO WARRANTY.
8 *
9 * This is free software, placed under the terms of the
10 * GNU Lesser General Public License, as published by the Free Software
11 * Foundation; either version 2.1 of the License, or (at your option) any
12 * later version. Please see the file LICENSE-LGPL for details.
13 *
14 * Web Page: http://brltty.app/
15 *
16 * This software is maintained by Dave Mielke <dave@mielke.cc>.
17 */
18
19#include "prologue.h"
20
21#include <string.h>
22#include <errno.h>
23
24#import <IOBluetooth/objc/IOBluetoothDevice.h>
25#import <IOBluetooth/objc/IOBluetoothSDPUUID.h>
26#import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
27#import <IOBluetooth/objc/IOBluetoothRFCOMMChannel.h>
28
29#include "log.h"
30#include "io_misc.h"
31#include "io_bluetooth.h"
32#include "bluetooth_internal.h"
33#include "system_darwin.h"
34
35@interface ServiceQueryResult: AsynchronousResult
36- (void) sdpQueryComplete
37 : (IOBluetoothDevice *) device
38 status: (IOReturn) status;
39@end
40
41@interface BluetoothConnectionDelegate: AsynchronousTask
42@property (assign) BluetoothConnectionExtension *bluetoothConnectionExtension;
43@end
44
45@interface RfcommChannelDelegate: BluetoothConnectionDelegate
46- (void) rfcommChannelData
47 : (IOBluetoothRFCOMMChannel *) rfcommChannel
48 data: (void *) dataPointer
49 length: (size_t) dataLength;
50
51- (void) rfcommChannelClosed
52 : (IOBluetoothRFCOMMChannel*) rfcommChannel;
53
54- (IOReturn) run;
55@end
56
57struct BluetoothConnectionExtensionStruct {
58 BluetoothDeviceAddress bluetoothAddress;
59 IOBluetoothDevice *bluetoothDevice;
60
61 IOBluetoothRFCOMMChannel *rfcommChannel;
62 RfcommChannelDelegate *rfcommDelegate;
63
64 int inputPipe[2];
65};
66
67static void
68bthSetError (IOReturn result, const char *action) {
69 setDarwinSystemError(result);
70 logSystemError(action);
71}
72
73static void
74bthInitializeRfcommChannel (BluetoothConnectionExtension *bcx) {
75 bcx->rfcommChannel = nil;
76}
77
78static void
79bthDestroyRfcommChannel (BluetoothConnectionExtension *bcx) {
80 if (bcx->rfcommChannel) {
81 [bcx->rfcommChannel closeChannel];
82 [bcx->rfcommChannel release];
83 bthInitializeRfcommChannel(bcx);
84 }
85}
86
87static void
88bthInitializeRfcommDelegate (BluetoothConnectionExtension *bcx) {
89 bcx->rfcommDelegate = nil;
90}
91
92static void
93bthDestroyRfcommDelegate (BluetoothConnectionExtension *bcx) {
94 if (bcx->rfcommDelegate) {
95 [bcx->rfcommDelegate stop];
96 [bcx->rfcommDelegate wait:5];
97 [bcx->rfcommDelegate release];
98 bthInitializeRfcommDelegate(bcx);
99 }
100}
101
102static void
103bthInitializeBluetoothDevice (BluetoothConnectionExtension *bcx) {
104 bcx->bluetoothDevice = nil;
105}
106
107static void
108bthDestroyBluetoothDevice (BluetoothConnectionExtension *bcx) {
109 if (bcx->bluetoothDevice) {
110 [bcx->bluetoothDevice closeConnection];
111 [bcx->bluetoothDevice release];
112 bthInitializeBluetoothDevice(bcx);
113 }
114}
115
116static void
117bthInitializeInputPipe (BluetoothConnectionExtension *bcx) {
118 bcx->inputPipe[0] = bcx->inputPipe[1] = INVALID_FILE_DESCRIPTOR;
119}
120
121static void
122bthDestroyInputPipe (BluetoothConnectionExtension *bcx) {
123 int *fileDescriptor = bcx->inputPipe;
124 const int *end = fileDescriptor + ARRAY_COUNT(bcx->inputPipe);
125
126 while (fileDescriptor < end) {
127 closeFile(fileDescriptor);
128 fileDescriptor += 1;
129 }
130}
131
132static void
133bthMakeAddress (BluetoothDeviceAddress *address, uint64_t bda) {
134 unsigned int index = sizeof(address->data);
135
136 while (index > 0) {
137 address->data[--index] = bda & 0XFF;
138 bda >>= 8;
139 }
140}
141
142BluetoothConnectionExtension *
143bthNewConnectionExtension (uint64_t bda) {
144 BluetoothConnectionExtension *bcx;
145
146 if ((bcx = malloc(sizeof(*bcx)))) {
147 memset(bcx, 0, sizeof(*bcx));
148 bthInitializeInputPipe(bcx);
149 bthMakeAddress(&bcx->bluetoothAddress, bda);
150
151 if ((bcx->bluetoothDevice = [IOBluetoothDevice deviceWithAddress:&bcx->bluetoothAddress])) {
152 [bcx->bluetoothDevice retain];
153
154 return bcx;
155 }
156
157 free(bcx);
158 } else {
159 logMallocError();
160 }
161
162 return NULL;
163}
164
165void
166bthReleaseConnectionExtension (BluetoothConnectionExtension *bcx) {
167 bthDestroyRfcommChannel(bcx);
168 bthDestroyRfcommDelegate(bcx);
169 bthDestroyBluetoothDevice(bcx);
170 bthDestroyInputPipe(bcx);
171 free(bcx);
172}
173
174int
175bthOpenChannel (BluetoothConnectionExtension *bcx, uint8_t channel, int timeout) {
176 IOReturn result;
177
178 if (pipe(bcx->inputPipe) != -1) {
179 if (setBlockingIo(bcx->inputPipe[0], 0)) {
180 if ((bcx->rfcommDelegate = [RfcommChannelDelegate new])) {
181 bcx->rfcommDelegate.bluetoothConnectionExtension = bcx;
182
183 if ((result = [bcx->bluetoothDevice openRFCOMMChannelSync:&bcx->rfcommChannel withChannelID:channel delegate:nil]) == kIOReturnSuccess) {
184 if ([bcx->rfcommDelegate start]) {
185 return 1;
186 }
187
188 bthDestroyRfcommChannel(bcx);
189 } else {
190 bthSetError(result, "RFCOMM channel open");
191 }
192
193 bthDestroyRfcommDelegate(bcx);
194 }
195 }
196
197 bthDestroyInputPipe(bcx);
198 } else {
199 logSystemError("pipe");
200 }
201
202 return 0;
203}
204
205static int
206bthPerformServiceQuery (BluetoothConnectionExtension *bcx) {
207 int ok = 0;
208 IOReturn result;
209 ServiceQueryResult *target = [ServiceQueryResult new];
210
211 if (target) {
212 if ((result = [bcx->bluetoothDevice performSDPQuery:target]) == kIOReturnSuccess) {
213 if ([target wait:10]) {
214 if ((result = target.finalStatus) == kIOReturnSuccess) {
215 ok = 1;
216 } else {
217 bthSetError(result, "service discovery response");
218 }
219 }
220 } else {
221 bthSetError(result, "service discovery request");
222 }
223
224 [target release];
225 }
226
227 return ok;
228}
229
230int
231bthDiscoverChannel (
232 uint8_t *channel, BluetoothConnectionExtension *bcx,
233 const void *uuidBytes, size_t uuidLength,
234 int timeout
235) {
236 IOReturn result;
237
238 if (bthPerformServiceQuery(bcx)) {
239 IOBluetoothSDPUUID *uuid = [IOBluetoothSDPUUID uuidWithBytes:uuidBytes length:uuidLength];
240
241 if (uuid) {
242 IOBluetoothSDPServiceRecord *record = [bcx->bluetoothDevice getServiceRecordForUUID:uuid];
243
244 if (record) {
245 if ((result = [record getRFCOMMChannelID:channel]) == kIOReturnSuccess) {
246 return 1;
247 } else {
248 bthSetError(result, "RFCOMM channel lookup");
249 }
250 }
251 }
252 }
253
254 return 0;
255}
256
257int
258bthMonitorInput (BluetoothConnection *connection, AsyncMonitorCallback *callback, void *data) {
259 return 0;
260}
261
262int
263bthPollInput (BluetoothConnectionExtension *bcx, int timeout) {
264 return awaitFileInput(bcx->inputPipe[0], timeout);
265}
266
267ssize_t
268bthGetData (
269 BluetoothConnectionExtension *bcx, void *buffer, size_t size,
270 int initialTimeout, int subsequentTimeout
271) {
272 return readFile(bcx->inputPipe[0], buffer, size, initialTimeout, subsequentTimeout);
273}
274
275ssize_t
276bthPutData (BluetoothConnectionExtension *bcx, const void *buffer, size_t size) {
277 IOReturn result = [bcx->rfcommChannel writeSync:(void *)buffer length:size];
278
279 if (result == kIOReturnSuccess) return size;
280 bthSetError(result, "RFCOMM channel write");
281 return -1;
282}
283
284char *
285bthObtainDeviceName (uint64_t bda, int timeout) {
286 IOReturn result;
287 BluetoothDeviceAddress address;
288
289 bthMakeAddress(&address, bda);
290
291 {
292 IOBluetoothDevice *device = [IOBluetoothDevice deviceWithAddress:&address];
293
294 if (device != nil) {
295 if ((result = [device remoteNameRequest:nil]) == kIOReturnSuccess) {
296 NSString *nsName = device.name;
297
298 if (nsName != nil) {
299 const char *utf8Name = [nsName UTF8String];
300
301 if (utf8Name != NULL) {
302 char *name = strdup(utf8Name);
303
304 if (name != NULL) {
305 return name;
306 }
307 }
308 }
309 } else {
310 bthSetError(result, "device name query");
311 }
312
313 [device closeConnection];
314 }
315 }
316
317 return NULL;
318}
319
320@implementation ServiceQueryResult
321- (void) sdpQueryComplete
322 : (IOBluetoothDevice *) device
323 status: (IOReturn) status
324 {
325 [self setStatus:status];
326 }
327@end
328
329@implementation BluetoothConnectionDelegate
330@synthesize bluetoothConnectionExtension;
331@end
332
333@implementation RfcommChannelDelegate
334- (void) rfcommChannelData
335 : (IOBluetoothRFCOMMChannel *) rfcommChannel
336 data: (void *) dataPointer
337 length: (size_t) dataLength
338 {
339 writeFile(self.bluetoothConnectionExtension->inputPipe[1], dataPointer, dataLength);
340 }
341
342- (void) rfcommChannelClosed
343 : (IOBluetoothRFCOMMChannel*) rfcommChannel
344 {
345 logMessage(LOG_NOTICE, "RFCOMM channel closed");
346 }
347
348- (IOReturn) run
349 {
350 IOReturn result;
351 logMessage(LOG_CATEGORY(BLUETOOTH_IO), "RFCOMM channel delegate started");
352
353 {
354 BluetoothConnectionExtension *bcx = self.bluetoothConnectionExtension;
355
356 if ((result = [bcx->rfcommChannel setDelegate:self]) == kIOReturnSuccess) {
357 CFRunLoopRun();
358 result = kIOReturnSuccess;
359 } else {
360 bthSetError(result, "RFCOMM channel delegate set");
361 }
362 }
363
364 logMessage(LOG_CATEGORY(BLUETOOTH_IO), "RFCOMM channel delegate finished");
365 return result;
366 }
367@end
368
369void
370bthProcessDiscoveredDevices (
371 DiscoveredBluetoothDeviceTester *testDevice, void *data
372) {
373}