mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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:
parent
6bf2b9fad8
commit
2f9f2439f0
@ -8,6 +8,7 @@ a backend that implement the Device interface for the SONY PRS500 Reader.
|
||||
import os
|
||||
|
||||
from calibre.customize import Plugin
|
||||
from calibre.constants import iswindows
|
||||
|
||||
class DevicePlugin(Plugin):
|
||||
"""
|
||||
@ -22,7 +23,14 @@ class DevicePlugin(Plugin):
|
||||
|
||||
# Ordered list of supported formats
|
||||
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
|
||||
#: An integer or a list of integers
|
||||
PRODUCT_ID = 0x0000
|
||||
# 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.
|
||||
@ -33,6 +41,85 @@ class DevicePlugin(Plugin):
|
||||
#: Path separator for paths to books on device
|
||||
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) :
|
||||
"""
|
||||
|
@ -67,44 +67,8 @@ class DeviceScanner(object):
|
||||
'''Fetch list of connected USB devices from operating system'''
|
||||
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):
|
||||
vendor_ids = device.VENDOR_ID if hasattr(device.VENDOR_ID, '__len__') else [device.VENDOR_ID]
|
||||
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
|
||||
return device.is_usb_connected(self.devices)
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
|
@ -211,8 +211,16 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
def windows_match_device(self, drive, attr):
|
||||
pnp_id = str(drive.PNPDeviceID).upper()
|
||||
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
|
||||
|
||||
if hasattr(device_id, 'search'):
|
||||
|
Loading…
x
Reference in New Issue
Block a user