Device drivers: Allow one driver to handle devices from multiple vendors. Also allow device interface plugins to override the USB device detection code

This commit is contained in:
Kovid Goyal 2009-12-02 14:09:04 -07:00
parent 6bf2b9fad8
commit 2f9f2439f0
3 changed files with 98 additions and 39 deletions

View File

@ -8,6 +8,7 @@ a backend that implement the Device interface for the SONY PRS500 Reader.
import os import os
from calibre.customize import Plugin from calibre.customize import Plugin
from calibre.constants import iswindows
class DevicePlugin(Plugin): class DevicePlugin(Plugin):
""" """
@ -22,7 +23,14 @@ class DevicePlugin(Plugin):
# Ordered list of supported formats # Ordered list of supported formats
FORMATS = ["lrf", "rtf", "pdf", "txt"] FORMATS = ["lrf", "rtf", "pdf", "txt"]
#: VENDOR_ID can be either an integer, a list of integers or a dictionary
#: If it is a dictionary, it must be a dictionary of dictionaries, of the form
#: {
#: integer_vendor_id : { product_id : [list of BCDs], ... },
#: ...
#: }
VENDOR_ID = 0x0000 VENDOR_ID = 0x0000
#: An integer or a list of integers
PRODUCT_ID = 0x0000 PRODUCT_ID = 0x0000
# BCD can be either None to not distinguish between devices based on BCD, or # BCD can be either None to not distinguish between devices based on BCD, or
# it can be a list of the BCD numbers of all devices supported by this driver. # it can be a list of the BCD numbers of all devices supported by this driver.
@ -33,6 +41,85 @@ class DevicePlugin(Plugin):
#: Path separator for paths to books on device #: Path separator for paths to books on device
path_sep = os.sep path_sep = os.sep
@classmethod
def test_bcd_windows(cls, device_id, bcd):
if bcd is None or len(bcd) == 0:
return True
for c in bcd:
# Bug in winutil.get_usb_devices converts a to :
rev = ('rev_%4.4x'%c).replace('a', ':')
if rev in device_id:
return True
return False
@classmethod
def is_usb_connected_windows(cls, devices_on_system):
def id_iterator():
if hasattr(cls.VENDOR_ID, 'keys'):
for vid in cls.VENDOR_ID:
vend = cls.VENDOR_ID[vid]
for pid in vend:
bcd = vend[pid]
yield vid, pid, bcd
else:
vendors = cls.VENDOR_ID if hasattr(cls.VENDOR_ID, '__len__') else [cls.VENDOR_ID]
products = cls.PRODUCT_ID if hasattr(cls.PRODUCT_ID, '__len__') else [cls.PRODUCT_ID]
for vid in vendors:
for pid in products:
yield vid, pid, cls.BCD
for vendor_id, product_id, bcd in id_iterator():
vid, pid = 'vid_%4.4x'%vendor_id, 'pid_%4.4x'%product_id
vidd, pidd = 'vid_%i'%vendor_id, 'pid_%i'%product_id
for device_id in devices_on_system:
if (vid in device_id or vidd in device_id) and (pid in device_id or pidd in device_id):
if cls.test_bcd_windows(device_id, bcd) and cls.can_handle(device_id):
return True
return False
@classmethod
def test_bcd(cls, bcdDevice, bcd):
if bcd is None or len(bcd) == 0:
return True
for c in bcd:
if c == bcdDevice:
return True
return False
@classmethod
def is_usb_connected(cls, devices_on_system):
'''
Return True if a device handled by this plugin is currently connected.
:param devices_on_system: List of devices currently connected
'''
if iswindows:
return cls.is_usb_connected_windows(devices_on_system)
vendors_on_system = set([x[0] for x in devices_on_system])
vendors = cls.VENDOR_ID if hasattr(cls.VENDOR_ID, '__len__') else [cls.VENDOR_ID]
if hasattr(cls.VENDOR_ID, 'keys'):
products = []
for ven in cls.VENDOR_ID:
products.extend(cls.VENDOR_ID[ven].keys())
else:
products = cls.PRODUCT_ID if hasattr(cls.PRODUCT_ID, '__len__') else [cls.PRODUCT_ID]
for vid in vendors:
if vid in vendors_on_system:
for cvid, pid, bcd in devices_on_system:
if cvid == vid:
if pid in products:
if hasattr(cls.VENDOR_ID, 'keys'):
cbcd = cls.VENDOR_ID[vid][pid]
else:
cbcd = cls.BCD
if cls.test_bcd(bcd, cbcd) and cls.can_handle((vid,
pid, bcd)):
return True
return False
def reset(self, key='-1', log_packets=False, report_progress=None) : def reset(self, key='-1', log_packets=False, report_progress=None) :
""" """

View File

@ -67,44 +67,8 @@ class DeviceScanner(object):
'''Fetch list of connected USB devices from operating system''' '''Fetch list of connected USB devices from operating system'''
self.devices = self.scanner() self.devices = self.scanner()
def test_bcd_windows(self, device_id, bcd):
if bcd is None or len(bcd) == 0:
return True
for c in bcd:
# Bug in winutil.get_usb_devices converts a to :
rev = ('rev_%4.4x'%c).replace('a', ':')
if rev in device_id:
return True
return False
def test_bcd(self, bcdDevice, bcd):
if bcd is None or len(bcd) == 0:
return True
for c in bcd:
if c == bcdDevice:
return True
return False
def is_device_connected(self, device): def is_device_connected(self, device):
vendor_ids = device.VENDOR_ID if hasattr(device.VENDOR_ID, '__len__') else [device.VENDOR_ID] return device.is_usb_connected(self.devices)
product_ids = device.PRODUCT_ID if hasattr(device.PRODUCT_ID, '__len__') else [device.PRODUCT_ID]
if iswindows:
for vendor_id in vendor_ids:
for product_id in product_ids:
vid, pid = 'vid_%4.4x'%vendor_id, 'pid_%4.4x'%product_id
vidd, pidd = 'vid_%i'%vendor_id, 'pid_%i'%product_id
for device_id in self.devices:
if (vid in device_id or vidd in device_id) and (pid in device_id or pidd in device_id):
if self.test_bcd_windows(device_id, getattr(device, 'BCD', None)):
if device.can_handle(device_id):
return True
else:
for vendor, product, bcdDevice in self.devices:
if vendor in vendor_ids and product in product_ids:
if self.test_bcd(bcdDevice, getattr(device, 'BCD', None)):
if device.can_handle((vendor, product, bcdDevice)):
return True
return False
def main(args=sys.argv): def main(args=sys.argv):

View File

@ -211,8 +211,16 @@ class Device(DeviceConfig, DevicePlugin):
def windows_match_device(self, drive, attr): def windows_match_device(self, drive, attr):
pnp_id = str(drive.PNPDeviceID).upper() pnp_id = str(drive.PNPDeviceID).upper()
device_id = getattr(self, attr) device_id = getattr(self, attr)
if device_id is None or \
'VEN_' + str(self.VENDOR_NAME).upper() not in pnp_id: def test_vendor():
vendors = [self.VENDOR_NAME] if isinstance(self.VENDOR_NAME,
basestring) else self.VENDOR_NAME
for v in vendors:
if 'VEN_'+str(v).upper() in pnp_id:
return True
return False
if device_id is None or not test_vendor():
return False return False
if hasattr(device_id, 'search'): if hasattr(device_id, 'search'):