VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/tests/usb/usbgadget.py@ 74978

Last change on this file since 74978 was 71983, checked in by vboxsync, 7 years ago

ValidationKit/tdUsb1: Needed more changes to truly turn gadget host connect failures into a reason for skipping the test, because the error when taskConnect detected a timeout was overriding the test status.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette