1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 """
43 Contains the logic for communication with the device (a SONY PRS-500).
44
45 The public interface of class L{PRS500Device} defines the methods for performing various tasks.
46 """
47 import usb, sys, os, time
48 from array import array
49
50 from prstypes import *
51 from errors import *
52
53 MINIMUM_COL_WIDTH = 12
54 _packet_number = 0
55 KNOWN_USB_PROTOCOL_VERSIONS = [0x3030303030303130L]
56
58 """ Log C{packet} to stream C{stream}. Header should be a small word describing the type of packet. """
59 global _packet_number
60 _packet_number += 1
61 print >>stream, str(_packet_number), header, "Type:", packet.__class__.__name__
62 print >>stream, packet
63 print >>stream, "--"
64
66 """ Wrapper that allows easy access to all information about files/directories """
77
79 """ Return path to self """
80 return "File:"+self.path
81
84
85
87 """
88 Describes a USB device.
89
90 A description is composed of the Vendor Id, Product Id and Interface Id.
91 See the U{USB spec<http://www.usb.org/developers/docs/usb_20_05122006.zip>}
92 """
93
94 - def __init__(self, vendor_id, product_id, interface_id) :
95 self.vendor_id = vendor_id
96 self.product_id = product_id
97 self.interface_id = interface_id
98
100 """
101 Return the device corresponding to the device descriptor if it is
102 available on a USB bus. Otherwise, return None. Note that the
103 returned device has yet to be claimed or opened.
104 """
105 buses = usb.busses()
106 for bus in buses :
107 for device in bus.devices :
108 if device.idVendor == self.vendor_id :
109 if device.idProduct == self.product_id :
110 return device
111 return None
112
113
115
116 """
117 Contains the logic for performing various tasks on the reader.
118
119 The implemented tasks are:
120 0. Getting information about the device
121 1. Getting a file from the device
122 2. Listing of directories. See the C{list} method.
123 """
124
125 SONY_VENDOR_ID = 0x054c
126 PRS500_PRODUCT_ID = 0x029b
127 PRS500_INTERFACE_ID = 0
128 PRS500_BULK_IN_EP = 0x81
129 PRS500_BULK_OUT_EP = 0x02
130
132 """
133 Decorator that wraps a call to C{func} to ensure that exceptions are handled correctly.
134
135 As a convenience, C{safe} automatically sends the a L{USBConnect} after calling func, unless func has
136 a keyword argument named C{end_session} set to C{False}.
137
138 An L{ArgumentError} will cause the L{USBConnect} command to be sent to the device, unless end_session is set to C{False}.
139 An L{usb.USBError} will cause the library to release control of the USB interface via a call to L{close}.
140 """
141 def run_session(*args, **kwargs):
142 dev = args[0]
143 res = None
144 try:
145 res = func(*args, **kwargs)
146 except ArgumentError, e:
147 if not kwargs.has_key("end_session") or kwargs["end_session"]:
148 dev._send_validated_command(USBConnect())
149 raise e
150 except usb.USBError, e:
151 dev.close()
152 raise e
153 if not kwargs.has_key("end_session") or kwargs["end_session"]:
154 dev._send_validated_command(USBConnect())
155 return res
156
157 return run_session
158
159 - def __init__(self, log_packets=False) :
167
168 @classmethod
170 """ Raise a ProtocolError if the type and number of C{res} is not the same as C{type} and C{number}. """
171 if type != res.type or number != res.rnumber:
172 raise ProtocolError("Inavlid response.\ntype: expected="+hex(type)+" actual="+hex(res.type)+
173 "\nrnumber: expected="+hex(number)+" actual="+hex(res.rnumber))
174
204
205
207 """ Release device interface """
208 self.handle.releaseInterface()
209 self.handle, self.device = None, None
210
212 """
213 Send L{command<Command>} to device and return its L{response<Response>}.
214
215 @param command: an object of type Command or one of its derived classes
216 @param response_type: an object of type 'type'. The return packet from the device is returned as an object of type response_type.
217 @param timeout: the time to wait for a response from the device, in milliseconds. If there is no response, a L{usb.USBError} is raised.
218 """
219 if self._log_packets: _log_packet(command, "Command")
220 bytes_sent = self.handle.controlMsg(0x40, 0x80, command)
221 if bytes_sent != len(command):
222 raise ControlError(desc="Could not send control request to device\n" + str(query.query))
223 response = response_type(self.handle.controlMsg(0xc0, 0x81, Response.SIZE, timeout=timeout))
224 if self._log_packets: _log_packet(response, "Response")
225 return response
226
236
238 """
239 Send data to device via a bulk transfer.
240 @type data: Any listable type supporting __getslice__
241 @param packet_size: Size of packets to be sent to device. C{data} is broken up into packets to be sent to device.
242 """
243 def bulk_write_packet(packet):
244 self.handle.bulkWrite(PRS500Device.PRS500_BULK_OUT_EP, packet)
245 if self._log_packets: _log_packet(Answer(packet), "Answer h->d")
246
247 bytes_left = len(data)
248 if bytes_left + 16 <= packet_size:
249 packet_size = bytes_left +16
250 first_packet = Answer(bytes_left+16)
251 first_packet[16:] = data
252 first_packet.length = len(data)
253 else:
254 first_packet = Answer(packet_size)
255 first_packet[16:] = data[0:packet_size-16]
256 first_packet.length = packet_size-16
257 first_packet.number = 0x10005
258 bulk_write_packet(first_packet)
259 pos = first_packet.length
260 bytes_left -= first_packet.length
261 while bytes_left > 0:
262 endpos = pos + packet_size if pos + packet_size <= len(data) else len(data)
263 bulk_write_packet(data[pos:endpos])
264 bytes_left -= endpos - pos
265 pos = endpos
266 res = Response(self.handle.controlMsg(0xc0, 0x81, Response.SIZE, timeout=5000))
267 if self._log_packets: _log_packet(res, "Response")
268 if res.rnumber != 0x10005 or res.code != 0:
269 raise ProtocolError("Sending via Bulk Transfer failed with response:\n"+str(res))
270 if res.data_size != len(data):
271 raise ProtocolError("Unable to transfer all data to device. Response packet:\n"+str(res))
272
273
274 - def _bulk_read(self, bytes, command_number=0x00, packet_size=4096, data_type=Answer):
275 """
276 Read in C{bytes} bytes via a bulk transfer in packets of size S{<=} C{packet_size}
277 @param data_type: an object of type type. The data packet is returned as an object of type C{data_type}.
278 @return: A list of packets read from the device. Each packet is of type data_type
279 """
280 def bulk_read_packet(data_type=Answer, size=0x1000):
281 data = data_type(self.handle.bulkRead(PRS500Device.PRS500_BULK_IN_EP, size))
282 if self._log_packets: _log_packet(data, "Answer d->h")
283 return data
284
285 bytes_left = bytes
286 packets = []
287 while bytes_left > 0:
288 if packet_size > bytes_left: packet_size = bytes_left
289 packet = bulk_read_packet(data_type=data_type, size=packet_size)
290 bytes_left -= len(packet)
291 packets.append(packet)
292 self._send_validated_command(AcknowledgeBulkRead(packets[0].number), cnumber=command_number)
293 return packets
294
295 @safe
304
305 @safe
317
318 @safe
319 - def get_file(self, path, outfile, end_session=True):
320 """
321 Read the file at path on the device and write it to outfile. For the logic see L{_get_file}.
322
323 The data is fetched in chunks of size S{<=} 32K. Each chunk is make of packets of size S{<=} 4K. See L{FileOpen},
324 L{FileRead} and L{FileClose} for details on the command packets used.
325
326 @param outfile: file object like C{sys.stdout} or the result of an C{open} call
327 """
328 if path.endswith("/"): path = path[:-1]
329 file = self.path_properties(path, end_session=False)
330 if file.is_dir: raise PathError("Cannot read as " + path + " is a directory")
331 bytes = file.file_size
332 res = self._send_validated_command(FileOpen(path))
333 if res.code != 0:
334 raise PathError("Unable to open " + path + " for reading. Response code: " + hex(res.code))
335 id = self._bulk_read(20, data_type=IdAnswer, command_number=FileOpen.NUMBER)[0].id
336 bytes_left, chunk_size, pos = bytes, 0x8000, 0
337 while bytes_left > 0:
338 if chunk_size > bytes_left: chunk_size = bytes_left
339 res = self._send_validated_command(FileIO(id, pos, chunk_size))
340 if res.code != 0:
341 self._send_validated_command(FileClose(id))
342 raise ProtocolError("Error while reading from " + path + ". Response code: " + hex(res.code))
343 packets = self._bulk_read(chunk_size+16, command_number=FileIO.RNUMBER, packet_size=4096)
344 try:
345 array('B', packets[0][16:]).tofile(outfile)
346 for i in range(1, len(packets)):
347 array('B', packets[i]).tofile(outfile)
348 except IOError, e:
349 self._send_validated_command(FileClose(id))
350 raise ArgumentError("File get operation failed. Could not write to local location: " + str(e))
351 bytes_left -= chunk_size
352 pos += chunk_size
353 self._send_validated_command(FileClose(id))
354
355
356
357
358 @safe
359 - def list(self, path, recurse=False, end_session=True):
360 """
361 Return a listing of path. See the code for details. See L{DirOpen},
362 L{DirRead} and L{DirClose} for details on the command packets used.
363
364 @type path: string
365 @param path: The path to list
366 @type recurse: boolean
367 @param recurse: If true do a recursive listing
368 @return: A list of tuples. The first element of each tuple is a path. The second element is a list of L{Files<File>}.
369 The path is the path we are listing, the C{Files} are the files/directories in that path. If it is a recursive
370 list, then the first element will be (C{path}, children), the next will be (child, its children) and so on. If it
371 is not recursive the length of the outermost list will be 1.
372 """
373 def _list(path):
374 if not path.endswith("/"): path += "/"
375 files = []
376 candidate = self.path_properties(path, end_session=False)
377 if not candidate.is_dir:
378 path = path[:-1]
379 data = self.path_properties(path, end_session=False)
380 files = [ File((path, data)) ]
381 else:
382
383 res = self._send_validated_command(DirOpen(path))
384 if res.code != 0:
385 raise PathError("Unable to open directory " + path + " for reading. Response code: " + hex(res.code))
386 id = self._bulk_read(0x14, data_type=IdAnswer, command_number=DirOpen.NUMBER)[0].id
387
388 next = DirRead(id)
389 items = []
390 while True:
391 res = self._send_validated_command(next, response_type=ListResponse)
392 size = res.data_size + 16
393 data = self._bulk_read(size, data_type=ListAnswer, command_number=DirRead.NUMBER)[0]
394
395 if res.is_eol or res.path_not_found: break
396 elif res.code != 0:
397 raise ProtocolError("Unknown error occured while reading contents of directory " + path + ". Response code: " + haex(res.code))
398 items.append(data.name)
399 self._send_validated_command(DirClose(id))
400 for item in items:
401 ipath = path + item
402 data = self.path_properties(ipath, end_session=False)
403 files.append( File( (ipath, data) ) )
404 files.sort()
405 return files
406
407 files = _list(path)
408 dirs = [(path, files)]
409
410 for file in files:
411 if recurse and file.is_dir and not file.path.startswith(("/dev","/proc")):
412 dirs[len(dirs):] = self.list(file.path, recurse=True, end_session=False)
413 return dirs
414
415 @safe
432
434 """ Return (True, FileProperties) if path exists or (False, None) otherwise """
435 dest = None
436 try:
437 dest = self.path_properties(path, end_session=False)
438 except PathError, e:
439 if "does not exist" in str(e): return (False, None)
440 else: raise e
441 return (True, dest)
442
443 @safe
444 - def touch(self, path, end_session=True):
458
459
460
461
462
463
464
465 @safe
466 - def put_file(self, infile, path, end_session=True):
467 exists, dest = self._exists(path)
468 if exists:
469 if not dest.is_dir: raise PathError("Cannot write to " + path + " as it already exists")
470 if not path.endswith("/"): path += "/"
471 path += os.path.basename(infile.name)
472 exists, dest = self._exists(path)
473 if exists: raise PathError("Cannot write to " + path + " as it already exists")
474 res = self._send_validated_command(FileCreate(path))
475 if res.code != 0:
476 raise ProtocolError("There was an error creating device:"+path+". Response code: "+hex(res.code))
477 chunk_size = 0x8000
478 data_left = True
479 res = self._send_validated_command(FileOpen(path, mode=FileOpen.WRITE))
480 if res.code != 0:
481 raise ProtocolError("Unable to open " + path + " for writing. Response code: " + hex(res.code))
482 id = self._bulk_read(20, data_type=IdAnswer, command_number=FileOpen.NUMBER)[0].id
483 pos = 0
484 while data_left:
485 data = array('B')
486 try:
487 data.fromfile(infile, chunk_size)
488 except EOFError:
489 data_left = False
490 res = self._send_validated_command(FileIO(id, pos, len(data), mode=FileIO.WNUMBER))
491 if res.code != 0:
492 raise ProtocolError("Unable to write to " + path + ". Response code: " + hex(res.code))
493 self._bulk_write(data)
494 pos += len(data)
495 self._send_validated_command(FileClose(id))
496 file = self.path_properties(path, end_session=False)
497 if file.file_size != pos:
498 raise ProtocolError("Copying to device failed. The file was truncated by " + str(data.file_size - pos) + " bytes")
499
500 @safe
501 - def del_file(self, path, end_session=True):
507
508 @safe
509 - def mkdir(self, path, end_session=True):
521
522 @safe
523 - def rm(self, path, end_session=True):
535
536
537
538
539
540
541
542