Linux MTP driver: Detect devices that have MTP interfaces even if their USB ids are not known

This commit is contained in:
Kovid Goyal 2013-01-11 15:25:04 +05:30
parent 4c09d25062
commit 251a057a53
3 changed files with 73 additions and 13 deletions

View File

@ -13,7 +13,7 @@ from collections import namedtuple
from functools import partial from functools import partial
from calibre import prints, as_unicode from calibre import prints, as_unicode
from calibre.constants import plugins from calibre.constants import plugins, islinux
from calibre.ptempfile import SpooledTemporaryFile from calibre.ptempfile import SpooledTemporaryFile
from calibre.devices.errors import OpenFailed, DeviceError, BlacklistedDevice from calibre.devices.errors import OpenFailed, DeviceError, BlacklistedDevice
from calibre.devices.mtp.base import MTPDeviceBase, synchronous, debug from calibre.devices.mtp.base import MTPDeviceBase, synchronous, debug
@ -44,6 +44,17 @@ class MTP_DEVICE(MTPDeviceBase):
self.blacklisted_devices = set() self.blacklisted_devices = set()
self.ejected_devices = set() self.ejected_devices = set()
self.currently_connected_dev = None 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): def set_debug_level(self, lvl):
self.libmtp.set_debug_level(lvl) self.libmtp.set_debug_level(lvl)
@ -77,7 +88,9 @@ class MTP_DEVICE(MTPDeviceBase):
for d in devs: for d in devs:
ans = cache.get(d, None) ans = cache.get(d, None)
if ans is 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 cache[d] = ans
if ans: if ans:
return d return d
@ -95,12 +108,13 @@ class MTP_DEVICE(MTPDeviceBase):
err = 'startup() not called on this device driver' err = 'startup() not called on this device driver'
p(err) p(err)
return False return False
devs = [d for d in devices_on_system if (d.vendor_id, d.product_id) devs = [d for d in devices_on_system if
in self.known_devices and d.vendor_id != APPLE] ( (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: if not devs:
p('No known MTP devices connected to system') p('No MTP devices connected to system')
return False return False
p('Known MTP devices connected:') p('MTP devices connected:')
for d in devs: p(d) for d in devs: p(d)
for d in devs: for d in devs:

View File

@ -662,13 +662,6 @@ is_mtp_device(PyObject *self, PyObject *args) {
if (!PyArg_ParseTuple(args, "ii", &busnum, &devnum)) return NULL; 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; Py_BEGIN_ALLOW_THREADS;
ans = LIBMTP_Check_Specific_Device(busnum, devnum); ans = LIBMTP_Check_Specific_Device(busnum, devnum);
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;

View File

@ -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 <kovid at kovidgoyal.net>'
__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