VirtualBox

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

Last change on this file since 78403 was 76553, checked in by vboxsync, 6 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.6 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: usbgadget.py 76553 2019-01-01 01:45:53Z vboxsync $
3# pylint: disable=C0302
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: 76553 $"
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 try:
985 if oXcpt[0] == errno.EWOULDBLOCK:
986 return True;
987 if utils.getHostOs == 'win' and oXcpt[0] == errno.WSAEWOULDBLOCK: # pylint: disable=E1101
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 == errno.ECONNREFUSED \
1091 or iRc == errno.EHOSTUNREACH \
1092 or iRc == errno.EINTR \
1093 or iRc == errno.ENETDOWN \
1094 or iRc == errno.ENETUNREACH \
1095 or iRc == errno.ETIMEDOUT:
1096 rc = False; # try again.
1097 else:
1098 if iRc != errno.EBADF or not self.fConnectCanceled:
1099 reporter.fatalXcpt('socket.connect((%s,%s)) failed; iRc=%s' % (self.sHostname, self.uPort, iRc));
1100 reporter.log2('TransportTcp::connect: rc=%s iRc=%s' % (rc, iRc));
1101 except:
1102 reporter.fatalXcpt('socket.connect((%s,%s)) failed' % (self.sHostname, self.uPort));
1103 return rc;
1104
1105
1106 def connect(self, cMsTimeout):
1107 # Create a non-blocking socket.
1108 reporter.log2('TransportTcp::connect: cMsTimeout=%s sHostname=%s uPort=%s' % (cMsTimeout, self.sHostname, self.uPort));
1109 try:
1110 oSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);
1111 except:
1112 reporter.fatalXcpt('socket.socket() failed');
1113 return None;
1114 try:
1115 oSocket.setblocking(0);
1116 except:
1117 oSocket.close();
1118 reporter.fatalXcpt('socket.socket() failed');
1119 return None;
1120
1121 # Create wakeup socket pair for unix (select doesn't wake up on socket close on Linux).
1122 oWakeupR = None;
1123 oWakeupW = None;
1124 if hasattr(socket, 'socketpair'):
1125 try: (oWakeupR, oWakeupW) = socket.socketpair(); # pylint: disable=E1101
1126 except: reporter.logXcpt('socket.socketpair() failed');
1127
1128 # Update the state.
1129 self.oCv.acquire();
1130 rc = None;
1131 if not self.fConnectCanceled:
1132 self.oSocket = oSocket;
1133 self.oWakeupW = oWakeupW;
1134 self.oWakeupR = oWakeupR;
1135 self.fIsConnecting = True;
1136 self.oCv.release();
1137
1138 # Try connect.
1139 if oWakeupR is None:
1140 oWakeupR = oSocket; # Avoid select failure.
1141 rc = self._connectAsClient(oSocket, oWakeupR, cMsTimeout);
1142 oSocket = None;
1143
1144 # Update the state and cleanup on failure/cancel.
1145 self.oCv.acquire();
1146 if rc is True and self.fConnectCanceled:
1147 rc = False;
1148 self.fIsConnecting = False;
1149
1150 if rc is not True:
1151 if self.oSocket is not None:
1152 self.oSocket.close();
1153 self.oSocket = None;
1154 self._closeWakeupSockets();
1155 self.oCv.release();
1156
1157 reporter.log2('TransportTcp::connect: returning %s' % (rc,));
1158 return rc;
1159
1160 def disconnect(self, fQuiet = False):
1161 if self.oSocket is not None:
1162 self.abReadAhead = array.array('B');
1163
1164 # Try a shutting down the socket gracefully (draining it).
1165 try:
1166 self.oSocket.shutdown(socket.SHUT_WR);
1167 except:
1168 if not fQuiet:
1169 reporter.error('shutdown(SHUT_WR)');
1170 try:
1171 self.oSocket.setblocking(0); # just in case it's not set.
1172 sData = "1";
1173 while sData:
1174 sData = self.oSocket.recv(16384);
1175 except:
1176 pass;
1177
1178 # Close it.
1179 self.oCv.acquire();
1180 try: self.oSocket.setblocking(1);
1181 except: pass;
1182 self.oSocket.close();
1183 self.oSocket = None;
1184 else:
1185 self.oCv.acquire();
1186 self._closeWakeupSockets();
1187 self.oCv.release();
1188
1189 def sendBytes(self, abBuf, cMsTimeout):
1190 if self.oSocket is None:
1191 reporter.error('TransportTcp.sendBytes: No connection.');
1192 return False;
1193
1194 # Try send it all.
1195 try:
1196 cbSent = self.oSocket.send(abBuf);
1197 if cbSent == len(abBuf):
1198 return True;
1199 except Exception as oXcpt:
1200 if not self.__isWouldBlockXcpt(oXcpt):
1201 reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
1202 return False;
1203 cbSent = 0;
1204
1205 # Do a timed send.
1206 msStart = base.timestampMilli();
1207 while True:
1208 cMsElapsed = base.timestampMilli() - msStart;
1209 if cMsElapsed > cMsTimeout:
1210 reporter.error('TranportTcp.sendBytes: %s bytes timed out (1)' % (len(abBuf)));
1211 break;
1212
1213 # wait.
1214 try:
1215 ttRc = select.select([], [self.oSocket], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
1216 if ttRc[2] and not ttRc[1]:
1217 reporter.error('TranportTcp.sendBytes: select returned with exception');
1218 break;
1219 if not ttRc[1]:
1220 reporter.error('TranportTcp.sendBytes: %s bytes timed out (2)' % (len(abBuf)));
1221 break;
1222 except:
1223 reporter.errorXcpt('TranportTcp.sendBytes: select failed');
1224 break;
1225
1226 # Try send more.
1227 try:
1228 cbSent += self.oSocket.send(abBuf[cbSent:]);
1229 if cbSent == len(abBuf):
1230 return True;
1231 except Exception as oXcpt:
1232 if not self.__isWouldBlockXcpt(oXcpt):
1233 reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
1234 break;
1235
1236 return False;
1237
1238 def __returnReadAheadBytes(self, cb):
1239 """ Internal worker for recvBytes. """
1240 assert(len(self.abReadAhead) >= cb);
1241 abRet = self.abReadAhead[:cb];
1242 self.abReadAhead = self.abReadAhead[cb:];
1243 return abRet;
1244
1245 def recvBytes(self, cb, cMsTimeout, fNoDataOk):
1246 if self.oSocket is None:
1247 reporter.error('TransportTcp.recvBytes(%s,%s): No connection.' % (cb, cMsTimeout));
1248 return None;
1249
1250 # Try read in some more data without bothering with timeout handling first.
1251 if len(self.abReadAhead) < cb:
1252 try:
1253 abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
1254 if abBuf:
1255 self.abReadAhead.extend(array.array('B', abBuf));
1256 except Exception as oXcpt:
1257 if not self.__isWouldBlockXcpt(oXcpt):
1258 reporter.errorXcpt('TranportTcp.recvBytes: 0/%s bytes' % (cb,));
1259 return None;
1260
1261 if len(self.abReadAhead) >= cb:
1262 return self.__returnReadAheadBytes(cb);
1263
1264 # Timeout loop.
1265 msStart = base.timestampMilli();
1266 while True:
1267 cMsElapsed = base.timestampMilli() - msStart;
1268 if cMsElapsed > cMsTimeout:
1269 if not fNoDataOk or self.abReadAhead:
1270 reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (1)' % (len(self.abReadAhead), cb));
1271 break;
1272
1273 # Wait.
1274 try:
1275 ttRc = select.select([self.oSocket], [], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
1276 if ttRc[2] and not ttRc[0]:
1277 reporter.error('TranportTcp.recvBytes: select returned with exception');
1278 break;
1279 if not ttRc[0]:
1280 if not fNoDataOk or self.abReadAhead:
1281 reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (2) fNoDataOk=%s'
1282 % (len(self.abReadAhead), cb, fNoDataOk));
1283 break;
1284 except:
1285 reporter.errorXcpt('TranportTcp.recvBytes: select failed');
1286 break;
1287
1288 # Try read more.
1289 try:
1290 abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
1291 if not abBuf:
1292 reporter.error('TranportTcp.recvBytes: %s/%s bytes (%s) - connection has been shut down'
1293 % (len(self.abReadAhead), cb, fNoDataOk));
1294 self.disconnect();
1295 return None;
1296
1297 self.abReadAhead.extend(array.array('B', abBuf));
1298
1299 except Exception as oXcpt:
1300 reporter.log('recv => exception %s' % (oXcpt,));
1301 if not self.__isWouldBlockXcpt(oXcpt):
1302 if not fNoDataOk or not self.__isConnectionReset(oXcpt) or self.abReadAhead:
1303 reporter.errorXcpt('TranportTcp.recvBytes: %s/%s bytes (%s)' % (len(self.abReadAhead), cb, fNoDataOk));
1304 break;
1305
1306 # Done?
1307 if len(self.abReadAhead) >= cb:
1308 return self.__returnReadAheadBytes(cb);
1309
1310 #reporter.log('recv => None len(self.abReadAhead) -> %d' % (len(self.abReadAhead), ));
1311 return None;
1312
1313 def isConnectionOk(self):
1314 if self.oSocket is None:
1315 return False;
1316 try:
1317 ttRc = select.select([], [], [self.oSocket], 0.0);
1318 if ttRc[2]:
1319 return False;
1320
1321 self.oSocket.send(array.array('B')); # send zero bytes.
1322 except:
1323 return False;
1324 return True;
1325
1326 def isRecvPending(self, cMsTimeout = 0):
1327 try:
1328 ttRc = select.select([self.oSocket], [], [], cMsTimeout / 1000.0);
1329 if not ttRc[0]:
1330 return False;
1331 except:
1332 pass;
1333 return True;
1334
1335
1336class UsbGadget(object):
1337 """
1338 USB Gadget control class using the USBT Test Service to talk to the external
1339 board behaving like a USB device.
1340 """
1341
1342 def __init__(self):
1343 self.oUtsSession = None;
1344 self.sImpersonation = g_ksGadgetImpersonationInvalid;
1345 self.idGadget = None;
1346 self.iBusId = None;
1347 self.iDevId = None;
1348 self.iUsbIpPort = None;
1349
1350 def clearImpersonation(self):
1351 """
1352 Removes the current impersonation of the gadget.
1353 """
1354 fRc = True;
1355
1356 if self.idGadget is not None:
1357 fRc = self.oUtsSession.syncGadgetDestroy(self.idGadget);
1358 self.idGadget = None;
1359 self.iBusId = None;
1360 self.iDevId = None;
1361
1362 return fRc;
1363
1364 def disconnectUsb(self):
1365 """
1366 Disconnects the USB gadget from the host. (USB connection not network
1367 connection used for control)
1368 """
1369 return self.oUtsSession.syncGadgetDisconnect(self.idGadget);
1370
1371 def connectUsb(self):
1372 """
1373 Connect the USB gadget to the host.
1374 """
1375 return self.oUtsSession.syncGadgetConnect(self.idGadget);
1376
1377 def impersonate(self, sImpersonation, fSuperSpeed = False):
1378 """
1379 Impersonate a given device.
1380 """
1381
1382 # Clear any previous impersonation
1383 self.clearImpersonation();
1384 self.sImpersonation = sImpersonation;
1385
1386 fRc = False;
1387 if sImpersonation == g_ksGadgetImpersonationTest:
1388 lstCfg = [];
1389 if fSuperSpeed is True:
1390 lstCfg.append( ('Gadget/SuperSpeed', g_kiGadgetCfgTypeBool, 'true') );
1391 fDone = self.oUtsSession.syncGadgetCreate(g_kiGadgetTypeTest, g_kiGadgetAccessUsbIp, lstCfg);
1392 if fDone is True and self.oUtsSession.isSuccess():
1393 # Get the gadget ID.
1394 _, _, abPayload = self.oUtsSession.getLastReply();
1395
1396 fRc = True;
1397 self.idGadget = getU32(abPayload, 16);
1398 self.iBusId = getU32(abPayload, 20);
1399 self.iDevId = getU32(abPayload, 24);
1400 else:
1401 reporter.log('Invalid or unsupported impersonation');
1402
1403 return fRc;
1404
1405 def getUsbIpPort(self):
1406 """
1407 Returns the port the USB/IP server is listening on if requested,
1408 None if USB/IP is not supported.
1409 """
1410 return self.iUsbIpPort;
1411
1412 def getGadgetBusAndDevId(self):
1413 """
1414 Returns the bus ad device ID of the gadget as a tuple.
1415 """
1416 return (self.iBusId, self.iDevId);
1417
1418 def connectTo(self, cMsTimeout, sHostname, uPort = None, fUsbIpSupport = True, cMsIdleFudge = 0, fTryConnect = False):
1419 """
1420 Connects to the specified target device.
1421 Returns True on Success.
1422 Returns False otherwise.
1423 """
1424 fRc = True;
1425
1426 # @todo
1427 if fUsbIpSupport is False:
1428 return False;
1429
1430 reporter.log2('openTcpSession(%s, %s, %s, %s)' % \
1431 (cMsTimeout, sHostname, uPort, cMsIdleFudge));
1432 try:
1433 oTransport = TransportTcp(sHostname, uPort);
1434 self.oUtsSession = Session(oTransport, cMsTimeout, cMsIdleFudge, fTryConnect);
1435
1436 if self.oUtsSession is not None:
1437 fDone = self.oUtsSession.waitForTask(30*1000);
1438 reporter.log('connect: waitForTask -> %s, result %s' % (fDone, self.oUtsSession.getResult()));
1439 if fDone is True and self.oUtsSession.isSuccess():
1440 # Parse the reply.
1441 _, _, abPayload = self.oUtsSession.getLastReply();
1442
1443 if getU32(abPayload, 20) is g_kiGadgetAccessUsbIp:
1444 fRc = True;
1445 self.iUsbIpPort = getU32(abPayload, 24);
1446 else:
1447 reporter.log('Gadget doesn\'t support access over USB/IP despite being requested');
1448 fRc = False;
1449 else:
1450 fRc = False;
1451 else:
1452 fRc = False;
1453 except:
1454 reporter.errorXcpt(None, 15);
1455 return False;
1456
1457 return fRc;
1458
1459 def disconnectFrom(self):
1460 """
1461 Disconnects from the target device.
1462 """
1463 fRc = True;
1464
1465 self.clearImpersonation();
1466 if self.oUtsSession is not None:
1467 fRc = self.oUtsSession.syncDisconnect();
1468
1469 return fRc;
1470
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