diff --git a/src/calibre/devices/mtp/unix/driver.py b/src/calibre/devices/mtp/unix/driver.py index 2a22b175e9..0e4461df7d 100644 --- a/src/calibre/devices/mtp/unix/driver.py +++ b/src/calibre/devices/mtp/unix/driver.py @@ -13,7 +13,7 @@ from collections import namedtuple from functools import partial from calibre import prints, as_unicode -from calibre.constants import plugins +from calibre.constants import plugins, islinux from calibre.ptempfile import SpooledTemporaryFile from calibre.devices.errors import OpenFailed, DeviceError, BlacklistedDevice from calibre.devices.mtp.base import MTPDeviceBase, synchronous, debug @@ -44,6 +44,17 @@ class MTP_DEVICE(MTPDeviceBase): self.blacklisted_devices = set() self.ejected_devices = set() self.currently_connected_dev = None + self._is_device_mtp = None + if islinux: + from calibre.devices.mtp.unix.sysfs import MTPDetect + self._is_device_mtp = MTPDetect() + + def is_device_mtp(self, d, debug=None): + ''' Returns True iff the _is_device_mtp check returns True and libmtp + is able to probe the device successfully. ''' + if self._is_device_mtp is None: return False + return (self._is_device_mtp(d, debug=debug) and + self.libmtp.is_mtp_device(d.busnum, d.devnum)) def set_debug_level(self, lvl): self.libmtp.set_debug_level(lvl) @@ -77,7 +88,9 @@ class MTP_DEVICE(MTPDeviceBase): for d in devs: ans = cache.get(d, None) if ans is None: - ans = (d.vendor_id, d.product_id) in self.known_devices + ans = ( + (d.vendor_id, d.product_id) in self.known_devices or + self.is_device_mtp(d)) cache[d] = ans if ans: return d @@ -95,12 +108,13 @@ class MTP_DEVICE(MTPDeviceBase): err = 'startup() not called on this device driver' p(err) return False - devs = [d for d in devices_on_system if (d.vendor_id, d.product_id) - in self.known_devices and d.vendor_id != APPLE] + devs = [d for d in devices_on_system if + ( (d.vendor_id, d.product_id) in self.known_devices or + self.is_device_mtp(d, debug=p)) and d.vendor_id != APPLE] if not devs: - p('No known MTP devices connected to system') + p('No MTP devices connected to system') return False - p('Known MTP devices connected:') + p('MTP devices connected:') for d in devs: p(d) for d in devs: diff --git a/src/calibre/devices/mtp/unix/libmtp.c b/src/calibre/devices/mtp/unix/libmtp.c index 5a274b2e89..0191e04ef1 100644 --- a/src/calibre/devices/mtp/unix/libmtp.c +++ b/src/calibre/devices/mtp/unix/libmtp.c @@ -662,13 +662,6 @@ is_mtp_device(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "ii", &busnum, &devnum)) return NULL; - /* - * LIBMTP_Check_Specific_Device does not seem to work at least on my linux - * system. Need to investigate why later. Most devices are in the device - * table so this is not terribly important. - */ - /* LIBMTP_Set_Debug(LIBMTP_DEBUG_ALL); */ - /* printf("Calling check: %d %d\n", busnum, devnum); */ Py_BEGIN_ALLOW_THREADS; ans = LIBMTP_Check_Specific_Device(busnum, devnum); Py_END_ALLOW_THREADS; diff --git a/src/calibre/devices/mtp/unix/sysfs.py b/src/calibre/devices/mtp/unix/sysfs.py new file mode 100644 index 0000000000..737ea1916b --- /dev/null +++ b/src/calibre/devices/mtp/unix/sysfs.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2013, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import os, glob + +class MTPDetect(object): + + SYSFS_PATH = os.environ.get('SYSFS_PATH', '/sys') + + def __init__(self): + self.base = os.path.join(self.SYSFS_PATH, 'subsystem', 'usb', 'devices') + if not os.path.exists(self.base): + self.base = os.path.join(self.SYSFS_PATH, 'bus', 'usb', 'devices') + self.ok = os.path.exists(self.base) + + def __call__(self, dev, debug=None): + ''' + Check if the device has an interface named "MTP" using sysfs, which + avoids probing the device. + ''' + if not self.ok: return False + + def read(x): + try: + with open(x, 'rb') as f: + return f.read() + except EnvironmentError: + pass + + ipath = os.path.join(self.base, '{0}-*/{0}-*/interface'.format(dev.busnum)) + for x in glob.glob(ipath): + raw = read(x) + if not raw or raw.strip() != b'MTP': continue + raw = read(os.path.join(os.path.dirname(os.path.dirname(x)), + 'devnum')) + try: + if raw and int(raw) == dev.devnum: + if debug is not None: + debug('Unknown device {0} claims to be an MTP device' + .format(dev)) + return True + except (ValueError, TypeError): + continue + + return False + +