Package libprs500 :: Module prstypes
[hide private]
[frames] | no frames]

Source Code for Module libprs500.prstypes

  1  ##    Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net 
  2  ##    This program is free software; you can redistribute it and/or modify 
  3  ##    it under the terms of the GNU General Public License as published by 
  4  ##    the Free Software Foundation; either version 2 of the License, or 
  5  ##    (at your option) any later version. 
  6  ## 
  7  ##    This program is distributed in the hope that it will be useful, 
  8  ##    but WITHOUT ANY WARRANTY; without even the implied warranty of 
  9  ##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 10  ##    GNU General Public License for more details. 
 11  ## 
 12  ##    You should have received a copy of the GNU General Public License along 
 13  ##    with this program; if not, write to the Free Software Foundation, Inc., 
 14  ##    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 15   
 16  """ 
 17  Defines the structure of packets that are sent to/received from the device.  
 18   
 19  Packet structure is defined using classes and inheritance. Each class is a view that imposes 
 20  structure on the underlying data buffer. The data buffer is encoded in little-endian format, but you don't 
 21  have to worry about that if you are using the classes. The classes have instance variables with getter/setter functions defined 
 22  to take care of the encoding/decoding. The classes are intended to mimic C structs.  
 23   
 24  There are three kinds of packets. L{Commands<Command>}, L{Responses<Response>}, and L{Answers<Answer>}.  
 25  C{Commands} are sent to the device on the control bus, C{Responses} are received from the device,  
 26  also on the control bus. C{Answers} and their sub-classes represent data packets sent to/received from 
 27  the device via bulk transfers.  
 28   
 29  Commands are organized as follows: G{classtree Command} 
 30   
 31  You will typically only use sub-classes of Command.  
 32   
 33  Responses are organized as follows: G{classtree Response} 
 34   
 35  Responses inherit Command as they share header structure. 
 36   
 37  Answers are organized as follows: G{classtree Answer} 
 38  """ 
 39   
 40  import struct 
 41  from errors import PacketError 
 42   
 43  DWORD     = "<I"    #: Unsigned integer little endian encoded in 4 bytes 
 44  DDWORD    = "<Q"    #: Unsigned long long little endian encoded in 8 bytes 
 45   
 46   
47 -class PathResponseCodes(object):
48 """ Known response commands to path related commands """ 49 NOT_FOUND = 0xffffffd7 50 INVALID = 0xfffffff9 51 IS_FILE = 0xffffffd2 52 HAS_CHILDREN = 0xffffffcc
53 54
55 -class TransferBuffer(list):
56 57 """ 58 Represents raw (unstructured) data packets sent over the usb bus. 59 60 C{TransferBuffer} is a wrapper around the tuples used by L{PyUSB<usb>} for communication. 61 It has convenience methods to read and write data from the underlying buffer. See 62 L{TransferBuffer.pack} and L{TransferBuffer.unpack}. 63 """ 64
65 - def __init__(self, packet):
66 """ 67 Create a L{TransferBuffer} from C{packet} or an empty buffer. 68 69 @type packet: integer or listable object 70 @param packet: If packet is a list, it is copied into the C{TransferBuffer} and then normalized (see L{TransferBuffer._normalize}). 71 If it is an integer, a zero buffer of that length is created. 72 """ 73 if "__len__" in dir(packet): 74 list.__init__(self, list(packet)) 75 self._normalize() 76 else: list.__init__(self, [0 for i in range(packet)])
77
78 - def __add__(self, tb):
79 """ Return a TransferBuffer rather than a list as the sum """ 80 return TransferBuffer(list.__add__(self, tb))
81
82 - def __getslice__(self, start, end):
83 """ Return a TransferBuffer rather than a list as the slice """ 84 return TransferBuffer(list.__getslice__(self, start, end))
85
86 - def __str__(self):
87 """ 88 Return a string representation of this buffer. 89 90 Packets are represented as hex strings, in 2-byte pairs, S{<=} 16 bytes to a line. An ASCII representation is included. For example:: 91 0700 0100 0000 0000 0000 0000 0c00 0000 ................ 92 0200 0000 0400 0000 4461 7461 ........Data 93 """ 94 ans, ascii = ": ".rjust(10,"0"), "" 95 for i in range(0, len(self), 2): 96 for b in range(2): 97 try: 98 ans += TransferBuffer.phex(self[i+b]) 99 ascii += chr(self[i+b]) if self[i+b] > 31 and self[i+b] < 127 else "." 100 except IndexError: break 101 ans = ans + " " 102 if (i+2)%16 == 0: 103 if i+2 < len(self): 104 ans += " " + ascii + "\n" + (TransferBuffer.phex(i+2)+": ").rjust(10, "0") 105 ascii = "" 106 last_line = ans[ans.rfind("\n")+1:] 107 padding = 50 - len(last_line) 108 ans += "".ljust(padding) + " " + ascii 109 return ans.strip()
110
111 - def unpack(self, fmt=DWORD, start=0):
112 """ 113 Return decoded data from buffer. 114 115 @param fmt: See U{struct<http://docs.python.org/lib/module-struct.html>} 116 @param start: Position in buffer from which to decode 117 """ 118 end = start + struct.calcsize(fmt) 119 return struct.unpack(fmt, "".join([ chr(i) for i in list.__getslice__(self, start, end) ]))
120
121 - def pack(self, val, fmt=DWORD, start=0):
122 """ 123 Encode C{val} and write it to buffer. 124 125 @param fmt: See U{struct<http://docs.python.org/lib/module-struct.html>} 126 @param start: Position in buffer at which to write encoded data 127 """ 128 self[start:start+struct.calcsize(fmt)] = [ ord(i) for i in struct.pack(fmt, val) ]
129
130 - def _normalize(self):
131 """ Replace negative bytes in C{self} by 256 + byte """ 132 for i in range(len(self)): 133 if self[i] < 0: 134 self[i] = 256 + self[i]
135 136 @classmethod
137 - def phex(cls, num):
138 """ 139 Return the hex representation of num without the 0x prefix. 140 141 If the hex representation is only 1 digit it is padded to the left with a zero. Used in L{TransferBuffer.__str__}. 142 """ 143 index, sign = 2, "" 144 if num < 0: 145 index, sign = 3, "-" 146 h=hex(num)[index:] 147 if len(h) < 2: 148 h = "0"+h 149 return sign + h
150 151
152 -class field(object):
153 """ A U{Descriptor<http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html>}, that implements access 154 to protocol packets in a human readable way. 155 """
156 - def __init__(self, start=16, fmt=DWORD):
157 """ 158 @param start: The byte at which this field is stored in the buffer 159 @param fmt: The packing format for this field. See U{struct<http://docs.python.org/lib/module-struct.html>}. 160 """ 161 self._fmt, self._start = fmt, start
162
163 - def __get__(self, obj, typ=None):
164 return obj.unpack(start=self._start, fmt=self._fmt)[0]
165
166 - def __set__(self, obj, val):
167 obj.pack(val, start=self._start, fmt=self._fmt)
168
169 - def __repr__(self):
170 if self._fmt == DWORD: typ = "unsigned int" 171 if self._fmt == DDWORD: typ = "unsigned long long" 172 return "An " + typ + " stored in " + str(struct.calcsize(self._fmt)) + " bytes starting at byte " + str(self._start)
173
174 -class stringfield(object):
175 """ A field storing a variable length string. """
176 - def __init__(self, length_field, start=16):
177 """ 178 @param length_field: A U{Descriptor<http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html>} 179 that returns the length of the string. 180 @param start: The byte at which this field is stored in the buffer 181 """ 182 self._length_field = length_field 183 self._start = start
184
185 - def __get__(self, obj, typ=None):
186 length = str(self._length_field.__get__(obj)) 187 return obj.unpack(start=self._start, fmt="<"+length+"s")[0]
188
189 - def __set__(self, obj, val):
190 obj.pack(val, start=self._start, fmt="<"+str(len(val))+"s")
191
192 - def __repr__(self):
193 return "A string starting at byte " + str(self._start)
194
195 -class Command(TransferBuffer):
196 197 """ Defines the structure of command packets sent to the device. """ 198 199 number = field(start=0, fmt=DWORD) 200 """ 201 Command number. C{unsigned int} stored in 4 bytes at byte 0. 202 203 Command numbers are: 204 0 GetUsbProtocolVersion 205 1 ReqUsbConnect 206 207 10 FskFileOpen 208 11 FskFileClose 209 12 FskGetSize 210 13 FskSetSize 211 14 FskFileSetPosition 212 15 FskGetPosition 213 16 FskFileRead 214 17 FskFileWrite 215 18 FskFileGetFileInfo 216 19 FskFileSetFileInfo 217 1A FskFileCreate 218 1B FskFileDelete 219 1C FskFileRename 220 221 30 FskFileCreateDirectory 222 31 FskFileDeleteDirectory 223 32 FskFileRenameDirectory 224 33 FskDirectoryIteratorNew 225 34 FskDirectoryIteratorDispose 226 35 FskDirectoryIteratorGetNext 227 228 52 FskVolumeGetInfo 229 53 FskVolumeGetInfoFromPath 230 231 80 FskFileTerminate 232 233 100 ConnectDevice 234 101 GetProperty 235 102 GetMediaInfo 236 103 GetFreeSpace 237 104 SetTime 238 105 DeviceBeginEnd 239 106 UnlockDevice 240 107 SetBulkSize 241 242 110 GetHttpRequest 243 111 SetHttpRespponse 244 112 Needregistration 245 114 GetMarlinState 246 247 200 ReqDiwStart 248 201 SetDiwPersonalkey 249 202 GetDiwPersonalkey 250 203 SetDiwDhkey 251 204 GetDiwDhkey 252 205 SetDiwChallengeserver 253 206 GetDiwChallengeserver 254 207 GetDiwChallengeclient 255 208 SetDiwChallengeclient 256 209 GetDiwVersion 257 20A SetDiwWriteid 258 20B GetDiwWriteid 259 20C SetDiwSerial 260 20D GetDiwModel 261 20C SetDiwSerial 262 20E GetDiwDeviceid 263 20F GetDiwSerial 264 210 ReqDiwCheckservicedata 265 211 ReqDiwCheckiddata 266 212 ReqDiwCheckserialdata 267 213 ReqDiwFactoryinitialize 268 214 GetDiwMacaddress 269 215 ReqDiwTest 270 216 ReqDiwDeletekey 271 272 300 UpdateChangemode 273 301 UpdateDeletePartition 274 302 UpdateCreatePartition 275 303 UpdateCreatePartitionWithImage 276 304 UpdateGetPartitionSize 277 """ 278 279 type = field(start=4, fmt=DDWORD) #: Known types are 0x00 and 0x01. Acknowledge commands are always type 0x00 280 281 length = field(start=12, fmt=DWORD) #: Length of the data part of this packet 282 283 @apply
284 - def data():
285 doc =\ 286 """ 287 The data part of this command. Returned/set as/by a TransferBuffer. Stored at byte 16. 288 289 Setting it by default changes self.length to the length of the new buffer. You may have to reset it to 290 the significant part of the buffer. You would normally use the C{command} property of L{ShortCommand} or L{LongCommand} instead. 291 """ 292 def fget(self): 293 return self[16:]
294 295 def fset(self, buffer): 296 self[16:] = buffer 297 self.length = len(buffer)
298 299 return property(**locals()) 300
301 - def __init__(self, packet):
302 """ 303 @param packet: len(packet) > 15 or packet > 15 304 """ 305 if ("__len__" in dir(packet) and len(packet) < 16) or ("__len__" not in dir(packet) and packet < 16): 306 raise PacketError(str(self.__class__)[7:-2] + " packets must have length atleast 16") 307 TransferBuffer.__init__(self, packet)
308 309 310
311 -class ShortCommand(Command):
312 313 """ A L{Command} whoose data section is 4 bytes long """ 314 315 SIZE = 20 #: Packet size in bytes 316 command = field(start=16, fmt=DWORD) #: Usually carries additional information 317
318 - def __init__(self, number=0x00, type=0x00, command=0x00):
319 """ 320 @param number: L{Command.number} 321 @param type: L{Command.type} 322 @param command: L{ShortCommand.command} 323 """ 324 Command.__init__(self, ShortCommand.SIZE) 325 self.number = number 326 self.type = type 327 self.length = 4 328 self.command = command
329
330 -class DirRead(ShortCommand):
331 """ The command that asks the device to send the next item in the list """ 332 NUMBER = 0x35 #: Command number
333 - def __init__(self, id):
334 """ @param id: The identifier returned as a result of a L{DirOpen} command """ 335 ShortCommand.__init__(self, number=DirRead.NUMBER, type=0x01, command=id)
336
337 -class DirClose(ShortCommand):
338 """ Close a previously opened directory """ 339 NUMBER = 0x34 #: Command number
340 - def __init__(self, id):
341 """ @param id: The identifier returned as a result of a L{DirOpen} command """ 342 ShortCommand.__init__(self, number=DirClose.NUMBER, type=0x01, command=id)
343
344 -class USBConnect(ShortCommand):
345 """ Ask device to change status to 'USB connected' i.e., tell the device that the present sequence of commands is complete """ 346 NUMBER=0x1 #: Command number
347 - def __init__(self):
349
350 -class GetUSBProtocolVersion(ShortCommand):
351 """ Get USB Protocol version used by device """ 352 NUMBER=0x0 #: Command number
353 - def __init__(self):
355
356 -class SetBulkSize(ShortCommand):
357 NUMBER = 0x107 #: Command number
358 - def __init__(self, size=0x028000):
360
361 -class UnlockDevice(ShortCommand):
362 NUMBER = 0x106 #: Command number
363 - def __init__(self, key=0x312d):
365
366 -class LongCommand(Command):
367 368 """ A L{Command} whoose data section is 16 bytes long """ 369 370 SIZE = 32 #: Size in bytes of C{LongCommand} packets 371
372 - def __init__(self, number=0x00, type=0x00, command=0x00):
373 """ 374 @param number: L{Command.number} 375 @param type: L{Command.type} 376 @param command: L{LongCommand.command} 377 """ 378 Command.__init__(self, LongCommand.SIZE) 379 self.number = number 380 self.type = type 381 self.length = 16 382 self.command = command
383 384 @apply
385 - def command():
386 doc =\ 387 """ 388 Usually carries extra information needed for the command 389 It is a list of C{unsigned integers} of length between 1 and 4. 4 C{unsigned int} stored in 16 bytes at byte 16. 390 """ 391 def fget(self): 392 return self.unpack(start=16, fmt="<"+str(self.length/4)+"I")
393 394 def fset(self, val): 395 if "__len__" not in dir(val): val = (val,) 396 start = 16 397 for command in val: 398 self.pack(command, start=start, fmt=DWORD) 399 start += struct.calcsize(DWORD)
400 401 return property(**locals()) 402
403 -class PathCommand(Command):
404 """ Abstract class that defines structure common to all path related commands. """ 405 406 path_length = field(start=16, fmt=DWORD) #: Length of the path to follow 407 path = stringfield(path_length, start=20) #: The path this query is about
408 - def __init__(self, path, number, path_len_at_byte=16):
409 Command.__init__(self, path_len_at_byte+4+len(path)) 410 self.path_length = len(path) 411 self.path = path 412 self.type = 0x01 413 self.length = len(self)-16 414 self.number = number
415
416 -class FreeSpaceQuery(PathCommand):
417 """ Query the free space available """ 418 NUMBER = 0x53 #; Command number
419 - def __init__(self, path):
421
422 -class DirCreate(PathCommand):
423 """ Create a directory """ 424 NUMBER = 0x30
425 - def __init__(self, path):
427
428 -class DirOpen(PathCommand):
429 """ Open a directory for reading its contents """ 430 NUMBER = 0x33 #: Command number
431 - def __init__(self, path):
432 PathCommand.__init__(self, path, DirOpen.NUMBER) 433 434
435 -class AcknowledgeBulkRead(LongCommand):
436 """ Must be sent to device after a bulk read """
437 - def __init__(self, bulk_read_id):
438 """ bulk_read_id is an integer, the id of the bulk read we are acknowledging. See L{Answer.id} """ 439 LongCommand.__init__(self, number=0x1000, type=0x00, command=bulk_read_id)
440
441 -class DeviceInfoQuery(Command):
442 """ The command used to ask for device information """ 443 NUMBER=0x0101 #: Command number
444 - def __init__(self):
445 Command.__init__(self, 16) 446 self.number=DeviceInfoQuery.NUMBER 447 self.type=0x01
448
449 -class FileClose(ShortCommand):
450 """ File close command """ 451 NUMBER = 0x11 #: Command number
452 - def __init__(self, id):
454
455 -class FileCreate(PathCommand):
456 """ Create a file """ 457 NUMBER=0x1a #: Command number
458 - def __init__(self, path):
460
461 -class FileDelete(PathCommand):
462 """ Delete a file """ 463 NUMBER=0x1B
464 - def __init__(self, path):
466
467 -class DirDelete(PathCommand):
468 """ Delete a directory """ 469 NUMBER=0x31
470 - def __init__(self, path):
472
473 -class FileOpen(PathCommand):
474 """ File open command """ 475 NUMBER = 0x10 #: Command number 476 READ = 0x00 #: Open file in read mode 477 WRITE = 0x01 #: Open file in write mode 478 path_length = field(start=20, fmt=DWORD) 479 path = stringfield(path_length, start=24) 480
481 - def __init__(self, path, mode=0x00):
482 PathCommand.__init__(self, path, FileOpen.NUMBER, path_len_at_byte=20) 483 self.mode = mode
484 485 @apply
486 - def mode():
487 doc =\ 488 """ The file open mode. Is either L{FileOpen.READ} or L{FileOpen.WRITE}. C{unsigned int} stored at byte 16. """ 489 def fget(self): 490 return self.unpack(start=16, fmt=DWORD)[0]
491 492 def fset(self, val): 493 self.pack(val, start=16, fmt=DWORD)
494 495 return property(**locals()) 496 497
498 -class FileIO(Command):
499 """ Command to read/write from an open file """ 500 RNUMBER = 0x16 #: Command number to read from a file 501 WNUMBER = 0x17 #: Command number to write to a file 502 id = field(start=16, fmt=DWORD) #: The file ID returned by a FileOpen command 503 offset = field(start=20, fmt=DDWORD) #: offset in the file at which to read 504 size = field(start=28, fmt=DWORD) #: The number of bytes to reead from file.
505 - def __init__(self, id, offset, size, mode=0x16):
506 """ 507 @param id: File identifier returned by a L{FileOpen} command 508 @type id: C{unsigned int} 509 @param offset: Position in file at which to read 510 @type offset: C{unsigned long long} 511 @param size: number of bytes to read 512 @type size: C{unsigned int} 513 @param mode: Either L{FileIO.RNUMBER} or L{File.WNUMBER} 514 """ 515 Command.__init__(self, 32) 516 self.number=mode 517 self.type = 0x01 518 self.length = 16 519 self.id = id 520 self.offset = offset 521 self.size = size
522 523
524 -class PathQuery(PathCommand):
525 """ Defines structure of command that requests information about a path """ 526 NUMBER = 0x18 #: Command number
527 - def __init__(self, path):
528 PathCommand.__init__(self, path, PathQuery.NUMBER) 529
530 -class SetFileInfo(PathCommand):
531 """ Set File information """ 532 NUMBER = 0x19 #: Command number
533 - def __init__(self, path):
535
536 -class Response(Command):
537 """ 538 Defines the structure of response packets received from the device. 539 540 C{Response} inherits from C{Command} as the first 16 bytes have the same structure. 541 """ 542 543 SIZE = 32 #: Size of response packets in the SONY protocol 544 rnumber = field(start=16, fmt=DWORD) #: Response number, the command number of a command packet sent sometime before this packet was received 545 code = field(start=20, fmt=DWORD) #: Used to indicate error conditions. A value of 0 means there was no error 546 data_size = field(start=28, fmt=DWORD) #: Used to indicate the size of the next bulk read 547
548 - def __init__(self, packet):
549 """ C{len(packet) == Response.SIZE} """ 550 if len(packet) != Response.SIZE: 551 raise PacketError(str(self.__class__)[7:-2] + " packets must have exactly " + str(Response.SIZE) + " bytes not " + str(len(packet))) 552 Command.__init__(self, packet) 553 if self.number != 0x00001000: 554 raise PacketError("Response packets must have their number set to " + hex(0x00001000))
555 556 @apply
557 - def data():
558 doc =\ 559 """ The last 3 DWORDs (12 bytes) of data in this response packet. Returned as a list of unsigned integers. """ 560 def fget(self): 561 return self.unpack(start=20, fmt="<III")
562 563 def fset(self, val): 564 self.pack(val, start=20, fmt="<III")
565 566 return property(**locals()) 567
568 -class ListResponse(Response):
569 570 """ Defines the structure of response packets received during list (ll) queries. See L{PathQuery}. """ 571 572 IS_FILE = 0xffffffd2 #: Queried path is a file 573 IS_INVALID = 0xfffffff9 #: Queried path is malformed/invalid 574 IS_UNMOUNTED = 0xffffffc8 #: Queried path is not mounted (i.e. a removed storage card/stick) 575 IS_EOL = 0xfffffffa #: There are no more entries in the list 576 PATH_NOT_FOUND = 0xffffffd7 #: Queried path is not found 577 578 @apply
579 - def is_file():
580 """ True iff queried path is a file """ 581 def fget(self): 582 return self.code == ListResponse.IS_FILE
583 return property(**locals())
584 585 @apply
586 - def is_invalid():
587 """ True iff queried path is invalid """ 588 def fget(self): 589 return self.code == ListResponse.IS_INVALID
590 return property(**locals()) 591 592 @apply
593 - def path_not_found():
594 """ True iff queried path is not found """ 595 def fget(self): 596 return self.code == ListResponse.PATH_NOT_FOUND
597 return property(**locals()) 598 599 @apply
600 - def is_unmounted():
601 """ True iff queried path is unmounted (i.e. removed storage card) """ 602 def fget(self): 603 return self.code == ListResponse.IS_UNMOUNTED
604 return property(**locals()) 605 606 @apply
607 - def is_eol():
608 """ True iff there are no more items in the list """ 609 def fget(self): 610 return self.code == ListResponse.IS_EOL
611 return property(**locals()) 612
613 -class Answer(TransferBuffer):
614 """ Defines the structure of packets sent to host via a bulk transfer (i.e., bulk reads) """ 615 616 number = field(start=0, fmt=DWORD) #: Answer identifier 617 length = field(start=12, fmt=DWORD) #: Length of data to follow 618
619 - def __init__(self, packet):
620 """ @param packet: C{len(packet)} S{>=} C{16} """ 621 if "__len__" in dir(packet): 622 if len(packet) < 16 : 623 raise PacketError(str(self.__class__)[7:-2] + " packets must have a length of atleast 16 bytes") 624 elif packet < 16: 625 raise PacketError(str(self.__class__)[7:-2] + " packets must have a length of atleast 16 bytes") 626 TransferBuffer.__init__(self, packet)
627 628
629 -class FileProperties(Answer):
630 631 """ Defines the structure of packets that contain size, date and permissions information about files/directories. """ 632 633 file_size = field(start=16, fmt=DDWORD) #: Size in bytes of the file 634 file_type = field(start=24, fmt=DWORD) #: 1 == file, 2 == dir 635 ctime = field(start=28, fmt=DWORD) #: Creation time 636 wtime = field(start=32, fmt=DWORD) #: Modification time 637 permissions = field(start=36, fmt=DWORD) #: 0 = default permissions, 4 = read only 638 639 @apply
640 - def is_dir():
641 doc = """True if path points to a directory, False if it points to a file.""" 642 643 def fget(self): 644 return (self.file_type == 2)
645 646 def fset(self, val): 647 if val: val = 2 648 else: val = 1 649 self.file_type = val
650 651 return property(**locals()) 652 653 654 @apply
655 - def is_readonly():
656 doc = """ Whether this file is readonly.""" 657 658 def fget(self): 659 return self.unpack(start=36, fmt=DWORD)[0] != 0
660 661 def fset(self, val): 662 if val: val = 4 663 else: val = 0 664 self.pack(val, start=36, fmt=DWORD) 665 666 return property(**locals()) 667 668
669 -class USBProtocolVersion(Answer):
670 version = field(start=16, fmt=DDWORD)
671
672 -class IdAnswer(Answer):
673 674 """ Defines the structure of packets that contain identifiers for queries. """ 675 676 @apply
677 - def id():
678 doc =\ 679 """ The identifier. C{unsigned int} stored in 4 bytes at byte 16. Should be sent in commands asking for the next item in the list. """ 680 681 def fget(self): 682 return self.unpack(start=16, fmt=DWORD)[0]
683 684 def fset(self, val): 685 self.pack(val, start=16, fmt=DWORD)
686 687 return property(**locals()) 688
689 -class DeviceInfo(Answer):
690 """ Defines the structure of the packet containing information about the device """ 691 device_name = field(start=16, fmt="<32s") 692 device_version = field(start=48, fmt="<32s") 693 software_version = field(start=80, fmt="<24s") 694 mime_type = field(start=104, fmt="<32s")
695 696
697 -class FreeSpaceAnswer(Answer):
698 total = field(start=24, fmt=DDWORD) 699 free_space = field(start=32, fmt=DDWORD)
700 701
702 -class ListAnswer(Answer):
703 """ Defines the structure of packets that contain items in a list. """ 704 name_length = field(start=20, fmt=DWORD) 705 name = stringfield(name_length, start=24) 706 707 @apply
708 - def is_dir():
709 doc =\ 710 """ True if list item points to a directory, False if it points to a file. C{unsigned int} stored in 4 bytes at byte 16. """ 711 712 def fget(self): 713 return (self.unpack(start=16, fmt=DWORD)[0] == 2)
714 715 def fset(self, val): 716 if val: val = 2 717 else: val = 1 718 self.pack(val, start=16, fmt=DWORD) 719 720 return property(**locals()) 721