mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
new project started (working ls implementation)
This commit is contained in:
commit
baa766ae3c
16
README
Normal file
16
README
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Library implementing a reverse engineered protocol to communicate with the Sony Reader PRS-500.
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
1) Python >= 2.5
|
||||||
|
2) PyUSB >= 0.3.4 (http://sourceforge.net/projects/pyusb/)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
At the moment all that it can do is a simple ls command. Add the following to /etc/udev/rules.d/90-local.rules
|
||||||
|
BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="plugdev"
|
||||||
|
and run udevstart to enable access to the reader for non-root users. You may have to adjust the GROUP and the location of the
|
||||||
|
rules file to suit your distribution.
|
||||||
|
|
||||||
|
To see the listing
|
||||||
|
./communicate.py /path/to/see
|
||||||
|
|
||||||
|
If the path does not exist, it will throw an Exception.
|
207
communicate.py
Executable file
207
communicate.py
Executable file
@ -0,0 +1,207 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License along
|
||||||
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
import sys, usb
|
||||||
|
from data import *
|
||||||
|
from types import *
|
||||||
|
from exceptions import Exception
|
||||||
|
|
||||||
|
### End point description for PRS-500 procductId=667
|
||||||
|
### Endpoint Descriptor:
|
||||||
|
### bLength 7
|
||||||
|
### bDescriptorType 5
|
||||||
|
### bEndpointAddress 0x81 EP 1 IN
|
||||||
|
### bmAttributes 2
|
||||||
|
### Transfer Type Bulk
|
||||||
|
### Synch Type None
|
||||||
|
### Usage Type Data
|
||||||
|
### wMaxPacketSize 0x0040 1x 64 bytes
|
||||||
|
### bInterval 0
|
||||||
|
### Endpoint Descriptor:
|
||||||
|
### bLength 7
|
||||||
|
### bDescriptorType 5
|
||||||
|
### bEndpointAddress 0x02 EP 2 OUT
|
||||||
|
### bmAttributes 2
|
||||||
|
### Transfer Type Bulk
|
||||||
|
### Synch Type None
|
||||||
|
### Usage Type Data
|
||||||
|
### wMaxPacketSize 0x0040 1x 64 bytes
|
||||||
|
### bInterval 0
|
||||||
|
###
|
||||||
|
###
|
||||||
|
### Endpoint 0x81 is device->host and endpoint 0x02 is host->device. You can establish Stream pipes to/from these endpoints for Bulk transfers.
|
||||||
|
### Has two configurations 1 is the USB charging config 2 is the self-powered config.
|
||||||
|
### I think config management is automatic. Endpoints are the same
|
||||||
|
|
||||||
|
class PathError(Exception):
|
||||||
|
def __init__(self, msg):
|
||||||
|
Exception.__init__(self, msg)
|
||||||
|
|
||||||
|
class ControlError(Exception):
|
||||||
|
def __init__(self, query=None, response=None, desc=None):
|
||||||
|
self.query = query
|
||||||
|
self.response = response
|
||||||
|
Exception.__init__(self, desc)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.query and self.response:
|
||||||
|
return "Got unexpected response:\n" + \
|
||||||
|
"query:\n"+str(self.query.query)+"\n"+\
|
||||||
|
"expected:\n"+str(self.query.response)+"\n" +\
|
||||||
|
"actual:\n"+str(self.response)
|
||||||
|
if self.desc:
|
||||||
|
return self.desc
|
||||||
|
return "Unknown control error occurred"
|
||||||
|
|
||||||
|
class DeviceDescriptor:
|
||||||
|
def __init__(self, vendor_id, product_id, interface_id) :
|
||||||
|
self.vendor_id = vendor_id
|
||||||
|
self.product_id = product_id
|
||||||
|
self.interface_id = interface_id
|
||||||
|
|
||||||
|
def getDevice(self) :
|
||||||
|
"""
|
||||||
|
Return the device corresponding to the device descriptor if it is
|
||||||
|
available on a USB bus. Otherwise, return None. Note that the
|
||||||
|
returned device has yet to be claimed or opened.
|
||||||
|
"""
|
||||||
|
buses = usb.busses()
|
||||||
|
for bus in buses :
|
||||||
|
for device in bus.devices :
|
||||||
|
if device.idVendor == self.vendor_id :
|
||||||
|
if device.idProduct == self.product_id :
|
||||||
|
return device
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class PRS500Device:
|
||||||
|
SONY_VENDOR_ID = 0x054c
|
||||||
|
PRS500_PRODUCT_ID = 0x029b
|
||||||
|
PRS500_INTERFACE_ID = 0
|
||||||
|
PRS500_BULK_IN_EP = 0x81
|
||||||
|
PRS500_BULK_OUT_EP = 0x02
|
||||||
|
|
||||||
|
def __init__(self) :
|
||||||
|
self.device_descriptor = DeviceDescriptor(PRS500Device.SONY_VENDOR_ID,
|
||||||
|
PRS500Device.PRS500_PRODUCT_ID,
|
||||||
|
PRS500Device.PRS500_INTERFACE_ID)
|
||||||
|
self.device = self.device_descriptor.getDevice()
|
||||||
|
self.handle = None
|
||||||
|
|
||||||
|
def open(self) :
|
||||||
|
self.device = self.device_descriptor.getDevice()
|
||||||
|
if not self.device:
|
||||||
|
print >> sys.stderr, "Unable to find Sony Reader. Is it connected?"
|
||||||
|
sys.exit(1)
|
||||||
|
self.handle = self.device.open()
|
||||||
|
if sys.platform == 'darwin' :
|
||||||
|
# XXX : For some reason, Mac OS X doesn't set the
|
||||||
|
# configuration automatically like Linux does.
|
||||||
|
self.handle.setConfiguration(1)
|
||||||
|
self.handle.claimInterface(self.device_descriptor.interface_id)
|
||||||
|
self.handle.reset()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.handle.releaseInterface()
|
||||||
|
self.handle, self.device = None, None
|
||||||
|
|
||||||
|
def send_sony_control_query(self, query, timeout=100):
|
||||||
|
r = self.handle.controlMsg(0x40, 0x80, query.query)
|
||||||
|
if r != len(query.query):
|
||||||
|
raise ControlError(desc="Could not send control request to device\n" + str(query.query))
|
||||||
|
res = normalize_buffer(self.handle.controlMsg(0xc0, 0x81, len(query.response), timeout=timeout))
|
||||||
|
if res != query.response:
|
||||||
|
raise ControlError(query=query, response=res)
|
||||||
|
|
||||||
|
def bulkRead(self, size):
|
||||||
|
return TransferBuffer(self.handle.bulkRead(PRS500Device.PRS500_BULK_IN_EP, size))
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self.handle.reset()
|
||||||
|
for query in initialization:
|
||||||
|
self.send_sony_control_query(query)
|
||||||
|
if query.bulkTransfer and "__len__" not in dir(query.bulkTransfer):
|
||||||
|
self.bulkRead(query.bulkTransfer)
|
||||||
|
|
||||||
|
def ls(self, path):
|
||||||
|
"""
|
||||||
|
ls path
|
||||||
|
|
||||||
|
Packet scheme: query, bulk read, acknowledge; repeat
|
||||||
|
Errors, EOF conditions are indicated in the reply to query. They also show up in the reply to acknowledge
|
||||||
|
I haven't figured out what the first bulk read is for
|
||||||
|
"""
|
||||||
|
if path[len(path)-1] != "/": path = path + "/"
|
||||||
|
self.initialize()
|
||||||
|
q1 = LSQuery(path, type=1)
|
||||||
|
files, res1, res2, error_type = [], None, None, 0
|
||||||
|
try:
|
||||||
|
self.send_sony_control_query(q1)
|
||||||
|
except ControlError, e:
|
||||||
|
if e.response == LSQuery.PATH_NOT_FOUND_RESPONSE:
|
||||||
|
error_type = 1
|
||||||
|
raise PathError(path[:-1] + " does not exist")
|
||||||
|
elif e.response == LSQuery.IS_FILE_RESPONSE: error_type = 2
|
||||||
|
elif e.response == LSQuery.NOT_MOUNTED_RESPONSE:
|
||||||
|
error_type = 3
|
||||||
|
raise PathError(path + " is not mounted")
|
||||||
|
elif e.response == LSQuery.INVALID_PATH_RESPONSE:
|
||||||
|
error_type = 4
|
||||||
|
raise PathError(path + " is an invalid path")
|
||||||
|
else: raise e
|
||||||
|
finally:
|
||||||
|
res1 = normalize_buffer(self.bulkRead(q1.bulkTransfer))
|
||||||
|
self.send_sony_control_query(q1.acknowledge_query(1, error_type=error_type))
|
||||||
|
|
||||||
|
if error_type == 2: # If path points to a file
|
||||||
|
files.append(path[:-1])
|
||||||
|
else:
|
||||||
|
q2 = LSQuery(path, type=2)
|
||||||
|
try:
|
||||||
|
self.send_sony_control_query(q2)
|
||||||
|
finally:
|
||||||
|
res2 = normalize_buffer(self.bulkRead(q2.bulkTransfer))
|
||||||
|
self.send_sony_control_query(q1.acknowledge_query(2))
|
||||||
|
|
||||||
|
send_name = q2.send_name_query(res2)
|
||||||
|
buffer_length = 0
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
self.send_sony_control_query(send_name)
|
||||||
|
except ControlError, e:
|
||||||
|
buffer_length = 16 + e.response[28] + e.response[29] + e.response[30] + e.response[31]
|
||||||
|
res = self.bulkRead(buffer_length)
|
||||||
|
if e.response == LSQuery.EOL_RESPONSE:
|
||||||
|
self.send_sony_control_query(q2.acknowledge_query(0))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.send_sony_control_query(q2.acknowledge_query(3))
|
||||||
|
files.append("".join([chr(i) for i in list(res)[23:]]))
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
def main(path):
|
||||||
|
dev = PRS500Device()
|
||||||
|
dev.open()
|
||||||
|
try:
|
||||||
|
print " ".join(dev.ls(path))
|
||||||
|
except PathError, e:
|
||||||
|
print >> sys.stderr, e
|
||||||
|
finally:
|
||||||
|
dev.close()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(sys.argv[1])
|
163
data.py
Executable file
163
data.py
Executable file
@ -0,0 +1,163 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License along
|
||||||
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
import sys, re
|
||||||
|
from prstypes import *
|
||||||
|
|
||||||
|
# The sequence of control commands to send the device before attempting any operations. Should be preceeded by a reset?
|
||||||
|
initialization = []
|
||||||
|
initialization.append(\
|
||||||
|
ControlQuery(TransferBuffer((0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0)),\
|
||||||
|
TransferBuffer((0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), bulkTransfer=24))
|
||||||
|
initialization.append(\
|
||||||
|
ControlQuery(TransferBuffer((0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 5, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), \
|
||||||
|
TransferBuffer((0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))))
|
||||||
|
initialization.append(\
|
||||||
|
ControlQuery(TransferBuffer((7, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 128, 2, 0)), \
|
||||||
|
TransferBuffer((0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 7, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))))
|
||||||
|
initialization.append(\
|
||||||
|
ControlQuery(TransferBuffer((0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0)), \
|
||||||
|
TransferBuffer((0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), bulkTransfer=24))
|
||||||
|
initialization.append(\
|
||||||
|
ControlQuery(TransferBuffer((0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 5, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), \
|
||||||
|
TransferBuffer((0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))))
|
||||||
|
initialization.append(\
|
||||||
|
ControlQuery(TransferBuffer((6, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 45, 49, 0, 0, 0, 0, 0, 0)), \
|
||||||
|
TransferBuffer((0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))))
|
||||||
|
initialization.append(\
|
||||||
|
ControlQuery(TransferBuffer((1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0)), \
|
||||||
|
TransferBuffer((0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))))
|
||||||
|
|
||||||
|
end_transaction = \
|
||||||
|
ControlQuery(TransferBuffer((1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0)),\
|
||||||
|
TransferBuffer((0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)))
|
||||||
|
|
||||||
|
def string_to_buffer(string):
|
||||||
|
""" Convert a string to a TransferBuffer """
|
||||||
|
return TransferBuffer([ ord(ch) for ch in string ])
|
||||||
|
|
||||||
|
class LSQuery(ControlQuery):
|
||||||
|
"""
|
||||||
|
Contains all the device specific data (packet formats) needed to implement a simple ls command.
|
||||||
|
See PRS500Device.ls() to understand how it is used.
|
||||||
|
"""
|
||||||
|
PATH_NOT_FOUND_RESPONSE = (0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 24, 0, 0, 0, 215, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
IS_FILE_RESPONSE = (0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 24, 0, 0, 0, 210, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
NOT_MOUNTED_RESPONSE = (0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 24, 0, 0, 0, 200, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
INVALID_PATH_RESPONSE = (0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 24, 0, 0, 0, 249, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
ACKNOWLEDGE_RESPONSE = (0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0x35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
ACKNOWLEDGE_COMMAND = (0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
SEND_NAME_COMMAND = (53, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4 , 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
EOL_RESPONSE = (0, 16, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 53, 0, 0, 0, 250, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, path, type=1):
|
||||||
|
self.path = path
|
||||||
|
if len(self.path) >= 8:
|
||||||
|
self.path_fragment = self.path[8:]
|
||||||
|
for i in range(4 - len(self.path_fragment)):
|
||||||
|
self.path_fragment += '\x00'
|
||||||
|
self.path_fragment = [ ord(self.path_fragment[i]) for i in range(4) ]
|
||||||
|
else:
|
||||||
|
self.path_fragment = [ 0x00 for i in range(4) ]
|
||||||
|
src = [ 0x00 for i in range(20) ]
|
||||||
|
if type == 1:
|
||||||
|
src[0] = 0x18
|
||||||
|
elif type == 2:
|
||||||
|
src[0] = 0x33
|
||||||
|
src[4], src[12], src[16] = 0x01, len(path)+4, len(path)
|
||||||
|
query = TransferBuffer(src) + string_to_buffer(path)
|
||||||
|
src = [ 0x00 for i in range(32) ]
|
||||||
|
src[1], src[4], src[12], src[16] = 0x10, 0x01, 0x0c, 0x18
|
||||||
|
if type == 2: src[16] = 0x33
|
||||||
|
ControlQuery.__init__(self, query, TransferBuffer(src), bulkTransfer = 0x28)
|
||||||
|
|
||||||
|
def acknowledge_query(self, type, error_type=0):
|
||||||
|
"""
|
||||||
|
Return the acknowledge query used after receiving data as part of an ls query
|
||||||
|
|
||||||
|
type - should only take values 0,1,2,3 corresponding to the 4 different types of acknowledge queries.
|
||||||
|
If it takes any other value it is assumed to be zero.
|
||||||
|
|
||||||
|
error_type - 0 = no error, 1 = path not found, 2 = is file, 3 = not mounted, 4 = invalid path
|
||||||
|
"""
|
||||||
|
if error_type == 1:
|
||||||
|
response = list(LSQuery.PATH_NOT_FOUND_RESPONSE)
|
||||||
|
response[4] = 0x00
|
||||||
|
elif error_type == 2:
|
||||||
|
response = list(LSQuery.IS_FILE_RESPONSE)
|
||||||
|
response[4] = 0x00
|
||||||
|
elif error_type == 3:
|
||||||
|
response = list(LSQuery.NOT_MOUNTED_RESPONSE)
|
||||||
|
response[4] = 0x00
|
||||||
|
elif error_type == 4:
|
||||||
|
response = list(LSQuery.INVALID_PATH_RESPONSE)
|
||||||
|
response[4] = 0x00
|
||||||
|
else: response = list(LSQuery.ACKNOWLEDGE_RESPONSE)
|
||||||
|
query = list(LSQuery.ACKNOWLEDGE_COMMAND)
|
||||||
|
response[-4:] = self.path_fragment
|
||||||
|
if type == 1:
|
||||||
|
query[16] = 0x03
|
||||||
|
response[16] = 0x18
|
||||||
|
elif type == 2:
|
||||||
|
query[16] = 0x06
|
||||||
|
response[16] = 0x33
|
||||||
|
elif type == 3:
|
||||||
|
query[16] = 0x07
|
||||||
|
response[16] = 0x35
|
||||||
|
else: # All other type values are mapped to 0, which is an EOL condition
|
||||||
|
response[20], response[21], response[22], response[23] = 0xfa, 0xff, 0xff, 0xff
|
||||||
|
|
||||||
|
return ControlQuery(TransferBuffer(query), TransferBuffer(response))
|
||||||
|
|
||||||
|
def send_name_query(self, buffer):
|
||||||
|
"""
|
||||||
|
Return a ControlQuery that will cause the device to send the next name in the list
|
||||||
|
|
||||||
|
buffer - TransferBuffer that contains 4 bytes of information that identify the directory we are listing.
|
||||||
|
|
||||||
|
Note that the response to this command contains information (the size of the receive buffer for the next bulk read) thus
|
||||||
|
the expected response is set to null.
|
||||||
|
"""
|
||||||
|
query = list(LSQuery.SEND_NAME_COMMAND)
|
||||||
|
query[-4:] = list(buffer)[-4:]
|
||||||
|
response = [ 0x00 for i in range(32) ]
|
||||||
|
return ControlQuery(TransferBuffer(query), TransferBuffer(response))
|
||||||
|
|
||||||
|
|
||||||
|
def main(file):
|
||||||
|
""" Convenience method for converting spike.pl output to python code. Used to read control packet data from USB logs """
|
||||||
|
PSF = open(file, 'r')
|
||||||
|
lines = PSF.readlines()
|
||||||
|
|
||||||
|
packets = []
|
||||||
|
temp = []
|
||||||
|
for line in lines:
|
||||||
|
if re.match("\s+$", line):
|
||||||
|
temp = "".join(temp)
|
||||||
|
packet = []
|
||||||
|
for i in range(0, len(temp), 2):
|
||||||
|
packet.append(int(temp[i]+temp[i+1], 16))
|
||||||
|
temp = []
|
||||||
|
packets.append(tuple(packet))
|
||||||
|
continue
|
||||||
|
temp = temp + line.split()
|
||||||
|
print r"seq = []"
|
||||||
|
for i in range(0, len(packets), 2):
|
||||||
|
print "seq.append(ControlQuery(TransferBuffer(" + str(packets[i]) + "), TransferBuffer(" + str(packets[i+1]) + ")))"
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(sys.argv[1])
|
27
decoding
Normal file
27
decoding
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Control packets (see pg 199 in usb1.1 spec)
|
||||||
|
|
||||||
|
00 01 02 03 04 05 06 07
|
||||||
|
|
||||||
|
00 - Request Type
|
||||||
|
01 - Request
|
||||||
|
02,03 - Value
|
||||||
|
04,05 - Index/Offset
|
||||||
|
06,07 - Data length
|
||||||
|
|
||||||
|
|
||||||
|
Request Type
|
||||||
|
80 - device to host; type standard; recipient device
|
||||||
|
c0 - device to host; type vendor ; recipient device
|
||||||
|
40 - host to device; type vendor ; recipient device
|
||||||
|
|
||||||
|
c0, 40 are used for sony communication
|
||||||
|
|
||||||
|
|
||||||
|
Request
|
||||||
|
06 - GET_DESCRIPTOR
|
||||||
|
80 - Sony proprietary. goes with 40 on first bit
|
||||||
|
81 - Sony proprietary. goes with c0 on first bit
|
||||||
|
|
||||||
|
|
||||||
|
Data length
|
||||||
|
Only the 6th byte seems to be used. The value of the 6th byte converted to decimal is the number of bytes to be read/written.
|
101
pr5-500.e3p
Normal file
101
pr5-500.e3p
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE Project SYSTEM "Project-3.9.dtd">
|
||||||
|
<!-- Project file for project pr5-500 -->
|
||||||
|
<!-- Saved: 2006-10-31, 09:35:02 -->
|
||||||
|
<!-- Copyright (C) 2006 Kovid Goyal, kovid@kovidgoyal.net -->
|
||||||
|
<Project version="3.9">
|
||||||
|
<ProgLanguage mixed="0">Python</ProgLanguage>
|
||||||
|
<UIType>Console</UIType>
|
||||||
|
<Description>Library to communicate with the Sony Reader prs-500 via USB</Description>
|
||||||
|
<Version></Version>
|
||||||
|
<Author>Kovid Goyal</Author>
|
||||||
|
<Email>kovid@kovidgoyal.net</Email>
|
||||||
|
<Sources>
|
||||||
|
<Source>
|
||||||
|
<Name>communicate.py</Name>
|
||||||
|
</Source>
|
||||||
|
<Source>
|
||||||
|
<Name>data.py</Name>
|
||||||
|
</Source>
|
||||||
|
<Source>
|
||||||
|
<Name>prstypes.py</Name>
|
||||||
|
</Source>
|
||||||
|
</Sources>
|
||||||
|
<Forms>
|
||||||
|
</Forms>
|
||||||
|
<Translations>
|
||||||
|
</Translations>
|
||||||
|
<Interfaces>
|
||||||
|
</Interfaces>
|
||||||
|
<Others>
|
||||||
|
</Others>
|
||||||
|
<MainScript>
|
||||||
|
<Name>communicate.py</Name>
|
||||||
|
</MainScript>
|
||||||
|
<Vcs>
|
||||||
|
<VcsType>Subversion</VcsType>
|
||||||
|
<VcsOptions>(dp0
|
||||||
|
S'status'
|
||||||
|
p1
|
||||||
|
(lp2
|
||||||
|
S''
|
||||||
|
p3
|
||||||
|
asS'log'
|
||||||
|
p4
|
||||||
|
(lp5
|
||||||
|
g3
|
||||||
|
asS'global'
|
||||||
|
p6
|
||||||
|
(lp7
|
||||||
|
g3
|
||||||
|
asS'update'
|
||||||
|
p8
|
||||||
|
(lp9
|
||||||
|
g3
|
||||||
|
asS'remove'
|
||||||
|
p10
|
||||||
|
(lp11
|
||||||
|
g3
|
||||||
|
asS'add'
|
||||||
|
p12
|
||||||
|
(lp13
|
||||||
|
g3
|
||||||
|
asS'tag'
|
||||||
|
p14
|
||||||
|
(lp15
|
||||||
|
g3
|
||||||
|
asS'export'
|
||||||
|
p16
|
||||||
|
(lp17
|
||||||
|
g3
|
||||||
|
asS'commit'
|
||||||
|
p18
|
||||||
|
(lp19
|
||||||
|
g3
|
||||||
|
asS'diff'
|
||||||
|
p20
|
||||||
|
(lp21
|
||||||
|
g3
|
||||||
|
asS'checkout'
|
||||||
|
p22
|
||||||
|
(lp23
|
||||||
|
g3
|
||||||
|
asS'history'
|
||||||
|
p24
|
||||||
|
(lp25
|
||||||
|
g3
|
||||||
|
as.</VcsOptions>
|
||||||
|
<VcsOtherData>(dp0
|
||||||
|
S'standardLayout'
|
||||||
|
p1
|
||||||
|
I01
|
||||||
|
s.</VcsOtherData>
|
||||||
|
</Vcs>
|
||||||
|
<FiletypeAssociations>
|
||||||
|
<FiletypeAssociation pattern="*.ui.h" type="FORMS" />
|
||||||
|
<FiletypeAssociation pattern="*.ui" type="FORMS" />
|
||||||
|
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
|
||||||
|
<FiletypeAssociation pattern="*.py" type="SOURCES" />
|
||||||
|
<FiletypeAssociation pattern="*.ptl" type="SOURCES" />
|
||||||
|
</FiletypeAssociations>
|
||||||
|
</Project>
|
96
prstypes.py
Executable file
96
prstypes.py
Executable file
@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License along
|
||||||
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Contains convenience wrappers for packet data that allow output in the same format as the logs produced by spike.pl
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_buffer(tb):
|
||||||
|
""" Replace negative bytes by 256 + byte """
|
||||||
|
nb = list(tb)
|
||||||
|
for i in range(len(nb)):
|
||||||
|
if nb[i] < 0:
|
||||||
|
nb[i] = 256 + nb[i]
|
||||||
|
return TransferBuffer(nb)
|
||||||
|
|
||||||
|
def phex(num):
|
||||||
|
"""
|
||||||
|
Return the hex representation of num without the 0x prefix.
|
||||||
|
|
||||||
|
If the hex representation is only 1 digit it is padded to the left with a zero.
|
||||||
|
"""
|
||||||
|
index, sign = 2, ""
|
||||||
|
if num < 0:
|
||||||
|
index, sign = 3, "-"
|
||||||
|
h=hex(num)[index:]
|
||||||
|
if len(h) < 2:
|
||||||
|
h = "0"+h
|
||||||
|
return sign + h
|
||||||
|
|
||||||
|
|
||||||
|
class TransferBuffer(tuple):
|
||||||
|
"""
|
||||||
|
Thin wrapper around tuple to present the string representation of a transfer buffer as in the output of spike.pl """
|
||||||
|
def __init__(self, packet):
|
||||||
|
tuple.__init__(packet)
|
||||||
|
self.packet = packet
|
||||||
|
|
||||||
|
def __add__(self, tb):
|
||||||
|
return TransferBuffer(tuple.__add__(self, tb))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""
|
||||||
|
Return a string representation of this packet in the same format as that produced by spike.pl
|
||||||
|
"""
|
||||||
|
ans = ""
|
||||||
|
for i in range(0, len(self), 2):
|
||||||
|
for b in range(2):
|
||||||
|
try:
|
||||||
|
ans = ans + phex(self[i+b])
|
||||||
|
except IndexError:
|
||||||
|
break
|
||||||
|
ans = ans + " "
|
||||||
|
if (i+2)%16 == 0:
|
||||||
|
ans = ans + "\n"
|
||||||
|
return ans.strip()
|
||||||
|
|
||||||
|
class ControlQuery:
|
||||||
|
"""
|
||||||
|
Container for all the transfer buffers that make up a single query.
|
||||||
|
|
||||||
|
A query has a transmitted buffer, an expected response and an optional buffer that is either read
|
||||||
|
from or written to via a bulk transfer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, query, response, bulkTransfer=None):
|
||||||
|
"""
|
||||||
|
Construct this query.
|
||||||
|
|
||||||
|
query - A TransferBuffer that should be sent to the device on the control pipe
|
||||||
|
response - A TransferBuffer that the device is expected to return. Used for error checking.
|
||||||
|
bulkTransfer - If it is a number, it indicates that a buffer of size bulkTransfer should be read from the device via a
|
||||||
|
bulk read. If it is a TransferBuffer then it will be sent to the device via a bulk write.
|
||||||
|
"""
|
||||||
|
self.query = query
|
||||||
|
self.response = response
|
||||||
|
self.bulkTransfer = bulkTransfer
|
||||||
|
|
||||||
|
def __eq__(self, cq):
|
||||||
|
""" Bulk transfers are not compared to decide equality. """
|
||||||
|
return self.query == cq.query and self.response == cq.response
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user