1 | # -*- coding: utf-8 -*-
|
---|
2 | # $Id: usbgadget.py 96407 2022-08-22 17:43:14Z vboxsync $
|
---|
3 | # pylint: disable=too-many-lines
|
---|
4 |
|
---|
5 | """
|
---|
6 | UTS (USB Test Service) client.
|
---|
7 | """
|
---|
8 | __copyright__ = \
|
---|
9 | """
|
---|
10 | Copyright (C) 2010-2022 Oracle and/or its affiliates.
|
---|
11 |
|
---|
12 | This file is part of VirtualBox base platform packages, as
|
---|
13 | available from https://www.virtualbox.org.
|
---|
14 |
|
---|
15 | This program is free software; you can redistribute it and/or
|
---|
16 | modify it under the terms of the GNU General Public License
|
---|
17 | as published by the Free Software Foundation, in version 3 of the
|
---|
18 | License.
|
---|
19 |
|
---|
20 | This program is distributed in the hope that it will be useful, but
|
---|
21 | WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
23 | General Public License for more details.
|
---|
24 |
|
---|
25 | You should have received a copy of the GNU General Public License
|
---|
26 | along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
27 |
|
---|
28 | The contents of this file may alternatively be used under the terms
|
---|
29 | of the Common Development and Distribution License Version 1.0
|
---|
30 | (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
|
---|
31 | in the VirtualBox distribution, in which case the provisions of the
|
---|
32 | CDDL are applicable instead of those of the GPL.
|
---|
33 |
|
---|
34 | You may elect to license modified versions of this file under the
|
---|
35 | terms and conditions of either the GPL or the CDDL or both.
|
---|
36 |
|
---|
37 | SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
|
---|
38 | """
|
---|
39 | __version__ = "$Revision: 96407 $"
|
---|
40 |
|
---|
41 | # Standard Python imports.
|
---|
42 | import array
|
---|
43 | import errno
|
---|
44 | import select
|
---|
45 | import socket
|
---|
46 | import sys;
|
---|
47 | import threading
|
---|
48 | import time
|
---|
49 | import zlib
|
---|
50 |
|
---|
51 | # Validation Kit imports.
|
---|
52 | from common import utils;
|
---|
53 | from testdriver import base;
|
---|
54 | from testdriver import reporter;
|
---|
55 | from testdriver.base import TdTaskBase;
|
---|
56 |
|
---|
57 | # Python 3 hacks:
|
---|
58 | if sys.version_info[0] >= 3:
|
---|
59 | long = int; # pylint: disable=redefined-builtin,invalid-name
|
---|
60 |
|
---|
61 |
|
---|
62 | ## @name USB gadget impersonation string constants.
|
---|
63 | ## @{
|
---|
64 | g_ksGadgetImpersonationInvalid = 'Invalid';
|
---|
65 | g_ksGadgetImpersonationTest = 'Test';
|
---|
66 | g_ksGadgetImpersonationMsd = 'Msd';
|
---|
67 | g_ksGadgetImpersonationWebcam = 'Webcam';
|
---|
68 | g_ksGadgetImpersonationEther = 'Ether';
|
---|
69 | ## @}
|
---|
70 |
|
---|
71 | ## @name USB gadget type used in the UTS protocol.
|
---|
72 | ## @{
|
---|
73 | g_kiGadgetTypeTest = 1;
|
---|
74 | ## @}
|
---|
75 |
|
---|
76 | ## @name USB gadget access methods used in the UTS protocol.
|
---|
77 | ## @{
|
---|
78 | g_kiGadgetAccessUsbIp = 1;
|
---|
79 | ## @}
|
---|
80 |
|
---|
81 | ## @name USB gadget config types.
|
---|
82 | ## @{
|
---|
83 | g_kiGadgetCfgTypeBool = 1;
|
---|
84 | g_kiGadgetCfgTypeString = 2;
|
---|
85 | g_kiGadgetCfgTypeUInt8 = 3;
|
---|
86 | g_kiGadgetCfgTypeUInt16 = 4;
|
---|
87 | g_kiGadgetCfgTypeUInt32 = 5;
|
---|
88 | g_kiGadgetCfgTypeUInt64 = 6;
|
---|
89 | g_kiGadgetCfgTypeInt8 = 7;
|
---|
90 | g_kiGadgetCfgTypeInt16 = 8;
|
---|
91 | g_kiGadgetCfgTypeInt32 = 9;
|
---|
92 | g_kiGadgetCfgTypeInt64 = 10;
|
---|
93 | ## @}
|
---|
94 |
|
---|
95 | #
|
---|
96 | # Helpers for decoding data received from the UTS.
|
---|
97 | # These are used both the Session and Transport classes.
|
---|
98 | #
|
---|
99 |
|
---|
100 | def getU64(abData, off):
|
---|
101 | """Get a U64 field."""
|
---|
102 | return abData[off] \
|
---|
103 | + abData[off + 1] * 256 \
|
---|
104 | + abData[off + 2] * 65536 \
|
---|
105 | + abData[off + 3] * 16777216 \
|
---|
106 | + abData[off + 4] * 4294967296 \
|
---|
107 | + abData[off + 5] * 1099511627776 \
|
---|
108 | + abData[off + 6] * 281474976710656 \
|
---|
109 | + abData[off + 7] * 72057594037927936;
|
---|
110 |
|
---|
111 | def getU32(abData, off):
|
---|
112 | """Get a U32 field."""
|
---|
113 | return abData[off] \
|
---|
114 | + abData[off + 1] * 256 \
|
---|
115 | + abData[off + 2] * 65536 \
|
---|
116 | + abData[off + 3] * 16777216;
|
---|
117 |
|
---|
118 | def getU16(abData, off):
|
---|
119 | """Get a U16 field."""
|
---|
120 | return abData[off] \
|
---|
121 | + abData[off + 1] * 256;
|
---|
122 |
|
---|
123 | def getU8(abData, off):
|
---|
124 | """Get a U8 field."""
|
---|
125 | return abData[off];
|
---|
126 |
|
---|
127 | def getSZ(abData, off, sDefault = None):
|
---|
128 | """
|
---|
129 | Get a zero-terminated string field.
|
---|
130 | Returns sDefault if the string is invalid.
|
---|
131 | """
|
---|
132 | cchStr = getSZLen(abData, off);
|
---|
133 | if cchStr >= 0:
|
---|
134 | abStr = abData[off:(off + cchStr)];
|
---|
135 | try:
|
---|
136 | return abStr.tostring().decode('utf_8');
|
---|
137 | except:
|
---|
138 | reporter.errorXcpt('getSZ(,%u)' % (off));
|
---|
139 | return sDefault;
|
---|
140 |
|
---|
141 | def getSZLen(abData, off):
|
---|
142 | """
|
---|
143 | Get the length of a zero-terminated string field, in bytes.
|
---|
144 | Returns -1 if off is beyond the data packet or not properly terminated.
|
---|
145 | """
|
---|
146 | cbData = len(abData);
|
---|
147 | if off >= cbData:
|
---|
148 | return -1;
|
---|
149 |
|
---|
150 | offCur = off;
|
---|
151 | while abData[offCur] != 0:
|
---|
152 | offCur = offCur + 1;
|
---|
153 | if offCur >= cbData:
|
---|
154 | return -1;
|
---|
155 |
|
---|
156 | return offCur - off;
|
---|
157 |
|
---|
158 | def isValidOpcodeEncoding(sOpcode):
|
---|
159 | """
|
---|
160 | Checks if the specified opcode is valid or not.
|
---|
161 | Returns True on success.
|
---|
162 | Returns False if it is invalid, details in the log.
|
---|
163 | """
|
---|
164 | sSet1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
---|
165 | sSet2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ ";
|
---|
166 | if len(sOpcode) != 8:
|
---|
167 | reporter.error("invalid opcode length: %s" % (len(sOpcode)));
|
---|
168 | return False;
|
---|
169 | for i in range(0, 1):
|
---|
170 | if sSet1.find(sOpcode[i]) < 0:
|
---|
171 | reporter.error("invalid opcode char #%u: %s" % (i, sOpcode));
|
---|
172 | return False;
|
---|
173 | for i in range(2, 7):
|
---|
174 | if sSet2.find(sOpcode[i]) < 0:
|
---|
175 | reporter.error("invalid opcode char #%u: %s" % (i, sOpcode));
|
---|
176 | return False;
|
---|
177 | return True;
|
---|
178 |
|
---|
179 | #
|
---|
180 | # Helper for encoding data sent to the UTS.
|
---|
181 | #
|
---|
182 |
|
---|
183 | def u32ToByteArray(u32):
|
---|
184 | """Encodes the u32 value as a little endian byte (B) array."""
|
---|
185 | return array.array('B', \
|
---|
186 | ( u32 % 256, \
|
---|
187 | (u32 // 256) % 256, \
|
---|
188 | (u32 // 65536) % 256, \
|
---|
189 | (u32 // 16777216) % 256) );
|
---|
190 |
|
---|
191 | def u16ToByteArray(u16):
|
---|
192 | """Encodes the u16 value as a little endian byte (B) array."""
|
---|
193 | return array.array('B', \
|
---|
194 | ( u16 % 256, \
|
---|
195 | (u16 // 256) % 256) );
|
---|
196 |
|
---|
197 | def u8ToByteArray(uint8):
|
---|
198 | """Encodes the u8 value as a little endian byte (B) array."""
|
---|
199 | return array.array('B', (uint8 % 256));
|
---|
200 |
|
---|
201 | def zeroByteArray(cb):
|
---|
202 | """Returns an array with the given size containing 0."""
|
---|
203 | abArray = array.array('B', (0, ));
|
---|
204 | cb = cb - 1;
|
---|
205 | for i in range(cb): # pylint: disable=unused-variable
|
---|
206 | abArray.append(0);
|
---|
207 | return abArray;
|
---|
208 |
|
---|
209 | def strToByteArry(sStr):
|
---|
210 | """Encodes the string as a little endian byte (B) array including the terminator."""
|
---|
211 | abArray = array.array('B');
|
---|
212 | sUtf8 = sStr.encode('utf_8');
|
---|
213 | for ch in sUtf8:
|
---|
214 | abArray.append(ord(ch));
|
---|
215 | abArray.append(0);
|
---|
216 | return abArray;
|
---|
217 |
|
---|
218 | def cfgListToByteArray(lst):
|
---|
219 | """Encodes the given config list as a little endian byte (B) array."""
|
---|
220 | abArray = array.array('B');
|
---|
221 | if lst is not None:
|
---|
222 | for t3Item in lst:
|
---|
223 | # Encode they key size
|
---|
224 | abArray.extend(u32ToByteArray(len(t3Item[0]) + 1)); # Include terminator
|
---|
225 | abArray.extend(u32ToByteArray(t3Item[1])) # Config type
|
---|
226 | abArray.extend(u32ToByteArray(len(t3Item[2]) + 1)); # Value size including temrinator.
|
---|
227 | abArray.extend(u32ToByteArray(0)); # Reserved field.
|
---|
228 |
|
---|
229 | abArray.extend(strToByteArry(t3Item[0]));
|
---|
230 | abArray.extend(strToByteArry(t3Item[2]));
|
---|
231 |
|
---|
232 | return abArray;
|
---|
233 |
|
---|
234 | class TransportBase(object):
|
---|
235 | """
|
---|
236 | Base class for the transport layer.
|
---|
237 | """
|
---|
238 |
|
---|
239 | def __init__(self, sCaller):
|
---|
240 | self.sDbgCreated = '%s: %s' % (utils.getTimePrefix(), sCaller);
|
---|
241 | self.fDummy = 0;
|
---|
242 | self.abReadAheadHdr = array.array('B');
|
---|
243 |
|
---|
244 | def toString(self):
|
---|
245 | """
|
---|
246 | Stringify the instance for logging and debugging.
|
---|
247 | """
|
---|
248 | return '<%s: abReadAheadHdr=%s, sDbgCreated=%s>' % (type(self).__name__, self.abReadAheadHdr, self.sDbgCreated);
|
---|
249 |
|
---|
250 | def __str__(self):
|
---|
251 | return self.toString();
|
---|
252 |
|
---|
253 | def cancelConnect(self):
|
---|
254 | """
|
---|
255 | Cancels any pending connect() call.
|
---|
256 | Returns None;
|
---|
257 | """
|
---|
258 | return None;
|
---|
259 |
|
---|
260 | def connect(self, cMsTimeout):
|
---|
261 | """
|
---|
262 | Quietly attempts to connect to the UTS.
|
---|
263 |
|
---|
264 | Returns True on success.
|
---|
265 | Returns False on retryable errors (no logging).
|
---|
266 | Returns None on fatal errors with details in the log.
|
---|
267 |
|
---|
268 | Override this method, don't call super.
|
---|
269 | """
|
---|
270 | _ = cMsTimeout;
|
---|
271 | return False;
|
---|
272 |
|
---|
273 | def disconnect(self, fQuiet = False):
|
---|
274 | """
|
---|
275 | Disconnect from the UTS.
|
---|
276 |
|
---|
277 | Returns True.
|
---|
278 |
|
---|
279 | Override this method, don't call super.
|
---|
280 | """
|
---|
281 | _ = fQuiet;
|
---|
282 | return True;
|
---|
283 |
|
---|
284 | def sendBytes(self, abBuf, cMsTimeout):
|
---|
285 | """
|
---|
286 | Sends the bytes in the buffer abBuf to the UTS.
|
---|
287 |
|
---|
288 | Returns True on success.
|
---|
289 | Returns False on failure and error details in the log.
|
---|
290 |
|
---|
291 | Override this method, don't call super.
|
---|
292 |
|
---|
293 | Remarks: len(abBuf) is always a multiple of 16.
|
---|
294 | """
|
---|
295 | _ = abBuf; _ = cMsTimeout;
|
---|
296 | return False;
|
---|
297 |
|
---|
298 | def recvBytes(self, cb, cMsTimeout, fNoDataOk):
|
---|
299 | """
|
---|
300 | Receive cb number of bytes from the UTS.
|
---|
301 |
|
---|
302 | Returns the bytes (array('B')) on success.
|
---|
303 | Returns None on failure and error details in the log.
|
---|
304 |
|
---|
305 | Override this method, don't call super.
|
---|
306 |
|
---|
307 | Remarks: cb is always a multiple of 16.
|
---|
308 | """
|
---|
309 | _ = cb; _ = cMsTimeout; _ = fNoDataOk;
|
---|
310 | return None;
|
---|
311 |
|
---|
312 | def isConnectionOk(self):
|
---|
313 | """
|
---|
314 | Checks if the connection is OK.
|
---|
315 |
|
---|
316 | Returns True if it is.
|
---|
317 | Returns False if it isn't (caller should call diconnect).
|
---|
318 |
|
---|
319 | Override this method, don't call super.
|
---|
320 | """
|
---|
321 | return True;
|
---|
322 |
|
---|
323 | def isRecvPending(self, cMsTimeout = 0):
|
---|
324 | """
|
---|
325 | Checks if there is incoming bytes, optionally waiting cMsTimeout
|
---|
326 | milliseconds for something to arrive.
|
---|
327 |
|
---|
328 | Returns True if there is, False if there isn't.
|
---|
329 |
|
---|
330 | Override this method, don't call super.
|
---|
331 | """
|
---|
332 | _ = cMsTimeout;
|
---|
333 | return False;
|
---|
334 |
|
---|
335 | def sendMsgInt(self, sOpcode, cMsTimeout, abPayload = array.array('B')):
|
---|
336 | """
|
---|
337 | Sends a message (opcode + encoded payload).
|
---|
338 |
|
---|
339 | Returns True on success.
|
---|
340 | Returns False on failure and error details in the log.
|
---|
341 | """
|
---|
342 | # Fix + check the opcode.
|
---|
343 | if len(sOpcode) < 2:
|
---|
344 | reporter.fatal('sendMsgInt: invalid opcode length: %d (\"%s\")' % (len(sOpcode), sOpcode));
|
---|
345 | return False;
|
---|
346 | sOpcode = sOpcode.ljust(8);
|
---|
347 | if not isValidOpcodeEncoding(sOpcode):
|
---|
348 | reporter.fatal('sendMsgInt: invalid opcode encoding: \"%s\"' % (sOpcode));
|
---|
349 | return False;
|
---|
350 |
|
---|
351 | # Start construct the message.
|
---|
352 | cbMsg = 16 + len(abPayload);
|
---|
353 | abMsg = array.array('B');
|
---|
354 | abMsg.extend(u32ToByteArray(cbMsg));
|
---|
355 | abMsg.extend((0, 0, 0, 0)); # uCrc32
|
---|
356 | try:
|
---|
357 | abMsg.extend(array.array('B', \
|
---|
358 | ( ord(sOpcode[0]), \
|
---|
359 | ord(sOpcode[1]), \
|
---|
360 | ord(sOpcode[2]), \
|
---|
361 | ord(sOpcode[3]), \
|
---|
362 | ord(sOpcode[4]), \
|
---|
363 | ord(sOpcode[5]), \
|
---|
364 | ord(sOpcode[6]), \
|
---|
365 | ord(sOpcode[7]) ) ) );
|
---|
366 | if abPayload:
|
---|
367 | abMsg.extend(abPayload);
|
---|
368 | except:
|
---|
369 | reporter.fatalXcpt('sendMsgInt: packing problem...');
|
---|
370 | return False;
|
---|
371 |
|
---|
372 | # checksum it, padd it and send it off.
|
---|
373 | uCrc32 = zlib.crc32(abMsg[8:]);
|
---|
374 | abMsg[4:8] = u32ToByteArray(uCrc32);
|
---|
375 |
|
---|
376 | while len(abMsg) % 16:
|
---|
377 | abMsg.append(0);
|
---|
378 |
|
---|
379 | reporter.log2('sendMsgInt: op=%s len=%d to=%d' % (sOpcode, len(abMsg), cMsTimeout));
|
---|
380 | return self.sendBytes(abMsg, cMsTimeout);
|
---|
381 |
|
---|
382 | def recvMsg(self, cMsTimeout, fNoDataOk = False):
|
---|
383 | """
|
---|
384 | Receives a message from the UTS.
|
---|
385 |
|
---|
386 | Returns the message three-tuple: length, opcode, payload.
|
---|
387 | Returns (None, None, None) on failure and error details in the log.
|
---|
388 | """
|
---|
389 |
|
---|
390 | # Read the header.
|
---|
391 | if self.abReadAheadHdr:
|
---|
392 | assert(len(self.abReadAheadHdr) == 16);
|
---|
393 | abHdr = self.abReadAheadHdr;
|
---|
394 | self.abReadAheadHdr = array.array('B');
|
---|
395 | else:
|
---|
396 | abHdr = self.recvBytes(16, cMsTimeout, fNoDataOk); # (virtual method) # pylint: disable=assignment-from-none
|
---|
397 | if abHdr is None:
|
---|
398 | return (None, None, None);
|
---|
399 | if len(abHdr) != 16:
|
---|
400 | reporter.fatal('recvBytes(16) returns %d bytes!' % (len(abHdr)));
|
---|
401 | return (None, None, None);
|
---|
402 |
|
---|
403 | # Unpack and validate the header.
|
---|
404 | cbMsg = getU32(abHdr, 0);
|
---|
405 | uCrc32 = getU32(abHdr, 4);
|
---|
406 | sOpcode = abHdr[8:16].tostring().decode('ascii');
|
---|
407 |
|
---|
408 | if cbMsg < 16:
|
---|
409 | reporter.fatal('recvMsg: message length is out of range: %s (min 16 bytes)' % (cbMsg));
|
---|
410 | return (None, None, None);
|
---|
411 | if cbMsg > 1024*1024:
|
---|
412 | reporter.fatal('recvMsg: message length is out of range: %s (max 1MB)' % (cbMsg));
|
---|
413 | return (None, None, None);
|
---|
414 | if not isValidOpcodeEncoding(sOpcode):
|
---|
415 | reporter.fatal('recvMsg: invalid opcode \"%s\"' % (sOpcode));
|
---|
416 | return (None, None, None);
|
---|
417 |
|
---|
418 | # Get the payload (if any), dropping the padding.
|
---|
419 | abPayload = array.array('B');
|
---|
420 | if cbMsg > 16:
|
---|
421 | if cbMsg % 16:
|
---|
422 | cbPadding = 16 - (cbMsg % 16);
|
---|
423 | else:
|
---|
424 | cbPadding = 0;
|
---|
425 | abPayload = self.recvBytes(cbMsg - 16 + cbPadding, cMsTimeout, False); # pylint: disable=assignment-from-none
|
---|
426 | if abPayload is None:
|
---|
427 | self.abReadAheadHdr = abHdr;
|
---|
428 | if not fNoDataOk :
|
---|
429 | reporter.log('recvMsg: failed to recv payload bytes!');
|
---|
430 | return (None, None, None);
|
---|
431 |
|
---|
432 | while cbPadding > 0:
|
---|
433 | abPayload.pop();
|
---|
434 | cbPadding = cbPadding - 1;
|
---|
435 |
|
---|
436 | # Check the CRC-32.
|
---|
437 | if uCrc32 != 0:
|
---|
438 | uActualCrc32 = zlib.crc32(abHdr[8:]);
|
---|
439 | if cbMsg > 16:
|
---|
440 | uActualCrc32 = zlib.crc32(abPayload, uActualCrc32);
|
---|
441 | uActualCrc32 = uActualCrc32 & 0xffffffff;
|
---|
442 | if uCrc32 != uActualCrc32:
|
---|
443 | reporter.fatal('recvMsg: crc error: expected %s, got %s' % (hex(uCrc32), hex(uActualCrc32)));
|
---|
444 | return (None, None, None);
|
---|
445 |
|
---|
446 | reporter.log2('recvMsg: op=%s len=%d' % (sOpcode, len(abPayload)));
|
---|
447 | return (cbMsg, sOpcode, abPayload);
|
---|
448 |
|
---|
449 | def sendMsg(self, sOpcode, cMsTimeout, aoPayload = ()):
|
---|
450 | """
|
---|
451 | Sends a message (opcode + payload tuple).
|
---|
452 |
|
---|
453 | Returns True on success.
|
---|
454 | Returns False on failure and error details in the log.
|
---|
455 | Returns None if you pass the incorrectly typed parameters.
|
---|
456 | """
|
---|
457 | # Encode the payload.
|
---|
458 | abPayload = array.array('B');
|
---|
459 | for o in aoPayload:
|
---|
460 | try:
|
---|
461 | if utils.isString(o):
|
---|
462 | # the primitive approach...
|
---|
463 | sUtf8 = o.encode('utf_8');
|
---|
464 | for ch in sUtf8:
|
---|
465 | abPayload.append(ord(ch))
|
---|
466 | abPayload.append(0);
|
---|
467 | elif isinstance(o, long):
|
---|
468 | if o < 0 or o > 0xffffffff:
|
---|
469 | reporter.fatal('sendMsg: uint32_t payload is out of range: %s' % (hex(o)));
|
---|
470 | return None;
|
---|
471 | abPayload.extend(u32ToByteArray(o));
|
---|
472 | elif isinstance(o, int):
|
---|
473 | if o < 0 or o > 0xffffffff:
|
---|
474 | reporter.fatal('sendMsg: uint32_t payload is out of range: %s' % (hex(o)));
|
---|
475 | return None;
|
---|
476 | abPayload.extend(u32ToByteArray(o));
|
---|
477 | elif isinstance(o, array.array):
|
---|
478 | abPayload.extend(o);
|
---|
479 | else:
|
---|
480 | reporter.fatal('sendMsg: unexpected payload type: %s (%s) (aoPayload=%s)' % (type(o), o, aoPayload));
|
---|
481 | return None;
|
---|
482 | except:
|
---|
483 | reporter.fatalXcpt('sendMsg: screwed up the encoding code...');
|
---|
484 | return None;
|
---|
485 | return self.sendMsgInt(sOpcode, cMsTimeout, abPayload);
|
---|
486 |
|
---|
487 |
|
---|
488 | class Session(TdTaskBase):
|
---|
489 | """
|
---|
490 | A USB Test Service (UTS) client session.
|
---|
491 | """
|
---|
492 |
|
---|
493 | def __init__(self, oTransport, cMsTimeout, cMsIdleFudge, fTryConnect = False):
|
---|
494 | """
|
---|
495 | Construct a UTS session.
|
---|
496 |
|
---|
497 | This starts by connecting to the UTS and will enter the signalled state
|
---|
498 | when connected or the timeout has been reached.
|
---|
499 | """
|
---|
500 | TdTaskBase.__init__(self, utils.getCallerName());
|
---|
501 | self.oTransport = oTransport;
|
---|
502 | self.sStatus = "";
|
---|
503 | self.cMsTimeout = 0;
|
---|
504 | self.fErr = True; # Whether to report errors as error.
|
---|
505 | self.msStart = 0;
|
---|
506 | self.oThread = None;
|
---|
507 | self.fnTask = self.taskDummy;
|
---|
508 | self.aTaskArgs = None;
|
---|
509 | self.oTaskRc = None;
|
---|
510 | self.t3oReply = (None, None, None);
|
---|
511 | self.fScrewedUpMsgState = False;
|
---|
512 | self.fTryConnect = fTryConnect;
|
---|
513 |
|
---|
514 | if not self.startTask(cMsTimeout, False, "connecting", self.taskConnect, (cMsIdleFudge,)):
|
---|
515 | raise base.GenError("startTask failed");
|
---|
516 |
|
---|
517 | def __del__(self):
|
---|
518 | """Make sure to cancel the task when deleted."""
|
---|
519 | self.cancelTask();
|
---|
520 |
|
---|
521 | def toString(self):
|
---|
522 | return '<%s fnTask=%s, aTaskArgs=%s, sStatus=%s, oTaskRc=%s, cMsTimeout=%s,' \
|
---|
523 | ' msStart=%s, fTryConnect=%s, fErr=%s, fScrewedUpMsgState=%s, t3oReply=%s oTransport=%s, oThread=%s>' \
|
---|
524 | % (TdTaskBase.toString(self), self.fnTask, self.aTaskArgs, self.sStatus, self.oTaskRc, self.cMsTimeout,
|
---|
525 | self.msStart, self.fTryConnect, self.fErr, self.fScrewedUpMsgState, self.t3oReply, self.oTransport, self.oThread);
|
---|
526 |
|
---|
527 | def taskDummy(self):
|
---|
528 | """Place holder to catch broken state handling."""
|
---|
529 | raise Exception();
|
---|
530 |
|
---|
531 | def startTask(self, cMsTimeout, fIgnoreErrors, sStatus, fnTask, aArgs = ()):
|
---|
532 | """
|
---|
533 | Kicks of a new task.
|
---|
534 |
|
---|
535 | cMsTimeout: The task timeout in milliseconds. Values less than
|
---|
536 | 500 ms will be adjusted to 500 ms. This means it is
|
---|
537 | OK to use negative value.
|
---|
538 | sStatus: The task status.
|
---|
539 | fnTask: The method that'll execute the task.
|
---|
540 | aArgs: Arguments to pass to fnTask.
|
---|
541 |
|
---|
542 | Returns True on success, False + error in log on failure.
|
---|
543 | """
|
---|
544 | if not self.cancelTask():
|
---|
545 | reporter.maybeErr(not fIgnoreErrors, 'utsclient.Session.startTask: failed to cancel previous task.');
|
---|
546 | return False;
|
---|
547 |
|
---|
548 | # Change status and make sure we're the
|
---|
549 | self.lockTask();
|
---|
550 | if self.sStatus != "":
|
---|
551 | self.unlockTask();
|
---|
552 | reporter.maybeErr(not fIgnoreErrors, 'utsclient.Session.startTask: race.');
|
---|
553 | return False;
|
---|
554 | self.sStatus = "setup";
|
---|
555 | self.oTaskRc = None;
|
---|
556 | self.t3oReply = (None, None, None);
|
---|
557 | self.resetTaskLocked();
|
---|
558 | self.unlockTask();
|
---|
559 |
|
---|
560 | self.cMsTimeout = max(cMsTimeout, 500);
|
---|
561 | self.fErr = not fIgnoreErrors;
|
---|
562 | self.fnTask = fnTask;
|
---|
563 | self.aTaskArgs = aArgs;
|
---|
564 | self.oThread = threading.Thread(target=self.taskThread, args=(), name=('UTS-%s' % (sStatus)));
|
---|
565 | self.oThread.setDaemon(True);
|
---|
566 | self.msStart = base.timestampMilli();
|
---|
567 |
|
---|
568 | self.lockTask();
|
---|
569 | self.sStatus = sStatus;
|
---|
570 | self.unlockTask();
|
---|
571 | self.oThread.start();
|
---|
572 |
|
---|
573 | return True;
|
---|
574 |
|
---|
575 | def cancelTask(self, fSync = True):
|
---|
576 | """
|
---|
577 | Attempts to cancel any pending tasks.
|
---|
578 | Returns success indicator (True/False).
|
---|
579 | """
|
---|
580 | self.lockTask();
|
---|
581 |
|
---|
582 | if self.sStatus == "":
|
---|
583 | self.unlockTask();
|
---|
584 | return True;
|
---|
585 | if self.sStatus == "setup":
|
---|
586 | self.unlockTask();
|
---|
587 | return False;
|
---|
588 | if self.sStatus == "cancelled":
|
---|
589 | self.unlockTask();
|
---|
590 | return False;
|
---|
591 |
|
---|
592 | reporter.log('utsclient: cancelling "%s"...' % (self.sStatus));
|
---|
593 | if self.sStatus == 'connecting':
|
---|
594 | self.oTransport.cancelConnect();
|
---|
595 |
|
---|
596 | self.sStatus = "cancelled";
|
---|
597 | oThread = self.oThread;
|
---|
598 | self.unlockTask();
|
---|
599 |
|
---|
600 | if not fSync:
|
---|
601 | return False;
|
---|
602 |
|
---|
603 | oThread.join(61.0);
|
---|
604 |
|
---|
605 | if sys.version_info < (3, 9, 0):
|
---|
606 | # Removed since Python 3.9.
|
---|
607 | return oThread.isAlive(); # pylint: disable=no-member
|
---|
608 | return oThread.is_alive();
|
---|
609 |
|
---|
610 | def taskThread(self):
|
---|
611 | """
|
---|
612 | The task thread function.
|
---|
613 | This does some housekeeping activities around the real task method call.
|
---|
614 | """
|
---|
615 | if not self.isCancelled():
|
---|
616 | try:
|
---|
617 | fnTask = self.fnTask;
|
---|
618 | oTaskRc = fnTask(*self.aTaskArgs);
|
---|
619 | except:
|
---|
620 | reporter.fatalXcpt('taskThread', 15);
|
---|
621 | oTaskRc = None;
|
---|
622 | else:
|
---|
623 | reporter.log('taskThread: cancelled already');
|
---|
624 |
|
---|
625 | self.lockTask();
|
---|
626 |
|
---|
627 | reporter.log('taskThread: signalling task with status "%s", oTaskRc=%s' % (self.sStatus, oTaskRc));
|
---|
628 | self.oTaskRc = oTaskRc;
|
---|
629 | self.oThread = None;
|
---|
630 | self.sStatus = '';
|
---|
631 | self.signalTaskLocked();
|
---|
632 |
|
---|
633 | self.unlockTask();
|
---|
634 | return None;
|
---|
635 |
|
---|
636 | def isCancelled(self):
|
---|
637 | """Internal method for checking if the task has been cancelled."""
|
---|
638 | self.lockTask();
|
---|
639 | sStatus = self.sStatus;
|
---|
640 | self.unlockTask();
|
---|
641 | if sStatus == "cancelled":
|
---|
642 | return True;
|
---|
643 | return False;
|
---|
644 |
|
---|
645 | def hasTimedOut(self):
|
---|
646 | """Internal method for checking if the task has timed out or not."""
|
---|
647 | cMsLeft = self.getMsLeft();
|
---|
648 | if cMsLeft <= 0:
|
---|
649 | return True;
|
---|
650 | return False;
|
---|
651 |
|
---|
652 | def getMsLeft(self, cMsMin = 0, cMsMax = -1):
|
---|
653 | """Gets the time left until the timeout."""
|
---|
654 | cMsElapsed = base.timestampMilli() - self.msStart;
|
---|
655 | if cMsElapsed < 0:
|
---|
656 | return cMsMin;
|
---|
657 | cMsLeft = self.cMsTimeout - cMsElapsed;
|
---|
658 | if cMsLeft <= cMsMin:
|
---|
659 | return cMsMin;
|
---|
660 | if cMsLeft > cMsMax > 0:
|
---|
661 | return cMsMax
|
---|
662 | return cMsLeft;
|
---|
663 |
|
---|
664 | def recvReply(self, cMsTimeout = None, fNoDataOk = False):
|
---|
665 | """
|
---|
666 | Wrapper for TransportBase.recvMsg that stashes the response away
|
---|
667 | so the client can inspect it later on.
|
---|
668 | """
|
---|
669 | if cMsTimeout is None:
|
---|
670 | cMsTimeout = self.getMsLeft(500);
|
---|
671 | cbMsg, sOpcode, abPayload = self.oTransport.recvMsg(cMsTimeout, fNoDataOk);
|
---|
672 | self.lockTask();
|
---|
673 | self.t3oReply = (cbMsg, sOpcode, abPayload);
|
---|
674 | self.unlockTask();
|
---|
675 | return (cbMsg, sOpcode, abPayload);
|
---|
676 |
|
---|
677 | def recvAck(self, fNoDataOk = False):
|
---|
678 | """
|
---|
679 | Receives an ACK or error response from the UTS.
|
---|
680 |
|
---|
681 | Returns True on success.
|
---|
682 | Returns False on timeout or transport error.
|
---|
683 | Returns (sOpcode, sDetails) tuple on failure. The opcode is stripped
|
---|
684 | and there are always details of some sort or another.
|
---|
685 | """
|
---|
686 | cbMsg, sOpcode, abPayload = self.recvReply(None, fNoDataOk);
|
---|
687 | if cbMsg is None:
|
---|
688 | return False;
|
---|
689 | sOpcode = sOpcode.strip()
|
---|
690 | if sOpcode == "ACK":
|
---|
691 | return True;
|
---|
692 | return (sOpcode, getSZ(abPayload, 16, sOpcode));
|
---|
693 |
|
---|
694 | def recvAckLogged(self, sCommand, fNoDataOk = False):
|
---|
695 | """
|
---|
696 | Wrapper for recvAck and logging.
|
---|
697 | Returns True on success (ACK).
|
---|
698 | Returns False on time, transport error and errors signalled by UTS.
|
---|
699 | """
|
---|
700 | rc = self.recvAck(fNoDataOk);
|
---|
701 | if rc is not True and not fNoDataOk:
|
---|
702 | if rc is False:
|
---|
703 | reporter.maybeErr(self.fErr, 'recvAckLogged: %s transport error' % (sCommand));
|
---|
704 | else:
|
---|
705 | reporter.maybeErr(self.fErr, 'recvAckLogged: %s response was %s: %s' % (sCommand, rc[0], rc[1]));
|
---|
706 | rc = False;
|
---|
707 | return rc;
|
---|
708 |
|
---|
709 | def recvTrueFalse(self, sCommand):
|
---|
710 | """
|
---|
711 | Receives a TRUE/FALSE response from the UTS.
|
---|
712 | Returns True on TRUE, False on FALSE and None on error/other (logged).
|
---|
713 | """
|
---|
714 | cbMsg, sOpcode, abPayload = self.recvReply();
|
---|
715 | if cbMsg is None:
|
---|
716 | reporter.maybeErr(self.fErr, 'recvAckLogged: %s transport error' % (sCommand));
|
---|
717 | return None;
|
---|
718 |
|
---|
719 | sOpcode = sOpcode.strip()
|
---|
720 | if sOpcode == "TRUE":
|
---|
721 | return True;
|
---|
722 | if sOpcode == "FALSE":
|
---|
723 | return False;
|
---|
724 | reporter.maybeErr(self.fErr, 'recvAckLogged: %s response was %s: %s' % \
|
---|
725 | (sCommand, sOpcode, getSZ(abPayload, 16, sOpcode)));
|
---|
726 | return None;
|
---|
727 |
|
---|
728 | def sendMsg(self, sOpcode, aoPayload = (), cMsTimeout = None):
|
---|
729 | """
|
---|
730 | Wrapper for TransportBase.sendMsg that inserts the correct timeout.
|
---|
731 | """
|
---|
732 | if cMsTimeout is None:
|
---|
733 | cMsTimeout = self.getMsLeft(500);
|
---|
734 | return self.oTransport.sendMsg(sOpcode, cMsTimeout, aoPayload);
|
---|
735 |
|
---|
736 | def asyncToSync(self, fnAsync, *aArgs):
|
---|
737 | """
|
---|
738 | Wraps an asynchronous task into a synchronous operation.
|
---|
739 |
|
---|
740 | Returns False on failure, task return status on success.
|
---|
741 | """
|
---|
742 | rc = fnAsync(*aArgs);
|
---|
743 | if rc is False:
|
---|
744 | reporter.log2('asyncToSync(%s): returns False (#1)' % (fnAsync));
|
---|
745 | return rc;
|
---|
746 |
|
---|
747 | rc = self.waitForTask(self.cMsTimeout + 5000);
|
---|
748 | if rc is False:
|
---|
749 | reporter.maybeErrXcpt(self.fErr, 'asyncToSync: waitForTask failed...');
|
---|
750 | self.cancelTask();
|
---|
751 | #reporter.log2('asyncToSync(%s): returns False (#2)' % (fnAsync, rc));
|
---|
752 | return False;
|
---|
753 |
|
---|
754 | rc = self.getResult();
|
---|
755 | #reporter.log2('asyncToSync(%s): returns %s' % (fnAsync, rc));
|
---|
756 | return rc;
|
---|
757 |
|
---|
758 | #
|
---|
759 | # Connection tasks.
|
---|
760 | #
|
---|
761 |
|
---|
762 | def taskConnect(self, cMsIdleFudge):
|
---|
763 | """Tries to connect to the UTS"""
|
---|
764 | while not self.isCancelled():
|
---|
765 | reporter.log2('taskConnect: connecting ...');
|
---|
766 | rc = self.oTransport.connect(self.getMsLeft(500));
|
---|
767 | if rc is True:
|
---|
768 | reporter.log('taskConnect: succeeded');
|
---|
769 | return self.taskGreet(cMsIdleFudge);
|
---|
770 | if rc is None:
|
---|
771 | reporter.log2('taskConnect: unable to connect');
|
---|
772 | return None;
|
---|
773 | if self.hasTimedOut():
|
---|
774 | reporter.log2('taskConnect: timed out');
|
---|
775 | if not self.fTryConnect:
|
---|
776 | reporter.maybeErr(self.fErr, 'taskConnect: timed out');
|
---|
777 | return False;
|
---|
778 | time.sleep(self.getMsLeft(1, 1000) / 1000.0);
|
---|
779 | if not self.fTryConnect:
|
---|
780 | reporter.maybeErr(self.fErr, 'taskConnect: cancelled');
|
---|
781 | return False;
|
---|
782 |
|
---|
783 | def taskGreet(self, cMsIdleFudge):
|
---|
784 | """Greets the UTS"""
|
---|
785 | sHostname = socket.gethostname().lower();
|
---|
786 | cbFill = 68 - len(sHostname) - 1;
|
---|
787 | rc = self.sendMsg("HOWDY", ((1 << 16) | 0, 0x1, len(sHostname), sHostname, zeroByteArray(cbFill)));
|
---|
788 | if rc is True:
|
---|
789 | rc = self.recvAckLogged("HOWDY", self.fTryConnect);
|
---|
790 | if rc is True:
|
---|
791 | while cMsIdleFudge > 0:
|
---|
792 | cMsIdleFudge -= 1000;
|
---|
793 | time.sleep(1);
|
---|
794 | else:
|
---|
795 | self.oTransport.disconnect(self.fTryConnect);
|
---|
796 | return rc;
|
---|
797 |
|
---|
798 | def taskBye(self):
|
---|
799 | """Says goodbye to the UTS"""
|
---|
800 | rc = self.sendMsg("BYE");
|
---|
801 | if rc is True:
|
---|
802 | rc = self.recvAckLogged("BYE");
|
---|
803 | self.oTransport.disconnect();
|
---|
804 | return rc;
|
---|
805 |
|
---|
806 | #
|
---|
807 | # Gadget tasks.
|
---|
808 | #
|
---|
809 |
|
---|
810 | def taskGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None):
|
---|
811 | """Creates a new gadget on UTS"""
|
---|
812 | cCfgItems = 0;
|
---|
813 | if lstCfg is not None:
|
---|
814 | cCfgItems = len(lstCfg);
|
---|
815 | fRc = self.sendMsg("GDGTCRT", (iGadgetType, iGadgetAccess, cCfgItems, 0, cfgListToByteArray(lstCfg)));
|
---|
816 | if fRc is True:
|
---|
817 | fRc = self.recvAckLogged("GDGTCRT");
|
---|
818 | return fRc;
|
---|
819 |
|
---|
820 | def taskGadgetDestroy(self, iGadgetId):
|
---|
821 | """Destroys the given gadget handle on UTS"""
|
---|
822 | fRc = self.sendMsg("GDGTDTOR", (iGadgetId, zeroByteArray(12)));
|
---|
823 | if fRc is True:
|
---|
824 | fRc = self.recvAckLogged("GDGTDTOR");
|
---|
825 | return fRc;
|
---|
826 |
|
---|
827 | def taskGadgetConnect(self, iGadgetId):
|
---|
828 | """Connects the given gadget handle on UTS"""
|
---|
829 | fRc = self.sendMsg("GDGTCNCT", (iGadgetId, zeroByteArray(12)));
|
---|
830 | if fRc is True:
|
---|
831 | fRc = self.recvAckLogged("GDGTCNCT");
|
---|
832 | return fRc;
|
---|
833 |
|
---|
834 | def taskGadgetDisconnect(self, iGadgetId):
|
---|
835 | """Disconnects the given gadget handle from UTS"""
|
---|
836 | fRc = self.sendMsg("GDGTDCNT", (iGadgetId, zeroByteArray(12)));
|
---|
837 | if fRc is True:
|
---|
838 | fRc = self.recvAckLogged("GDGTDCNT");
|
---|
839 | return fRc;
|
---|
840 |
|
---|
841 | #
|
---|
842 | # Public methods - generic task queries
|
---|
843 | #
|
---|
844 |
|
---|
845 | def isSuccess(self):
|
---|
846 | """Returns True if the task completed successfully, otherwise False."""
|
---|
847 | self.lockTask();
|
---|
848 | sStatus = self.sStatus;
|
---|
849 | oTaskRc = self.oTaskRc;
|
---|
850 | self.unlockTask();
|
---|
851 | if sStatus != "":
|
---|
852 | return False;
|
---|
853 | if oTaskRc is False or oTaskRc is None:
|
---|
854 | return False;
|
---|
855 | return True;
|
---|
856 |
|
---|
857 | def getResult(self):
|
---|
858 | """
|
---|
859 | Returns the result of a completed task.
|
---|
860 | Returns None if not completed yet or no previous task.
|
---|
861 | """
|
---|
862 | self.lockTask();
|
---|
863 | sStatus = self.sStatus;
|
---|
864 | oTaskRc = self.oTaskRc;
|
---|
865 | self.unlockTask();
|
---|
866 | if sStatus != "":
|
---|
867 | return None;
|
---|
868 | return oTaskRc;
|
---|
869 |
|
---|
870 | def getLastReply(self):
|
---|
871 | """
|
---|
872 | Returns the last reply three-tuple: cbMsg, sOpcode, abPayload.
|
---|
873 | Returns a None, None, None three-tuple if there was no last reply.
|
---|
874 | """
|
---|
875 | self.lockTask();
|
---|
876 | t3oReply = self.t3oReply;
|
---|
877 | self.unlockTask();
|
---|
878 | return t3oReply;
|
---|
879 |
|
---|
880 | #
|
---|
881 | # Public methods - connection.
|
---|
882 | #
|
---|
883 |
|
---|
884 | def asyncDisconnect(self, cMsTimeout = 30000, fIgnoreErrors = False):
|
---|
885 | """
|
---|
886 | Initiates a disconnect task.
|
---|
887 |
|
---|
888 | Returns True on success, False on failure (logged).
|
---|
889 |
|
---|
890 | The task returns True on success and False on failure.
|
---|
891 | """
|
---|
892 | return self.startTask(cMsTimeout, fIgnoreErrors, "bye", self.taskBye);
|
---|
893 |
|
---|
894 | def syncDisconnect(self, cMsTimeout = 30000, fIgnoreErrors = False):
|
---|
895 | """Synchronous version."""
|
---|
896 | return self.asyncToSync(self.asyncDisconnect, cMsTimeout, fIgnoreErrors);
|
---|
897 |
|
---|
898 | #
|
---|
899 | # Public methods - gadget API
|
---|
900 | #
|
---|
901 |
|
---|
902 | def asyncGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None, cMsTimeout = 30000, fIgnoreErrors = False):
|
---|
903 | """
|
---|
904 | Initiates a gadget create task.
|
---|
905 |
|
---|
906 | Returns True on success, False on failure (logged).
|
---|
907 |
|
---|
908 | The task returns True on success and False on failure.
|
---|
909 | """
|
---|
910 | return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetCreate", self.taskGadgetCreate, \
|
---|
911 | (iGadgetType, iGadgetAccess, lstCfg));
|
---|
912 |
|
---|
913 | def syncGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None, cMsTimeout = 30000, fIgnoreErrors = False):
|
---|
914 | """Synchronous version."""
|
---|
915 | return self.asyncToSync(self.asyncGadgetCreate, iGadgetType, iGadgetAccess, lstCfg, cMsTimeout, fIgnoreErrors);
|
---|
916 |
|
---|
917 | def asyncGadgetDestroy(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
|
---|
918 | """
|
---|
919 | Initiates a gadget destroy task.
|
---|
920 |
|
---|
921 | Returns True on success, False on failure (logged).
|
---|
922 |
|
---|
923 | The task returns True on success and False on failure.
|
---|
924 | """
|
---|
925 | return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetDestroy", self.taskGadgetDestroy, \
|
---|
926 | (iGadgetId, ));
|
---|
927 |
|
---|
928 | def syncGadgetDestroy(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
|
---|
929 | """Synchronous version."""
|
---|
930 | return self.asyncToSync(self.asyncGadgetDestroy, iGadgetId, cMsTimeout, fIgnoreErrors);
|
---|
931 |
|
---|
932 | def asyncGadgetConnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
|
---|
933 | """
|
---|
934 | Initiates a gadget connect task.
|
---|
935 |
|
---|
936 | Returns True on success, False on failure (logged).
|
---|
937 |
|
---|
938 | The task returns True on success and False on failure.
|
---|
939 | """
|
---|
940 | return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetConnect", self.taskGadgetConnect, \
|
---|
941 | (iGadgetId, ));
|
---|
942 |
|
---|
943 | def syncGadgetConnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
|
---|
944 | """Synchronous version."""
|
---|
945 | return self.asyncToSync(self.asyncGadgetConnect, iGadgetId, cMsTimeout, fIgnoreErrors);
|
---|
946 |
|
---|
947 | def asyncGadgetDisconnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
|
---|
948 | """
|
---|
949 | Initiates a gadget disconnect task.
|
---|
950 |
|
---|
951 | Returns True on success, False on failure (logged).
|
---|
952 |
|
---|
953 | The task returns True on success and False on failure.
|
---|
954 | """
|
---|
955 | return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetDisconnect", self.taskGadgetDisconnect, \
|
---|
956 | (iGadgetId, ));
|
---|
957 |
|
---|
958 | def syncGadgetDisconnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
|
---|
959 | """Synchronous version."""
|
---|
960 | return self.asyncToSync(self.asyncGadgetDisconnect, iGadgetId, cMsTimeout, fIgnoreErrors);
|
---|
961 |
|
---|
962 |
|
---|
963 | class TransportTcp(TransportBase):
|
---|
964 | """
|
---|
965 | TCP transport layer for the UTS client session class.
|
---|
966 | """
|
---|
967 |
|
---|
968 | def __init__(self, sHostname, uPort):
|
---|
969 | """
|
---|
970 | Save the parameters. The session will call us back to make the
|
---|
971 | connection later on its worker thread.
|
---|
972 | """
|
---|
973 | TransportBase.__init__(self, utils.getCallerName());
|
---|
974 | self.sHostname = sHostname;
|
---|
975 | self.uPort = uPort if uPort is not None else 6042;
|
---|
976 | self.oSocket = None;
|
---|
977 | self.oWakeupW = None;
|
---|
978 | self.oWakeupR = None;
|
---|
979 | self.fConnectCanceled = False;
|
---|
980 | self.fIsConnecting = False;
|
---|
981 | self.oCv = threading.Condition();
|
---|
982 | self.abReadAhead = array.array('B');
|
---|
983 |
|
---|
984 | def toString(self):
|
---|
985 | return '<%s sHostname=%s, uPort=%s, oSocket=%s,'\
|
---|
986 | ' fConnectCanceled=%s, fIsConnecting=%s, oCv=%s, abReadAhead=%s>' \
|
---|
987 | % (TransportBase.toString(self), self.sHostname, self.uPort, self.oSocket,
|
---|
988 | self.fConnectCanceled, self.fIsConnecting, self.oCv, self.abReadAhead);
|
---|
989 |
|
---|
990 | def __isInProgressXcpt(self, oXcpt):
|
---|
991 | """ In progress exception? """
|
---|
992 | try:
|
---|
993 | if isinstance(oXcpt, socket.error):
|
---|
994 | try:
|
---|
995 | if oXcpt[0] == errno.EINPROGRESS:
|
---|
996 | return True;
|
---|
997 | except: pass;
|
---|
998 | try:
|
---|
999 | if oXcpt[0] == errno.EWOULDBLOCK:
|
---|
1000 | return True;
|
---|
1001 | if utils.getHostOs() == 'win' and oXcpt[0] == errno.WSAEWOULDBLOCK: # pylint: disable=no-member
|
---|
1002 | return True;
|
---|
1003 | except: pass;
|
---|
1004 | except:
|
---|
1005 | pass;
|
---|
1006 | return False;
|
---|
1007 |
|
---|
1008 | def __isWouldBlockXcpt(self, oXcpt):
|
---|
1009 | """ Would block exception? """
|
---|
1010 | try:
|
---|
1011 | if isinstance(oXcpt, socket.error):
|
---|
1012 | try:
|
---|
1013 | if oXcpt[0] == errno.EWOULDBLOCK:
|
---|
1014 | return True;
|
---|
1015 | except: pass;
|
---|
1016 | try:
|
---|
1017 | if oXcpt[0] == errno.EAGAIN:
|
---|
1018 | return True;
|
---|
1019 | except: pass;
|
---|
1020 | except:
|
---|
1021 | pass;
|
---|
1022 | return False;
|
---|
1023 |
|
---|
1024 | def __isConnectionReset(self, oXcpt):
|
---|
1025 | """ Connection reset by Peer or others. """
|
---|
1026 | try:
|
---|
1027 | if isinstance(oXcpt, socket.error):
|
---|
1028 | try:
|
---|
1029 | if oXcpt[0] == errno.ECONNRESET:
|
---|
1030 | return True;
|
---|
1031 | except: pass;
|
---|
1032 | try:
|
---|
1033 | if oXcpt[0] == errno.ENETRESET:
|
---|
1034 | return True;
|
---|
1035 | except: pass;
|
---|
1036 | except:
|
---|
1037 | pass;
|
---|
1038 | return False;
|
---|
1039 |
|
---|
1040 | def _closeWakeupSockets(self):
|
---|
1041 | """ Closes the wakup sockets. Caller should own the CV. """
|
---|
1042 | oWakeupR = self.oWakeupR;
|
---|
1043 | self.oWakeupR = None;
|
---|
1044 | if oWakeupR is not None:
|
---|
1045 | oWakeupR.close();
|
---|
1046 |
|
---|
1047 | oWakeupW = self.oWakeupW;
|
---|
1048 | self.oWakeupW = None;
|
---|
1049 | if oWakeupW is not None:
|
---|
1050 | oWakeupW.close();
|
---|
1051 |
|
---|
1052 | return None;
|
---|
1053 |
|
---|
1054 | def cancelConnect(self):
|
---|
1055 | # This is bad stuff.
|
---|
1056 | self.oCv.acquire();
|
---|
1057 | reporter.log2('TransportTcp::cancelConnect: fIsConnecting=%s oSocket=%s' % (self.fIsConnecting, self.oSocket));
|
---|
1058 | self.fConnectCanceled = True;
|
---|
1059 | if self.fIsConnecting:
|
---|
1060 | oSocket = self.oSocket;
|
---|
1061 | self.oSocket = None;
|
---|
1062 | if oSocket is not None:
|
---|
1063 | reporter.log2('TransportTcp::cancelConnect: closing the socket');
|
---|
1064 | oSocket.close();
|
---|
1065 |
|
---|
1066 | oWakeupW = self.oWakeupW;
|
---|
1067 | self.oWakeupW = None;
|
---|
1068 | if oWakeupW is not None:
|
---|
1069 | reporter.log2('TransportTcp::cancelConnect: wakeup call');
|
---|
1070 | try: oWakeupW.send('cancelled!\n');
|
---|
1071 | except: reporter.logXcpt();
|
---|
1072 | try: oWakeupW.shutdown(socket.SHUT_WR);
|
---|
1073 | except: reporter.logXcpt();
|
---|
1074 | oWakeupW.close();
|
---|
1075 | self.oCv.release();
|
---|
1076 |
|
---|
1077 | def _connectAsClient(self, oSocket, oWakeupR, cMsTimeout):
|
---|
1078 | """ Connects to the UTS server as client. """
|
---|
1079 |
|
---|
1080 | # Connect w/ timeouts.
|
---|
1081 | rc = None;
|
---|
1082 | try:
|
---|
1083 | oSocket.connect((self.sHostname, self.uPort));
|
---|
1084 | rc = True;
|
---|
1085 | except socket.error as oXcpt:
|
---|
1086 | iRc = oXcpt.errno;
|
---|
1087 | if self.__isInProgressXcpt(oXcpt):
|
---|
1088 | # Do the actual waiting.
|
---|
1089 | reporter.log2('TransportTcp::connect: operation in progress (%s)...' % (oXcpt,));
|
---|
1090 | try:
|
---|
1091 | ttRc = select.select([oWakeupR], [oSocket], [oSocket, oWakeupR], cMsTimeout / 1000.0);
|
---|
1092 | if len(ttRc[1]) + len(ttRc[2]) == 0:
|
---|
1093 | raise socket.error(errno.ETIMEDOUT, 'select timed out');
|
---|
1094 | iRc = oSocket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR);
|
---|
1095 | rc = iRc == 0;
|
---|
1096 | except socket.error as oXcpt2:
|
---|
1097 | iRc = oXcpt2.errno;
|
---|
1098 | except:
|
---|
1099 | iRc = -42;
|
---|
1100 | reporter.fatalXcpt('socket.select() on connect failed');
|
---|
1101 |
|
---|
1102 | if rc is True:
|
---|
1103 | pass;
|
---|
1104 | elif iRc in (errno.ECONNREFUSED, errno.EHOSTUNREACH, errno.EINTR, errno.ENETDOWN, errno.ENETUNREACH, errno.ETIMEDOUT):
|
---|
1105 | rc = False; # try again.
|
---|
1106 | else:
|
---|
1107 | if iRc != errno.EBADF or not self.fConnectCanceled:
|
---|
1108 | reporter.fatalXcpt('socket.connect((%s,%s)) failed; iRc=%s' % (self.sHostname, self.uPort, iRc));
|
---|
1109 | reporter.log2('TransportTcp::connect: rc=%s iRc=%s' % (rc, iRc));
|
---|
1110 | except:
|
---|
1111 | reporter.fatalXcpt('socket.connect((%s,%s)) failed' % (self.sHostname, self.uPort));
|
---|
1112 | return rc;
|
---|
1113 |
|
---|
1114 |
|
---|
1115 | def connect(self, cMsTimeout):
|
---|
1116 | # Create a non-blocking socket.
|
---|
1117 | reporter.log2('TransportTcp::connect: cMsTimeout=%s sHostname=%s uPort=%s' % (cMsTimeout, self.sHostname, self.uPort));
|
---|
1118 | try:
|
---|
1119 | oSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);
|
---|
1120 | except:
|
---|
1121 | reporter.fatalXcpt('socket.socket() failed');
|
---|
1122 | return None;
|
---|
1123 | try:
|
---|
1124 | oSocket.setblocking(0);
|
---|
1125 | except:
|
---|
1126 | oSocket.close();
|
---|
1127 | reporter.fatalXcpt('socket.socket() failed');
|
---|
1128 | return None;
|
---|
1129 |
|
---|
1130 | # Create wakeup socket pair for unix (select doesn't wake up on socket close on Linux).
|
---|
1131 | oWakeupR = None;
|
---|
1132 | oWakeupW = None;
|
---|
1133 | if hasattr(socket, 'socketpair'):
|
---|
1134 | try: (oWakeupR, oWakeupW) = socket.socketpair(); # pylint: disable=no-member
|
---|
1135 | except: reporter.logXcpt('socket.socketpair() failed');
|
---|
1136 |
|
---|
1137 | # Update the state.
|
---|
1138 | self.oCv.acquire();
|
---|
1139 | rc = None;
|
---|
1140 | if not self.fConnectCanceled:
|
---|
1141 | self.oSocket = oSocket;
|
---|
1142 | self.oWakeupW = oWakeupW;
|
---|
1143 | self.oWakeupR = oWakeupR;
|
---|
1144 | self.fIsConnecting = True;
|
---|
1145 | self.oCv.release();
|
---|
1146 |
|
---|
1147 | # Try connect.
|
---|
1148 | if oWakeupR is None:
|
---|
1149 | oWakeupR = oSocket; # Avoid select failure.
|
---|
1150 | rc = self._connectAsClient(oSocket, oWakeupR, cMsTimeout);
|
---|
1151 | oSocket = None;
|
---|
1152 |
|
---|
1153 | # Update the state and cleanup on failure/cancel.
|
---|
1154 | self.oCv.acquire();
|
---|
1155 | if rc is True and self.fConnectCanceled:
|
---|
1156 | rc = False;
|
---|
1157 | self.fIsConnecting = False;
|
---|
1158 |
|
---|
1159 | if rc is not True:
|
---|
1160 | if self.oSocket is not None:
|
---|
1161 | self.oSocket.close();
|
---|
1162 | self.oSocket = None;
|
---|
1163 | self._closeWakeupSockets();
|
---|
1164 | self.oCv.release();
|
---|
1165 |
|
---|
1166 | reporter.log2('TransportTcp::connect: returning %s' % (rc,));
|
---|
1167 | return rc;
|
---|
1168 |
|
---|
1169 | def disconnect(self, fQuiet = False):
|
---|
1170 | if self.oSocket is not None:
|
---|
1171 | self.abReadAhead = array.array('B');
|
---|
1172 |
|
---|
1173 | # Try a shutting down the socket gracefully (draining it).
|
---|
1174 | try:
|
---|
1175 | self.oSocket.shutdown(socket.SHUT_WR);
|
---|
1176 | except:
|
---|
1177 | if not fQuiet:
|
---|
1178 | reporter.error('shutdown(SHUT_WR)');
|
---|
1179 | try:
|
---|
1180 | self.oSocket.setblocking(0); # just in case it's not set.
|
---|
1181 | sData = "1";
|
---|
1182 | while sData:
|
---|
1183 | sData = self.oSocket.recv(16384);
|
---|
1184 | except:
|
---|
1185 | pass;
|
---|
1186 |
|
---|
1187 | # Close it.
|
---|
1188 | self.oCv.acquire();
|
---|
1189 | try: self.oSocket.setblocking(1);
|
---|
1190 | except: pass;
|
---|
1191 | self.oSocket.close();
|
---|
1192 | self.oSocket = None;
|
---|
1193 | else:
|
---|
1194 | self.oCv.acquire();
|
---|
1195 | self._closeWakeupSockets();
|
---|
1196 | self.oCv.release();
|
---|
1197 |
|
---|
1198 | def sendBytes(self, abBuf, cMsTimeout):
|
---|
1199 | if self.oSocket is None:
|
---|
1200 | reporter.error('TransportTcp.sendBytes: No connection.');
|
---|
1201 | return False;
|
---|
1202 |
|
---|
1203 | # Try send it all.
|
---|
1204 | try:
|
---|
1205 | cbSent = self.oSocket.send(abBuf);
|
---|
1206 | if cbSent == len(abBuf):
|
---|
1207 | return True;
|
---|
1208 | except Exception as oXcpt:
|
---|
1209 | if not self.__isWouldBlockXcpt(oXcpt):
|
---|
1210 | reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
|
---|
1211 | return False;
|
---|
1212 | cbSent = 0;
|
---|
1213 |
|
---|
1214 | # Do a timed send.
|
---|
1215 | msStart = base.timestampMilli();
|
---|
1216 | while True:
|
---|
1217 | cMsElapsed = base.timestampMilli() - msStart;
|
---|
1218 | if cMsElapsed > cMsTimeout:
|
---|
1219 | reporter.error('TranportTcp.sendBytes: %s bytes timed out (1)' % (len(abBuf)));
|
---|
1220 | break;
|
---|
1221 |
|
---|
1222 | # wait.
|
---|
1223 | try:
|
---|
1224 | ttRc = select.select([], [self.oSocket], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
|
---|
1225 | if ttRc[2] and not ttRc[1]:
|
---|
1226 | reporter.error('TranportTcp.sendBytes: select returned with exception');
|
---|
1227 | break;
|
---|
1228 | if not ttRc[1]:
|
---|
1229 | reporter.error('TranportTcp.sendBytes: %s bytes timed out (2)' % (len(abBuf)));
|
---|
1230 | break;
|
---|
1231 | except:
|
---|
1232 | reporter.errorXcpt('TranportTcp.sendBytes: select failed');
|
---|
1233 | break;
|
---|
1234 |
|
---|
1235 | # Try send more.
|
---|
1236 | try:
|
---|
1237 | cbSent += self.oSocket.send(abBuf[cbSent:]);
|
---|
1238 | if cbSent == len(abBuf):
|
---|
1239 | return True;
|
---|
1240 | except Exception as oXcpt:
|
---|
1241 | if not self.__isWouldBlockXcpt(oXcpt):
|
---|
1242 | reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
|
---|
1243 | break;
|
---|
1244 |
|
---|
1245 | return False;
|
---|
1246 |
|
---|
1247 | def __returnReadAheadBytes(self, cb):
|
---|
1248 | """ Internal worker for recvBytes. """
|
---|
1249 | assert(len(self.abReadAhead) >= cb);
|
---|
1250 | abRet = self.abReadAhead[:cb];
|
---|
1251 | self.abReadAhead = self.abReadAhead[cb:];
|
---|
1252 | return abRet;
|
---|
1253 |
|
---|
1254 | def recvBytes(self, cb, cMsTimeout, fNoDataOk):
|
---|
1255 | if self.oSocket is None:
|
---|
1256 | reporter.error('TransportTcp.recvBytes(%s,%s): No connection.' % (cb, cMsTimeout));
|
---|
1257 | return None;
|
---|
1258 |
|
---|
1259 | # Try read in some more data without bothering with timeout handling first.
|
---|
1260 | if len(self.abReadAhead) < cb:
|
---|
1261 | try:
|
---|
1262 | abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
|
---|
1263 | if abBuf:
|
---|
1264 | self.abReadAhead.extend(array.array('B', abBuf));
|
---|
1265 | except Exception as oXcpt:
|
---|
1266 | if not self.__isWouldBlockXcpt(oXcpt):
|
---|
1267 | reporter.errorXcpt('TranportTcp.recvBytes: 0/%s bytes' % (cb,));
|
---|
1268 | return None;
|
---|
1269 |
|
---|
1270 | if len(self.abReadAhead) >= cb:
|
---|
1271 | return self.__returnReadAheadBytes(cb);
|
---|
1272 |
|
---|
1273 | # Timeout loop.
|
---|
1274 | msStart = base.timestampMilli();
|
---|
1275 | while True:
|
---|
1276 | cMsElapsed = base.timestampMilli() - msStart;
|
---|
1277 | if cMsElapsed > cMsTimeout:
|
---|
1278 | if not fNoDataOk or self.abReadAhead:
|
---|
1279 | reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (1)' % (len(self.abReadAhead), cb));
|
---|
1280 | break;
|
---|
1281 |
|
---|
1282 | # Wait.
|
---|
1283 | try:
|
---|
1284 | ttRc = select.select([self.oSocket], [], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
|
---|
1285 | if ttRc[2] and not ttRc[0]:
|
---|
1286 | reporter.error('TranportTcp.recvBytes: select returned with exception');
|
---|
1287 | break;
|
---|
1288 | if not ttRc[0]:
|
---|
1289 | if not fNoDataOk or self.abReadAhead:
|
---|
1290 | reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (2) fNoDataOk=%s'
|
---|
1291 | % (len(self.abReadAhead), cb, fNoDataOk));
|
---|
1292 | break;
|
---|
1293 | except:
|
---|
1294 | reporter.errorXcpt('TranportTcp.recvBytes: select failed');
|
---|
1295 | break;
|
---|
1296 |
|
---|
1297 | # Try read more.
|
---|
1298 | try:
|
---|
1299 | abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
|
---|
1300 | if not abBuf:
|
---|
1301 | reporter.error('TranportTcp.recvBytes: %s/%s bytes (%s) - connection has been shut down'
|
---|
1302 | % (len(self.abReadAhead), cb, fNoDataOk));
|
---|
1303 | self.disconnect();
|
---|
1304 | return None;
|
---|
1305 |
|
---|
1306 | self.abReadAhead.extend(array.array('B', abBuf));
|
---|
1307 |
|
---|
1308 | except Exception as oXcpt:
|
---|
1309 | reporter.log('recv => exception %s' % (oXcpt,));
|
---|
1310 | if not self.__isWouldBlockXcpt(oXcpt):
|
---|
1311 | if not fNoDataOk or not self.__isConnectionReset(oXcpt) or self.abReadAhead:
|
---|
1312 | reporter.errorXcpt('TranportTcp.recvBytes: %s/%s bytes (%s)' % (len(self.abReadAhead), cb, fNoDataOk));
|
---|
1313 | break;
|
---|
1314 |
|
---|
1315 | # Done?
|
---|
1316 | if len(self.abReadAhead) >= cb:
|
---|
1317 | return self.__returnReadAheadBytes(cb);
|
---|
1318 |
|
---|
1319 | #reporter.log('recv => None len(self.abReadAhead) -> %d' % (len(self.abReadAhead), ));
|
---|
1320 | return None;
|
---|
1321 |
|
---|
1322 | def isConnectionOk(self):
|
---|
1323 | if self.oSocket is None:
|
---|
1324 | return False;
|
---|
1325 | try:
|
---|
1326 | ttRc = select.select([], [], [self.oSocket], 0.0);
|
---|
1327 | if ttRc[2]:
|
---|
1328 | return False;
|
---|
1329 |
|
---|
1330 | self.oSocket.send(array.array('B')); # send zero bytes.
|
---|
1331 | except:
|
---|
1332 | return False;
|
---|
1333 | return True;
|
---|
1334 |
|
---|
1335 | def isRecvPending(self, cMsTimeout = 0):
|
---|
1336 | try:
|
---|
1337 | ttRc = select.select([self.oSocket], [], [], cMsTimeout / 1000.0);
|
---|
1338 | if not ttRc[0]:
|
---|
1339 | return False;
|
---|
1340 | except:
|
---|
1341 | pass;
|
---|
1342 | return True;
|
---|
1343 |
|
---|
1344 |
|
---|
1345 | class UsbGadget(object):
|
---|
1346 | """
|
---|
1347 | USB Gadget control class using the USBT Test Service to talk to the external
|
---|
1348 | board behaving like a USB device.
|
---|
1349 | """
|
---|
1350 |
|
---|
1351 | def __init__(self):
|
---|
1352 | self.oUtsSession = None;
|
---|
1353 | self.sImpersonation = g_ksGadgetImpersonationInvalid;
|
---|
1354 | self.idGadget = None;
|
---|
1355 | self.iBusId = None;
|
---|
1356 | self.iDevId = None;
|
---|
1357 | self.iUsbIpPort = None;
|
---|
1358 |
|
---|
1359 | def clearImpersonation(self):
|
---|
1360 | """
|
---|
1361 | Removes the current impersonation of the gadget.
|
---|
1362 | """
|
---|
1363 | fRc = True;
|
---|
1364 |
|
---|
1365 | if self.idGadget is not None:
|
---|
1366 | fRc = self.oUtsSession.syncGadgetDestroy(self.idGadget);
|
---|
1367 | self.idGadget = None;
|
---|
1368 | self.iBusId = None;
|
---|
1369 | self.iDevId = None;
|
---|
1370 |
|
---|
1371 | return fRc;
|
---|
1372 |
|
---|
1373 | def disconnectUsb(self):
|
---|
1374 | """
|
---|
1375 | Disconnects the USB gadget from the host. (USB connection not network
|
---|
1376 | connection used for control)
|
---|
1377 | """
|
---|
1378 | return self.oUtsSession.syncGadgetDisconnect(self.idGadget);
|
---|
1379 |
|
---|
1380 | def connectUsb(self):
|
---|
1381 | """
|
---|
1382 | Connect the USB gadget to the host.
|
---|
1383 | """
|
---|
1384 | return self.oUtsSession.syncGadgetConnect(self.idGadget);
|
---|
1385 |
|
---|
1386 | def impersonate(self, sImpersonation, fSuperSpeed = False):
|
---|
1387 | """
|
---|
1388 | Impersonate a given device.
|
---|
1389 | """
|
---|
1390 |
|
---|
1391 | # Clear any previous impersonation
|
---|
1392 | self.clearImpersonation();
|
---|
1393 | self.sImpersonation = sImpersonation;
|
---|
1394 |
|
---|
1395 | fRc = False;
|
---|
1396 | if sImpersonation == g_ksGadgetImpersonationTest:
|
---|
1397 | lstCfg = [];
|
---|
1398 | if fSuperSpeed is True:
|
---|
1399 | lstCfg.append( ('Gadget/SuperSpeed', g_kiGadgetCfgTypeBool, 'true') );
|
---|
1400 | fDone = self.oUtsSession.syncGadgetCreate(g_kiGadgetTypeTest, g_kiGadgetAccessUsbIp, lstCfg);
|
---|
1401 | if fDone is True and self.oUtsSession.isSuccess():
|
---|
1402 | # Get the gadget ID.
|
---|
1403 | _, _, abPayload = self.oUtsSession.getLastReply();
|
---|
1404 |
|
---|
1405 | fRc = True;
|
---|
1406 | self.idGadget = getU32(abPayload, 16);
|
---|
1407 | self.iBusId = getU32(abPayload, 20);
|
---|
1408 | self.iDevId = getU32(abPayload, 24);
|
---|
1409 | else:
|
---|
1410 | reporter.log('Invalid or unsupported impersonation');
|
---|
1411 |
|
---|
1412 | return fRc;
|
---|
1413 |
|
---|
1414 | def getUsbIpPort(self):
|
---|
1415 | """
|
---|
1416 | Returns the port the USB/IP server is listening on if requested,
|
---|
1417 | None if USB/IP is not supported.
|
---|
1418 | """
|
---|
1419 | return self.iUsbIpPort;
|
---|
1420 |
|
---|
1421 | def getGadgetBusAndDevId(self):
|
---|
1422 | """
|
---|
1423 | Returns the bus ad device ID of the gadget as a tuple.
|
---|
1424 | """
|
---|
1425 | return (self.iBusId, self.iDevId);
|
---|
1426 |
|
---|
1427 | def connectTo(self, cMsTimeout, sHostname, uPort = None, fUsbIpSupport = True, cMsIdleFudge = 0, fTryConnect = False):
|
---|
1428 | """
|
---|
1429 | Connects to the specified target device.
|
---|
1430 | Returns True on Success.
|
---|
1431 | Returns False otherwise.
|
---|
1432 | """
|
---|
1433 | fRc = True;
|
---|
1434 |
|
---|
1435 | # @todo
|
---|
1436 | if fUsbIpSupport is False:
|
---|
1437 | return False;
|
---|
1438 |
|
---|
1439 | reporter.log2('openTcpSession(%s, %s, %s, %s)' % \
|
---|
1440 | (cMsTimeout, sHostname, uPort, cMsIdleFudge));
|
---|
1441 | try:
|
---|
1442 | oTransport = TransportTcp(sHostname, uPort);
|
---|
1443 | self.oUtsSession = Session(oTransport, cMsTimeout, cMsIdleFudge, fTryConnect);
|
---|
1444 |
|
---|
1445 | if self.oUtsSession is not None:
|
---|
1446 | fDone = self.oUtsSession.waitForTask(30*1000);
|
---|
1447 | reporter.log('connect: waitForTask -> %s, result %s' % (fDone, self.oUtsSession.getResult()));
|
---|
1448 | if fDone is True and self.oUtsSession.isSuccess():
|
---|
1449 | # Parse the reply.
|
---|
1450 | _, _, abPayload = self.oUtsSession.getLastReply();
|
---|
1451 |
|
---|
1452 | if getU32(abPayload, 20) is g_kiGadgetAccessUsbIp:
|
---|
1453 | fRc = True;
|
---|
1454 | self.iUsbIpPort = getU32(abPayload, 24);
|
---|
1455 | else:
|
---|
1456 | reporter.log('Gadget doesn\'t support access over USB/IP despite being requested');
|
---|
1457 | fRc = False;
|
---|
1458 | else:
|
---|
1459 | fRc = False;
|
---|
1460 | else:
|
---|
1461 | fRc = False;
|
---|
1462 | except:
|
---|
1463 | reporter.errorXcpt(None, 15);
|
---|
1464 | return False;
|
---|
1465 |
|
---|
1466 | return fRc;
|
---|
1467 |
|
---|
1468 | def disconnectFrom(self):
|
---|
1469 | """
|
---|
1470 | Disconnects from the target device.
|
---|
1471 | """
|
---|
1472 | fRc = True;
|
---|
1473 |
|
---|
1474 | self.clearImpersonation();
|
---|
1475 | if self.oUtsSession is not None:
|
---|
1476 | fRc = self.oUtsSession.syncDisconnect();
|
---|
1477 |
|
---|
1478 | return fRc;
|
---|