VirtualBox

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

Last change on this file since 106743 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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