VirtualBox

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

Last change on this file since 79449 was 79092, checked in by vboxsync, 6 years ago

ValKit,++: Pylint 2.3.1 adjustments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.6 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: usbgadget.py 79092 2019-06-11 15:26:40Z vboxsync $
3# pylint: disable=too-many-lines
4
5"""
6UTS (USB Test Service) client.
7"""
8__copyright__ = \
9"""
10Copyright (C) 2010-2019 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: 79092 $"
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=unused-variable
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); # (virtual method) # pylint: disable=assignment-from-none
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); # pylint: disable=assignment-from-none
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 > 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 try:
985 if oXcpt[0] == errno.EWOULDBLOCK:
986 return True;
987 if utils.getHostOs() == 'win' and oXcpt[0] == errno.WSAEWOULDBLOCK: # pylint: disable=no-member
988 return True;
989 except: pass;
990 except:
991 pass;
992 return False;
993
994 def __isWouldBlockXcpt(self, oXcpt):
995 """ Would block exception? """
996 try:
997 if isinstance(oXcpt, socket.error):
998 try:
999 if oXcpt[0] == errno.EWOULDBLOCK:
1000 return True;
1001 except: pass;
1002 try:
1003 if oXcpt[0] == errno.EAGAIN:
1004 return True;
1005 except: pass;
1006 except:
1007 pass;
1008 return False;
1009
1010 def __isConnectionReset(self, oXcpt):
1011 """ Connection reset by Peer or others. """
1012 try:
1013 if isinstance(oXcpt, socket.error):
1014 try:
1015 if oXcpt[0] == errno.ECONNRESET:
1016 return True;
1017 except: pass;
1018 try:
1019 if oXcpt[0] == errno.ENETRESET:
1020 return True;
1021 except: pass;
1022 except:
1023 pass;
1024 return False;
1025
1026 def _closeWakeupSockets(self):
1027 """ Closes the wakup sockets. Caller should own the CV. """
1028 oWakeupR = self.oWakeupR;
1029 self.oWakeupR = None;
1030 if oWakeupR is not None:
1031 oWakeupR.close();
1032
1033 oWakeupW = self.oWakeupW;
1034 self.oWakeupW = None;
1035 if oWakeupW is not None:
1036 oWakeupW.close();
1037
1038 return None;
1039
1040 def cancelConnect(self):
1041 # This is bad stuff.
1042 self.oCv.acquire();
1043 reporter.log2('TransportTcp::cancelConnect: fIsConnecting=%s oSocket=%s' % (self.fIsConnecting, self.oSocket));
1044 self.fConnectCanceled = True;
1045 if self.fIsConnecting:
1046 oSocket = self.oSocket;
1047 self.oSocket = None;
1048 if oSocket is not None:
1049 reporter.log2('TransportTcp::cancelConnect: closing the socket');
1050 oSocket.close();
1051
1052 oWakeupW = self.oWakeupW;
1053 self.oWakeupW = None;
1054 if oWakeupW is not None:
1055 reporter.log2('TransportTcp::cancelConnect: wakeup call');
1056 try: oWakeupW.send('cancelled!\n');
1057 except: reporter.logXcpt();
1058 try: oWakeupW.shutdown(socket.SHUT_WR);
1059 except: reporter.logXcpt();
1060 oWakeupW.close();
1061 self.oCv.release();
1062
1063 def _connectAsClient(self, oSocket, oWakeupR, cMsTimeout):
1064 """ Connects to the UTS server as client. """
1065
1066 # Connect w/ timeouts.
1067 rc = None;
1068 try:
1069 oSocket.connect((self.sHostname, self.uPort));
1070 rc = True;
1071 except socket.error as oXcpt:
1072 iRc = oXcpt.errno;
1073 if self.__isInProgressXcpt(oXcpt):
1074 # Do the actual waiting.
1075 reporter.log2('TransportTcp::connect: operation in progress (%s)...' % (oXcpt,));
1076 try:
1077 ttRc = select.select([oWakeupR], [oSocket], [oSocket, oWakeupR], cMsTimeout / 1000.0);
1078 if len(ttRc[1]) + len(ttRc[2]) == 0:
1079 raise socket.error(errno.ETIMEDOUT, 'select timed out');
1080 iRc = oSocket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR);
1081 rc = iRc == 0;
1082 except socket.error as oXcpt2:
1083 iRc = oXcpt2.errno;
1084 except:
1085 iRc = -42;
1086 reporter.fatalXcpt('socket.select() on connect failed');
1087
1088 if rc is True:
1089 pass;
1090 elif iRc in (errno.ECONNREFUSED, errno.EHOSTUNREACH, errno.EINTR, errno.ENETDOWN, errno.ENETUNREACH, errno.ETIMEDOUT):
1091 rc = False; # try again.
1092 else:
1093 if iRc != errno.EBADF or not self.fConnectCanceled:
1094 reporter.fatalXcpt('socket.connect((%s,%s)) failed; iRc=%s' % (self.sHostname, self.uPort, iRc));
1095 reporter.log2('TransportTcp::connect: rc=%s iRc=%s' % (rc, iRc));
1096 except:
1097 reporter.fatalXcpt('socket.connect((%s,%s)) failed' % (self.sHostname, self.uPort));
1098 return rc;
1099
1100
1101 def connect(self, cMsTimeout):
1102 # Create a non-blocking socket.
1103 reporter.log2('TransportTcp::connect: cMsTimeout=%s sHostname=%s uPort=%s' % (cMsTimeout, self.sHostname, self.uPort));
1104 try:
1105 oSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);
1106 except:
1107 reporter.fatalXcpt('socket.socket() failed');
1108 return None;
1109 try:
1110 oSocket.setblocking(0);
1111 except:
1112 oSocket.close();
1113 reporter.fatalXcpt('socket.socket() failed');
1114 return None;
1115
1116 # Create wakeup socket pair for unix (select doesn't wake up on socket close on Linux).
1117 oWakeupR = None;
1118 oWakeupW = None;
1119 if hasattr(socket, 'socketpair'):
1120 try: (oWakeupR, oWakeupW) = socket.socketpair(); # pylint: disable=no-member
1121 except: reporter.logXcpt('socket.socketpair() failed');
1122
1123 # Update the state.
1124 self.oCv.acquire();
1125 rc = None;
1126 if not self.fConnectCanceled:
1127 self.oSocket = oSocket;
1128 self.oWakeupW = oWakeupW;
1129 self.oWakeupR = oWakeupR;
1130 self.fIsConnecting = True;
1131 self.oCv.release();
1132
1133 # Try connect.
1134 if oWakeupR is None:
1135 oWakeupR = oSocket; # Avoid select failure.
1136 rc = self._connectAsClient(oSocket, oWakeupR, cMsTimeout);
1137 oSocket = None;
1138
1139 # Update the state and cleanup on failure/cancel.
1140 self.oCv.acquire();
1141 if rc is True and self.fConnectCanceled:
1142 rc = False;
1143 self.fIsConnecting = False;
1144
1145 if rc is not True:
1146 if self.oSocket is not None:
1147 self.oSocket.close();
1148 self.oSocket = None;
1149 self._closeWakeupSockets();
1150 self.oCv.release();
1151
1152 reporter.log2('TransportTcp::connect: returning %s' % (rc,));
1153 return rc;
1154
1155 def disconnect(self, fQuiet = False):
1156 if self.oSocket is not None:
1157 self.abReadAhead = array.array('B');
1158
1159 # Try a shutting down the socket gracefully (draining it).
1160 try:
1161 self.oSocket.shutdown(socket.SHUT_WR);
1162 except:
1163 if not fQuiet:
1164 reporter.error('shutdown(SHUT_WR)');
1165 try:
1166 self.oSocket.setblocking(0); # just in case it's not set.
1167 sData = "1";
1168 while sData:
1169 sData = self.oSocket.recv(16384);
1170 except:
1171 pass;
1172
1173 # Close it.
1174 self.oCv.acquire();
1175 try: self.oSocket.setblocking(1);
1176 except: pass;
1177 self.oSocket.close();
1178 self.oSocket = None;
1179 else:
1180 self.oCv.acquire();
1181 self._closeWakeupSockets();
1182 self.oCv.release();
1183
1184 def sendBytes(self, abBuf, cMsTimeout):
1185 if self.oSocket is None:
1186 reporter.error('TransportTcp.sendBytes: No connection.');
1187 return False;
1188
1189 # Try send it all.
1190 try:
1191 cbSent = self.oSocket.send(abBuf);
1192 if cbSent == len(abBuf):
1193 return True;
1194 except Exception as oXcpt:
1195 if not self.__isWouldBlockXcpt(oXcpt):
1196 reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
1197 return False;
1198 cbSent = 0;
1199
1200 # Do a timed send.
1201 msStart = base.timestampMilli();
1202 while True:
1203 cMsElapsed = base.timestampMilli() - msStart;
1204 if cMsElapsed > cMsTimeout:
1205 reporter.error('TranportTcp.sendBytes: %s bytes timed out (1)' % (len(abBuf)));
1206 break;
1207
1208 # wait.
1209 try:
1210 ttRc = select.select([], [self.oSocket], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
1211 if ttRc[2] and not ttRc[1]:
1212 reporter.error('TranportTcp.sendBytes: select returned with exception');
1213 break;
1214 if not ttRc[1]:
1215 reporter.error('TranportTcp.sendBytes: %s bytes timed out (2)' % (len(abBuf)));
1216 break;
1217 except:
1218 reporter.errorXcpt('TranportTcp.sendBytes: select failed');
1219 break;
1220
1221 # Try send more.
1222 try:
1223 cbSent += self.oSocket.send(abBuf[cbSent:]);
1224 if cbSent == len(abBuf):
1225 return True;
1226 except Exception as oXcpt:
1227 if not self.__isWouldBlockXcpt(oXcpt):
1228 reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
1229 break;
1230
1231 return False;
1232
1233 def __returnReadAheadBytes(self, cb):
1234 """ Internal worker for recvBytes. """
1235 assert(len(self.abReadAhead) >= cb);
1236 abRet = self.abReadAhead[:cb];
1237 self.abReadAhead = self.abReadAhead[cb:];
1238 return abRet;
1239
1240 def recvBytes(self, cb, cMsTimeout, fNoDataOk):
1241 if self.oSocket is None:
1242 reporter.error('TransportTcp.recvBytes(%s,%s): No connection.' % (cb, cMsTimeout));
1243 return None;
1244
1245 # Try read in some more data without bothering with timeout handling first.
1246 if len(self.abReadAhead) < cb:
1247 try:
1248 abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
1249 if abBuf:
1250 self.abReadAhead.extend(array.array('B', abBuf));
1251 except Exception as oXcpt:
1252 if not self.__isWouldBlockXcpt(oXcpt):
1253 reporter.errorXcpt('TranportTcp.recvBytes: 0/%s bytes' % (cb,));
1254 return None;
1255
1256 if len(self.abReadAhead) >= cb:
1257 return self.__returnReadAheadBytes(cb);
1258
1259 # Timeout loop.
1260 msStart = base.timestampMilli();
1261 while True:
1262 cMsElapsed = base.timestampMilli() - msStart;
1263 if cMsElapsed > cMsTimeout:
1264 if not fNoDataOk or self.abReadAhead:
1265 reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (1)' % (len(self.abReadAhead), cb));
1266 break;
1267
1268 # Wait.
1269 try:
1270 ttRc = select.select([self.oSocket], [], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
1271 if ttRc[2] and not ttRc[0]:
1272 reporter.error('TranportTcp.recvBytes: select returned with exception');
1273 break;
1274 if not ttRc[0]:
1275 if not fNoDataOk or self.abReadAhead:
1276 reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (2) fNoDataOk=%s'
1277 % (len(self.abReadAhead), cb, fNoDataOk));
1278 break;
1279 except:
1280 reporter.errorXcpt('TranportTcp.recvBytes: select failed');
1281 break;
1282
1283 # Try read more.
1284 try:
1285 abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
1286 if not abBuf:
1287 reporter.error('TranportTcp.recvBytes: %s/%s bytes (%s) - connection has been shut down'
1288 % (len(self.abReadAhead), cb, fNoDataOk));
1289 self.disconnect();
1290 return None;
1291
1292 self.abReadAhead.extend(array.array('B', abBuf));
1293
1294 except Exception as oXcpt:
1295 reporter.log('recv => exception %s' % (oXcpt,));
1296 if not self.__isWouldBlockXcpt(oXcpt):
1297 if not fNoDataOk or not self.__isConnectionReset(oXcpt) or self.abReadAhead:
1298 reporter.errorXcpt('TranportTcp.recvBytes: %s/%s bytes (%s)' % (len(self.abReadAhead), cb, fNoDataOk));
1299 break;
1300
1301 # Done?
1302 if len(self.abReadAhead) >= cb:
1303 return self.__returnReadAheadBytes(cb);
1304
1305 #reporter.log('recv => None len(self.abReadAhead) -> %d' % (len(self.abReadAhead), ));
1306 return None;
1307
1308 def isConnectionOk(self):
1309 if self.oSocket is None:
1310 return False;
1311 try:
1312 ttRc = select.select([], [], [self.oSocket], 0.0);
1313 if ttRc[2]:
1314 return False;
1315
1316 self.oSocket.send(array.array('B')); # send zero bytes.
1317 except:
1318 return False;
1319 return True;
1320
1321 def isRecvPending(self, cMsTimeout = 0):
1322 try:
1323 ttRc = select.select([self.oSocket], [], [], cMsTimeout / 1000.0);
1324 if not ttRc[0]:
1325 return False;
1326 except:
1327 pass;
1328 return True;
1329
1330
1331class UsbGadget(object):
1332 """
1333 USB Gadget control class using the USBT Test Service to talk to the external
1334 board behaving like a USB device.
1335 """
1336
1337 def __init__(self):
1338 self.oUtsSession = None;
1339 self.sImpersonation = g_ksGadgetImpersonationInvalid;
1340 self.idGadget = None;
1341 self.iBusId = None;
1342 self.iDevId = None;
1343 self.iUsbIpPort = None;
1344
1345 def clearImpersonation(self):
1346 """
1347 Removes the current impersonation of the gadget.
1348 """
1349 fRc = True;
1350
1351 if self.idGadget is not None:
1352 fRc = self.oUtsSession.syncGadgetDestroy(self.idGadget);
1353 self.idGadget = None;
1354 self.iBusId = None;
1355 self.iDevId = None;
1356
1357 return fRc;
1358
1359 def disconnectUsb(self):
1360 """
1361 Disconnects the USB gadget from the host. (USB connection not network
1362 connection used for control)
1363 """
1364 return self.oUtsSession.syncGadgetDisconnect(self.idGadget);
1365
1366 def connectUsb(self):
1367 """
1368 Connect the USB gadget to the host.
1369 """
1370 return self.oUtsSession.syncGadgetConnect(self.idGadget);
1371
1372 def impersonate(self, sImpersonation, fSuperSpeed = False):
1373 """
1374 Impersonate a given device.
1375 """
1376
1377 # Clear any previous impersonation
1378 self.clearImpersonation();
1379 self.sImpersonation = sImpersonation;
1380
1381 fRc = False;
1382 if sImpersonation == g_ksGadgetImpersonationTest:
1383 lstCfg = [];
1384 if fSuperSpeed is True:
1385 lstCfg.append( ('Gadget/SuperSpeed', g_kiGadgetCfgTypeBool, 'true') );
1386 fDone = self.oUtsSession.syncGadgetCreate(g_kiGadgetTypeTest, g_kiGadgetAccessUsbIp, lstCfg);
1387 if fDone is True and self.oUtsSession.isSuccess():
1388 # Get the gadget ID.
1389 _, _, abPayload = self.oUtsSession.getLastReply();
1390
1391 fRc = True;
1392 self.idGadget = getU32(abPayload, 16);
1393 self.iBusId = getU32(abPayload, 20);
1394 self.iDevId = getU32(abPayload, 24);
1395 else:
1396 reporter.log('Invalid or unsupported impersonation');
1397
1398 return fRc;
1399
1400 def getUsbIpPort(self):
1401 """
1402 Returns the port the USB/IP server is listening on if requested,
1403 None if USB/IP is not supported.
1404 """
1405 return self.iUsbIpPort;
1406
1407 def getGadgetBusAndDevId(self):
1408 """
1409 Returns the bus ad device ID of the gadget as a tuple.
1410 """
1411 return (self.iBusId, self.iDevId);
1412
1413 def connectTo(self, cMsTimeout, sHostname, uPort = None, fUsbIpSupport = True, cMsIdleFudge = 0, fTryConnect = False):
1414 """
1415 Connects to the specified target device.
1416 Returns True on Success.
1417 Returns False otherwise.
1418 """
1419 fRc = True;
1420
1421 # @todo
1422 if fUsbIpSupport is False:
1423 return False;
1424
1425 reporter.log2('openTcpSession(%s, %s, %s, %s)' % \
1426 (cMsTimeout, sHostname, uPort, cMsIdleFudge));
1427 try:
1428 oTransport = TransportTcp(sHostname, uPort);
1429 self.oUtsSession = Session(oTransport, cMsTimeout, cMsIdleFudge, fTryConnect);
1430
1431 if self.oUtsSession is not None:
1432 fDone = self.oUtsSession.waitForTask(30*1000);
1433 reporter.log('connect: waitForTask -> %s, result %s' % (fDone, self.oUtsSession.getResult()));
1434 if fDone is True and self.oUtsSession.isSuccess():
1435 # Parse the reply.
1436 _, _, abPayload = self.oUtsSession.getLastReply();
1437
1438 if getU32(abPayload, 20) is g_kiGadgetAccessUsbIp:
1439 fRc = True;
1440 self.iUsbIpPort = getU32(abPayload, 24);
1441 else:
1442 reporter.log('Gadget doesn\'t support access over USB/IP despite being requested');
1443 fRc = False;
1444 else:
1445 fRc = False;
1446 else:
1447 fRc = False;
1448 except:
1449 reporter.errorXcpt(None, 15);
1450 return False;
1451
1452 return fRc;
1453
1454 def disconnectFrom(self):
1455 """
1456 Disconnects from the target device.
1457 """
1458 fRc = True;
1459
1460 self.clearImpersonation();
1461 if self.oUtsSession is not None:
1462 fRc = self.oUtsSession.syncDisconnect();
1463
1464 return fRc;
1465
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