|
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* |
|
4 * Copyright 2009, The Android Open Source Project |
|
5 * |
|
6 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
7 * you may not use this file except in compliance with the License. |
|
8 * You may obtain a copy of the License at |
|
9 * |
|
10 * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 * |
|
12 * Unless required by applicable law or agreed to in writing, software |
|
13 * distributed under the License is distributed on an "AS IS" BASIS, |
|
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 * See the License for the specific language governing permissions and |
|
16 * limitations under the License. |
|
17 * |
|
18 * NOTE: Due to being based on the dbus compatibility layer for |
|
19 * android's bluetooth implementation, this file is licensed under the |
|
20 * apache license instead of MPL. |
|
21 * |
|
22 */ |
|
23 |
|
24 #include <fcntl.h> |
|
25 #include <unistd.h> |
|
26 #include <stdlib.h> |
|
27 #include <errno.h> |
|
28 |
|
29 #include <sys/socket.h> |
|
30 #ifdef MOZ_B2G_BT_BLUEZ |
|
31 #include <bluetooth/bluetooth.h> |
|
32 #include <bluetooth/l2cap.h> |
|
33 #include <bluetooth/rfcomm.h> |
|
34 #include <bluetooth/sco.h> |
|
35 #endif |
|
36 #include "BluetoothUnixSocketConnector.h" |
|
37 #include "nsThreadUtils.h" |
|
38 |
|
39 using namespace mozilla::ipc; |
|
40 USING_BLUETOOTH_NAMESPACE |
|
41 |
|
42 static const int RFCOMM_SO_SNDBUF = 70 * 1024; // 70 KB send buffer |
|
43 static const int L2CAP_SO_SNDBUF = 400 * 1024; // 400 KB send buffer |
|
44 static const int L2CAP_SO_RCVBUF = 400 * 1024; // 400 KB receive buffer |
|
45 static const int L2CAP_MAX_MTU = 65000; |
|
46 |
|
47 #ifdef MOZ_B2G_BT_BLUEZ |
|
48 static |
|
49 int get_bdaddr(const char *str, bdaddr_t *ba) |
|
50 { |
|
51 char *d = ((char*)ba) + 5, *endp; |
|
52 for (int i = 0; i < 6; i++) { |
|
53 *d-- = strtol(str, &endp, 16); |
|
54 MOZ_ASSERT(!(*endp != ':' && i != 5)); |
|
55 str = endp + 1; |
|
56 } |
|
57 return 0; |
|
58 } |
|
59 |
|
60 static |
|
61 void get_bdaddr_as_string(const bdaddr_t *ba, char *str) { |
|
62 const uint8_t *b = (const uint8_t *)ba; |
|
63 sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", |
|
64 b[5], b[4], b[3], b[2], b[1], b[0]); |
|
65 } |
|
66 |
|
67 #endif |
|
68 |
|
69 BluetoothUnixSocketConnector::BluetoothUnixSocketConnector( |
|
70 BluetoothSocketType aType, |
|
71 int aChannel, |
|
72 bool aAuth, |
|
73 bool aEncrypt) : mType(aType) |
|
74 , mChannel(aChannel) |
|
75 , mAuth(aAuth) |
|
76 , mEncrypt(aEncrypt) |
|
77 { |
|
78 } |
|
79 |
|
80 bool |
|
81 BluetoothUnixSocketConnector::SetUp(int aFd) |
|
82 { |
|
83 #ifdef MOZ_B2G_BT_BLUEZ |
|
84 int lm = 0; |
|
85 int sndbuf, rcvbuf; |
|
86 |
|
87 /* kernel does not yet support LM for SCO */ |
|
88 switch (mType) { |
|
89 case BluetoothSocketType::RFCOMM: |
|
90 lm |= mAuth ? RFCOMM_LM_AUTH : 0; |
|
91 lm |= mEncrypt ? RFCOMM_LM_ENCRYPT : 0; |
|
92 break; |
|
93 case BluetoothSocketType::L2CAP: |
|
94 case BluetoothSocketType::EL2CAP: |
|
95 lm |= mAuth ? L2CAP_LM_AUTH : 0; |
|
96 lm |= mEncrypt ? L2CAP_LM_ENCRYPT : 0; |
|
97 break; |
|
98 case BluetoothSocketType::SCO: |
|
99 break; |
|
100 default: |
|
101 MOZ_CRASH("Unknown socket type!"); |
|
102 } |
|
103 |
|
104 if (lm) { |
|
105 if (mType == BluetoothSocketType::RFCOMM) { |
|
106 if (setsockopt(aFd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) { |
|
107 BT_WARNING("setsockopt(RFCOMM_LM) failed, throwing"); |
|
108 return false; |
|
109 } |
|
110 } else if (mType == BluetoothSocketType::L2CAP || |
|
111 mType == BluetoothSocketType::EL2CAP) { |
|
112 if (setsockopt(aFd, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm))) { |
|
113 BT_WARNING("setsockopt(L2CAP_LM) failed, throwing"); |
|
114 return false; |
|
115 } |
|
116 } |
|
117 } |
|
118 |
|
119 if (mType == BluetoothSocketType::RFCOMM) { |
|
120 sndbuf = RFCOMM_SO_SNDBUF; |
|
121 if (setsockopt(aFd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) { |
|
122 BT_WARNING("setsockopt(SO_SNDBUF) failed, throwing"); |
|
123 return false; |
|
124 } |
|
125 } |
|
126 |
|
127 /* Setting L2CAP socket options */ |
|
128 if (mType == BluetoothSocketType::L2CAP || |
|
129 mType == BluetoothSocketType::EL2CAP) { |
|
130 struct l2cap_options opts; |
|
131 socklen_t optlen = sizeof(opts); |
|
132 int err; |
|
133 err = getsockopt(aFd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen); |
|
134 if (!err) { |
|
135 /* setting MTU for [E]L2CAP */ |
|
136 opts.omtu = opts.imtu = L2CAP_MAX_MTU; |
|
137 |
|
138 /* Enable ERTM for [E]L2CAP */ |
|
139 if (mType == BluetoothSocketType::EL2CAP) { |
|
140 opts.flush_to = 0xffff; /* infinite */ |
|
141 opts.mode = L2CAP_MODE_ERTM; |
|
142 opts.fcs = 1; |
|
143 opts.txwin_size = 64; |
|
144 opts.max_tx = 10; |
|
145 } |
|
146 |
|
147 err = setsockopt(aFd, SOL_L2CAP, L2CAP_OPTIONS, &opts, optlen); |
|
148 } |
|
149 |
|
150 /* Set larger SNDBUF & RCVBUF for EL2CAP connections */ |
|
151 if (mType == BluetoothSocketType::EL2CAP) { |
|
152 sndbuf = L2CAP_SO_SNDBUF; |
|
153 if (setsockopt(aFd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) { |
|
154 BT_WARNING("setsockopt(SO_SNDBUF) failed, throwing"); |
|
155 return false; |
|
156 } |
|
157 |
|
158 rcvbuf = L2CAP_SO_RCVBUF; |
|
159 if (setsockopt(aFd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf))) { |
|
160 BT_WARNING("setsockopt(SO_RCVBUF) failed, throwing"); |
|
161 return false; |
|
162 } |
|
163 } |
|
164 } |
|
165 #endif |
|
166 return true; |
|
167 } |
|
168 |
|
169 bool |
|
170 BluetoothUnixSocketConnector::SetUpListenSocket(int aFd) |
|
171 { |
|
172 // Nothing to do here. |
|
173 return true; |
|
174 } |
|
175 |
|
176 int |
|
177 BluetoothUnixSocketConnector::Create() |
|
178 { |
|
179 MOZ_ASSERT(!NS_IsMainThread()); |
|
180 int fd = -1; |
|
181 |
|
182 #ifdef MOZ_B2G_BT_BLUEZ |
|
183 switch (mType) { |
|
184 case BluetoothSocketType::RFCOMM: |
|
185 fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); |
|
186 break; |
|
187 case BluetoothSocketType::SCO: |
|
188 fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); |
|
189 break; |
|
190 case BluetoothSocketType::L2CAP: |
|
191 fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); |
|
192 break; |
|
193 case BluetoothSocketType::EL2CAP: |
|
194 fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_L2CAP); |
|
195 break; |
|
196 default: |
|
197 MOZ_CRASH(); |
|
198 } |
|
199 |
|
200 if (fd < 0) { |
|
201 BT_WARNING("Could not open bluetooth socket!"); |
|
202 return -1; |
|
203 } |
|
204 |
|
205 if (!SetUp(fd)) { |
|
206 BT_WARNING("Could not set up socket!"); |
|
207 return -1; |
|
208 } |
|
209 #endif |
|
210 return fd; |
|
211 } |
|
212 |
|
213 bool |
|
214 BluetoothUnixSocketConnector::CreateAddr(bool aIsServer, |
|
215 socklen_t& aAddrSize, |
|
216 sockaddr_any& aAddr, |
|
217 const char* aAddress) |
|
218 { |
|
219 #ifdef MOZ_B2G_BT_BLUEZ |
|
220 // Set to BDADDR_ANY, if it's not a server, we'll reset. |
|
221 bdaddr_t bd_address_obj = {{0, 0, 0, 0, 0, 0}}; |
|
222 |
|
223 if (!aIsServer && aAddress && strlen(aAddress) > 0) { |
|
224 if (get_bdaddr(aAddress, &bd_address_obj)) { |
|
225 BT_WARNING("Can't get bluetooth address!"); |
|
226 return false; |
|
227 } |
|
228 } |
|
229 |
|
230 // Initialize |
|
231 memset(&aAddr, 0, sizeof(aAddr)); |
|
232 |
|
233 switch (mType) { |
|
234 case BluetoothSocketType::RFCOMM: |
|
235 struct sockaddr_rc addr_rc; |
|
236 aAddrSize = sizeof(addr_rc); |
|
237 aAddr.rc.rc_family = AF_BLUETOOTH; |
|
238 aAddr.rc.rc_channel = mChannel; |
|
239 memcpy(&aAddr.rc.rc_bdaddr, &bd_address_obj, sizeof(bd_address_obj)); |
|
240 break; |
|
241 case BluetoothSocketType::L2CAP: |
|
242 case BluetoothSocketType::EL2CAP: |
|
243 struct sockaddr_l2 addr_l2; |
|
244 aAddrSize = sizeof(addr_l2); |
|
245 aAddr.l2.l2_family = AF_BLUETOOTH; |
|
246 aAddr.l2.l2_psm = mChannel; |
|
247 memcpy(&aAddr.l2.l2_bdaddr, &bd_address_obj, sizeof(bdaddr_t)); |
|
248 break; |
|
249 case BluetoothSocketType::SCO: |
|
250 struct sockaddr_sco addr_sco; |
|
251 aAddrSize = sizeof(addr_sco); |
|
252 aAddr.sco.sco_family = AF_BLUETOOTH; |
|
253 memcpy(&aAddr.sco.sco_bdaddr, &bd_address_obj, sizeof(bd_address_obj)); |
|
254 break; |
|
255 default: |
|
256 BT_WARNING("Socket type unknown!"); |
|
257 return false; |
|
258 } |
|
259 #endif |
|
260 return true; |
|
261 } |
|
262 |
|
263 void |
|
264 BluetoothUnixSocketConnector::GetSocketAddr(const sockaddr_any& aAddr, |
|
265 nsAString& aAddrStr) |
|
266 { |
|
267 #ifdef MOZ_B2G_BT_BLUEZ |
|
268 char addr[18]; |
|
269 switch (mType) { |
|
270 case BluetoothSocketType::RFCOMM: |
|
271 get_bdaddr_as_string((bdaddr_t*)(&aAddr.rc.rc_bdaddr), addr); |
|
272 break; |
|
273 case BluetoothSocketType::SCO: |
|
274 get_bdaddr_as_string((bdaddr_t*)(&aAddr.sco.sco_bdaddr), addr); |
|
275 break; |
|
276 case BluetoothSocketType::L2CAP: |
|
277 case BluetoothSocketType::EL2CAP: |
|
278 get_bdaddr_as_string((bdaddr_t*)(&aAddr.l2.l2_bdaddr), addr); |
|
279 break; |
|
280 default: |
|
281 MOZ_CRASH("Socket should be either RFCOMM or SCO!"); |
|
282 } |
|
283 aAddrStr.AssignASCII(addr); |
|
284 #endif |
|
285 } |