VirtualBox

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

Last change on this file since 95818 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.7 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: usbgadget.py 93115 2022-01-01 11:31:46Z vboxsync $
3# pylint: disable=too-many-lines
4
5"""
6UTS (USB Test Service) client.
7"""
8__copyright__ = \
9"""
10Copyright (C) 2010-2022 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: 93115 $"
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
595 if sys.version_info < (3, 9, 0):
596 # Removed since Python 3.9.
597 return oThread.isAlive(); # pylint: disable=no-member
598 return oThread.is_alive();
599
600 def taskThread(self):
601 """
602 The task thread function.
603 This does some housekeeping activities around the real task method call.
604 """
605 if not self.isCancelled():
606 try:
607 fnTask = self.fnTask;
608 oTaskRc = fnTask(*self.aTaskArgs);
609 except:
610 reporter.fatalXcpt('taskThread', 15);
611 oTaskRc = None;
612 else:
613 reporter.log('taskThread: cancelled already');
614
615 self.lockTask();
616
617 reporter.log('taskThread: signalling task with status "%s", oTaskRc=%s' % (self.sStatus, oTaskRc));
618 self.oTaskRc = oTaskRc;
619 self.oThread = None;
620 self.sStatus = '';
621 self.signalTaskLocked();
622
623 self.unlockTask();
624 return None;
625
626 def isCancelled(self):
627 """Internal method for checking if the task has been cancelled."""
628 self.lockTask();
629 sStatus = self.sStatus;
630 self.unlockTask();
631 if sStatus == "cancelled":
632 return True;
633 return False;
634
635 def hasTimedOut(self):
636 """Internal method for checking if the task has timed out or not."""
637 cMsLeft = self.getMsLeft();
638 if cMsLeft <= 0:
639 return True;
640 return False;
641
642 def getMsLeft(self, cMsMin = 0, cMsMax = -1):
643 """Gets the time left until the timeout."""
644 cMsElapsed = base.timestampMilli() - self.msStart;
645 if cMsElapsed < 0:
646 return cMsMin;
647 cMsLeft = self.cMsTimeout - cMsElapsed;
648 if cMsLeft <= cMsMin:
649 return cMsMin;
650 if cMsLeft > cMsMax > 0:
651 return cMsMax
652 return cMsLeft;
653
654 def recvReply(self, cMsTimeout = None, fNoDataOk = False):
655 """
656 Wrapper for TransportBase.recvMsg that stashes the response away
657 so the client can inspect it later on.
658 """
659 if cMsTimeout is None:
660 cMsTimeout = self.getMsLeft(500);
661 cbMsg, sOpcode, abPayload = self.oTransport.recvMsg(cMsTimeout, fNoDataOk);
662 self.lockTask();
663 self.t3oReply = (cbMsg, sOpcode, abPayload);
664 self.unlockTask();
665 return (cbMsg, sOpcode, abPayload);
666
667 def recvAck(self, fNoDataOk = False):
668 """
669 Receives an ACK or error response from the UTS.
670
671 Returns True on success.
672 Returns False on timeout or transport error.
673 Returns (sOpcode, sDetails) tuple on failure. The opcode is stripped
674 and there are always details of some sort or another.
675 """
676 cbMsg, sOpcode, abPayload = self.recvReply(None, fNoDataOk);
677 if cbMsg is None:
678 return False;
679 sOpcode = sOpcode.strip()
680 if sOpcode == "ACK":
681 return True;
682 return (sOpcode, getSZ(abPayload, 16, sOpcode));
683
684 def recvAckLogged(self, sCommand, fNoDataOk = False):
685 """
686 Wrapper for recvAck and logging.
687 Returns True on success (ACK).
688 Returns False on time, transport error and errors signalled by UTS.
689 """
690 rc = self.recvAck(fNoDataOk);
691 if rc is not True and not fNoDataOk:
692 if rc is False:
693 reporter.maybeErr(self.fErr, 'recvAckLogged: %s transport error' % (sCommand));
694 else:
695 reporter.maybeErr(self.fErr, 'recvAckLogged: %s response was %s: %s' % (sCommand, rc[0], rc[1]));
696 rc = False;
697 return rc;
698
699 def recvTrueFalse(self, sCommand):
700 """
701 Receives a TRUE/FALSE response from the UTS.
702 Returns True on TRUE, False on FALSE and None on error/other (logged).
703 """
704 cbMsg, sOpcode, abPayload = self.recvReply();
705 if cbMsg is None:
706 reporter.maybeErr(self.fErr, 'recvAckLogged: %s transport error' % (sCommand));
707 return None;
708
709 sOpcode = sOpcode.strip()
710 if sOpcode == "TRUE":
711 return True;
712 if sOpcode == "FALSE":
713 return False;
714 reporter.maybeErr(self.fErr, 'recvAckLogged: %s response was %s: %s' % \
715 (sCommand, sOpcode, getSZ(abPayload, 16, sOpcode)));
716 return None;
717
718 def sendMsg(self, sOpcode, aoPayload = (), cMsTimeout = None):
719 """
720 Wrapper for TransportBase.sendMsg that inserts the correct timeout.
721 """
722 if cMsTimeout is None:
723 cMsTimeout = self.getMsLeft(500);
724 return self.oTransport.sendMsg(sOpcode, cMsTimeout, aoPayload);
725
726 def asyncToSync(self, fnAsync, *aArgs):
727 """
728 Wraps an asynchronous task into a synchronous operation.
729
730 Returns False on failure, task return status on success.
731 """
732 rc = fnAsync(*aArgs);
733 if rc is False:
734 reporter.log2('asyncToSync(%s): returns False (#1)' % (fnAsync));
735 return rc;
736
737 rc = self.waitForTask(self.cMsTimeout + 5000);
738 if rc is False:
739 reporter.maybeErrXcpt(self.fErr, 'asyncToSync: waitForTask failed...');
740 self.cancelTask();
741 #reporter.log2('asyncToSync(%s): returns False (#2)' % (fnAsync, rc));
742 return False;
743
744 rc = self.getResult();
745 #reporter.log2('asyncToSync(%s): returns %s' % (fnAsync, rc));
746 return rc;
747
748 #
749 # Connection tasks.
750 #
751
752 def taskConnect(self, cMsIdleFudge):
753 """Tries to connect to the UTS"""
754 while not self.isCancelled():
755 reporter.log2('taskConnect: connecting ...');
756 rc = self.oTransport.connect(self.getMsLeft(500));
757 if rc is True:
758 reporter.log('taskConnect: succeeded');
759 return self.taskGreet(cMsIdleFudge);
760 if rc is None:
761 reporter.log2('taskConnect: unable to connect');
762 return None;
763 if self.hasTimedOut():
764 reporter.log2('taskConnect: timed out');
765 if not self.fTryConnect:
766 reporter.maybeErr(self.fErr, 'taskConnect: timed out');
767 return False;
768 time.sleep(self.getMsLeft(1, 1000) / 1000.0);
769 if not self.fTryConnect:
770 reporter.maybeErr(self.fErr, 'taskConnect: cancelled');
771 return False;
772
773 def taskGreet(self, cMsIdleFudge):
774 """Greets the UTS"""
775 sHostname = socket.gethostname().lower();
776 cbFill = 68 - len(sHostname) - 1;
777 rc = self.sendMsg("HOWDY", ((1 << 16) | 0, 0x1, len(sHostname), sHostname, zeroByteArray(cbFill)));
778 if rc is True:
779 rc = self.recvAckLogged("HOWDY", self.fTryConnect);
780 if rc is True:
781 while cMsIdleFudge > 0:
782 cMsIdleFudge -= 1000;
783 time.sleep(1);
784 else:
785 self.oTransport.disconnect(self.fTryConnect);
786 return rc;
787
788 def taskBye(self):
789 """Says goodbye to the UTS"""
790 rc = self.sendMsg("BYE");
791 if rc is True:
792 rc = self.recvAckLogged("BYE");
793 self.oTransport.disconnect();
794 return rc;
795
796 #
797 # Gadget tasks.
798 #
799
800 def taskGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None):
801 """Creates a new gadget on UTS"""
802 cCfgItems = 0;
803 if lstCfg is not None:
804 cCfgItems = len(lstCfg);
805 fRc = self.sendMsg("GDGTCRT", (iGadgetType, iGadgetAccess, cCfgItems, 0, cfgListToByteArray(lstCfg)));
806 if fRc is True:
807 fRc = self.recvAckLogged("GDGTCRT");
808 return fRc;
809
810 def taskGadgetDestroy(self, iGadgetId):
811 """Destroys the given gadget handle on UTS"""
812 fRc = self.sendMsg("GDGTDTOR", (iGadgetId, zeroByteArray(12)));
813 if fRc is True:
814 fRc = self.recvAckLogged("GDGTDTOR");
815 return fRc;
816
817 def taskGadgetConnect(self, iGadgetId):
818 """Connects the given gadget handle on UTS"""
819 fRc = self.sendMsg("GDGTCNCT", (iGadgetId, zeroByteArray(12)));
820 if fRc is True:
821 fRc = self.recvAckLogged("GDGTCNCT");
822 return fRc;
823
824 def taskGadgetDisconnect(self, iGadgetId):
825 """Disconnects the given gadget handle from UTS"""
826 fRc = self.sendMsg("GDGTDCNT", (iGadgetId, zeroByteArray(12)));
827 if fRc is True:
828 fRc = self.recvAckLogged("GDGTDCNT");
829 return fRc;
830
831 #
832 # Public methods - generic task queries
833 #
834
835 def isSuccess(self):
836 """Returns True if the task completed successfully, otherwise False."""
837 self.lockTask();
838 sStatus = self.sStatus;
839 oTaskRc = self.oTaskRc;
840 self.unlockTask();
841 if sStatus != "":
842 return False;
843 if oTaskRc is False or oTaskRc is None:
844 return False;
845 return True;
846
847 def getResult(self):
848 """
849 Returns the result of a completed task.
850 Returns None if not completed yet or no previous task.
851 """
852 self.lockTask();
853 sStatus = self.sStatus;
854 oTaskRc = self.oTaskRc;
855 self.unlockTask();
856 if sStatus != "":
857 return None;
858 return oTaskRc;
859
860 def getLastReply(self):
861 """
862 Returns the last reply three-tuple: cbMsg, sOpcode, abPayload.
863 Returns a None, None, None three-tuple if there was no last reply.
864 """
865 self.lockTask();
866 t3oReply = self.t3oReply;
867 self.unlockTask();
868 return t3oReply;
869
870 #
871 # Public methods - connection.
872 #
873
874 def asyncDisconnect(self, cMsTimeout = 30000, fIgnoreErrors = False):
875 """
876 Initiates a disconnect task.
877
878 Returns True on success, False on failure (logged).
879
880 The task returns True on success and False on failure.
881 """
882 return self.startTask(cMsTimeout, fIgnoreErrors, "bye", self.taskBye);
883
884 def syncDisconnect(self, cMsTimeout = 30000, fIgnoreErrors = False):
885 """Synchronous version."""
886 return self.asyncToSync(self.asyncDisconnect, cMsTimeout, fIgnoreErrors);
887
888 #
889 # Public methods - gadget API
890 #
891
892 def asyncGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None, cMsTimeout = 30000, fIgnoreErrors = False):
893 """
894 Initiates a gadget create task.
895
896 Returns True on success, False on failure (logged).
897
898 The task returns True on success and False on failure.
899 """
900 return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetCreate", self.taskGadgetCreate, \
901 (iGadgetType, iGadgetAccess, lstCfg));
902
903 def syncGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None, cMsTimeout = 30000, fIgnoreErrors = False):
904 """Synchronous version."""
905 return self.asyncToSync(self.asyncGadgetCreate, iGadgetType, iGadgetAccess, lstCfg, cMsTimeout, fIgnoreErrors);
906
907 def asyncGadgetDestroy(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
908 """
909 Initiates a gadget destroy task.
910
911 Returns True on success, False on failure (logged).
912
913 The task returns True on success and False on failure.
914 """
915 return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetDestroy", self.taskGadgetDestroy, \
916 (iGadgetId, ));
917
918 def syncGadgetDestroy(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
919 """Synchronous version."""
920 return self.asyncToSync(self.asyncGadgetDestroy, iGadgetId, cMsTimeout, fIgnoreErrors);
921
922 def asyncGadgetConnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
923 """
924 Initiates a gadget connect task.
925
926 Returns True on success, False on failure (logged).
927
928 The task returns True on success and False on failure.
929 """
930 return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetConnect", self.taskGadgetConnect, \
931 (iGadgetId, ));
932
933 def syncGadgetConnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
934 """Synchronous version."""
935 return self.asyncToSync(self.asyncGadgetConnect, iGadgetId, cMsTimeout, fIgnoreErrors);
936
937 def asyncGadgetDisconnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
938 """
939 Initiates a gadget disconnect task.
940
941 Returns True on success, False on failure (logged).
942
943 The task returns True on success and False on failure.
944 """
945 return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetDisconnect", self.taskGadgetDisconnect, \
946 (iGadgetId, ));
947
948 def syncGadgetDisconnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
949 """Synchronous version."""
950 return self.asyncToSync(self.asyncGadgetDisconnect, iGadgetId, cMsTimeout, fIgnoreErrors);
951
952
953class TransportTcp(TransportBase):
954 """
955 TCP transport layer for the UTS client session class.
956 """
957
958 def __init__(self, sHostname, uPort):
959 """
960 Save the parameters. The session will call us back to make the
961 connection later on its worker thread.
962 """
963 TransportBase.__init__(self, utils.getCallerName());
964 self.sHostname = sHostname;
965 self.uPort = uPort if uPort is not None else 6042;
966 self.oSocket = None;
967 self.oWakeupW = None;
968 self.oWakeupR = None;
969 self.fConnectCanceled = False;
970 self.fIsConnecting = False;
971 self.oCv = threading.Condition();
972 self.abReadAhead = array.array('B');
973
974 def toString(self):
975 return '<%s sHostname=%s, uPort=%s, oSocket=%s,'\
976 ' fConnectCanceled=%s, fIsConnecting=%s, oCv=%s, abReadAhead=%s>' \
977 % (TransportBase.toString(self), self.sHostname, self.uPort, self.oSocket,
978 self.fConnectCanceled, self.fIsConnecting, self.oCv, self.abReadAhead);
979
980 def __isInProgressXcpt(self, oXcpt):
981 """ In progress exception? """
982 try:
983 if isinstance(oXcpt, socket.error):
984 try:
985 if oXcpt[0] == errno.EINPROGRESS:
986 return True;
987 except: pass;
988 try:
989 if oXcpt[0] == errno.EWOULDBLOCK:
990 return True;
991 if utils.getHostOs() == 'win' and oXcpt[0] == errno.WSAEWOULDBLOCK: # pylint: disable=no-member
992 return True;
993 except: pass;
994 except:
995 pass;
996 return False;
997
998 def __isWouldBlockXcpt(self, oXcpt):
999 """ Would block exception? """
1000 try:
1001 if isinstance(oXcpt, socket.error):
1002 try:
1003 if oXcpt[0] == errno.EWOULDBLOCK:
1004 return True;
1005 except: pass;
1006 try:
1007 if oXcpt[0] == errno.EAGAIN:
1008 return True;
1009 except: pass;
1010 except:
1011 pass;
1012 return False;
1013
1014 def __isConnectionReset(self, oXcpt):
1015 """ Connection reset by Peer or others. """
1016 try:
1017 if isinstance(oXcpt, socket.error):
1018 try:
1019 if oXcpt[0] == errno.ECONNRESET:
1020 return True;
1021 except: pass;
1022 try:
1023 if oXcpt[0] == errno.ENETRESET:
1024 return True;
1025 except: pass;
1026 except:
1027 pass;
1028 return False;
1029
1030 def _closeWakeupSockets(self):
1031 """ Closes the wakup sockets. Caller should own the CV. """
1032 oWakeupR = self.oWakeupR;
1033 self.oWakeupR = None;
1034 if oWakeupR is not None:
1035 oWakeupR.close();
1036
1037 oWakeupW = self.oWakeupW;
1038 self.oWakeupW = None;
1039 if oWakeupW is not None:
1040 oWakeupW.close();
1041
1042 return None;
1043
1044 def cancelConnect(self):
1045 # This is bad stuff.
1046 self.oCv.acquire();
1047 reporter.log2('TransportTcp::cancelConnect: fIsConnecting=%s oSocket=%s' % (self.fIsConnecting, self.oSocket));
1048 self.fConnectCanceled = True;
1049 if self.fIsConnecting:
1050 oSocket = self.oSocket;
1051 self.oSocket = None;
1052 if oSocket is not None:
1053 reporter.log2('TransportTcp::cancelConnect: closing the socket');
1054 oSocket.close();
1055
1056 oWakeupW = self.oWakeupW;
1057 self.oWakeupW = None;
1058 if oWakeupW is not None:
1059 reporter.log2('TransportTcp::cancelConnect: wakeup call');
1060 try: oWakeupW.send('cancelled!\n');
1061 except: reporter.logXcpt();
1062 try: oWakeupW.shutdown(socket.SHUT_WR);
1063 except: reporter.logXcpt();
1064 oWakeupW.close();
1065 self.oCv.release();
1066
1067 def _connectAsClient(self, oSocket, oWakeupR, cMsTimeout):
1068 """ Connects to the UTS server as client. """
1069
1070 # Connect w/ timeouts.
1071 rc = None;
1072 try:
1073 oSocket.connect((self.sHostname, self.uPort));
1074 rc = True;
1075 except socket.error as oXcpt:
1076 iRc = oXcpt.errno;
1077 if self.__isInProgressXcpt(oXcpt):
1078 # Do the actual waiting.
1079 reporter.log2('TransportTcp::connect: operation in progress (%s)...' % (oXcpt,));
1080 try:
1081 ttRc = select.select([oWakeupR], [oSocket], [oSocket, oWakeupR], cMsTimeout / 1000.0);
1082 if len(ttRc[1]) + len(ttRc[2]) == 0:
1083 raise socket.error(errno.ETIMEDOUT, 'select timed out');
1084 iRc = oSocket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR);
1085 rc = iRc == 0;
1086 except socket.error as oXcpt2:
1087 iRc = oXcpt2.errno;
1088 except:
1089 iRc = -42;
1090 reporter.fatalXcpt('socket.select() on connect failed');
1091
1092 if rc is True:
1093 pass;
1094 elif iRc in (errno.ECONNREFUSED, errno.EHOSTUNREACH, errno.EINTR, errno.ENETDOWN, errno.ENETUNREACH, 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=no-member
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;
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