Diffstat (limited to 'noncore/settings/networksettings2/opietooth2/OTPeer.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | noncore/settings/networksettings2/opietooth2/OTPeer.cpp | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/noncore/settings/networksettings2/opietooth2/OTPeer.cpp b/noncore/settings/networksettings2/opietooth2/OTPeer.cpp new file mode 100644 index 0000000..0d7e943 --- a/dev/null +++ b/noncore/settings/networksettings2/opietooth2/OTPeer.cpp | |||
@@ -0,0 +1,366 @@ | |||
1 | //-*-c++-*- | ||
2 | /*************************************************************************** | ||
3 | * Copyright (C) 2003 by Fred Schaettgen * | ||
4 | * kdebluetooth@schaettgen.de * | ||
5 | * * | ||
6 | * This program is free software; you can redistribute it and/or modify * | ||
7 | * it under the terms of the GNU General Public License as published by * | ||
8 | * the Free Software Foundation; either version 2 of the License, or * | ||
9 | * (at your option) any later version. * | ||
10 | ***************************************************************************/ | ||
11 | |||
12 | #include <assert.h> | ||
13 | #include <sys/poll.h> | ||
14 | #include <string.h> | ||
15 | |||
16 | #include <bluezlib.h> | ||
17 | #include <qarray.h> | ||
18 | #include <qtextstream.h> | ||
19 | |||
20 | #include <opie2/odebug.h> | ||
21 | |||
22 | #include <OTDeviceAddress.h> | ||
23 | #include <OTSDPAttribute.h> | ||
24 | #include <OTSDPService.h> | ||
25 | #include <OTPeer.h> | ||
26 | #include <OTGateway.h> | ||
27 | #include <OTDriver.h> | ||
28 | |||
29 | using namespace Opietooth2; | ||
30 | |||
31 | OTPeer::OTPeer( OTGateway * _OT ) { | ||
32 | OT = _OT; | ||
33 | State = Peer_Unknown; | ||
34 | ConnectedTo = 0; | ||
35 | } | ||
36 | |||
37 | OTPeer::OTPeer( QTextStream & TS, OTGateway * _OT ) { | ||
38 | OT = _OT; | ||
39 | State = Peer_Unknown; | ||
40 | ConnectedTo = 0; | ||
41 | |||
42 | load( TS ); | ||
43 | } | ||
44 | |||
45 | OTPeer::~OTPeer( ) { | ||
46 | |||
47 | } | ||
48 | |||
49 | void OTPeer::updateServices( void ) { | ||
50 | sdp_session_t *session; | ||
51 | |||
52 | serviceList.clear(); | ||
53 | |||
54 | owarn << "Get services from " << Addr.toString() << oendl; | ||
55 | |||
56 | session = sdp_connect( &(OTDeviceAddress::any.getBDAddr()), | ||
57 | &(Addr.getBDAddr()), | ||
58 | 0); | ||
59 | |||
60 | if (!session) { | ||
61 | owarn << "sdp_connect(" | ||
62 | << Addr.toString() | ||
63 | << ") failed" | ||
64 | << oendl; | ||
65 | return; // error | ||
66 | } | ||
67 | |||
68 | uint32_t range = 0x0000ffff; | ||
69 | sdp_list_t* attrId = sdp_list_append(0, &range); | ||
70 | |||
71 | // search all public features | ||
72 | uuid_t grp; | ||
73 | sdp_uuid16_create( &grp, PUBLIC_BROWSE_GROUP ); | ||
74 | sdp_list_t * search = sdp_list_append(0, &grp ); | ||
75 | |||
76 | // get data from peer | ||
77 | sdp_list_t* seq; | ||
78 | if (sdp_service_search_attr_req( session, | ||
79 | search, | ||
80 | SDP_ATTR_REQ_RANGE, | ||
81 | attrId, | ||
82 | &seq ) ) { | ||
83 | owarn << "Service Search failed" << oendl; | ||
84 | sdp_close(session); | ||
85 | return; | ||
86 | } | ||
87 | |||
88 | sdp_list_free(attrId, 0); | ||
89 | sdp_list_free(search, 0); | ||
90 | |||
91 | // process result | ||
92 | sdp_list_t* next = NULL; | ||
93 | |||
94 | for (; seq; seq = next) { | ||
95 | sdp_record_t *rec = (sdp_record_t *) seq->data; | ||
96 | |||
97 | sdp_list_t* attrlist = rec->attrlist; | ||
98 | AttributeVector alist; | ||
99 | OTSDPService * service; | ||
100 | |||
101 | service = new OTSDPService(); | ||
102 | |||
103 | for (; attrlist; attrlist = attrlist->next) { | ||
104 | int attrID = ((sdp_data_t*)(attrlist->data))->attrId; | ||
105 | service->addAttribute( | ||
106 | attrID, | ||
107 | new OTSDPAttribute( (sdp_data_t*)(attrlist->data) ) | ||
108 | ); | ||
109 | } | ||
110 | |||
111 | serviceList.resize( serviceList.size() + 1 ); | ||
112 | serviceList.insert( serviceList.size() - 1, service ); | ||
113 | |||
114 | next = seq->next; | ||
115 | free(seq); | ||
116 | sdp_record_free(rec); | ||
117 | } | ||
118 | sdp_close(session); | ||
119 | } | ||
120 | |||
121 | bool OTPeer::hasServiceClassID( const OTUUID & uuid) { | ||
122 | for( unsigned int i = 0; | ||
123 | i < serviceList.count(); | ||
124 | i ++ ) { | ||
125 | if( serviceList[i]->hasClassID(uuid)) | ||
126 | return true; | ||
127 | } | ||
128 | return false; | ||
129 | } | ||
130 | /** Get a vector of Rfcomm channels of the services having "uuid" in the class ID List*/ | ||
131 | QArray<int> OTPeer::rfcommList( const OTUUID & uuid) { | ||
132 | |||
133 | QArray<int> rfcommList; | ||
134 | unsigned int channel; | ||
135 | |||
136 | for( unsigned int i = 0; | ||
137 | i < serviceList.count(); | ||
138 | i ++ ) { | ||
139 | if( serviceList[i]->hasClassID(uuid)) { | ||
140 | if( serviceList[i]->rfcommChannel(channel) ) { | ||
141 | rfcommList.resize( rfcommList.size()+1 ); | ||
142 | rfcommList[rfcommList.size()-1] = channel; | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | return rfcommList; | ||
147 | } | ||
148 | |||
149 | void OTPeer::save( QTextStream & TS ) { | ||
150 | TS << "bdaddr " << address().toString() << endl; | ||
151 | TS << "name " << name() << endl; | ||
152 | TS << "class " << deviceClass() << endl; | ||
153 | } | ||
154 | |||
155 | void OTPeer::load( QTextStream & TS ) { | ||
156 | QString S; | ||
157 | S = TS.readLine(); | ||
158 | setAddress( OTDeviceAddress( S.mid( 7 ) ) ); | ||
159 | |||
160 | S = TS.readLine(); | ||
161 | setName( S.mid( 5 ) ); | ||
162 | |||
163 | S = TS.readLine(); | ||
164 | setDeviceClass( S.mid( 6 ).toLong() ); | ||
165 | } | ||
166 | |||
167 | #define MAGICNR -99999 | ||
168 | #define POLLDELAY 1000 | ||
169 | #define PREMAGICNR (MAGICNR+POLLDELAY) | ||
170 | |||
171 | void OTPeer::findOutState( int timeoutInSec, bool Force ) { | ||
172 | ProbeFD = -1; | ||
173 | if( Force && ConnectedTo == 0 ) { | ||
174 | State = OTPeer::Peer_Unknown; | ||
175 | } // else keep state or is connected to us | ||
176 | |||
177 | if( State == OTPeer::Peer_Unknown ) { | ||
178 | ProbePhase = 0; | ||
179 | ProbeTimeout = timeoutInSec*1000; | ||
180 | owarn << "Ping " << address().toString() << oendl; | ||
181 | startTimer( POLLDELAY ); | ||
182 | } else { | ||
183 | ProbeTimeout = 0; | ||
184 | startTimer( 0 ); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | #define PINGSIZE 20 | ||
189 | void OTPeer::timerEvent( QTimerEvent * ev ) { | ||
190 | |||
191 | ProbeTimeout -= POLLDELAY; | ||
192 | |||
193 | if( State == OTPeer::Peer_Unknown ) { | ||
194 | switch( ProbePhase ) { | ||
195 | case 0 : // connect nonblock | ||
196 | { struct sockaddr_l2 addr; | ||
197 | |||
198 | if (( ProbeFD = ::socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) { | ||
199 | ProbeTimeout = 0; | ||
200 | break; | ||
201 | } | ||
202 | |||
203 | memset(&addr, 0, sizeof(addr)); | ||
204 | |||
205 | addr.l2_family = AF_BLUETOOTH; | ||
206 | addr.l2_bdaddr = OTDeviceAddress::any.getBDAddr(); | ||
207 | |||
208 | if( ::bind( ProbeFD, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | ||
209 | ProbeTimeout = 0; | ||
210 | break; | ||
211 | } | ||
212 | |||
213 | // non blocking | ||
214 | if( ::fcntl( ProbeFD, F_SETFL, O_NONBLOCK ) < 0 ) { | ||
215 | ProbeTimeout = 0; | ||
216 | break; | ||
217 | } | ||
218 | |||
219 | // to this peer | ||
220 | addr.l2_bdaddr = address().getBDAddr(); | ||
221 | if( ::connect( ProbeFD, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | ||
222 | if( errno != EAGAIN && errno != EINPROGRESS ) { | ||
223 | ProbeTimeout = 0; | ||
224 | break; | ||
225 | } // wait for connect to fail or succeed | ||
226 | } | ||
227 | } | ||
228 | ProbePhase = 1; // wait for connect | ||
229 | break; | ||
230 | case 1 : | ||
231 | { struct pollfd pf[1]; | ||
232 | char buf[L2CAP_CMD_HDR_SIZE + PINGSIZE + 20]; | ||
233 | int n; | ||
234 | |||
235 | pf[0].fd = ProbeFD; | ||
236 | pf[0].events = POLLOUT; | ||
237 | if( (n = ::poll(pf, 1, 0)) < 0 ) { | ||
238 | owarn << address().toString() | ||
239 | << " : errno " | ||
240 | << errno | ||
241 | << " " | ||
242 | << strerror(errno)<<oendl; | ||
243 | ProbeTimeout = 0; | ||
244 | break; | ||
245 | } | ||
246 | |||
247 | if( ! n ) { | ||
248 | // not ready -> try again | ||
249 | break; | ||
250 | } | ||
251 | |||
252 | // send ping | ||
253 | for( unsigned int i = L2CAP_CMD_HDR_SIZE; i < sizeof(buf); i++) | ||
254 | buf[i] = (i % 40) + 'A'; | ||
255 | |||
256 | l2cap_cmd_hdr *cmd = (l2cap_cmd_hdr *) buf; | ||
257 | |||
258 | /* Build command header */ | ||
259 | cmd->code = L2CAP_ECHO_REQ; | ||
260 | cmd->ident = *(char *)this; // get some byte | ||
261 | cmd->len = PINGSIZE; | ||
262 | |||
263 | /* Send Echo Request */ | ||
264 | if( ::send(ProbeFD, buf, PINGSIZE + L2CAP_CMD_HDR_SIZE, 0) <= 0) { | ||
265 | if( errno == EACCES ) { | ||
266 | // permission denied means that we could not | ||
267 | // connect because the device does not allow us | ||
268 | // but it is UP | ||
269 | owarn << address().toString() | ||
270 | << " good send error " | ||
271 | << errno | ||
272 | << " " | ||
273 | << strerror( errno) | ||
274 | << oendl; | ||
275 | State = OTPeer::Peer_Up; | ||
276 | ProbeTimeout = 0; | ||
277 | break; | ||
278 | } else if( errno != EBUSY ) { | ||
279 | owarn << address().toString() | ||
280 | << " : errno " | ||
281 | << errno | ||
282 | << " " | ||
283 | << strerror(errno) | ||
284 | << oendl; | ||
285 | ProbeTimeout = 0; | ||
286 | break; | ||
287 | } // else want some more | ||
288 | } | ||
289 | |||
290 | ProbePhase = 2; // wait for ping reply | ||
291 | } | ||
292 | break; | ||
293 | case 2 : // wait for reply | ||
294 | { struct pollfd pf[1]; | ||
295 | char buf[L2CAP_CMD_HDR_SIZE + PINGSIZE + 20]; | ||
296 | l2cap_cmd_hdr *cmd = (l2cap_cmd_hdr *) buf; | ||
297 | int n; | ||
298 | |||
299 | pf[0].fd = ProbeFD; | ||
300 | pf[0].events = POLLIN; | ||
301 | if( (n = ::poll(pf, 1, 0)) < 0 ) { | ||
302 | owarn << address().toString() | ||
303 | << " : errno " | ||
304 | << errno | ||
305 | << " " | ||
306 | << strerror(errno) | ||
307 | <<oendl; | ||
308 | ProbeTimeout = 0; | ||
309 | break; | ||
310 | } | ||
311 | |||
312 | if( ! n ) { | ||
313 | // not ready -> try again | ||
314 | break; | ||
315 | } | ||
316 | |||
317 | if( (n = ::recv( ProbeFD, buf, sizeof(buf), 0)) < 0) { | ||
318 | owarn << address().toString() | ||
319 | << "errno " | ||
320 | << errno | ||
321 | << " " | ||
322 | << strerror(errno) | ||
323 | << oendl; | ||
324 | ProbeTimeout = 0; | ||
325 | break; | ||
326 | } | ||
327 | |||
328 | /* Check for our id */ | ||
329 | if( cmd->ident != *(char *)this ) | ||
330 | // not our reply | ||
331 | break; | ||
332 | |||
333 | owarn << "reply from " | ||
334 | << address().toString() | ||
335 | << oendl; | ||
336 | // whatever reply we get is a valid reply | ||
337 | State = OTPeer::Peer_Up; | ||
338 | ProbeTimeout = 0; | ||
339 | } | ||
340 | break; | ||
341 | } | ||
342 | |||
343 | if( State != OTPeer::Peer_Unknown ) { | ||
344 | ProbeTimeout = 0; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | if( ProbeTimeout <= 0 ) { | ||
349 | // regular timeout | ||
350 | emit peerStateReport( this ); | ||
351 | if( State == Peer_Unknown ) { | ||
352 | State = Peer_Down; | ||
353 | } | ||
354 | if( ProbeFD >= 0 ) { | ||
355 | // requested to stop by caller -> stop probing | ||
356 | ::close( ProbeFD ); | ||
357 | } | ||
358 | // no more waiting | ||
359 | killTimer( ev->timerId() ); | ||
360 | } // else sleep some more | ||
361 | } | ||
362 | |||
363 | void OTPeer::stopFindingOutState( void ) { | ||
364 | ProbeTimeout = PREMAGICNR; | ||
365 | } | ||
366 | |||