From da170b77bd8cced61aee121a1eba49c01964e0c0 Mon Sep 17 00:00:00 2001 From: Walter Bender Date: Thu, 16 May 2013 22:05:58 +0000 Subject: oz plugins --- (limited to 'plugins') diff --git a/plugins/currency/__init__.py b/plugins/currency/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/currency/__init__.py diff --git a/plugins/currency/currency.py b/plugins/currency/currency.py new file mode 100644 index 0000000..4d9a7ed --- /dev/null +++ b/plugins/currency/currency.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +#Copyright (c) 2011, Walter Bender +# +# 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +import gtk +from time import time +import os.path +from gettext import gettext as _ + +try: + from sugar.datastore import datastore +except ImportError: + pass + +from plugins.plugin import Plugin +from TurtleArt.tapalette import make_palette, define_logo_function +from TurtleArt.talogo import primitive_dictionary, logoerror, \ + media_blocks_dictionary +from TurtleArt.taconstants import MEDIA_SHAPES, NO_IMPORT, SKIN_PATHS, \ + EXPAND_SKIN, BLOCKS_WITH_SKIN + +class Currency(Plugin): + """ a class for defining palette of money """ + + def __init__(self, parent): + self.tw = parent + + def setup(self): + self.heap = self.tw.lc.heap + self.keyboard = self.tw.lc.keyboard + self.title_height = int((self.tw.canvas.height / 20) * self.tw.scale) + + SKIN_PATHS.append('plugins/currency/images') + + self._currency_palette() + + def _currency_palette(self): + palette = make_palette( + 'currency', colors=["#FFFFFF", "#A0A0A0"], + help_string=_('Palette of Australian currencies')) + + self._make_constant(palette, '5 cents', 0.05, expand=(0, 10)) + self._make_constant(palette, '10 cents', 0.1, expand=(0, 10)) + self._make_constant(palette, '20 cents', 0.2, expand=(0, 10)) + self._make_constant(palette, '50 cents', 0.5, expand=(0, 10)) + self._make_constant(palette, '1 dollar', 1, expand=(0, 10)) + self._make_constant(palette, '2 dollars', 2, expand=(0, 10)) + + self._make_constant(palette, '5 dollars', 5, expand=(60, 20)) + self._make_constant(palette, '10 dollars', 10, expand=(60, 20)) + self._make_constant(palette, '20 dollars', 20, expand=(60, 20)) + self._make_constant(palette, '50 dollars', 50, expand=(60, 20)) + self._make_constant(palette, '100 dollars', 100, expand=(60, 20)) + + def _make_constant(self, palette, block_name, constant, expand=(0, 0)): + """ Factory for constant blocks """ + palette.add_block(block_name, + style='box-style-media', + label='', + default=constant, + prim_name=block_name, + help_string=_(str(constant))) + BLOCKS_WITH_SKIN.append(block_name) + NO_IMPORT.append(block_name) + MEDIA_SHAPES.append(block_name + 'off') + MEDIA_SHAPES.append(block_name + 'small') + if expand > 0: + EXPAND_SKIN[block_name] = expand + self.tw.lc.def_prim(block_name, 0, lambda self: constant) + diff --git a/plugins/currency/icons/currencyoff.svg b/plugins/currency/icons/currencyoff.svg new file mode 100644 index 0000000..7a4825a --- /dev/null +++ b/plugins/currency/icons/currencyoff.svg @@ -0,0 +1,45 @@ + + + + + + + + image/svg+xml + + + + + + + + $ + diff --git a/plugins/currency/icons/currencyon.svg b/plugins/currency/icons/currencyon.svg new file mode 100644 index 0000000..9bcba86 --- /dev/null +++ b/plugins/currency/icons/currencyon.svg @@ -0,0 +1,121 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + $ + diff --git a/plugins/currency/images/1 dollaroff.svg b/plugins/currency/images/1 dollaroff.svg new file mode 100644 index 0000000..1299d26 --- /dev/null +++ b/plugins/currency/images/1 dollaroff.svg @@ -0,0 +1,298 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/1 dollarsmall.svg b/plugins/currency/images/1 dollarsmall.svg new file mode 100644 index 0000000..4f887a0 --- /dev/null +++ b/plugins/currency/images/1 dollarsmall.svg @@ -0,0 +1,54 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/10 centsoff.svg b/plugins/currency/images/10 centsoff.svg new file mode 100644 index 0000000..821f6f0 --- /dev/null +++ b/plugins/currency/images/10 centsoff.svg @@ -0,0 +1,292 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/10 centssmall.svg b/plugins/currency/images/10 centssmall.svg new file mode 100644 index 0000000..825d0ef --- /dev/null +++ b/plugins/currency/images/10 centssmall.svg @@ -0,0 +1,54 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/10 dollarsoff.svg b/plugins/currency/images/10 dollarsoff.svg new file mode 100644 index 0000000..73d94e6 --- /dev/null +++ b/plugins/currency/images/10 dollarsoff.svg @@ -0,0 +1,104 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/10 dollarssmall.svg b/plugins/currency/images/10 dollarssmall.svg new file mode 100644 index 0000000..f2784ea --- /dev/null +++ b/plugins/currency/images/10 dollarssmall.svg @@ -0,0 +1,55 @@ + +image/svg+xml + \ No newline at end of file diff --git a/plugins/currency/images/100 dollarsoff.svg b/plugins/currency/images/100 dollarsoff.svg new file mode 100644 index 0000000..8a65bb9 --- /dev/null +++ b/plugins/currency/images/100 dollarsoff.svg @@ -0,0 +1,54 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/100 dollarssmall.svg b/plugins/currency/images/100 dollarssmall.svg new file mode 100644 index 0000000..3695601 --- /dev/null +++ b/plugins/currency/images/100 dollarssmall.svg @@ -0,0 +1,105 @@ + +image/svg+xml + \ No newline at end of file diff --git a/plugins/currency/images/2 dollarsoff.svg b/plugins/currency/images/2 dollarsoff.svg new file mode 100644 index 0000000..0f1a6ce --- /dev/null +++ b/plugins/currency/images/2 dollarsoff.svg @@ -0,0 +1,279 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/2 dollarssmall.svg b/plugins/currency/images/2 dollarssmall.svg new file mode 100644 index 0000000..ef8e77d --- /dev/null +++ b/plugins/currency/images/2 dollarssmall.svg @@ -0,0 +1,54 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/20 centsoff.svg b/plugins/currency/images/20 centsoff.svg new file mode 100644 index 0000000..25e77fe --- /dev/null +++ b/plugins/currency/images/20 centsoff.svg @@ -0,0 +1,257 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/20 centssmall.svg b/plugins/currency/images/20 centssmall.svg new file mode 100644 index 0000000..10d8e80 --- /dev/null +++ b/plugins/currency/images/20 centssmall.svg @@ -0,0 +1,54 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/20 dollarsoff.svg b/plugins/currency/images/20 dollarsoff.svg new file mode 100644 index 0000000..64eabd7 --- /dev/null +++ b/plugins/currency/images/20 dollarsoff.svg @@ -0,0 +1,105 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/20 dollarssmall.svg b/plugins/currency/images/20 dollarssmall.svg new file mode 100644 index 0000000..c5ad6a3 --- /dev/null +++ b/plugins/currency/images/20 dollarssmall.svg @@ -0,0 +1,55 @@ + +image/svg+xml + \ No newline at end of file diff --git a/plugins/currency/images/5 centsoff.svg b/plugins/currency/images/5 centsoff.svg new file mode 100644 index 0000000..94b3ab4 --- /dev/null +++ b/plugins/currency/images/5 centsoff.svg @@ -0,0 +1,288 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/5 centssmall.svg b/plugins/currency/images/5 centssmall.svg new file mode 100644 index 0000000..c59b97a --- /dev/null +++ b/plugins/currency/images/5 centssmall.svg @@ -0,0 +1,54 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/5 dollarsoff.svg b/plugins/currency/images/5 dollarsoff.svg new file mode 100644 index 0000000..e16f9a7 --- /dev/null +++ b/plugins/currency/images/5 dollarsoff.svg @@ -0,0 +1,95 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/5 dollarssmall.svg b/plugins/currency/images/5 dollarssmall.svg new file mode 100644 index 0000000..91e4b3e --- /dev/null +++ b/plugins/currency/images/5 dollarssmall.svg @@ -0,0 +1,55 @@ + +image/svg+xml + \ No newline at end of file diff --git a/plugins/currency/images/50 centsoff.svg b/plugins/currency/images/50 centsoff.svg new file mode 100644 index 0000000..2992ae5 --- /dev/null +++ b/plugins/currency/images/50 centsoff.svg @@ -0,0 +1,54 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/50 centssmall.svg b/plugins/currency/images/50 centssmall.svg new file mode 100644 index 0000000..533e287 --- /dev/null +++ b/plugins/currency/images/50 centssmall.svg @@ -0,0 +1,54 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/50 dollarsoff.svg b/plugins/currency/images/50 dollarsoff.svg new file mode 100644 index 0000000..48169d9 --- /dev/null +++ b/plugins/currency/images/50 dollarsoff.svg @@ -0,0 +1,54 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugins/currency/images/50 dollarssmall.svg b/plugins/currency/images/50 dollarssmall.svg new file mode 100644 index 0000000..4312f4a --- /dev/null +++ b/plugins/currency/images/50 dollarssmall.svg @@ -0,0 +1,105 @@ + +image/svg+xml + \ No newline at end of file diff --git a/plugins/wedo_plugin/WeDoMore.py b/plugins/wedo_plugin/WeDoMore.py new file mode 100644 index 0000000..62eed58 --- /dev/null +++ b/plugins/wedo_plugin/WeDoMore.py @@ -0,0 +1,183 @@ +#Copyright (c) 2011, 2012, Ian Daniher +#Copyright (c) 2012, Tony Forster, Walter Bender +# +# 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +import sys +import os +sys.path.append(os.path.dirname(__file__)) +import usb.core + +import logging +logger = logging.getLogger('WeDoMore') + + +UNAVAILABLE = None +TILTSENSOR = [38, 39] +DISTANCESENSOR = [176, 177, 178, 179] +MOTOR = [0, 1, 2, 3, 238, 239] +TILT_FORWARD = 0 +TILT_LEFT = 1 +TILT_RIGHT = 2 +TILT_BACK = 3 +NO_TILT = -1 + + +def scan_for_devices(): + ''' Find all available devices ''' + return usb.core.find(find_all=True, idVendor=0x0694, idProduct=0x0003) + + +class WeDo: + def __init__(self, device=None): + """Find a USB device with the VID and PID of the Lego + WeDo. If the HID kernel driver is active, detatch + it.""" + self.dev = None + self.number = 0 + self.dev = device + if self.dev is None: + logging.debug("No Lego WeDo found") + else: + self.init_device() + self.valMotorA = 0 + self.valMotorB = 0 + + def init_device(self): + ''' Reinit device associated with the WeDo instance ''' + if self.dev is not None: + try: + if self.dev.is_kernel_driver_active(0): + try: + self.dev.detach_kernel_driver(0) + except usb.core.USBError as e: + logger.error( + "Could not detatch kernel driver: %s" % (str(e))) + except usb.core.USBError as e: + logger.error("Could not talk to WeDo device: %s" % (str(e))) + self.endpoint = self.dev[0][(0,0)][0] + + def getRawData(self): + """Read 64 bytes from the WeDo's endpoint, but only + return the last eight.""" + try: + data = list(self.endpoint.read(64)[-8:]) + except usb.core.USBError as e: + logger.error("Could not read from WeDo device: %s" % (str(e))) + return None + return data + + def processMotorValues(self, value): + """Check to make sure motor values are sane.""" + retValue = int(value) + if 0 < value < 101: + retValue += 27 + elif -101 < value < 0: + retValue -= 27 + elif value == 0: + retValue = 0 + return retValue + + def setMotors(self, valMotorA, valMotorB): + """Arguments should be in form of a number between 0 + and 100, positive or negative. Magic numbers used for + the ctrl_transfer derived from sniffing USB coms.""" + if self.dev is None: + return + self.valMotorA = self.processMotorValues(valMotorA) + self.valMotorB = self.processMotorValues(valMotorB) + data = [64, self.valMotorA&0xFF, self.valMotorB&0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00] + try: + self.dev.ctrl_transfer(bmRequestType = 0x21, bRequest = 0x09, + wValue = 0x0200, wIndex = 0, + data_or_wLength = data) + except usb.core.USBError as e: + logger.error("Could not write to driver: %s" % (str(e))) + + def getData(self): + """Sensor data is contained in the 2nd and 4th byte, with + sensor IDs being contained in the 3rd and 5th byte + respectively.""" + rawData = self.getRawData() + if rawData is not None: + sensorData = {rawData[3]: rawData[2], rawData[5]: rawData[4]} + else: + sensorData = {} + return sensorData + + def processTilt(self, v): + """Use a series of elif/value-checks to process the tilt + sensor data.""" + if v in range(10, 40): + return TILT_BACK + elif v in range(60, 90): + return TILT_RIGHT + elif v in range(170, 190): + return TILT_FORWARD + elif v in range(220, 240): + return TILT_LEFT + elif v in range(120, 140): + return NO_TILT + else: + return NO_TILT + + def getTilt(self): + if self.dev is None: + return UNAVAILABLE + data = self.getData() + for num in data.keys(): + if num in TILTSENSOR: + return self.processTilt(data[num]) + return UNAVAILABLE + + def getDistance(self): + if self.dev is None: + return UNAVAILABLE + data = self.getData() + for num in data.keys(): + if num in DISTANCESENSOR: + return data[num] - 69 + return UNAVAILABLE + + # TODO: check motor availability + + def setMotorA(self, valMotorA): + self.setMotors(valMotorA, self.valMotorB) + return self.valMotorA + + def setMotorB(self, valMotorB): + self.setMotors(self.valMotorA, valMotorB) + return self.valMotorB + + def getMotorA(self): + if self.dev is None: + return UNAVAILABLE + if self.valMotorA == 0: + return 0 + elif self.valMotorA < 0: + return self.valMotorA + 27 + else: + return self.valMotorA - 27 + + def getMotorB(self): + if self.dev is None: + return UNAVAILABLE + if self.valMotorB == 0: + return 0 + elif self.valMotorB < 0: + return self.valMotorB + 27 + else: + return self.valMotorB - 27 diff --git a/plugins/wedo_plugin/__init__.py b/plugins/wedo_plugin/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/wedo_plugin/__init__.py diff --git a/plugins/wedo_plugin/icons/WeDooff.svg b/plugins/wedo_plugin/icons/WeDooff.svg new file mode 100644 index 0000000..cc43d44 --- /dev/null +++ b/plugins/wedo_plugin/icons/WeDooff.svg @@ -0,0 +1,71 @@ + + + +image/svg+xml + + + + + diff --git a/plugins/wedo_plugin/icons/WeDoon.svg b/plugins/wedo_plugin/icons/WeDoon.svg new file mode 100644 index 0000000..16d54d2 --- /dev/null +++ b/plugins/wedo_plugin/icons/WeDoon.svg @@ -0,0 +1,71 @@ + + + +image/svg+xml + + + + + \ No newline at end of file diff --git a/plugins/wedo_plugin/usb/__init__.py b/plugins/wedo_plugin/usb/__init__.py new file mode 100644 index 0000000..8909cf2 --- /dev/null +++ b/plugins/wedo_plugin/usb/__init__.py @@ -0,0 +1,92 @@ +# Copyright (C) 2009-2011 Wander Lairson Costa +# +# The following terms apply to all files associated +# with the software unless explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. + +r"""PyUSB - Easy USB access in Python + +This package exports the following modules and subpackages: + + core - the main USB implementation + legacy - the compatibility layer with 0.x version + backend - the support for backend implementations. + +Since version 1.0, main PyUSB implementation lives in the 'usb.core' +module. New applications are encouraged to use it. +""" + +import logging +import os + +__author__ = 'Wander Lairson Costa' + +__all__ = ['legacy', 'core', 'backend', 'util'] + + +def _setup_log(): + from usb import _debug + logger = logging.getLogger('usb') + debug_level = os.getenv('PYUSB_DEBUG_LEVEL') + + if debug_level is not None: + _debug.enable_tracing(True) + filename = os.getenv('PYUSB_LOG_FILENAME') + + LEVELS = {'debug': logging.DEBUG, + 'info': logging.INFO, + 'warning': logging.WARNING, + 'error': logging.ERROR, + 'critical': logging.CRITICAL} + + level = LEVELS.get(debug_level, logging.CRITICAL + 10) + logger.setLevel(level = level) + + try: + handler = logging.FileHandler(filename) + except: + handler = logging.StreamHandler() + + fmt = logging.Formatter('%(asctime)s %(levelname)s:%(name)s:%(message)s') + handler.setFormatter(fmt) + logger.addHandler(handler) + else: + class NullHandler(logging.Handler): + def emit(self, record): + pass + + # We set the log level to avoid delegation to the + # parent log handler (if there is one). + # Thanks to Chris Clark to pointing this out. + logger.setLevel(logging.CRITICAL + 10) + + logger.addHandler(NullHandler()) + + +_setup_log() + +# We import all 'legacy' module symbols to provide compatility +# with applications that use 0.x versions. +from usb.legacy import * diff --git a/plugins/wedo_plugin/usb/_debug.py b/plugins/wedo_plugin/usb/_debug.py new file mode 100644 index 0000000..13b0ced --- /dev/null +++ b/plugins/wedo_plugin/usb/_debug.py @@ -0,0 +1,77 @@ +# Copyright (C) 2009-2011 Wander Lairson Costa +# +# The following terms apply to all files associated +# with the software unless explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. + +__author__ = 'Wander Lairson Costa' + +__all__ = ['methodtrace', 'functiontrace'] + +import logging +import usb._interop as _interop + +_enable_tracing = False + +def enable_tracing(enable): + global _enable_tracing + _enable_tracing = enable + +def _trace_function_call(logger, fname, *args, **named_args): + logger.debug( + # TODO: check if 'f' is a method or a free function + fname + '(' + \ + ', '.join((str(val) for val in args)) + \ + ', '.join((name + '=' + str(val) for name, val in named_args.items())) + ')' + ) + +# decorator for methods calls tracing +def methodtrace(logger): + def decorator_logging(f): + if not _enable_tracing: + return f + def do_trace(*args, **named_args): + # this if is just a optimization to avoid unecessary string formatting + if logging.DEBUG >= logger.getEffectiveLevel(): + fn = type(args[0]).__name__ + '.' + f.__name__ + _trace_function_call(logger, fn, *args[1:], **named_args) + return f(*args, **named_args) + _interop._update_wrapper(do_trace, f) + return do_trace + return decorator_logging + +# decorator for methods calls tracing +def functiontrace(logger): + def decorator_logging(f): + if not _enable_tracing: + return f + def do_trace(*args, **named_args): + # this if is just a optimization to avoid unecessary string formatting + if logging.DEBUG >= logger.getEffectiveLevel(): + _trace_function_call(logger, f.__name__, *args, **named_args) + return f(*args, **named_args) + _interop._update_wrapper(do_trace, f) + return do_trace + return decorator_logging diff --git a/plugins/wedo_plugin/usb/_interop.py b/plugins/wedo_plugin/usb/_interop.py new file mode 100644 index 0000000..6069d5e --- /dev/null +++ b/plugins/wedo_plugin/usb/_interop.py @@ -0,0 +1,135 @@ +# Copyright (C) 2009-2011 Wander Lairson Costa +# +# The following terms apply to all files associated +# with the software unless explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. + +# All the hacks necessary to assure compatibility across all +# supported versions come here. +# Please, note that there is one version check for each +# hack we need to do, this makes maintenance easier... ^^ + +import sys +import array + +__all__ = ['_reduce', '_set', '_next', '_groupby', '_sorted', '_update_wrapper'] + +# we support Python >= 2.3 +assert sys.hexversion >= 0x020300f0 + +# On Python 3, reduce became a functools module function +try: + import functools + _reduce = functools.reduce +except (ImportError, AttributeError): + _reduce = reduce + +# we only have the builtin set type since 2.5 version +try: + _set = set +except NameError: + import sets + _set = sets.Set + +# On Python >= 2.6, we have the builtin next() function +# On Python 2.5 and before, we have to call the iterator method next() +def _next(iter): + try: + return next(iter) + except NameError: + return iter.next() + +# groupby is available only since 2.4 version +try: + import itertools + _groupby = itertools.groupby +except (ImportError, AttributeError): + # stolen from Python docs + class _groupby(object): + # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B + # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D + def __init__(self, iterable, key=None): + if key is None: + key = lambda x: x + self.keyfunc = key + self.it = iter(iterable) + self.tgtkey = self.currkey = self.currvalue = object() + def __iter__(self): + return self + def next(self): + while self.currkey == self.tgtkey: + self.currvalue = _next(self.it) # Exit on StopIteration + self.currkey = self.keyfunc(self.currvalue) + self.tgtkey = self.currkey + return (self.currkey, self._grouper(self.tgtkey)) + def _grouper(self, tgtkey): + while self.currkey == tgtkey: + yield self.currvalue + self.currvalue = _next(self.it) # Exit on StopIteration + self.currkey = self.keyfunc(self.currvalue) + +# builtin sorted function is only availale since 2.4 version +try: + _sorted = sorted +except NameError: + def _sorted(l, key=None, reverse=False): + # sort function on Python 2.3 does not + # support 'key' parameter + class KeyToCmp(object): + def __init__(self, K): + self.key = K + def __call__(self, x, y): + kx = self.key(x) + ky = self.key(y) + if kx < ky: + return reverse and 1 or -1 + elif kx > ky: + return reverse and -1 or 1 + else: + return 0 + tmp = list(l) + tmp.sort(KeyToCmp(key)) + return tmp + +try: + import functools + _update_wrapper = functools.update_wrapper +except (ImportError, AttributeError): + def _update_wrapper(wrapper, wrapped): + wrapper.__name__ = wrapped.__name__ + wrapper.__module__ = wrapped.__module__ + wrapper.__doc__ = wrapped.__doc__ + wrapper.__dict__ = wrapped.__dict__ + +def as_array(data=None): + if data is None: + return array.array('B') + try: + return array.array('B', data) + except TypeError: + # When you pass a unicode string, you got a TypeError + # if first parameter is not 'u' + return array.array('u', data) + diff --git a/plugins/wedo_plugin/usb/backend/__init__.py b/plugins/wedo_plugin/usb/backend/__init__.py new file mode 100644 index 0000000..67ee00f --- /dev/null +++ b/plugins/wedo_plugin/usb/backend/__init__.py @@ -0,0 +1,368 @@ +# Copyright (C) 2009-2011 Wander Lairson Costa +# +# The following terms apply to all files associated +# with the software unless explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. + +r"""usb.backend - Backend interface. + +This module exports: + +IBackend - backend interface. + +Backends are Python objects which implement the IBackend interface. +The easiest way to do so is inherinting from IBackend. + +PyUSB already provides backends for libusb versions 0.1 and 1.0, +and OpenUSB library. Backends modules included with PyUSB are required to +export the get_backend() function, which returns an instance of a backend +object. You can provide your own customized backend if you +want to. Bellow you find a skeleton of a backend implementation module: + +import usb.backend + +class MyBackend(usb.backend.IBackend): + pass + +def get_backend(): + return MyBackend() + +You can use your customized backend by passing it as the backend parameter of the +usb.core.find() function. For example: + +import custom_backend +import usb.core + +myidVendor = 0xfffe +myidProduct = 0x0001 + +mybackend = custom_backend.get_backend() + +dev = usb.core.find(backend = mybackend, idProduct=myidProduct, + idVendor=myidVendor) + +For custom backends, you are not required to supply the get_backend() function, +since the application code will instantiate the backend. + +If you do not provide a backend to the find() function, it will use one of the +defaults backend according to its internal rules. For details, consult the +find() function documentation. +""" + +__author__ = 'Wander Lairson Costa' + +__all__ = ['IBackend', 'libusb01', 'libusb10', 'openusb'] + +def _not_implemented(func): + raise NotImplementedError(func.__name__) + +class IBackend(object): + r"""Backend interface. + + IBackend is the basic interface for backend implementations. By default, + the methods of the interface raise a NotImplementedError exception. A + backend implementation should replace the methods to provide the funcionality + necessary. + + As Python is a dynamic typed language, you are not obligated to inherit from + IBackend: everything that bahaves like an IBackend is an IBackend. But you + are strongly recommended to do so, inheriting from IBackend provides consistent + default behavior. + """ + def enumerate_devices(self): + r"""This function is required to return an iterable object which + yields an implementation defined device identification for each + USB device found in the system. + + The device identification object is used as argument to other methods + of the interface. + """ + _not_implemented(self.enumerate_devices) + + def get_device_descriptor(self, dev): + r"""Return the device descriptor of the given device. + + The object returned is required to have all the Device Descriptor + fields accessible as member variables. They must be convertible (but + not required to be equal) to the int type. + + dev is an object yielded by the iterator returned by the enumerate_devices() + method. + """ + _not_implemented(self.get_device_descriptor) + + def get_configuration_descriptor(self, dev, config): + r"""Return a configuration descriptor of the given device. + + The object returned is required to have all the Configuration Descriptor + fields acessible as member variables. They must be convertible (but + not required to be equal) to the int type. + + The dev parameter is the already described device identification object. + config is the logical index of the configuration (not the bConfigurationValue + field). By "logical index" we mean the relative order of the configurations + returned by the peripheral as a result of GET_DESCRIPTOR request. + """ + _not_implemented(self.get_configuration_descriptor) + + def get_interface_descriptor(self, dev, intf, alt, config): + r"""Return an interface descriptor of the given device. + + The object returned is required to have all the Interface Descriptor + fields accessible as member variables. They must be convertible (but + not required to be equal) to the int type. + + The dev parameter is the already described device identification object. + The intf parameter is the interface logical index (not the bInterfaceNumber field) + and alt is the alternate setting logical index (not the bAlternateSetting value). + Not every interface has more than one alternate setting. In this case, the alt + parameter should be zero. config is the configuration logical index (not the + bConfigurationValue field). + """ + _not_implemented(self.get_interface_descriptor) + + def get_endpoint_descriptor(self, dev, ep, intf, alt, config): + r"""Return an endpoint descriptor of the given device. + + The object returned is required to have all the Endpoint Descriptor + fields acessible as member variables. They must be convertible (but + not required to be equal) to the int type. + + The ep parameter is the endpoint logical index (not the bEndpointAddress + field) of the endpoint descriptor desired. intf, alt and config are the same + values already described in the get_interface_descriptor() method. + """ + _not_implemented(self.get_endpoint_descriptor) + + def open_device(self, dev): + r"""Open the device for data exchange. + + This method opens the device identified by the dev parameter for communication. + This method must be called before calling any communication related method, such + as transfer methods. + + It returns a handle identifying the communication instance. This handle must be + passed to the communication methods. + """ + _not_implemented(self.open_device) + + def close_device(self, dev_handle): + r"""Close the device handle. + + This method closes the device communication channel and releases any + system resources related to it. + """ + _not_implemented(self.close_device) + + def set_configuration(self, dev_handle, config_value): + r"""Set the active device configuration. + + This method should be called to set the active configuration + of the device. The dev_handle parameter is the value returned + by the open_device() method and the config_value parameter is the + bConfigurationValue field of the related configuration descriptor. + """ + _not_implemented(self.set_configuration) + + def get_configuration(self, dev_handle): + r"""Get the current active device configuration. + + This method returns the bConfigurationValue of the currently + active configuration. Depending on the backend and the OS, + either a cached value may be returned or a control request may + be issued. The dev_handle parameter is the value returned by + the open_device method. + """ + _not_implemented(self.get_configuration) + + def set_interface_altsetting(self, dev_handle, intf, altsetting): + r"""Set the interface alternate setting. + + This method should only be called when the interface has more than + one alternate setting. The dev_handle is the value returned by the + open_device() method. intf and altsetting are respectivelly the + bInterfaceNumber and bAlternateSetting fields of the related interface. + """ + _not_implemented(self.set_interface_altsetting) + + def claim_interface(self, dev_handle, intf): + r"""Claim the given interface. + + Interface claiming is not related to USB spec itself, but it is + generally an necessary call of the USB libraries. It requests exclusive + access to the interface on the system. This method must be called + before using one of the transfer methods. + + dev_handle is the value returned by the open_device() method and + intf is the bInterfaceNumber field of the desired interface. + """ + _not_implemented(self.claim_interface) + + def release_interface(self, dev_handle, intf): + r"""Release the claimed interface. + + dev_handle and intf are the same parameters of the claim_interface + method. + """ + _not_implemented(self.release_interface) + + def bulk_write(self, dev_handle, ep, intf, data, timeout): + r"""Perform a bulk write. + + dev_handle is the value returned by the open_device() method. + The ep parameter is the bEndpointAddress field whose endpoint + the data will be sent to. intf is the bInterfaceNumber field + of the interface containing the endpoint. The data parameter + is the data to be sent. It must be an instance of the array.array + class. The timeout parameter specifies a time limit to the operation + in miliseconds. + + The method returns the number of bytes written. + """ + _not_implemented(self.bulk_write) + + def bulk_read(self, dev_handle, ep, intf, size, timeout): + r"""Perform a bulk read. + + dev_handle is the value returned by the open_device() method. + The ep parameter is the bEndpointAddress field whose endpoint + the data will be received from. intf is the bInterfaceNumber field + of the interface containing the endpoint. The size parameter + is the number of bytes to be read. The timeout parameter specifies + a time limit to the operation in miliseconds. + + The method returns an array.array object containing the data read. + """ + _not_implemented(self.bulk_read) + + def intr_write(self, dev_handle, ep, intf, data, timeout): + r"""Perform an interrupt write. + + dev_handle is the value returned by the open_device() method. + The ep parameter is the bEndpointAddress field whose endpoint + the data will be sent to. intf is the bInterfaceNumber field + of the interface containing the endpoint. The data parameter + is the data to be sent. It must be an instance of the array.array + class. The timeout parameter specifies a time limit to the operation + in miliseconds. + + The method returns the number of bytes written. + """ + _not_implemented(self.intr_write) + + def intr_read(self, dev_handle, ep, intf, size, timeout): + r"""Perform an interrut read. + + dev_handle is the value returned by the open_device() method. + The ep parameter is the bEndpointAddress field whose endpoint + the data will be received from. intf is the bInterfaceNumber field + of the interface containing the endpoint. The size parameter + is the number of bytes to be read. The timeout parameter specifies + a time limit to the operation in miliseconds. + + The method returns an array.array object containing the data read. + """ + _not_implemented(self.intr_read) + + def iso_write(self, dev_handle, ep, intf, data, timeout): + r"""Perform an isochronous write. + + dev_handle is the value returned by the open_device() method. + The ep parameter is the bEndpointAddress field whose endpoint + the data will be sent to. intf is the bInterfaceNumber field + of the interface containing the endpoint. The data parameter + is the data to be sent.It must be an instance of the array.array + class. The timeout parameter specifies a time limit to the operation + in miliseconds. + + The method returns the number of bytes written. + """ + _not_implemented(self.iso_write) + + def iso_read(self, dev_handle, ep, intf, size, timeout): + r"""Perform an isochronous read. + + dev_handle is the value returned by the open_device() method. + The ep parameter is the bEndpointAddress field whose endpoint + the data will be received from. intf is the bInterfaceNumber field + of the interface containing the endpoint. The size parameter + is the number of bytes to be read. The timeout parameter specifies + a time limit to the operation in miliseconds. + + The method returns an array.array object containing the data read. + """ + _not_implemented(self.iso_read) + + def ctrl_transfer(self, + dev_handle, + bmRequestType, + bRequest, + wValue, + wIndex, + data_or_wLength, + timeout): + r"""Perform a control transfer on the endpoint 0. + + The direction of the transfer is inferred from the bmRequestType + field of the setup packet. + + dev_handle is the value returned by the open_device() method. + bmRequestType, bRequest, wValue and wIndex are the same fields + of the setup packet. data_or_wLength is either the payload to be sent + to the device, if any, as an array.array object (None there is no + payload) for OUT requests in the data stage or the wLength field + specifying the number of bytes to read for IN requests in the data + stage. The timeout parameter specifies a time limit to the operation + in miliseconds. + + Return the number of bytes written (for OUT transfers) or the data + read (for IN transfers), as an array.array object. + """ + _not_implemented(self.ctrl_transfer) + + def reset_device(self, dev_handle): + r"""Reset the device.""" + _not_implemented(self.reset_device) + + def is_kernel_driver_active(self, dev_handle, intf): + r"""Determine if a kernel driver is active on an interface. + + If a kernel driver is active, you cannot claim the interface, + and the backend will be unable to perform I/O. + """ + _not_implemented(self.is_kernel_driver_active) + + def detach_kernel_driver(self, dev_handle, intf): + r"""Detach a kernel driver from an interface. + + If successful, you will then be able to claim the interface + and perform I/O. + """ + _not_implemented(self.detach_kernel_driver) + + def attach_kernel_driver(self, dev_handle, intf): + r"""Re-attach an interface's kernel driver, which was previously + detached using detach_kernel_driver().""" + _not_implemented(self.attach_kernel_driver) diff --git a/plugins/wedo_plugin/usb/backend/libusb01.py b/plugins/wedo_plugin/usb/backend/libusb01.py new file mode 100644 index 0000000..cf344c0 --- /dev/null +++ b/plugins/wedo_plugin/usb/backend/libusb01.py @@ -0,0 +1,582 @@ +# Copyright (C) 2009-2011 Wander Lairson Costa +# +# The following terms apply to all files associated +# with the software unless explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. + +from ctypes import * +import ctypes.util +import os +import usb.backend +import usb.util +import sys +from usb.core import USBError +from usb._debug import methodtrace +import usb._interop as _interop +import logging + +__author__ = 'Wander Lairson Costa' + +__all__ = ['get_backend'] + +_logger = logging.getLogger('usb.backend.libusb01') + +# usb.h + +_PC_PATH_MAX = 4 + +if sys.platform != 'win32' and sys.platform != 'cygwin': + _PATH_MAX = os.pathconf('.', _PC_PATH_MAX) +else: + _PATH_MAX = 511 + +# libusb-win32 makes all structures packed, while +# default libusb only does for some structures +# _PackPolicy defines the structure packing according +# to the platform. +class _PackPolicy(object): + pass + +if sys.platform == 'win32' or sys.platform == 'cygwin': + _PackPolicy._pack_ = 1 + +# Data structures + +class _usb_descriptor_header(Structure): + _pack_ = 1 + _fields_ = [('blength', c_uint8), + ('bDescriptorType', c_uint8)] + +class _usb_string_descriptor(Structure): + _pack_ = 1 + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('wData', c_uint16)] + +class _usb_endpoint_descriptor(Structure, _PackPolicy): + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('bEndpointAddress', c_uint8), + ('bmAttributes', c_uint8), + ('wMaxPacketSize', c_uint16), + ('bInterval', c_uint8), + ('bRefresh', c_uint8), + ('bSynchAddress', c_uint8), + ('extra', POINTER(c_uint8)), + ('extralen', c_int)] + +class _usb_interface_descriptor(Structure, _PackPolicy): + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('bInterfaceNumber', c_uint8), + ('bAlternateSetting', c_uint8), + ('bNumEndpoints', c_uint8), + ('bInterfaceClass', c_uint8), + ('bInterfaceSubClass', c_uint8), + ('bInterfaceProtocol', c_uint8), + ('iInterface', c_uint8), + ('endpoint', POINTER(_usb_endpoint_descriptor)), + ('extra', POINTER(c_uint8)), + ('extralen', c_int)] + +class _usb_interface(Structure, _PackPolicy): + _fields_ = [('altsetting', POINTER(_usb_interface_descriptor)), + ('num_altsetting', c_int)] + +class _usb_config_descriptor(Structure, _PackPolicy): + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('wTotalLength', c_uint16), + ('bNumInterfaces', c_uint8), + ('bConfigurationValue', c_uint8), + ('iConfiguration', c_uint8), + ('bmAttributes', c_uint8), + ('bMaxPower', c_uint8), + ('interface', POINTER(_usb_interface)), + ('extra', POINTER(c_uint8)), + ('extralen', c_int)] + +class _usb_device_descriptor(Structure, _PackPolicy): + _pack_ = 1 + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('bcdUSB', c_uint16), + ('bDeviceClass', c_uint8), + ('bDeviceSubClass', c_uint8), + ('bDeviceProtocol', c_uint8), + ('bMaxPacketSize0', c_uint8), + ('idVendor', c_uint16), + ('idProduct', c_uint16), + ('bcdDevice', c_uint16), + ('iManufacturer', c_uint8), + ('iProduct', c_uint8), + ('iSerialNumber', c_uint8), + ('bNumConfigurations', c_uint8)] + +class _usb_device(Structure, _PackPolicy): + pass + +class _usb_bus(Structure, _PackPolicy): + pass + +_usb_device._fields_ = [('next', POINTER(_usb_device)), + ('prev', POINTER(_usb_device)), + ('filename', c_int8 * (_PATH_MAX + 1)), + ('bus', POINTER(_usb_bus)), + ('descriptor', _usb_device_descriptor), + ('config', POINTER(_usb_config_descriptor)), + ('dev', c_void_p), + ('devnum', c_uint8), + ('num_children', c_ubyte), + ('children', POINTER(POINTER(_usb_device)))] + +_usb_bus._fields_ = [('next', POINTER(_usb_bus)), + ('prev', POINTER(_usb_bus)), + ('dirname', c_char * (_PATH_MAX + 1)), + ('devices', POINTER(_usb_device)), + ('location', c_uint32), + ('root_dev', POINTER(_usb_device))] + +_usb_dev_handle = c_void_p + +class _DeviceDescriptor: + def __init__(self, dev): + desc = dev.descriptor + self.bLength = desc.bLength + self.bDescriptorType = desc.bDescriptorType + self.bcdUSB = desc.bcdUSB + self.bDeviceClass = desc.bDeviceClass + self.bDeviceSubClass = desc.bDeviceSubClass + self.bDeviceProtocol = desc.bDeviceProtocol + self.bMaxPacketSize0 = desc.bMaxPacketSize0 + self.idVendor = desc.idVendor + self.idProduct = desc.idProduct + self.bcdDevice = desc.bcdDevice + self.iManufacturer = desc.iManufacturer + self.iProduct = desc.iProduct + self.iSerialNumber = desc.iSerialNumber + self.bNumConfigurations = desc.bNumConfigurations + self.address = dev.devnum + self.bus = dev.bus[0].location + +_lib = None + +def _load_library(): + if sys.platform != 'cygwin': + candidates = ('usb-0.1', 'usb', 'libusb0') + for candidate in candidates: + libname = ctypes.util.find_library(candidate) + if libname is not None: break + else: + # corner cases + # cygwin predefines library names with 'cyg' instead of 'lib' + try: + return CDLL('cygusb0.dll') + except: + _logger.error('Libusb 0 could not be loaded in cygwin', exc_info=True) + + raise OSError('USB library could not be found') + return CDLL(libname) + +def _setup_prototypes(lib): + # usb_dev_handle *usb_open(struct usb_device *dev); + lib.usb_open.argtypes = [POINTER(_usb_device)] + lib.usb_open.restype = _usb_dev_handle + + # int usb_close(usb_dev_handle *dev); + lib.usb_close.argtypes = [_usb_dev_handle] + + # int usb_get_string(usb_dev_handle *dev, + # int index, + # int langid, + # char *buf, + # size_t buflen); + lib.usb_get_string.argtypes = [ + _usb_dev_handle, + c_int, + c_int, + c_char_p, + c_size_t + ] + + # int usb_get_string_simple(usb_dev_handle *dev, + # int index, + # char *buf, + # size_t buflen); + lib.usb_get_string_simple.argtypes = [ + _usb_dev_handle, + c_int, + c_char_p, + c_size_t + ] + + # int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, + # int ep, + # unsigned char type, + # unsigned char index, + # void *buf, + # int size); + lib.usb_get_descriptor_by_endpoint.argtypes = [ + _usb_dev_handle, + c_int, + c_ubyte, + c_ubyte, + c_void_p, + c_int + ] + + # int usb_get_descriptor(usb_dev_handle *udev, + # unsigned char type, + # unsigned char index, + # void *buf, + # int size); + lib.usb_get_descriptor.argtypes = [ + _usb_dev_handle, + c_ubyte, + c_ubyte, + c_void_p, + c_int + ] + + # int usb_bulk_write(usb_dev_handle *dev, + # int ep, + # const char *bytes, + # int size, + # int timeout); + lib.usb_bulk_write.argtypes = [ + _usb_dev_handle, + c_int, + c_char_p, + c_int, + c_int + ] + + # int usb_bulk_read(usb_dev_handle *dev, + # int ep, + # char *bytes, + # int size, + # int timeout); + lib.usb_bulk_read.argtypes = [ + _usb_dev_handle, + c_int, + c_char_p, + c_int, + c_int + ] + + # int usb_interrupt_write(usb_dev_handle *dev, + # int ep, + # const char *bytes, + # int size, + # int timeout); + lib.usb_interrupt_write.argtypes = [ + _usb_dev_handle, + c_int, + c_char_p, + c_int, + c_int + ] + + # int usb_interrupt_read(usb_dev_handle *dev, + # int ep, + # char *bytes, + # int size, + # int timeout); + lib.usb_interrupt_read.argtypes = [ + _usb_dev_handle, + c_int, + c_char_p, + c_int, + c_int + ] + + # int usb_control_msg(usb_dev_handle *dev, + # int requesttype, + # int request, + # int value, + # int index, + # char *bytes, + # int size, + # int timeout); + lib.usb_control_msg.argtypes = [ + _usb_dev_handle, + c_int, + c_int, + c_int, + c_int, + c_char_p, + c_int, + c_int + ] + + # int usb_set_configuration(usb_dev_handle *dev, int configuration); + lib.usb_set_configuration.argtypes = [_usb_dev_handle, c_int] + + # int usb_claim_interface(usb_dev_handle *dev, int interface); + lib.usb_claim_interface.argtypes = [_usb_dev_handle, c_int] + + # int usb_release_interface(usb_dev_handle *dev, int interface); + lib.usb_release_interface.argtypes = [_usb_dev_handle, c_int] + + # int usb_set_altinterface(usb_dev_handle *dev, int alternate); + lib.usb_set_altinterface.argtypes = [_usb_dev_handle, c_int] + + # int usb_resetep(usb_dev_handle *dev, unsigned int ep); + lib.usb_resetep.argtypes = [_usb_dev_handle, c_int] + + # int usb_clear_halt(usb_dev_handle *dev, unsigned int ep); + lib.usb_clear_halt.argtypes = [_usb_dev_handle, c_int] + + # int usb_reset(usb_dev_handle *dev); + lib.usb_reset.argtypes = [_usb_dev_handle] + + # char *usb_strerror(void); + lib.usb_strerror.argtypes = [] + lib.usb_strerror.restype = c_char_p + + # void usb_set_debug(int level); + lib.usb_set_debug.argtypes = [c_int] + + # struct usb_device *usb_device(usb_dev_handle *dev); + lib.usb_device.argtypes = [_usb_dev_handle] + lib.usb_device.restype = POINTER(_usb_device) + + # struct usb_bus *usb_get_busses(void); + lib.usb_get_busses.restype = POINTER(_usb_bus) + +def _check(retval): + if retval is None: + errmsg = _lib.usb_strerror() + else: + ret = int(retval) + if ret < 0: + errmsg = _lib.usb_strerror() + # No error means that we need to get the error + # message from the return code + # Thanks to Nicholas Wheeler to point out the problem... + # Also see issue #2860940 + if errmsg.lower() == 'no error': + errmsg = os.strerror(-ret) + else: + return ret + raise USBError(errmsg, ret) + +# implementation of libusb 0.1.x backend +class _LibUSB(usb.backend.IBackend): + @methodtrace(_logger) + def enumerate_devices(self): + _check(_lib.usb_find_busses()) + _check(_lib.usb_find_devices()) + bus = _lib.usb_get_busses() + while bool(bus): + dev = bus[0].devices + while bool(dev): + yield dev[0] + dev = dev[0].next + bus = bus[0].next + + @methodtrace(_logger) + def get_device_descriptor(self, dev): + return _DeviceDescriptor(dev) + + @methodtrace(_logger) + def get_configuration_descriptor(self, dev, config): + if config >= dev.descriptor.bNumConfigurations: + raise IndexError('Invalid configuration index ' + str(config)) + return dev.config[config] + + @methodtrace(_logger) + def get_interface_descriptor(self, dev, intf, alt, config): + cfgdesc = self.get_configuration_descriptor(dev, config) + if intf >= cfgdesc.bNumInterfaces: + raise IndexError('Invalid interface index ' + str(interface)) + interface = cfgdesc.interface[intf] + if alt >= interface.num_altsetting: + raise IndexError('Invalid alternate setting index ' + str(alt)) + return interface.altsetting[alt] + + @methodtrace(_logger) + def get_endpoint_descriptor(self, dev, ep, intf, alt, config): + interface = self.get_interface_descriptor(dev, intf, alt, config) + if ep >= interface.bNumEndpoints: + raise IndexError('Invalid endpoint index ' + str(ep)) + return interface.endpoint[ep] + + @methodtrace(_logger) + def open_device(self, dev): + return _check(_lib.usb_open(dev)) + + @methodtrace(_logger) + def close_device(self, dev_handle): + _check(_lib.usb_close(dev_handle)) + + @methodtrace(_logger) + def set_configuration(self, dev_handle, config_value): + _check(_lib.usb_set_configuration(dev_handle, config_value)) + + @methodtrace(_logger) + def set_interface_altsetting(self, dev_handle, intf, altsetting): + _check(_lib.usb_set_altinterface(dev_handle, altsetting)) + + @methodtrace(_logger) + def get_configuration(self, dev_handle): + bmRequestType = usb.util.build_request_type( + usb.util.CTRL_IN, + usb.util.CTRL_TYPE_STANDARD, + usb.util.CTRL_RECIPIENT_DEVICE + ) + return self.ctrl_transfer(dev_handle, + bmRequestType, + 0x08, + 0, + 0, + 1, + 100 + )[0] + + + @methodtrace(_logger) + def claim_interface(self, dev_handle, intf): + _check(_lib.usb_claim_interface(dev_handle, intf)) + + @methodtrace(_logger) + def release_interface(self, dev_handle, intf): + _check(_lib.usb_release_interface(dev_handle, intf)) + + @methodtrace(_logger) + def bulk_write(self, dev_handle, ep, intf, data, timeout): + return self.__write(_lib.usb_bulk_write, + dev_handle, + ep, + intf, + data, timeout) + + @methodtrace(_logger) + def bulk_read(self, dev_handle, ep, intf, size, timeout): + return self.__read(_lib.usb_bulk_read, + dev_handle, + ep, + intf, + size, + timeout) + + @methodtrace(_logger) + def intr_write(self, dev_handle, ep, intf, data, timeout): + return self.__write(_lib.usb_interrupt_write, + dev_handle, + ep, + intf, + data, + timeout) + + @methodtrace(_logger) + def intr_read(self, dev_handle, ep, intf, size, timeout): + return self.__read(_lib.usb_interrupt_read, + dev_handle, + ep, + intf, + size, + timeout) + + @methodtrace(_logger) + def ctrl_transfer(self, + dev_handle, + bmRequestType, + bRequest, + wValue, + wIndex, + data_or_wLength, + timeout): + if usb.util.ctrl_direction(bmRequestType) == usb.util.CTRL_OUT: + address, length = data_or_wLength.buffer_info() + length *= data_or_wLength.itemsize + return _check(_lib.usb_control_msg( + dev_handle, + bmRequestType, + bRequest, + wValue, + wIndex, + cast(address, c_char_p), + length, + timeout + )) + else: + data = _interop.as_array((0,) * data_or_wLength) + read = int(_check(_lib.usb_control_msg( + dev_handle, + bmRequestType, + bRequest, + wValue, + wIndex, + cast(data.buffer_info()[0], + c_char_p), + data_or_wLength, + timeout + ))) + return data[:read] + + @methodtrace(_logger) + def reset_device(self, dev_handle): + _check(_lib.usb_reset(dev_handle)) + + @methodtrace(_logger) + def detach_kernel_driver(self, dev_handle, intf): + _check(_lib.usb_detach_kernel_driver_np(dev_handle, intf)) + + def __write(self, fn, dev_handle, ep, intf, data, timeout): + address, length = data.buffer_info() + length *= data.itemsize + return int(_check(fn( + dev_handle, + ep, + cast(address, c_char_p), + length, + timeout + ))) + + def __read(self, fn, dev_handle, ep, intf, size, timeout): + data = _interop.as_array((0,) * size) + address, length = data.buffer_info() + length *= data.itemsize + ret = int(_check(fn( + dev_handle, + ep, + cast(address, c_char_p), + length, + timeout + ))) + return data[:ret] + +def get_backend(): + global _lib + try: + if _lib is None: + _lib = _load_library() + _setup_prototypes(_lib) + _lib.usb_init() + return _LibUSB() + except Exception: + _logger.error('Error loading libusb 0.1 backend', exc_info=True) + return None diff --git a/plugins/wedo_plugin/usb/backend/libusb10.py b/plugins/wedo_plugin/usb/backend/libusb10.py new file mode 100644 index 0000000..cb2a566 --- /dev/null +++ b/plugins/wedo_plugin/usb/backend/libusb10.py @@ -0,0 +1,654 @@ +# Copyright (C) 2009-2011 Wander Lairson Costa +# +# The following terms apply to all files associated +# with the software unless explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. + +from ctypes import * +import ctypes.util +import usb.util +import sys +import logging +from usb._debug import methodtrace +import usb._interop as _interop +import errno + +__author__ = 'Wander Lairson Costa' + +__all__ = [ + 'get_backend', + 'LIBUSB_SUCESS', + 'LIBUSB_ERROR_IO', + 'LIBUSB_ERROR_INVALID_PARAM', + 'LIBUSB_ERROR_ACCESS', + 'LIBUSB_ERROR_NO_DEVICE', + 'LIBUSB_ERROR_NOT_FOUND', + 'LIBUSB_ERROR_BUSY', + 'LIBUSB_ERROR_TIMEOUT', + 'LIBUSB_ERROR_OVERFLOW', + 'LIBUSB_ERROR_PIPE', + 'LIBUSB_ERROR_INTERRUPTED', + 'LIBUSB_ERROR_NO_MEM', + 'LIBUSB_ERROR_NOT_SUPPORTED', + 'LIBUSB_ERROR_OTHER' + ] + +_logger = logging.getLogger('usb.backend.libusb10') + +# libusb.h + +# return codes + +LIBUSB_SUCCESS = 0 +LIBUSB_ERROR_IO = -1 +LIBUSB_ERROR_INVALID_PARAM = -2 +LIBUSB_ERROR_ACCESS = -3 +LIBUSB_ERROR_NO_DEVICE = -4 +LIBUSB_ERROR_NOT_FOUND = -5 +LIBUSB_ERROR_BUSY = -6 +LIBUSB_ERROR_TIMEOUT = -7 +LIBUSB_ERROR_OVERFLOW = -8 +LIBUSB_ERROR_PIPE = -9 +LIBUSB_ERROR_INTERRUPTED = -10 +LIBUSB_ERROR_NO_MEM = -11 +LIBUSB_ERROR_NOT_SUPPORTED = -12 +LIBUSB_ERROR_OTHER = -99 + +# map return codes to strings +_str_error = { + LIBUSB_SUCCESS:'Success (no error)', + LIBUSB_ERROR_IO:'Input/output error', + LIBUSB_ERROR_INVALID_PARAM:'Invalid parameter', + LIBUSB_ERROR_ACCESS:'Access denied (insufficient permissions)', + LIBUSB_ERROR_NO_DEVICE:'No such device (it may have been disconnected)', + LIBUSB_ERROR_NOT_FOUND:'Entity not found', + LIBUSB_ERROR_BUSY:'Resource busy', + LIBUSB_ERROR_TIMEOUT:'Operation timed out', + LIBUSB_ERROR_OVERFLOW:'Overflow', + LIBUSB_ERROR_PIPE:'Pipe error', + LIBUSB_ERROR_INTERRUPTED:'System call interrupted (perhaps due to signal)', + LIBUSB_ERROR_NO_MEM:'Insufficient memory', + LIBUSB_ERROR_NOT_SUPPORTED:'Operation not supported or unimplemented on this platform', + LIBUSB_ERROR_OTHER:'Unknown error' +} + +# map return code to errno values +_libusb_errno = { + LIBUSB_SUCCESS:None, + LIBUSB_ERROR_IO:errno.__dict__.get('EIO', None), + LIBUSB_ERROR_INVALID_PARAM:errno.__dict__.get('EINVAL', None), + LIBUSB_ERROR_ACCESS:errno.__dict__.get('EACCES', None), + LIBUSB_ERROR_NO_DEVICE:errno.__dict__.get('ENODEV', None), + LIBUSB_ERROR_NOT_FOUND:errno.__dict__.get('ENOENT', None), + LIBUSB_ERROR_BUSY:errno.__dict__.get('EBUSY', None), + LIBUSB_ERROR_TIMEOUT:errno.__dict__.get('ETIMEDOUT', None), + LIBUSB_ERROR_OVERFLOW:errno.__dict__.get('EOVERFLOW', None), + LIBUSB_ERROR_PIPE:errno.__dict__.get('EPIPE', None), + LIBUSB_ERROR_INTERRUPTED:errno.__dict__.get('EINTR', None), + LIBUSB_ERROR_NO_MEM:errno.__dict__.get('ENOMEM', None), + LIBUSB_ERROR_NOT_SUPPORTED:errno.__dict__.get('ENOSYS', None), + LIBUSB_ERROR_OTHER:None +} + +# Data structures + +class _libusb_endpoint_descriptor(Structure): + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('bEndpointAddress', c_uint8), + ('bmAttributes', c_uint8), + ('wMaxPacketSize', c_uint16), + ('bInterval', c_uint8), + ('bRefresh', c_uint8), + ('bSynchAddress', c_uint8), + ('extra', POINTER(c_ubyte)), + ('extra_length', c_int)] + +class _libusb_interface_descriptor(Structure): + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('bInterfaceNumber', c_uint8), + ('bAlternateSetting', c_uint8), + ('bNumEndpoints', c_uint8), + ('bInterfaceClass', c_uint8), + ('bInterfaceSubClass', c_uint8), + ('bInterfaceProtocol', c_uint8), + ('iInterface', c_uint8), + ('endpoint', POINTER(_libusb_endpoint_descriptor)), + ('extra', POINTER(c_ubyte)), + ('extra_length', c_int)] + +class _libusb_interface(Structure): + _fields_ = [('altsetting', POINTER(_libusb_interface_descriptor)), + ('num_altsetting', c_int)] + +class _libusb_config_descriptor(Structure): + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('wTotalLength', c_uint16), + ('bNumInterfaces', c_uint8), + ('bConfigurationValue', c_uint8), + ('iConfiguration', c_uint8), + ('bmAttributes', c_uint8), + ('bMaxPower', c_uint8), + ('interface', POINTER(_libusb_interface)), + ('extra', POINTER(c_ubyte)), + ('extra_length', c_int)] + +class _libusb_device_descriptor(Structure): + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('bcdUSB', c_uint16), + ('bDeviceClass', c_uint8), + ('bDeviceSubClass', c_uint8), + ('bDeviceProtocol', c_uint8), + ('bMaxPacketSize0', c_uint8), + ('idVendor', c_uint16), + ('idProduct', c_uint16), + ('bcdDevice', c_uint16), + ('iManufacturer', c_uint8), + ('iProduct', c_uint8), + ('iSerialNumber', c_uint8), + ('bNumConfigurations', c_uint8)] + +_lib = None +_init = None + +_libusb_device_handle = c_void_p + +def _load_library(): + if sys.platform != 'cygwin': + candidates = ('usb-1.0', 'libusb-1.0', 'usb') + for candidate in candidates: + libname = ctypes.util.find_library(candidate) + if libname is not None: break + else: + # corner cases + # cygwin predefines library names with 'cyg' instead of 'lib' + try: + return CDLL('cygusb-1.0.dll') + except Exception: + _logger.error('Libusb 1.0 could not be loaded in cygwin', exc_info=True) + + raise OSError('USB library could not be found') + # Windows backend uses stdcall calling convention + if sys.platform == 'win32': + l = WinDLL(libname) + else: + l = CDLL(libname) + # On FreeBSD 8/9, libusb 1.0 and libusb 0.1 are in the same shared + # object libusb.so, so if we found libusb library name, we must assure + # it is 1.0 version. We just try to get some symbol from 1.0 version + if not hasattr(l, 'libusb_init'): + raise OSError('USB library could not be found') + return l + +def _setup_prototypes(lib): + # void libusb_set_debug (libusb_context *ctx, int level) + lib.libusb_set_debug.argtypes = [c_void_p, c_int] + + # int libusb_init (libusb_context **context) + lib.libusb_init.argtypes = [POINTER(c_void_p)] + + # void libusb_exit (struct libusb_context *ctx) + lib.libusb_exit.argtypes = [c_void_p] + + # ssize_t libusb_get_device_list (libusb_context *ctx, + # libusb_device ***list) + lib.libusb_get_device_list.argtypes = [ + c_void_p, + POINTER(POINTER(c_void_p)) + ] + + # void libusb_free_device_list (libusb_device **list, + # int unref_devices) + lib.libusb_free_device_list.argtypes = [ + POINTER(c_void_p), + c_int + ] + + # libusb_device *libusb_ref_device (libusb_device *dev) + lib.libusb_ref_device.argtypes = [c_void_p] + lib.libusb_ref_device.restype = c_void_p + + # void libusb_unref_device(libusb_device *dev) + lib.libusb_unref_device.argtypes = [c_void_p] + + # int libusb_open(libusb_device *dev, libusb_device_handle **handle) + lib.libusb_open.argtypes = [c_void_p, POINTER(_libusb_device_handle)] + + # void libusb_close(libusb_device_handle *dev_handle) + lib.libusb_close.argtypes = [_libusb_device_handle] + + # int libusb_set_configuration(libusb_device_handle *dev, + # int configuration) + lib.libusb_set_configuration.argtypes = [_libusb_device_handle, c_int] + + # int libusb_get_configuration(libusb_device_handle *dev, + # int *config) + lib.libusb_get_configuration.argtypes = [_libusb_device_handle, POINTER(c_int)] + + # int libusb_claim_interface(libusb_device_handle *dev, + # int interface_number) + lib.libusb_claim_interface.argtypes = [_libusb_device_handle, c_int] + + # int libusb_release_interface(libusb_device_handle *dev, + # int interface_number) + lib.libusb_release_interface.argtypes = [_libusb_device_handle, c_int] + + # int libusb_set_interface_alt_setting(libusb_device_handle *dev, + # int interface_number, + # int alternate_setting) + lib.libusb_set_interface_alt_setting.argtypes = [ + _libusb_device_handle, + c_int, + c_int + ] + + # int libusb_reset_device (libusb_device_handle *dev) + lib.libusb_reset_device.argtypes = [_libusb_device_handle] + + # int libusb_kernel_driver_active(libusb_device_handle *dev, + # int interface) + lib.libusb_kernel_driver_active.argtypes = [ + _libusb_device_handle, + c_int + ] + + # int libusb_detach_kernel_driver(libusb_device_handle *dev, + # int interface) + lib.libusb_detach_kernel_driver.argtypes = [ + _libusb_device_handle, + c_int + ] + + # int libusb_attach_kernel_driver(libusb_device_handle *dev, + # int interface) + lib.libusb_attach_kernel_driver.argtypes = [ + _libusb_device_handle, + c_int + ] + + # int libusb_get_device_descriptor( + # libusb_device *dev, + # struct libusb_device_descriptor *desc + # ) + lib.libusb_get_device_descriptor.argtypes = [ + c_void_p, + POINTER(_libusb_device_descriptor) + ] + + # int libusb_get_config_descriptor( + # libusb_device *dev, + # uint8_t config_index, + # struct libusb_config_descriptor **config + # ) + lib.libusb_get_config_descriptor.argtypes = [ + c_void_p, + c_uint8, + POINTER(POINTER(_libusb_config_descriptor)) + ] + + # void libusb_free_config_descriptor( + # struct libusb_config_descriptor *config + # ) + lib.libusb_free_config_descriptor.argtypes = [ + POINTER(_libusb_config_descriptor) + ] + + # int libusb_get_string_descriptor_ascii(libusb_device_handle *dev, + # uint8_t desc_index, + # unsigned char *data, + # int length) + lib.libusb_get_string_descriptor_ascii.argtypes = [ + _libusb_device_handle, + c_uint8, + POINTER(c_ubyte), + c_int + ] + + # int libusb_control_transfer(libusb_device_handle *dev_handle, + # uint8_t bmRequestType, + # uint8_t bRequest, + # uint16_t wValue, + # uint16_t wIndex, + # unsigned char *data, + # uint16_t wLength, + # unsigned int timeout) + lib.libusb_control_transfer.argtypes = [ + _libusb_device_handle, + c_uint8, + c_uint8, + c_uint16, + c_uint16, + POINTER(c_ubyte), + c_uint16, + c_uint + ] + + #int libusb_bulk_transfer( + # struct libusb_device_handle *dev_handle, + # unsigned char endpoint, + # unsigned char *data, + # int length, + # int *transferred, + # unsigned int timeout + # ) + lib.libusb_bulk_transfer.argtypes = [ + _libusb_device_handle, + c_ubyte, + POINTER(c_ubyte), + c_int, + POINTER(c_int), + c_uint + ] + + # int libusb_interrupt_transfer( + # libusb_device_handle *dev_handle, + # unsigned char endpoint, + # unsigned char *data, + # int length, + # int *actual_length, + # unsigned int timeout + # ); + lib.libusb_interrupt_transfer.argtypes = [ + _libusb_device_handle, + c_ubyte, + POINTER(c_ubyte), + c_int, + POINTER(c_int), + c_uint + ] + + # uint8_t libusb_get_bus_number(libusb_device *dev) + lib.libusb_get_bus_number.argtypes = [c_void_p] + lib.libusb_get_bus_number.restype = c_uint8 + + # uint8_t libusb_get_device_address(libusb_device *dev) + lib.libusb_get_device_address.argtypes = [c_void_p] + lib.libusb_get_device_address.restype = c_uint8 + + + +# check a libusb function call +def _check(retval): + if isinstance(retval, int): + retval = c_int(retval) + if isinstance(retval, c_int): + if retval.value < 0: + from usb.core import USBError + ret = retval.value + raise USBError(_str_error[ret], ret, _libusb_errno[ret]) + return retval + +# wrap a device +class _Device(object): + def __init__(self, devid): + self.devid = _lib.libusb_ref_device(devid) + def __del__(self): + _lib.libusb_unref_device(self.devid) + +# wrap a descriptor and keep a reference to another object +# Thanks to Thomas Reitmayr. +class _WrapDescriptor(object): + def __init__(self, desc, obj = None): + self.obj = obj + self.desc = desc + def __getattr__(self, name): + return getattr(self.desc, name) + +# wrap a configuration descriptor +class _ConfigDescriptor(object): + def __init__(self, desc): + self.desc = desc + def __del__(self): + _lib.libusb_free_config_descriptor(self.desc) + def __getattr__(self, name): + return getattr(self.desc.contents, name) + +# initialize and finalize the library +class _Initializer(object): + def __init__(self): + _check(_lib.libusb_init(None)) + def __del__(self): + _lib.libusb_exit(None) + + +# iterator for libusb devices +class _DevIterator(object): + def __init__(self): + self.dev_list = POINTER(c_void_p)() + self.num_devs = _check(_lib.libusb_get_device_list( + None, + byref(self.dev_list)) + ).value + def __iter__(self): + for i in range(self.num_devs): + yield _Device(self.dev_list[i]) + def __del__(self): + _lib.libusb_free_device_list(self.dev_list, 1) + +# implementation of libusb 1.0 backend +class _LibUSB(usb.backend.IBackend): + @methodtrace(_logger) + def enumerate_devices(self): + return _DevIterator() + + @methodtrace(_logger) + def get_device_descriptor(self, dev): + dev_desc = _libusb_device_descriptor() + _check(_lib.libusb_get_device_descriptor(dev.devid, byref(dev_desc))) + dev_desc.bus = _lib.libusb_get_bus_number(dev.devid) + dev_desc.address = _lib.libusb_get_device_address(dev.devid) + return dev_desc + + @methodtrace(_logger) + def get_configuration_descriptor(self, dev, config): + cfg = POINTER(_libusb_config_descriptor)() + _check(_lib.libusb_get_config_descriptor(dev.devid, + config, byref(cfg))) + return _ConfigDescriptor(cfg) + + @methodtrace(_logger) + def get_interface_descriptor(self, dev, intf, alt, config): + cfg = self.get_configuration_descriptor(dev, config) + if intf >= cfg.bNumInterfaces: + raise IndexError('Invalid interface index ' + str(intf)) + i = cfg.interface[intf] + if alt >= i.num_altsetting: + raise IndexError('Invalid alternate setting index ' + str(alt)) + return _WrapDescriptor(i.altsetting[alt], cfg) + + @methodtrace(_logger) + def get_endpoint_descriptor(self, dev, ep, intf, alt, config): + i = self.get_interface_descriptor(dev, intf, alt, config) + if ep > i.bNumEndpoints: + raise IndexError('Invalid endpoint index ' + str(ep)) + return _WrapDescriptor(i.endpoint[ep], i) + + @methodtrace(_logger) + def open_device(self, dev): + handle = _libusb_device_handle() + _check(_lib.libusb_open(dev.devid, byref(handle))) + return handle + + @methodtrace(_logger) + def close_device(self, dev_handle): + _lib.libusb_close(dev_handle) + + @methodtrace(_logger) + def set_configuration(self, dev_handle, config_value): + _check(_lib.libusb_set_configuration(dev_handle, config_value)) + + @methodtrace(_logger) + def get_configuration(self, dev_handle): + config = c_int() + _check(_lib.libusb_get_configuration(dev_handle, byref(config))) + return config.value + + @methodtrace(_logger) + def set_interface_altsetting(self, dev_handle, intf, altsetting): + _check(_lib.libusb_set_interface_alt_setting(dev_handle, + intf, + altsetting)) + + @methodtrace(_logger) + def claim_interface(self, dev_handle, intf): + _check(_lib.libusb_claim_interface(dev_handle, intf)) + + @methodtrace(_logger) + def release_interface(self, dev_handle, intf): + _check(_lib.libusb_release_interface(dev_handle, intf)) + + @methodtrace(_logger) + def bulk_write(self, dev_handle, ep, intf, data, timeout): + return self.__write(_lib.libusb_bulk_transfer, + dev_handle, + ep, + intf, + data, + timeout) + + @methodtrace(_logger) + def bulk_read(self, dev_handle, ep, intf, size, timeout): + return self.__read(_lib.libusb_bulk_transfer, + dev_handle, + ep, + intf, + size, + timeout) + + @methodtrace(_logger) + def intr_write(self, dev_handle, ep, intf, data, timeout): + return self.__write(_lib.libusb_interrupt_transfer, + dev_handle, + ep, + intf, + data, + timeout) + + @methodtrace(_logger) + def intr_read(self, dev_handle, ep, intf, size, timeout): + return self.__read(_lib.libusb_interrupt_transfer, + dev_handle, + ep, + intf, + size, + timeout) + +# TODO: implement isochronous +# @methodtrace(_logger) +# def iso_write(self, dev_handle, ep, intf, data, timeout): +# pass + + +# @methodtrace(_logger) +# def iso_read(self, dev_handle, ep, intf, size, timeout): +# pass + + @methodtrace(_logger) + def ctrl_transfer(self, + dev_handle, + bmRequestType, + bRequest, + wValue, + wIndex, + data_or_wLength, + timeout): + if usb.util.ctrl_direction(bmRequestType) == usb.util.CTRL_OUT: + buff = data_or_wLength + else: + buff = _interop.as_array((0,) * data_or_wLength) + + addr, length = buff.buffer_info() + length *= buff.itemsize + + ret = _check(_lib.libusb_control_transfer(dev_handle, + bmRequestType, + bRequest, + wValue, + wIndex, + cast(addr, + POINTER(c_ubyte)), + length, + timeout)) + + if usb.util.ctrl_direction(bmRequestType) == usb.util.CTRL_OUT: + return ret.value + else: + return buff[:ret.value] + + @methodtrace(_logger) + def reset_device(self, dev_handle): + _check(_lib.libusb_reset_device(dev_handle)) + + @methodtrace(_logger) + def is_kernel_driver_active(self, dev_handle, intf): + return bool(_check(_lib.libusb_kernel_driver_active(dev_handle, intf))) + + @methodtrace(_logger) + def detach_kernel_driver(self, dev_handle, intf): + _check(_lib.libusb_detach_kernel_driver(dev_handle, intf)) + + @methodtrace(_logger) + def attach_kernel_driver(self, dev_handle, intf): + _check(_lib.libusb_attach_kernel_driver(dev_handle, intf)) + + def __write(self, fn, dev_handle, ep, intf, data, timeout): + address, length = data.buffer_info() + length *= data.itemsize + transferred = c_int() + _check(fn(dev_handle, + ep, + cast(address, POINTER(c_ubyte)), + length, + byref(transferred), + timeout)) + return transferred.value + + def __read(self, fn, dev_handle, ep, intf, size, timeout): + data = _interop.as_array((0,) * size) + address, length = data.buffer_info() + length *= data.itemsize + transferred = c_int() + _check(fn(dev_handle, + ep, + cast(address, POINTER(c_ubyte)), + length, + byref(transferred), + timeout)) + return data[:transferred.value] + +def get_backend(): + global _lib, _init + try: + if _lib is None: + _lib = _load_library() + _setup_prototypes(_lib) + _init = _Initializer() + return _LibUSB() + except Exception: + _logger.error('Error loading libusb 1.0 backend', exc_info=True) + return None diff --git a/plugins/wedo_plugin/usb/backend/openusb.py b/plugins/wedo_plugin/usb/backend/openusb.py new file mode 100644 index 0000000..23e4d45 --- /dev/null +++ b/plugins/wedo_plugin/usb/backend/openusb.py @@ -0,0 +1,707 @@ +# Copyright (C) 2009-2011 Wander Lairson Costa +# +# The following terms apply to all files associated +# with the software unless explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. + +from ctypes import * +import ctypes.util +import usb.util +from usb._debug import methodtrace +import logging +import errno + +__author__ = 'Wander Lairson Costa' + +__all__ = [ + 'get_backend' + 'OPENUSB_SUCCESS' + 'OPENUSB_PLATFORM_FAILURE' + 'OPENUSB_NO_RESOURCES' + 'OPENUSB_NO_BANDWIDTH' + 'OPENUSB_NOT_SUPPORTED' + 'OPENUSB_HC_HARDWARE_ERROR' + 'OPENUSB_INVALID_PERM' + 'OPENUSB_BUSY' + 'OPENUSB_BADARG' + 'OPENUSB_NOACCESS' + 'OPENUSB_PARSE_ERROR' + 'OPENUSB_UNKNOWN_DEVICE' + 'OPENUSB_INVALID_HANDLE' + 'OPENUSB_SYS_FUNC_FAILURE' + 'OPENUSB_NULL_LIST' + 'OPENUSB_CB_CONTINUE' + 'OPENUSB_CB_TERMINATE' + 'OPENUSB_IO_STALL' + 'OPENUSB_IO_CRC_ERROR' + 'OPENUSB_IO_DEVICE_HUNG' + 'OPENUSB_IO_REQ_TOO_BIG' + 'OPENUSB_IO_BIT_STUFFING' + 'OPENUSB_IO_UNEXPECTED_PID' + 'OPENUSB_IO_DATA_OVERRUN' + 'OPENUSB_IO_DATA_UNDERRUN' + 'OPENUSB_IO_BUFFER_OVERRUN' + 'OPENUSB_IO_BUFFER_UNDERRUN' + 'OPENUSB_IO_PID_CHECK_FAILURE' + 'OPENUSB_IO_DATA_TOGGLE_MISMATCH' + 'OPENUSB_IO_TIMEOUT' + 'OPENUSB_IO_CANCELED' + ] + +_logger = logging.getLogger('usb.backend.openusb') + +OPENUSB_SUCCESS = 0 +OPENUSB_PLATFORM_FAILURE = -1 +OPENUSB_NO_RESOURCES = -2 +OPENUSB_NO_BANDWIDTH = -3 +OPENUSB_NOT_SUPPORTED = -4 +OPENUSB_HC_HARDWARE_ERROR = -5 +OPENUSB_INVALID_PERM = -6 +OPENUSB_BUSY = -7 +OPENUSB_BADARG = -8 +OPENUSB_NOACCESS = -9 +OPENUSB_PARSE_ERROR = -10 +OPENUSB_UNKNOWN_DEVICE = -11 +OPENUSB_INVALID_HANDLE = -12 +OPENUSB_SYS_FUNC_FAILURE = -13 +OPENUSB_NULL_LIST = -14 +OPENUSB_CB_CONTINUE = -20 +OPENUSB_CB_TERMINATE = -21 +OPENUSB_IO_STALL = -50 +OPENUSB_IO_CRC_ERROR = -51 +OPENUSB_IO_DEVICE_HUNG = -52 +OPENUSB_IO_REQ_TOO_BIG = -53 +OPENUSB_IO_BIT_STUFFING = -54 +OPENUSB_IO_UNEXPECTED_PID = -55 +OPENUSB_IO_DATA_OVERRUN = -56 +OPENUSB_IO_DATA_UNDERRUN = -57 +OPENUSB_IO_BUFFER_OVERRUN = -58 +OPENUSB_IO_BUFFER_UNDERRUN = -59 +OPENUSB_IO_PID_CHECK_FAILURE = -60 +OPENUSB_IO_DATA_TOGGLE_MISMATCH = -61 +OPENUSB_IO_TIMEOUT = -62 +OPENUSB_IO_CANCELED = -63 + +_openusb_errno = { + OPENUSB_SUCCESS:None, + OPENUSB_PLATFORM_FAILURE:None, + OPENUSB_NO_RESOURCES:errno.__dict__.get('ENOMEM', None), + OPENUSB_NO_BANDWIDTH:None, + OPENUSB_NOT_SUPPORTED:errno.__dict__.get('ENOSYS', None), + OPENUSB_HC_HARDWARE_ERROR:errno.__dict__.get('EIO', None), + OPENUSB_INVALID_PERM:errno.__dict__.get('EBADF', None), + OPENUSB_BUSY:errno.__dict__.get('EBUSY', None), + OPENUSB_BADARG:errno.__dict__.get('EINVAL', None), + OPENUSB_NOACCESS:errno.__dict__.get('EACCES', None), + OPENUSB_PARSE_ERROR:None, + OPENUSB_UNKNOWN_DEVICE:errno.__dict__.get('ENODEV', None), + OPENUSB_INVALID_HANDLE:errno.__dict__.get('EINVAL', None), + OPENUSB_SYS_FUNC_FAILURE:None, + OPENUSB_NULL_LIST:None, + OPENUSB_CB_CONTINUE:None, + OPENUSB_CB_TERMINATE:None, + OPENUSB_IO_STALL:errno.__dict__.get('EIO', None), + OPENUSB_IO_CRC_ERROR:errno.__dict__.get('EIO', None), + OPENUSB_IO_DEVICE_HUNG:errno.__dict__.get('EIO', None), + OPENUSB_IO_REQ_TOO_BIG:errno.__dict__.get('E2BIG', None), + OPENUSB_IO_BIT_STUFFING:None, + OPENUSB_IO_UNEXPECTED_PID:errno.__dict__.get('ESRCH', None), + OPENUSB_IO_DATA_OVERRUN:errno.__dict__.get('EOVERFLOW', None), + OPENUSB_IO_DATA_UNDERRUN:None, + OPENUSB_IO_BUFFER_OVERRUN:errno.__dict__.get('EOVERFLOW', None), + OPENUSB_IO_BUFFER_UNDERRUN:None, + OPENUSB_IO_PID_CHECK_FAILURE:None, + OPENUSB_IO_DATA_TOGGLE_MISMATCH:None, + OPENUSB_IO_TIMEOUT:errno.__dict__.get('ETIMEDOUT', None), + OPENUSB_IO_CANCELED:errno.__dict__.get('EINTR', None) +} + +class _usb_endpoint_desc(Structure): + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('bEndpointAddress', c_uint8), + ('bmAttributes', c_uint8), + ('wMaxPacketSize', c_uint16), + ('bInterval', c_uint8), + ('bRefresh', c_uint8), + ('bSynchAddress', c_uint8)] + +class _usb_interface_desc(Structure): + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('bInterfaceNumber', c_uint8), + ('bAlternateSetting', c_uint8), + ('bNumEndpoints', c_uint8), + ('bInterfaceClass', c_uint8), + ('bInterfaceSubClass', c_uint8), + ('bInterfaceProtocol', c_uint8), + ('iInterface', c_uint8)] + +class _usb_config_desc(Structure): + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('wTotalLength', c_uint16), + ('bNumInterfaces', c_uint8), + ('bConfigurationValue', c_uint8), + ('iConfiguration', c_uint8), + ('bmAttributes', c_uint8), + ('bMaxPower', c_uint8)] + +class _usb_device_desc(Structure): + _fields_ = [('bLength', c_uint8), + ('bDescriptorType', c_uint8), + ('bcdUSB', c_uint16), + ('bDeviceClass', c_uint8), + ('bDeviceSubClass', c_uint8), + ('bDeviceProtocol', c_uint8), + ('bMaxPacketSize0', c_uint8), + ('idVendor', c_uint16), + ('idProduct', c_uint16), + ('bcdDevice', c_uint16), + ('iManufacturer', c_uint8), + ('iProduct', c_uint8), + ('iSerialNumber', c_uint8), + ('bNumConfigurations', c_uint8)] + +class _openusb_request_result(Structure): + _fields_ = [('status', c_int32), + ('transfered_bytes', c_uint32)] + +class _openusb_ctrl_request(Structure): + class _openusb_ctrl_setup(Structure): + _fields_ = [('bmRequestType', c_uint8), + ('bRequest', c_uint8), + ('wValue', c_uint16), + ('wIndex', c_uint16)] + _fields_ = [('payload', POINTER(c_uint8)), + ('length', c_uint32), + ('timeout', c_uint32), + ('flags', c_uint32), + ('result', _openusb_request_result), + ('next', c_void_p)] + +class _openusb_intr_request(Structure): + _fields_ = [('interval', c_uint16), + ('payload', POINTER(c_uint8)), + ('length', c_uint32), + ('timeout', c_uint32), + ('flags', c_uint32), + ('result', _openusb_request_result), + ('next', c_void_p)] + +class _openusb_bulk_request(Structure): + _fields_ = [('payload', POINTER(c_uint8)), + ('length', c_uint32), + ('timeout', c_uint32), + ('flags', c_uint32), + ('result', _openusb_request_result), + ('next', c_void_p)] + +class _openusb_isoc_pkts(Structure): + class _openusb_isoc_packet(Structure): + _fields_ = [('payload', POINTER(c_uint8)), + ('length', c_uint32)] + _fields_ = [('num_packets', c_uint32), + ('packets', POINTER(_openusb_isoc_packet))] + +class _openusb_isoc_request(Structure): + _fields_ = [('start_frame', c_uint32), + ('flags', c_uint32), + ('pkts', _openusb_isoc_pkts), + ('isoc_results', POINTER(_openusb_request_result)), + ('isoc_status', c_int32), + ('next', c_void_p)] + +_openusb_devid = c_uint64 +_openusb_busid = c_uint64 +_openusb_handle = c_uint64 +_openusb_dev_handle = c_uint64 + +_lib = None +_ctx = None + +def _load_library(): + libname = ctypes.util.find_library('openusb') + if libname is None: + raise OSError('USB library could not be found') + return CDLL(libname) + +def _setup_prototypes(lib): + # int32_t openusb_init(uint32_t flags , openusb_handle_t *handle); + lib.openusb_init.argtypes = [c_uint32, POINTER(_openusb_handle)] + lib.openusb_init.restype = c_int32 + + # void openusb_fini(openusb_handle_t handle ); + lib.openusb_fini.argtypes = [_openusb_handle] + + # uint32_t openusb_get_busid_list(openusb_handle_t handle, + # openusb_busid_t **busids, + # uint32_t *num_busids); + lib.openusb_get_busid_list.argtypes = [ + _openusb_handle, + POINTER(POINTER(_openusb_busid)), + POINTER(c_uint32) + ] + + # void openusb_free_busid_list(openusb_busid_t * busids); + lib.openusb_free_busid_list.argtypes = [POINTER(_openusb_busid)] + + # uint32_t openusb_get_devids_by_bus(openusb_handle_t handle, + # openusb_busid_t busid, + # openusb_devid_t **devids, + # uint32_t *num_devids); + lib.openusb_get_devids_by_bus.argtypes = [ + _openusb_handle, + _openusb_busid, + POINTER(POINTER(_openusb_devid)), + POINTER(c_uint32) + ] + + lib.openusb_get_devids_by_bus.restype = c_int32 + + # void openusb_free_devid_list(openusb_devid_t * devids); + lib.openusb_free_devid_list.argtypes = [POINTER(_openusb_devid)] + + # int32_t openusb_open_device(openusb_handle_t handle, + # openusb_devid_t devid , + # uint32_t flags, + # openusb_dev_handle_t *dev); + lib.openusb_open_device.argtypes = [ + _openusb_handle, + _openusb_devid, + c_uint32, + POINTER(_openusb_dev_handle) + ] + + lib.openusb_open_device.restype = c_int32 + + # int32_t openusb_close_device(openusb_dev_handle_t dev); + lib.openusb_close_device.argtypes = [_openusb_dev_handle] + lib.openusb_close_device.restype = c_int32 + + # int32_t openusb_set_configuration(openusb_dev_handle_t dev, + # uint8_t cfg); + lib.openusb_set_configuration.argtypes = [_openusb_dev_handle, c_uint8] + lib.openusb_set_configuration.restype = c_int32 + + # int32_t openusb_get_configuration(openusb_dev_handle_t dev, + # uint8_t *cfg); + lib.openusb_get_configuration.argtypes = [_openusb_dev_handle, POINTER(c_uint8)] + lib.openusb_get_configuration.restype = c_int32 + + # int32_t openusb_claim_interface(openusb_dev_handle_t dev, + # uint8_t ifc, + # openusb_init_flag_t flags); + lib.openusb_claim_interface.argtypes = [ + _openusb_dev_handle, + c_uint8, + c_int + ] + + lib.openusb_claim_interface.restype = c_int32 + + # int32_t openusb_release_interface(openusb_dev_handle_t dev, + # uint8_t ifc); + lib.openusb_release_interface.argtypes = [ + _openusb_dev_handle, + c_uint8 + ] + + lib.openusb_release_interface.restype = c_int32 + + # int32_topenusb_set_altsetting(openusb_dev_handle_t dev, + # uint8_t ifc, + # uint8_t alt); + lib.openusb_set_altsetting.argtypes = [ + _openusb_dev_handle, + c_uint8, + c_uint8 + ] + lib.openusb_set_altsetting.restype = c_int32 + + # int32_t openusb_reset(openusb_dev_handle_t dev); + lib.openusb_reset.argtypes = [_openusb_dev_handle] + lib.openusb_reset.restype = c_int32 + + # int32_t openusb_parse_device_desc(openusb_handle_t handle, + # openusb_devid_t devid, + # uint8_t *buffer, + # uint16_t buflen, + # usb_device_desc_t *devdesc); + lib.openusb_parse_device_desc.argtypes = [ + _openusb_handle, + _openusb_devid, + POINTER(c_uint8), + c_uint16, + POINTER(_usb_device_desc) + ] + + lib.openusb_parse_device_desc.restype = c_int32 + + # int32_t openusb_parse_config_desc(openusb_handle_t handle, + # openusb_devid_t devid, + # uint8_t *buffer, + # uint16_t buflen, + # uint8_t cfgidx, + # usb_config_desc_t *cfgdesc); + lib.openusb_parse_config_desc.argtypes = [ + _openusb_handle, + _openusb_devid, + POINTER(c_uint8), + c_uint16, + c_uint8, + POINTER(_usb_config_desc) + ] + lib.openusb_parse_config_desc.restype = c_int32 + + # int32_t openusb_parse_interface_desc(openusb_handle_t handle, + # openusb_devid_t devid, + # uint8_t *buffer, + # uint16_t buflen, + # uint8_t cfgidx, + # uint8_t ifcidx, + # uint8_t alt, + # usb_interface_desc_t *ifcdesc); + lib.openusb_parse_interface_desc.argtypes = [ + _openusb_handle, + _openusb_devid, + POINTER(c_uint8), + c_uint16, + c_uint8, + c_uint8, + c_uint8, + POINTER(_usb_interface_desc) + ] + + lib.openusb_parse_interface_desc.restype = c_int32 + + # int32_t openusb_parse_endpoint_desc(openusb_handle_t handle, + # openusb_devid_t devid, + # uint8_t *buffer, + # uint16_t buflen, + # uint8_t cfgidx, + # uint8_t ifcidx, + # uint8_t alt, + # uint8_t eptidx, + # usb_endpoint_desc_t *eptdesc); + lib.openusb_parse_endpoint_desc.argtypes = [ + _openusb_handle, + _openusb_devid, + POINTER(c_uint8), + c_uint16, + c_uint8, + c_uint8, + c_uint8, + c_uint8, + POINTER(_usb_endpoint_desc) + ] + + lib.openusb_parse_interface_desc.restype = c_int32 + + # const char *openusb_strerror(int32_t error ); + lib.openusb_strerror.argtypes = [c_int32] + lib.openusb_strerror.restype = c_char_p + + # int32_t openusb_ctrl_xfer(openusb_dev_handle_t dev, + # uint8_t ifc, + # uint8_t ept, + # openusb_ctrl_request_t *ctrl); + lib.openusb_ctrl_xfer.argtypes = [ + _openusb_dev_handle, + c_uint8, + c_uint8, + POINTER(_openusb_ctrl_request) + ] + + lib.openusb_ctrl_xfer.restype = c_int32 + + # int32_t openusb_intr_xfer(openusb_dev_handle_t dev, + # uint8_t ifc, + # uint8_t ept, + # openusb_intr_request_t *intr); + lib.openusb_intr_xfer.argtypes = [ + _openusb_dev_handle, + c_uint8, + c_uint8, + POINTER(_openusb_intr_request) + ] + + lib.openusb_bulk_xfer.restype = c_int32 + + # int32_t openusb_bulk_xfer(openusb_dev_handle_t dev, + # uint8_t ifc, + # uint8_t ept, + # openusb_bulk_request_t *bulk); + lib.openusb_bulk_xfer.argtypes = [ + _openusb_dev_handle, + c_uint8, + c_uint8, + POINTER(_openusb_bulk_request) + ] + + lib.openusb_bulk_xfer.restype = c_int32 + + # int32_t openusb_isoc_xfer(openusb_dev_handle_t dev, + # uint8_t ifc, + # uint8_t ept, + # openusb_isoc_request_t *isoc); + lib.openusb_isoc_xfer.argtypes = [ + _openusb_dev_handle, + c_uint8, + c_uint8, + POINTER(_openusb_isoc_request) + ] + + lib.openusb_isoc_xfer.restype = c_int32 + +def _check(retval): + ret = retval.value + if ret != 0: + from usb.core import USBError + raise USBError(_lib.openusb_strerror(ret), ret, _openusb_errno[ret]) + return retval + +class _Context(object): + def __init__(self): + self.handle = _openusb_handle() + _check(_lib.openusb_init(0, byref(self.handle))) + def __del__(self): + _lib.openusb_fini(self.handle) + +class _BusIterator(object): + def __init__(self): + self.buslist = POINTER(openusb_busid)() + num_busids = c_uint32() + _check(_lib.openusb_get_busid_list(_ctx.handle, + byref(self.buslist), + byref(num_busids))) + self.num_busids = num_busids.value + def __iter__(self): + for i in range(self.num_busids): + yield self.buslist[i] + def __del__(self): + _lib.openusb_free_busid_list(self.buslist) + +class _DevIterator(object): + def __init__(self, busid): + self.devlist = POINTER(_openusb_devid)() + num_devids = c_uint32() + _check(_lib.openusb_get_devids_by_bus(_ctx.handle, + busid, + byref(self.devlist), + byref(num_devids))) + self.num_devids = num_devids.value + def __iter__(self): + for i in range(self.num_devids): + yield self.devlist[i] + def __del__(self): + _lib.openusb_free_devid_list(self.devlist) + +class _OpenUSB(usb.backend.IBackend): + @methodtrace(_logger) + def enumerate_devices(self): + for bus in _BusIterator(): + for devid in _DevIterator(bus): + yield devid + + @methodtrace(_logger) + def get_device_descriptor(self, dev): + desc = _usb_device_desc() + _check(_lib.openusb_parse_device_desc(_ctx.handle, + dev, + None, + 0, + byref(desc))) + desc.bus = None + desc.address = None + return desc + + @methodtrace(_logger) + def get_configuration_descriptor(self, dev, config): + desc = _usb_config_desc() + _check(_lib.openusb_parse_config_desc(_ctx.handle, + dev, + None, + 0, + config, + byref(desc))) + return desc + + @methodtrace(_logger) + def get_interface_descriptor(self, dev, intf, alt, config): + desc = _usb_interface_desc() + _check(_lib.openusb_parse_interface_desc(_ctx.handle, + dev, + None, + 0, + config, + intf, + alt, + byref(desc))) + return desc + + @methodtrace(_logger) + def get_endpoint_descriptor(self, dev, ep, intf, alt, config): + desc = _usb_endpoint_desc() + _check(_lib.openusb_parse_endpoint_desc(_ctx.handle, + dev, + None, + 0, + config, + intf, + alt, + ep, + byref(desc))) + return desc + + @methodtrace(_logger) + def open_device(self, dev): + handle = _openusb_dev_handle() + _check(_lib.openusb_open_device(_ctx.handle, dev, 0, byref(handle))) + return handle + + @methodtrace(_logger) + def close_device(self, dev_handle): + _lib.openusb_close_device(dev_handle) + + @methodtrace(_logger) + def set_configuration(self, dev_handle, config_value): + _check(_lib.openusb_set_configuration(dev_handle, config_value)) + + @methodtrace(_logger) + def get_configuration(self, dev_handle): + config = c_uint8() + _check(_lib.openusb_get_configuration(dev_handle, byref(config))) + return config.value + + @methodtrace(_logger) + def set_interface_altsetting(self, dev_handle, intf, altsetting): + _check(_lib.set_altsetting(dev_handle, intf, altsetting)) + + @methodtrace(_logger) + def claim_interface(self, dev_handle, intf): + _check(_lib.openusb_claim_interface(dev_handle, intf, 0)) + + @methodtrace(_logger) + def release_interface(self, dev_handle, intf): + _lib.openusb_release_interface(dev_handle, intf) + + @methodtrace(_logger) + def bulk_write(self, dev_handle, ep, intf, data, timeout): + request = _openusb_bulk_request() + memset(byref(request), 0, sizeof(request)) + request.payload, request.length = data.buffer_info() + request.timeout = timeout + _check(_lib.openusb_bulk_xfer(dev_handle, intf, ep, byref(request))) + return request.transfered_bytes.value + + @methodtrace(_logger) + def bulk_read(self, dev_handle, ep, intf, size, timeout): + request = _openusb_bulk_request() + buffer = array.array('B', '\x00' * size) + memset(byref(request), 0, sizeof(request)) + request.payload, request.length = buffer.buffer_info() + request.timeout = timeout + _check(_lib.openusb_bulk_xfer(dev_handle, intf, ep, byref(request))) + return buffer[:request.transfered_bytes.value] + + @methodtrace(_logger) + def intr_write(self, dev_handle, ep, intf, data, timeout): + request = _openusb_intr_request() + memset(byref(request), 0, sizeof(request)) + payload, request.length = data.buffer_info() + request.payload = cast(payload, POINTER(c_uint8)) + request.timeout = timeout + _check(_lib.openusb_intr_xfer(dev_handle, intf, ep, byref(request))) + return request.transfered_bytes.value + + @methodtrace(_logger) + def intr_read(self, dev_handle, ep, intf, size, timeout): + request = _openusb_intr_request() + buffer = array.array('B', '\x00' * size) + memset(byref(request), 0, sizeof(request)) + payload, request.length = buffer.buffer_info() + request.payload = cast(payload, POINTER(c_uint8)) + request.timeout = timeout + _check(_lib.openusb_intr_xfer(dev_handle, intf, ep, byref(request))) + return buffer[:request.transfered_bytes.value] + +# TODO: implement isochronous +# @methodtrace(_logger) +# def iso_write(self, dev_handle, ep, intf, data, timeout): +# pass + +# @methodtrace(_logger) +# def iso_read(self, dev_handle, ep, intf, size, timeout): +# pass + + @methodtrace(_logger) + def ctrl_transfer(self, + dev_handle, + bmRequestType, + bRequest, + wValue, + wIndex, + data_or_wLength, + timeout): + request = _openusb_ctrl_request() + request.setup.bmRequestType = bmRequestType + request.setup.bRequest = bRequest + request.setup.wValue + request.setup.wIndex + request.timeout = timeout + + direction = usb.util.ctrl_direction(bmRequestType) + + if direction == ENDPOINT_OUT: + buffer = data_or_wLength + else: + buffer = array.array('B', '\x00' * data_or_wLength) + + payload, request.length = buffer.buffer_info() + request.payload = cast(payload, POINTER(c_uint8)) + + ret = _check(_lib.openusb_ctrl_xfer(dev_handle, 0, 0, byref(request))) + + if direction == ENDPOINT_OUT: + ret + else: + buffer[:ret] + + @methodtrace(_logger) + def reset_device(self, dev_handle): + _check(_lib.openusb_reset(dev_handle)) + +def get_backend(): + try: + global _lib, _ctx + if _lib is None: + _lib = _load_library() + _setup_prototypes(_lib) + _ctx = _Context() + return _OpenUSB() + except Exception: + _logger.error('Error loading OpenUSB backend', exc_info=True) + return None diff --git a/plugins/wedo_plugin/usb/control.py b/plugins/wedo_plugin/usb/control.py new file mode 100644 index 0000000..8647c14 --- /dev/null +++ b/plugins/wedo_plugin/usb/control.py @@ -0,0 +1,252 @@ +# Copyright (C) 2009-2011 Wander Lairson Costa +# +# The following terms apply to all files associated +# with the software unless explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. + +r"""usb.control - USB standard control requests + +This module exports: + +get_status - get recipeint status +clear_feature - clear a recipient feature +set_feature - set a recipient feature +get_descriptor - get a device descriptor +set_descriptor - set a device descriptor +get_configuration - get a device configuration +set_configuration - set a device configuration +get_interface - get a device interface +set_interface - set a device interface +""" + +__author__ = 'Wander Lairson Costa' + +__all__ = ['get_status', + 'clear_feature', + 'set_feature', + 'get_descriptor', + 'set_descriptor', + 'get_configuration', + 'set_configuration', + 'get_interface', + 'set_interface', + 'ENDPOINT_HALT', + 'FUNCTION_SUSPEND', + 'DEVICE_REMOTE_WAKEUP', + 'U1_ENABLE', + 'U2_ENABLE', + 'LTM_ENABLE'] + +import usb.util as util +import usb.core as core + +def _parse_recipient(recipient, direction): + if recipient is None: + r = util.CTRL_RECIPIENT_DEVICE + wIndex = 0 + elif isinstance(recipient, core.Interface): + r = util.CTRL_RECIPIENT_INTERFACE + wIndex = recipient.bInterfaceNumber + elif isinstance(recipient, core.Endpoint): + r = util.CTRL_RECIPIENT_ENDPOINT + wIndex = recipient.bEndpointAddress + else: + raise ValueError('Invalid recipient.') + bmRequestType = util.build_request_type( + direction, + util.CTRL_TYPE_STANDARD, + r + ) + return (bmRequestType, wIndex) + +# standard feature selectors from USB 2.0/3.0 +ENDPOINT_HALT = 0 +FUNCTION_SUSPEND = 0 +DEVICE_REMOTE_WAKEUP = 1 +U1_ENABLE = 48 +U2_ENABLE = 49 +LTM_ENABLE = 50 + +def get_status(dev, recipient = None): + r"""Return the status for the specified recipient. + + dev is the Device object to which the request will be + sent to. + + The recipient can be None (on which the status will be queried + on the device), an Interface or Endpoint descriptors. + + The status value is returned as an integer with the lower + word being the two bytes status value. + """ + bmRequestType, wIndex = _parse_recipient(recipient, util.CTRL_IN) + ret = dev.ctrl_transfer(bmRequestType = bmRequestType, + bRequest = 0x00, + wIndex = wIndex, + data_or_wLength = 2) + return ret[0] | (ret[1] << 8) + +def clear_feature(dev, feature, recipient = None): + r"""Clear/disable a specific feature. + + dev is the Device object to which the request will be + sent to. + + feature is the feature you want to disable. + + The recipient can be None (on which the status will be queried + on the device), an Interface or Endpoint descriptors. + """ + bmRequestType, wIndex = _parse_recipient(recipient, util.CTRL_OUT) + dev.ctrl_transfer(bmRequestType = bmRequestType, + bRequest = 0x01, + wIndex = wIndex, + wValue = feature) + +def set_feature(dev, feature, recipient = None): + r"""Set/enable a specific feature. + + dev is the Device object to which the request will be + sent to. + + feature is the feature you want to enable. + + The recipient can be None (on which the status will be queried + on the device), an Interface or Endpoint descriptors. + """ + bmRequestType, wIndex = _parse_recipient(recipient, util.CTRL_OUT) + dev.ctrl_transfer(bmRequestType = bmRequestType, + bRequest = 0x03, + wIndex = wIndex, + wValue = feature) + +def get_descriptor(dev, desc_size, desc_type, desc_index, wIndex = 0): + r"""Return the specified descriptor. + + dev is the Device object to which the request will be + sent to. + + desc_size is the descriptor size. + + desc_type and desc_index are the descriptor type and index, + respectively. wIndex index is used for string descriptors + and represents the Language ID. For other types of descriptors, + it is zero. + """ + wValue = desc_index | (desc_type << 8) + bmRequestType = util.build_request_type( + util.CTRL_IN, + util.CTRL_TYPE_STANDARD, + util.CTRL_RECIPIENT_DEVICE + ) + return dev.ctrl_transfer( + bmRequestType = bmRequestType, + bRequest = 0x06, + wValue = wValue, + wIndex = wIndex, + data_or_wLength = desc_size + ) + +def set_descriptor(dev, desc, desc_type, desc_index, wIndex = None): + r"""Update an existing descriptor or add a new one. + + dev is the Device object to which the request will be + sent to. + + The desc parameter is the descriptor to be sent to the device. + desc_type and desc_index are the descriptor type and index, + respectively. wIndex index is used for string descriptors + and represents the Language ID. For other types of descriptors, + it is zero. + """ + wValue = desc_index | (desc_type << 8) + bmRequestType = util.build_request_type( + util.CTRL_OUT, + util.CTRL_TYPE_STANDARD, + util.CTRL_RECIPIENT_DEVICE + ) + dev.ctrl_transfer( + bmRequestType = bmRequestType, + bRequest = 0x07, + wValue = wValue, + wIndex = wIndex, + data_or_wLength = desc + ) + +def get_configuration(dev): + r"""Get the current active configuration of the device. + + dev is the Device object to which the request will be + sent to. + + This function differs from the Device.get_active_configuration + method because the later may use cached data, while this + function always does a device request. + """ + bmRequestType = util.build_request_type( + util.CTRL_IN, + util.CTRL_TYPE_STANDARD, + util.CTRL_RECIPIENT_DEVICE + ) + return dev.ctrl_transfer( + bmRequestType, + bRequest = 0x08, + data_or_wLength = 1 + )[0] + +def set_configuration(dev, bConfigurationNumber): + r"""Set the current device configuration. + + dev is the Device object to which the request will be + sent to. + """ + dev.set_configuration(bConfigurationNumber) + +def get_interface(dev, bInterfaceNumber): + r"""Get the current alternate setting of the interface. + + dev is the Device object to which the request will be + sent to. + """ + bmRequestType = util.build_request_type( + util.CTRL_IN, + util.CTRL_TYPE_STANDARD, + util.CTRL_RECIPIENT_INTERFACE + ) + return dev.ctrl_transfer( + bmRequestType = bmRequestType, + bRequest = 0x0a, + wIndex = bInterfaceNumber, + data_or_wLength = 1 + )[0] + +def set_interface(dev, bInterfaceNumber, bAlternateSetting): + r"""Set the alternate setting of the interface. + + dev is the Device object to which the request will be + sent to. + """ + dev.set_interface_altsetting(bInterfaceNumber, bAlternateSetting) + diff --git a/plugins/wedo_plugin/usb/core.py b/plugins/wedo_plugin/usb/core.py new file mode 100644 index 0000000..c90d011 --- /dev/null +++ b/plugins/wedo_plugin/usb/core.py @@ -0,0 +1,856 @@ +# Copyright (C) 2009-2011 Wander Lairson Costa +# +# The following terms apply to all files associated +# with the software unless explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. + +r"""usb.core - Core USB features. + +This module exports: + +Device - a class representing a USB device. +Configuration - a class representing a configuration descriptor. +Interface - a class representing an interface descriptor. +Endpoint - a class representing an endpoint descriptor. +find() - a function to find USB devices. +""" + +__author__ = 'Wander Lairson Costa' + +__all__ = ['Device', 'Configuration', 'Interface', 'Endpoint', 'find'] + +import usb.util as util +import copy +import operator +import usb._interop as _interop +import logging + +_logger = logging.getLogger('usb.core') + +_DEFAULT_TIMEOUT = 1000 + +def _set_attr(input, output, fields): + for f in fields: + setattr(output, f, int(getattr(input, f))) + +class _ResourceManager(object): + def __init__(self, dev, backend): + self.backend = backend + self._active_cfg_index = None + self.dev = dev + self.handle = None + self._claimed_intf = _interop._set() + self._alt_set = {} + self._ep_type_map = {} + + def managed_open(self): + if self.handle is None: + self.handle = self.backend.open_device(self.dev) + return self.handle + + def managed_close(self): + if self.handle is not None: + self.backend.close_device(self.handle) + self.handle = None + + def managed_set_configuration(self, device, config): + if config is None: + cfg = device[0] + elif isinstance(config, Configuration): + cfg = config + elif config == 0: # unconfigured state + class FakeConfiguration(object): + def __init__(self): + self.index = None + self.bConfigurationValue = 0 + cfg = FakeConfiguration() + else: + cfg = util.find_descriptor(device, bConfigurationValue=config) + self.managed_open() + self.backend.set_configuration(self.handle, cfg.bConfigurationValue) + # cache the index instead of the object to avoid cyclic references + # of the device and Configuration (Device tracks the _ResourceManager, + # which tracks the Configuration, which tracks the Device) + self._active_cfg_index = cfg.index + # after changing configuration, our alternate setting and endpoint type caches + # are not valid anymore + self._ep_type_map.clear() + self._alt_set.clear() + + def managed_claim_interface(self, device, intf): + self.managed_open() + if intf is None: + cfg = self.get_active_configuration(device) + i = cfg[(0,0)].bInterfaceNumber + elif isinstance(intf, Interface): + i = intf.bInterfaceNumber + else: + i = intf + if i not in self._claimed_intf: + self.backend.claim_interface(self.handle, i) + self._claimed_intf.add(i) + + def managed_release_interface(self, device, intf): + if intf is None: + cfg = self.get_active_configuration(device) + i = cfg[(0,0)].bInterfaceNumber + elif isinstance(intf, Interface): + i = intf.bInterfaceNumber + else: + i = intf + if i in self._claimed_intf: + self.backend.release_interface(self.handle, i) + self._claimed_intf.remove(i) + + def managed_set_interface(self, device, intf, alt): + if intf is None: + i = self.get_interface(device, intf) + elif isinstance(intf, Interface): + i = intf + else: + cfg = self.get_active_configuration(device) + if alt is not None: + i = util.find_descriptor(cfg, bInterfaceNumber=intf, bAlternateSetting=alt) + else: + i = util.find_descriptor(cfg, bInterfaceNumber=intf) + self.managed_claim_interface(device, i) + if alt is None: + alt = i.bAlternateSetting + self.backend.set_interface_altsetting(self.handle, i.bInterfaceNumber, alt) + self._alt_set[i.bInterfaceNumber] = alt + + def get_interface(self, device, intf): + # TODO: check the viability of issuing a GET_INTERFACE + # request when we don't have a alternate setting cached + if intf is None: + cfg = self.get_active_configuration(device) + return cfg[(0,0)] + elif isinstance(intf, Interface): + return intf + else: + cfg = self.get_active_configuration(device) + if intf in self._alt_set: + return util.find_descriptor(cfg, + bInterfaceNumber=intf, + bAlternateSetting=self._alt_set[intf]) + else: + return util.find_descriptor(cfg, bInterfaceNumber=intf) + + def get_active_configuration(self, device): + if self._active_cfg_index is None: + self.managed_open() + cfg = util.find_descriptor( + device, + bConfigurationValue=self.backend.get_configuration(self.handle) + ) + if cfg is None: + raise USBError('Configuration not set') + self._active_cfg_index = cfg.index + return cfg + return device[self._active_cfg_index] + + def get_endpoint_type(self, device, address, intf): + intf = self.get_interface(device, intf) + key = (address, intf.bInterfaceNumber, intf.bAlternateSetting) + try: + return self._ep_type_map[key] + except KeyError: + e = util.find_descriptor(intf, bEndpointAddress=address) + etype = util.endpoint_type(e.bmAttributes) + self._ep_type_map[key] = etype + return etype + + def release_all_interfaces(self, device): + claimed = copy.copy(self._claimed_intf) + for i in claimed: + self.managed_release_interface(device, i) + + def dispose(self, device, close_handle = True): + self.release_all_interfaces(device) + if close_handle: + self.managed_close() + self._ep_type_map.clear() + self._alt_set.clear() + self._active_cfg_index = None + +class USBError(IOError): + r"""Exception class for USB errors. + + Backends must raise this exception when USB related errors occur. + The backend specific error code is available through the + 'backend_error_code' member variable. + """ + + def __init__(self, strerror, error_code = None, errno = None): + r"""Initialize the object. + + This initializes the USBError object. The strerror and errno are passed + to the parent object. The error_code parameter is attributed to the + backend_error_code member variable. + """ + IOError.__init__(self, errno, strerror) + self.backend_error_code = error_code + +class Endpoint(object): + r"""Represent an endpoint object. + + This class contains all fields of the Endpoint Descriptor + according to the USB Specification. You may access them as class + properties. For example, to access the field bEndpointAddress + of the endpoint descriptor: + + >>> import usb.core + >>> dev = usb.core.find() + >>> for cfg in dev: + >>> for i in cfg: + >>> for e in i: + >>> print e.bEndpointAddress + """ + + def __init__(self, device, endpoint, interface = 0, + alternate_setting = 0, configuration = 0): + r"""Initialize the Endpoint object. + + The device parameter is the device object returned by the find() + function. endpoint is the endpoint logical index (not the endpoint address). + The configuration parameter is the logical index of the + configuration (not the bConfigurationValue field). The interface + parameter is the interface logical index (not the bInterfaceNumber field) + and alternate_setting is the alternate setting logical index (not the + bAlternateSetting value). Not every interface has more than one alternate + setting. In this case, the alternate_setting parameter should be zero. + By "logical index" we mean the relative order of the configurations returned by the + peripheral as a result of GET_DESCRIPTOR request. + """ + self.device = device + intf = Interface(device, interface, alternate_setting, configuration) + self.interface = intf.bInterfaceNumber + self.index = endpoint + + backend = device._ctx.backend + + desc = backend.get_endpoint_descriptor( + device._ctx.dev, + endpoint, + interface, + alternate_setting, + configuration + ) + + _set_attr( + desc, + self, + ( + 'bLength', + 'bDescriptorType', + 'bEndpointAddress', + 'bmAttributes', + 'wMaxPacketSize', + 'bInterval', + 'bRefresh', + 'bSynchAddress' + ) + ) + + def write(self, data, timeout = None): + r"""Write data to the endpoint. + + The parameter data contains the data to be sent to the endpoint and + timeout is the time limit of the operation. The transfer type and + endpoint address are automatically inferred. + + The method returns the number of bytes written. + + For details, see the Device.write() method. + """ + return self.device.write(self.bEndpointAddress, data, self.interface, timeout) + + def read(self, size, timeout = None): + r"""Read data from the endpoint. + + The parameter size is the number of bytes to read and timeout is the + time limit of the operation.The transfer type and endpoint address + are automatically inferred. + + The method returns an array.array object with the data read. + + For details, see the Device.read() method. + """ + return self.device.read(self.bEndpointAddress, size, self.interface, timeout) + +class Interface(object): + r"""Represent an interface object. + + This class contains all fields of the Interface Descriptor + according to the USB Specification. You may access them as class + properties. For example, to access the field bInterfaceNumber + of the interface descriptor: + + >>> import usb.core + >>> dev = usb.core.find() + >>> for cfg in dev: + >>> for i in cfg: + >>> print i.bInterfaceNumber + """ + + def __init__(self, device, interface = 0, + alternate_setting = 0, configuration = 0): + r"""Initialize the interface object. + + The device parameter is the device object returned by the find() + function. The configuration parameter is the logical index of the + configuration (not the bConfigurationValue field). The interface + parameter is the interface logical index (not the bInterfaceNumber field) + and alternate_setting is the alternate setting logical index (not the + bAlternateSetting value). Not every interface has more than one alternate + setting. In this case, the alternate_setting parameter should be zero. + By "logical index" we mean the relative order of the configurations returned by the + peripheral as a result of GET_DESCRIPTOR request. + """ + self.device = device + self.alternate_index = alternate_setting + self.index = interface + self.configuration = configuration + + backend = device._ctx.backend + + desc = backend.get_interface_descriptor( + self.device._ctx.dev, + interface, + alternate_setting, + configuration + ) + + _set_attr( + desc, + self, + ( + 'bLength', + 'bDescriptorType', + 'bInterfaceNumber', + 'bAlternateSetting', + 'bNumEndpoints', + 'bInterfaceClass', + 'bInterfaceSubClass', + 'bInterfaceProtocol', + 'iInterface', + ) + ) + + def set_altsetting(self): + r"""Set the interface alternate setting.""" + self.device.set_interface_altsetting( + self.bInterfaceNumber, + self.bAlternateSetting + ) + + def __iter__(self): + r"""Iterate over all endpoints of the interface.""" + for i in range(self.bNumEndpoints): + yield Endpoint( + self.device, + i, + self.index, + self.alternate_index, + self.configuration + ) + def __getitem__(self, index): + r"""Return the Endpoint object in the given position.""" + return Endpoint( + self.device, + index, + self.index, + self.alternate_index, + self.configuration + ) + +class Configuration(object): + r"""Represent a configuration object. + + This class contains all fields of the Configuration Descriptor + according to the USB Specification. You may access them as class + properties. For example, to access the field bConfigurationValue + of the configuration descriptor: + + >>> import usb.core + >>> dev = usb.core.find() + >>> for cfg in dev: + >>> print cfg.bConfigurationValue + """ + + def __init__(self, device, configuration = 0): + r"""Initialize the configuration object. + + The device parameter is the device object returned by the find() + function. The configuration parameter is the logical index of the + configuration (not the bConfigurationValue field). By "logical index" + we mean the relative order of the configurations returned by the + peripheral as a result of GET_DESCRIPTOR request. + """ + self.device = device + self.index = configuration + + backend = device._ctx.backend + + desc = backend.get_configuration_descriptor( + self.device._ctx.dev, + configuration + ) + + _set_attr( + desc, + self, + ( + 'bLength', + 'bDescriptorType', + 'wTotalLength', + 'bNumInterfaces', + 'bConfigurationValue', + 'iConfiguration', + 'bmAttributes', + 'bMaxPower' + ) + ) + + def set(self): + r"""Set this configuration as the active one.""" + self.device.set_configuration(self.bConfigurationValue) + + def __iter__(self): + r"""Iterate over all interfaces of the configuration.""" + for i in range(self.bNumInterfaces): + alt = 0 + try: + while True: + yield Interface(self.device, i, alt, self.index) + alt += 1 + except (USBError, IndexError): + pass + def __getitem__(self, index): + r"""Return the Interface object in the given position. + + index is a tuple of two values with interface index and + alternate setting index, respectivally. Example: + + >>> interface = config[(0, 0)] + """ + return Interface(self.device, index[0], index[1], self.index) + + +class Device(object): + r"""Device object. + + This class contains all fields of the Device Descriptor according + to the USB Specification. You may access them as class properties. + For example, to access the field bDescriptorType of the device + descriptor: + + >>> import usb.core + >>> dev = usb.core.find() + >>> dev.bDescriptorType + + Additionally, the class provides methods to communicate with + the hardware. Typically, an application will first call the + set_configuration() method to put the device in a known configured + state, optionally call the set_interface_altsetting() to select the + alternate setting (if there is more than one) of the interface used, + and call the write() and read() method to send and receive data. + + When working in a new hardware, one first try would be like this: + + >>> import usb.core + >>> dev = usb.core.find(idVendor=myVendorId, idProduct=myProductId) + >>> dev.set_configuration() + >>> dev.write(1, 'test') + + This sample finds the device of interest (myVendorId and myProductId should be + replaced by the corresponding values of your device), then configures the device + (by default, the configuration value is 1, which is a typical value for most + devices) and then writes some data to the endpoint 0x01. + + Timeout values for the write, read and ctrl_transfer methods are specified in + miliseconds. If the parameter is omitted, Device.default_timeout value will + be used instead. This property can be set by the user at anytime. + """ + + def __init__(self, dev, backend): + r"""Initialize the Device object. + + Library users should normally get a Device instance through + the find function. The dev parameter is the identification + of a device to the backend and its meaning is opaque outside + of it. The backend parameter is a instance of a backend + object. + """ + self._ctx = _ResourceManager(dev, backend) + self.__default_timeout = _DEFAULT_TIMEOUT + + desc = backend.get_device_descriptor(dev) + + _set_attr( + desc, + self, + ( + 'bLength', + 'bDescriptorType', + 'bcdUSB', + 'bDeviceClass', + 'bDeviceSubClass', + 'bDeviceProtocol', + 'bMaxPacketSize0', + 'idVendor', + 'idProduct', + 'bcdDevice', + 'iManufacturer', + 'iProduct', + 'iSerialNumber', + 'bNumConfigurations', + 'address', + 'bus' + ) + ) + + self.bus = int(desc.bus) if desc.bus is not None else None + self.address = int(desc.address) if desc.address is not None else None + + def set_configuration(self, configuration = None): + r"""Set the active configuration. + + The configuration parameter is the bConfigurationValue field of the + configuration you want to set as active. If you call this method + without parameter, it will use the first configuration found. + As a device hardly ever has more than one configuration, calling + the method without parameter is enough to get the device ready. + """ + self._ctx.managed_set_configuration(self, configuration) + + def get_active_configuration(self): + r"""Return a Configuration object representing the current configuration set.""" + return self._ctx.get_active_configuration(self) + + def set_interface_altsetting(self, interface = None, alternate_setting = None): + r"""Set the alternate setting for an interface. + + When you want to use an interface and it has more than one alternate setting, + you should call this method to select the alternate setting you would like + to use. If you call the method without one or the two parameters, it will + be selected the first one found in the Device in the same way of set_configuration + method. + + Commonly, an interface has only one alternate setting and this call is + not necessary. For most of the devices, either it has more than one alternate + setting or not, it is not harmful to make a call to this method with no arguments, + as devices will silently ignore the request when there is only one alternate + setting, though the USB Spec allows devices with no additional alternate setting + return an error to the Host in response to a SET_INTERFACE request. + + If you are in doubt, you may want to call it with no arguments wrapped by + a try/except clause: + + >>> try: + >>> dev.set_interface_altsetting() + >>> except usb.core.USBError: + >>> pass + """ + self._ctx.managed_set_interface(self, interface, alternate_setting) + + def reset(self): + r"""Reset the device.""" + self._ctx.dispose(self, False) + self._ctx.backend.reset_device(self._ctx.handle) + self._ctx.dispose(self, True) + + def write(self, endpoint, data, interface = None, timeout = None): + r"""Write data to the endpoint. + + This method is used to send data to the device. The endpoint parameter + corresponds to the bEndpointAddress member whose endpoint you want to + communicate with. The interface parameter is the bInterfaceNumber field + of the interface descriptor which contains the endpoint. If you do not + provide one, the first one found will be used, as explained in the + set_interface_altsetting() method. + + The data parameter should be a sequence like type convertible to + array type (see array module). + + The timeout is specified in miliseconds. + + The method returns the number of bytes written. + """ + backend = self._ctx.backend + + fn_map = { + util.ENDPOINT_TYPE_BULK:backend.bulk_write, + util.ENDPOINT_TYPE_INTR:backend.intr_write, + util.ENDPOINT_TYPE_ISO:backend.iso_write + } + + intf = self._ctx.get_interface(self, interface) + fn = fn_map[self._ctx.get_endpoint_type(self, endpoint, intf)] + self._ctx.managed_claim_interface(self, intf) + + return fn( + self._ctx.handle, + endpoint, + intf.bInterfaceNumber, + _interop.as_array(data), + self.__get_timeout(timeout) + ) + + def read(self, endpoint, size, interface = None, timeout = None): + r"""Read data from the endpoint. + + This method is used to receive data from the device. The endpoint parameter + corresponds to the bEndpointAddress member whose endpoint you want to + communicate with. The interface parameter is the bInterfaceNumber field + of the interface descriptor which contains the endpoint. If you do not + provide one, the first one found will be used, as explained in the + set_interface_altsetting() method. The size parameters tells how many + bytes you want to read. + + The timeout is specified in miliseconds. + + The method returns an array object with the data read. + """ + backend = self._ctx.backend + + fn_map = { + util.ENDPOINT_TYPE_BULK:backend.bulk_read, + util.ENDPOINT_TYPE_INTR:backend.intr_read, + util.ENDPOINT_TYPE_ISO:backend.iso_read + } + + intf = self._ctx.get_interface(self, interface) + fn = fn_map[self._ctx.get_endpoint_type(self, endpoint, intf)] + self._ctx.managed_claim_interface(self, intf) + + return fn( + self._ctx.handle, + endpoint, + intf.bInterfaceNumber, + size, + self.__get_timeout(timeout) + ) + + + def ctrl_transfer(self, bmRequestType, bRequest, wValue=0, wIndex=0, + data_or_wLength = None, timeout = None): + r"""Do a control transfer on the endpoint 0. + + This method is used to issue a control transfer over the + endpoint 0(endpoint 0 is required to always be a control endpoint). + + The parameters bmRequestType, bRequest, wValue and wIndex are the + same of the USB Standard Control Request format. + + Control requests may or may not have a data payload to write/read. + In cases which it has, the direction bit of the bmRequestType + field is used to infere the desired request direction. For + host to device requests (OUT), data_or_wLength parameter is + the data payload to send, and it must be a sequence type convertible + to an array object. In this case, the return value is the number of data + payload written. For device to host requests (IN), data_or_wLength + is the wLength parameter of the control request specifying the + number of bytes to read in data payload. In this case, the return + value is the data payload read, as an array object. + """ + if util.ctrl_direction(bmRequestType) == util.CTRL_OUT: + a = _interop.as_array(data_or_wLength) + elif data_or_wLength is None: + a = 0 + else: + a = data_or_wLength + + self._ctx.managed_open() + + return self._ctx.backend.ctrl_transfer( + self._ctx.handle, + bmRequestType, + bRequest, + wValue, + wIndex, + a, + self.__get_timeout(timeout) + ) + + def is_kernel_driver_active(self, interface): + r"""Determine if there is kernel driver associated with the interface. + + If a kernel driver is active, and the object will be unable to perform I/O. + """ + self._ctx.managed_open() + return self._ctx.backend.is_kernel_driver_active(self._ctx.handle, interface) + + def detach_kernel_driver(self, interface): + r"""Detach a kernel driver. + + If successful, you will then be able to perform I/O. + """ + self._ctx.managed_open() + self._ctx.backend.detach_kernel_driver(self._ctx.handle, interface) + + def attach_kernel_driver(self, interface): + r"""Re-attach an interface's kernel driver, which was previously + detached using detach_kernel_driver().""" + self._ctx.managed_open() + self._ctx.backend.attach_kernel_driver(self._ctx.handle, interface) + + def __iter__(self): + r"""Iterate over all configurations of the device.""" + for i in range(self.bNumConfigurations): + yield Configuration(self, i) + + def __getitem__(self, index): + r"""Return the Configuration object in the given position.""" + return Configuration(self, index) + + def __del__(self): + self._ctx.dispose(self) + + def __get_timeout(self, timeout): + if timeout is not None: + return timeout + return self.__default_timeout + + def __set_def_tmo(self, tmo): + if tmo < 0: + raise ValueError('Timeout cannot be a negative value') + self.__default_timeout = tmo + + def __get_def_tmo(self): + return self.__default_timeout + + default_timeout = property( + __get_def_tmo, + __set_def_tmo, + doc = 'Default timeout for transfer I/O functions' + ) + +def find(find_all=False, backend = None, custom_match = None, **args): + r"""Find an USB device and return it. + + find() is the function used to discover USB devices. + You can pass as arguments any combination of the + USB Device Descriptor fields to match a device. For example: + + find(idVendor=0x3f4, idProduct=0x2009) + + will return the Device object for the device with + idVendor Device descriptor field equals to 0x3f4 and + idProduct equals to 0x2009. + + If there is more than one device which matchs the criteria, + the first one found will be returned. If a matching device cannot + be found the function returns None. If you want to get all + devices, you can set the parameter find_all to True, then find + will return an list with all matched devices. If no matching device + is found, it will return an empty list. Example: + + printers = find(find_all=True, bDeviceClass=7) + + This call will get all the USB printers connected to the system. + (actually may be not, because some devices put their class + information in the Interface Descriptor). + + You can also use a customized match criteria: + + dev = find(custom_match = lambda d: d.idProduct=0x3f4 and d.idvendor=0x2009) + + A more accurate printer finder using a customized match would be like + so: + + def is_printer(dev): + import usb.util + if dev.bDeviceClass == 7: + return True + for cfg in dev: + if usb.util.find_descriptor(cfg, bInterfaceClass=7) is not None: + return True + + printers = find(find_all=True, custom_match = is_printer) + + Now even if the device class code is in the interface descriptor the + printer will be found. + + You can combine a customized match with device descriptor fields. In this + case, the fields must match and the custom_match must return True. In the our + previous example, if we would like to get all printers belonging to the + manufacturer 0x3f4, the code would be like so: + + printers = find(find_all=True, idVendor=0x3f4, custom_match=is_printer) + + If you want to use find as a 'list all devices' function, just call + it with find_all = True: + + devices = find(find_all=True) + + Finally, you may pass a custom backend to the find function: + + find(backend = MyBackend()) + + PyUSB has builtin backends for libusb 0.1, libusb 1.0 and OpenUSB. + If you do not supply a backend explicitly, find() function will select + one of the predefineds backends according to system availability. + + Backends are explained in the usb.backend module. + """ + + def device_iter(k, v): + for dev in backend.enumerate_devices(): + d = Device(dev, backend) + if (custom_match is None or custom_match(d)) and \ + _interop._reduce( + lambda a, b: a and b, + map( + operator.eq, + v, + map(lambda i: getattr(d, i), k) + ), + True + ): + yield d + + if backend is None: + import usb.backend.libusb10 as libusb10 + import usb.backend.libusb01 as libusb01 + import usb.backend.openusb as openusb + + for m in (libusb10, openusb, libusb01): + backend = m.get_backend() + if backend is not None: + _logger.info('find(): using backend "%s"', m.__name__) + break + else: + raise ValueError('No backend available') + + k, v = args.keys(), args.values() + + if find_all: + return [d for d in device_iter(k, v)] + else: + try: + return _interop._next(device_iter(k, v)) + except StopIteration: + return None diff --git a/plugins/wedo_plugin/usb/legacy.py b/plugins/wedo_plugin/usb/legacy.py new file mode 100644 index 0000000..0c9c52c --- /dev/null +++ b/plugins/wedo_plugin/usb/legacy.py @@ -0,0 +1,334 @@ +# Copyright (C) 2009-2011 Wander Lairson Costa +# +# The following terms apply to all files associated +# with the software unless explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. + +import usb.core as core +import usb.util as util +import usb._interop as _interop +import usb.control as control + +__author__ = 'Wander Lairson Costa' + +USBError = core.USBError + +CLASS_AUDIO = 1 +CLASS_COMM = 2 +CLASS_DATA = 10 +CLASS_HID = 3 +CLASS_HUB = 9 +CLASS_MASS_STORAGE = 8 +CLASS_PER_INTERFACE = 0 +CLASS_PRINTER = 7 +CLASS_VENDOR_SPEC = 255 +DT_CONFIG = 2 +DT_CONFIG_SIZE = 9 +DT_DEVICE = 1 +DT_DEVICE_SIZE = 18 +DT_ENDPOINT = 5 +DT_ENDPOINT_AUDIO_SIZE = 9 +DT_ENDPOINT_SIZE = 7 +DT_HID = 33 +DT_HUB = 41 +DT_HUB_NONVAR_SIZE = 7 +DT_INTERFACE = 4 +DT_INTERFACE_SIZE = 9 +DT_PHYSICAL = 35 +DT_REPORT = 34 +DT_STRING = 3 +ENDPOINT_ADDRESS_MASK = 15 +ENDPOINT_DIR_MASK = 128 +ENDPOINT_IN = 128 +ENDPOINT_OUT = 0 +ENDPOINT_TYPE_BULK = 2 +ENDPOINT_TYPE_CONTROL = 0 +ENDPOINT_TYPE_INTERRUPT = 3 +ENDPOINT_TYPE_ISOCHRONOUS = 1 +ENDPOINT_TYPE_MASK = 3 +ERROR_BEGIN = 500000 +MAXALTSETTING = 128 +MAXCONFIG = 8 +MAXENDPOINTS = 32 +MAXINTERFACES = 32 +RECIP_DEVICE = 0 +RECIP_ENDPOINT = 2 +RECIP_INTERFACE = 1 +RECIP_OTHER = 3 +REQ_CLEAR_FEATURE = 1 +REQ_GET_CONFIGURATION = 8 +REQ_GET_DESCRIPTOR = 6 +REQ_GET_INTERFACE = 10 +REQ_GET_STATUS = 0 +REQ_SET_ADDRESS = 5 +REQ_SET_CONFIGURATION = 9 +REQ_SET_DESCRIPTOR = 7 +REQ_SET_FEATURE = 3 +REQ_SET_INTERFACE = 11 +REQ_SYNCH_FRAME = 12 +TYPE_CLASS = 32 +TYPE_RESERVED = 96 +TYPE_STANDARD = 0 +TYPE_VENDOR = 64 + +class Endpoint(object): + r"""Endpoint descriptor object.""" + def __init__(self, ep): + self.address = ep.bEndpointAddress + self.interval = ep.bInterval + self.maxPacketSize = ep.wMaxPacketSize + self.type = util.endpoint_type(ep.bmAttributes) + +class Interface(object): + r"""Interface descriptor object.""" + def __init__(self, intf): + self.alternateSetting = intf.bAlternateSetting + self.interfaceNumber = intf.bInterfaceNumber + self.iInterface = intf.iInterface + self.interfaceClass = intf.bInterfaceClass + self.interfaceSubClass = intf.bInterfaceSubClass + self.interfaceProtocol = intf.bInterfaceProtocol + self.endpoints = [Endpoint(e) for e in intf] + +class Configuration(object): + r"""Configuration descriptor object.""" + def __init__(self, cfg): + self.iConfiguration = cfg.iConfiguration + self.maxPower = cfg.bMaxPower << 2 + self.remoteWakeup = (cfg.bmAttributes >> 5) & 1 + self.selfPowered = (cfg.bmAttributes >> 6) & 1 + self.totalLength = cfg.wTotalLength + self.value = cfg.bConfigurationValue + self.interfaces = [ + list(g) for k, g in _interop._groupby( + _interop._sorted( + [Interface(i) for i in cfg], + key=lambda i: i.interfaceNumber + ), + lambda i: i.alternateSetting) + ] + +class DeviceHandle(object): + def __init__(self, dev): + self.dev = dev + self.__claimed_interface = -1 + + def bulkWrite(self, endpoint, buffer, timeout = 100): + r"""Perform a bulk write request to the endpoint specified. + + Arguments: + endpoint: endpoint number. + buffer: sequence data buffer to write. + This parameter can be any sequence type. + timeout: operation timeout in miliseconds. (default: 100) + Returns the number of bytes written. + """ + return self.dev.write(endpoint, buffer, self.__claimed_interface, timeout) + + def bulkRead(self, endpoint, size, timeout = 100): + r"""Performs a bulk read request to the endpoint specified. + + Arguments: + endpoint: endpoint number. + size: number of bytes to read. + timeout: operation timeout in miliseconds. (default: 100) + Return a tuple with the data read. + """ + return self.dev.read(endpoint, size, self.__claimed_interface, timeout) + + def interruptWrite(self, endpoint, buffer, timeout = 100): + r"""Perform a interrupt write request to the endpoint specified. + + Arguments: + endpoint: endpoint number. + buffer: sequence data buffer to write. + This parameter can be any sequence type. + timeout: operation timeout in miliseconds. (default: 100) + Returns the number of bytes written. + """ + return self.dev.write(endpoint, buffer, self.__claimed_interface, timeout) + + def interruptRead(self, endpoint, size, timeout = 100): + r"""Performs a interrupt read request to the endpoint specified. + + Arguments: + endpoint: endpoint number. + size: number of bytes to read. + timeout: operation timeout in miliseconds. (default: 100) + Return a tuple with the data read. + """ + return self.dev.read(endpoint, size, self.__claimed_interface, timeout) + + def controlMsg(self, requestType, request, buffer, value = 0, index = 0, timeout = 100): + r"""Perform a control request to the default control pipe on a device. + + Arguments: + requestType: specifies the direction of data flow, the type + of request, and the recipient. + request: specifies the request. + buffer: if the transfer is a write transfer, buffer is a sequence + with the transfer data, otherwise, buffer is the number of + bytes to read. + value: specific information to pass to the device. (default: 0) + index: specific information to pass to the device. (default: 0) + timeout: operation timeout in miliseconds. (default: 100) + Return the number of bytes written. + """ + return self.dev.ctrl_transfer( + requestType, + request, + wValue = value, + wIndex = index, + data_or_wLength = buffer, + timeout = timeout + ) + + def clearHalt(self, endpoint): + r"""Clears any halt status on the specified endpoint. + + Arguments: + endpoint: endpoint number. + """ + cfg = self.dev.get_active_configuration() + intf = util.find_descriptor(cfg, bInterfaceNumber = self.__claimed_interface) + e = util.find_descriptor(intf, bEndpointAddress = endpoint) + control.clear_feature(self.dev, control.ENDPOINT_HALT, e) + + def claimInterface(self, interface): + r"""Claims the interface with the Operating System. + + Arguments: + interface: interface number or an Interface object. + """ + if_num = interface.interfaceNumber \ + if isinstance(interface, Interface) else interface + + util.claim_interface(self.dev, if_num) + self.__claimed_interface = if_num + + def releaseInterface(self): + r"""Release an interface previously claimed with claimInterface.""" + util.release_interface(self.dev, self.__claimed_interface) + self.__claimed_interface = -1 + + def reset(self): + r"""Reset the specified device by sending a RESET + down the port it is connected to.""" + self.dev.reset() + + def resetEndpoint(self, endpoint): + r"""Reset all states for the specified endpoint. + + Arguments: + endpoint: endpoint number. + """ + self.clearHalt(endpoint) + + def setConfiguration(self, configuration): + r"""Set the active configuration of a device. + + Arguments: + configuration: a configuration value or a Configuration object. + """ + self.dev.set_configuration(configuration) + + def setAltInterface(self, alternate): + r"""Sets the active alternate setting of the current interface. + + Arguments: + alternate: an alternate setting number or an Interface object. + """ + self.dev.set_interface_altsetting(self.__claimed_interface, alternate) + + def getString(self, index, length, langid = None): + r"""Retrieve the string descriptor specified by index + and langid from a device. + + Arguments: + index: index of descriptor in the device. + length: number of bytes of the string + langid: Language ID. If it is omittedi, will be + used the first language. + """ + return util.get_string(self.dev, length, index, langid).encode('ascii') + + def getDescriptor(self, desc_type, desc_index, length, endpoint = -1): + r"""Retrieves a descriptor from the device identified by the type + and index of the descriptor. + + Arguments: + desc_type: descriptor type. + desc_index: index of the descriptor. + len: descriptor length. + endpoint: ignored. + """ + return control.get_descriptor(self.dev, length, desc_type, desc_index) + + def detachKernelDriver(self, interface): + r"""Detach a kernel driver from the interface (if one is attached, + we have permission and the operation is supported by the OS) + + Arguments: + interface: interface number or an Interface object. + """ + self.dev.detach_kernel_driver(interface) + +class Device(object): + r"""Device descriptor object""" + def __init__(self, dev): + self.deviceClass = dev.bDeviceClass + self.deviceSubClass = dev.bDeviceSubClass + self.deviceProtocol = dev.bDeviceProtocol + self.deviceVersion = dev.bcdDevice + self.devnum = None + self.filename = '' + self.iManufacturer = dev.iManufacturer + self.iProduct = dev.iProduct + self.iSerialNumber = dev.iSerialNumber + self.idProduct = dev.idProduct + self.idVendor = dev.idVendor + self.maxPacketSize = dev.bMaxPacketSize0 + self.usbVersion = dev.bcdUSB + self.configurations = [Configuration(c) for c in dev] + self.dev = dev + + def open(self): + r"""Open the device for use. + + Return a DeviceHandle object + """ + return DeviceHandle(self.dev) + +class Bus(object): + r"""Bus object.""" + def __init__(self): + self.dirname = '' + self.localtion = 0 + self.devices = [Device(d) for d in core.find(find_all=True)] + +def busses(): + r"""Return a tuple with the usb busses.""" + return (Bus(),) + diff --git a/plugins/wedo_plugin/usb/util.py b/plugins/wedo_plugin/usb/util.py new file mode 100644 index 0000000..1f8cee3 --- /dev/null +++ b/plugins/wedo_plugin/usb/util.py @@ -0,0 +1,260 @@ +# Copyright (C) 2009-2011 Wander Lairson Costa +# +# The following terms apply to all files associated +# with the software unless explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. + +r"""usb.util - Utility functions. + +This module exports: + +endpoint_address - return the endpoint absolute address. +endpoint_direction - return the endpoint transfer direction. +endpoint_type - return the endpoint type +ctrl_direction - return the direction of a control transfer +build_request_type - build a bmRequestType field of a control transfer. +find_descriptor - find an inner descriptor. +claim_interface - explicitly claim an interface. +release_interface - explicitly release an interface. +dispose_resources - release internal resources allocated by the object. +get_string - retrieve a string descriptor from the device. +""" + +__author__ = 'Wander Lairson Costa' + +import operator +import usb._interop as _interop + +# descriptor type +DESC_TYPE_DEVICE = 0x01 +DESC_TYPE_CONFIG = 0x02 +DESC_TYPE_STRING = 0x03 +DESC_TYPE_INTERFACE = 0x04 +DESC_TYPE_ENDPOINT = 0x05 + +# endpoint direction +ENDPOINT_IN = 0x80 +ENDPOINT_OUT = 0x00 + +# endpoint type +ENDPOINT_TYPE_CTRL = 0x00 +ENDPOINT_TYPE_ISO = 0x01 +ENDPOINT_TYPE_BULK = 0x02 +ENDPOINT_TYPE_INTR = 0x03 + +# control request type +CTRL_TYPE_STANDARD = (0 << 5) +CTRL_TYPE_CLASS = (1 << 5) +CTRL_TYPE_VENDOR = (2 << 5) +CTRL_TYPE_RESERVED = (3 << 5) + +# control request recipient +CTRL_RECIPIENT_DEVICE = 0 +CTRL_RECIPIENT_INTERFACE = 1 +CTRL_RECIPIENT_ENDPOINT = 2 +CTRL_RECIPIENT_OTHER = 3 + +# control request direction +CTRL_OUT = 0x00 +CTRL_IN = 0x80 + +_ENDPOINT_ADDR_MASK = 0x0f +_ENDPOINT_DIR_MASK = 0x80 +_ENDPOINT_TRANSFER_TYPE_MASK = 0x03 +_CTRL_DIR_MASK = 0x80 + +def endpoint_address(address): + r"""Return the endpoint absolute address. + + The address parameter is the bEndpointAddress field + of the endpoint descriptor. + """ + return address & _ENDPOINT_ADDR_MASK + +def endpoint_direction(address): + r"""Return the endpoint direction. + + The address parameter is the bEndpointAddress field + of the endpoint descriptor. + The possible return values are ENDPOINT_OUT or ENDPOINT_IN. + """ + return address & _ENDPOINT_DIR_MASK + +def endpoint_type(bmAttributes): + r"""Return the transfer type of the endpoint. + + The bmAttributes parameter is the bmAttributes field + of the endpoint descriptor. + The possible return values are: ENDPOINT_TYPE_CTRL, + ENDPOINT_TYPE_ISO, ENDPOINT_TYPE_BULK or ENDPOINT_TYPE_INTR. + """ + return bmAttributes & _ENDPOINT_TRANSFER_TYPE_MASK + +def ctrl_direction(bmRequestType): + r"""Return the direction of a control request. + + The bmRequestType parameter is the value of the + bmRequestType field of a control transfer. + The possible return values are CTRL_OUT or CTRL_IN. + """ + return bmRequestType & _CTRL_DIR_MASK + +def build_request_type(direction, type, recipient): + r"""Build a bmRequestType field for control requests. + + These is a conventional function to build a bmRequestType + for a control request. + + The direction parameter can be CTRL_OUT or CTRL_IN. + The type parameter can be CTRL_TYPE_STANDARD, CTRL_TYPE_CLASS, + CTRL_TYPE_VENDOR or CTRL_TYPE_RESERVED values. + The recipient can be CTRL_RECIPIENT_DEVICE, CTRL_RECIPIENT_INTERFACE, + CTRL_RECIPIENT_ENDPOINT or CTRL_RECIPIENT_OTHER. + + Return the bmRequestType value. + """ + return recipient | type | direction + +def find_descriptor(desc, find_all=False, custom_match=None, **args): + r"""Find an inner descriptor. + + find_descriptor works in the same way the core.find() function does, + but it acts on general descriptor objects. For example, suppose you + have a Device object called dev and want a Configuration of this + object with its bConfigurationValue equals to 1, the code would + be like so: + + >>> cfg = util.find_descriptor(dev, bConfigurationValue=1) + + You can use any field of the Descriptor as a match criteria, and you + can supply a customized match just like core.find() does. The + find_descriptor function also accepts the find_all parameter to get + a list of descriptor instead of just one. + """ + def desc_iter(k, v): + for d in desc: + if (custom_match is None or custom_match(d)) and \ + _interop._reduce( + lambda a, b: a and b, + map( + operator.eq, + v, + map(lambda i: getattr(d, i), k) + ), + True + ): + yield d + + k, v = args.keys(), args.values() + + if find_all: + return [d for d in desc_iter(k, v)] + else: + try: + return _interop._next(desc_iter(k, v)) + except StopIteration: + return None + +def claim_interface(device, interface): + r"""Explicitly claim an interface. + + PyUSB users normally do not have to worry about interface claiming, + as the library takes care of it automatically. But there are situations + where you need deterministic interface claiming. For these uncommon + cases, you can use claim_interface. + + If the interface is already claimed, either through a previously call + to claim_interface or internally by the device object, nothing happens. + """ + device._ctx.managed_claim_interface(device, interface) + +def release_interface(device, interface): + r"""Explicitly release an interface. + + This function is used to release an interface previously claimed, + either through a call to claim_interface or internally by the + device object. + + Normally, you do not need to worry about claiming policies, as + the device object takes care of it automatically. + """ + device._ctx.managed_release_interface(device, interface) + +def dispose_resources(device): + r"""Release internal resources allocated by the object. + + Sometimes you need to provide deterministic resources + freeing, for example to allow another application to + talk to the device. As Python does not provide deterministic + destruction, this function releases all internal resources + allocated by the device, like device handle and interface + policy. + + After calling this function, you can continue using the device + object normally. If the resources will be necessary again, it + will allocate them automatically. + """ + device._ctx.dispose(device) + +def get_string(dev, length, index, langid = None): + r"""Retrieve a string descriptor from the device. + + dev is the Device object to which the request will be + sent to. + + length is the length of string in number of characters. + + index is the string descriptor index and langid is the Language + ID of the descriptor. If langid is omitted, the string descriptor + of the first Language ID will be returned. + + The return value is the unicode string present in the descriptor. + """ + from usb.control import get_descriptor + if langid is None: + # Asking for the zero'th index is special - it returns a string + # descriptor that contains all the language IDs supported by the device. + # Typically there aren't many - often only one. The language IDs are 16 + # bit numbers, and they start at the third byte in the descriptor. See + # USB 2.0 specification section 9.6.7 for more information. + # + # Note from libusb 1.0 sources (descriptor.c) + buf = get_descriptor( + dev, + 1024, + DESC_TYPE_STRING, + 0 + ) + assert len(buf) >= 4 + langid = buf[2] | (buf[3] << 8) + + buf = get_descriptor( + dev, + length * 2 + 2, # string is utf16 + 2 bytes of the descriptor + DESC_TYPE_STRING, + index, + langid + ) + return buf[2:].tostring().decode('utf-16-le') diff --git a/plugins/wedo_plugin/wedo_plugin.py b/plugins/wedo_plugin/wedo_plugin.py new file mode 100644 index 0000000..2c92535 --- /dev/null +++ b/plugins/wedo_plugin/wedo_plugin.py @@ -0,0 +1,302 @@ +#Copyright (c) 2012, Tony Forster, Ian Daniher, Walter Bender +# +# 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +from gettext import gettext as _ + +from plugins.plugin import Plugin +from WeDoMore import WeDo, scan_for_devices, UNAVAILABLE + +from TurtleArt.tapalette import make_palette +from TurtleArt.taconstants import XO1, XO15 +from TurtleArt.talogo import primitive_dictionary + +import logging +_logger = logging.getLogger('turtleart-activity WeDo plugin') + + +class Wedo_plugin(Plugin): + + def __init__(self, parent): + ''' Scan for WeDo devices ''' + self.WeDos = [] + device_list = scan_for_devices() + for i, dev in enumerate(device_list): + self.WeDos.append(WeDo(device=dev)) + self.WeDos[-1].number = i + self._parent = parent + if len(self.WeDos) == 0: + self._status = False # no WeDo devices found + self.active_wedo = None + else: + self._status = True + self.active_wedo = 0 + + def setup(self): + ''' Setup palettes ''' + palette = make_palette('WeDo', colors=["#FF6060", "#A06060"], + help_string=_('Palette of WeDo blocks')) + + primitive_dictionary['wedoselect'] = self.wedo_select + palette.add_block('wedoselect', + style='basic-style-1arg', + default = 1, + label=_('WeDo'), + help_string=_('set current WeDo device'), + prim_name = 'wedoselect') + + self._parent.lc.def_prim( + 'wedoselect', 1, + lambda self, n: primitive_dictionary['wedoselect'](n)) + + primitive_dictionary['wedocount'] = self.wedo_count + palette.add_block('wedogetcount', + style='box-style', + label=_('number of WeDos'), + help_string=_('number of WeDo devices'), + prim_name = 'wedocount') + + self._parent.lc.def_prim( + 'wedocount', 0, lambda self: primitive_dictionary['wedocount']()) + + primitive_dictionary['wedotilt'] = self.wedo_gettilt + palette.add_block('tilt', + style='box-style', + label=_('tilt'), + help_string=_('tilt sensor output: (-1 == no tilt,\ + 0 == tilt forward, 3 == tilt back, 1 == tilt left, 2 == tilt right)'), + value_block=True, + prim_name = 'wedotilt') + self._parent.lc.def_prim( + 'wedotilt', 0, lambda self: primitive_dictionary['wedotilt']()) + + primitive_dictionary['wedodistance'] = self.wedo_getdistance + palette.add_block('wedodistance', + style='box-style', + label=_('distance'), + help_string=_('distance sensor output'), + value_block=True, + prim_name = 'wedodistance') + self._parent.lc.def_prim( + 'wedodistance', 0, + lambda self: primitive_dictionary['wedodistance']()) + + primitive_dictionary['wedogetMotorA'] = self.wedo_getmotora + palette.add_block('wedogetMotorA', + style='box-style', + label=_('Motor A'), + help_string=_('returns the current value of Motor A'), + value_block=True, + prim_name = 'wedogetMotorA') + + self._parent.lc.def_prim( + 'wedogetMotorA', 0, + lambda self: primitive_dictionary['wedogetMotorA']()) + + primitive_dictionary['wedogetMotorB'] = self.wedo_getmotorb + palette.add_block('wedogetMotorB', + style='box-style', + label=_('Motor B'), + help_string=_('returns the current value of Motor B'), + value_block=True, + prim_name = 'wedogetMotorB') + self._parent.lc.def_prim( + 'wedogetMotorB', 0, + lambda self: primitive_dictionary['wedogetMotorB']()) + + primitive_dictionary['wedosetMotorA'] = self.wedo_setmotora + palette.add_block('wedosetMotorA', + style = 'basic-style-1arg', + label = _('Motor A'), + default = 30, + prim_name = 'wedosetMotorA', + help_string = _('set the value for Motor A')) + self._parent.lc.def_prim( + 'wedosetMotorA', 1, + lambda self, a: primitive_dictionary['wedosetMotorA'](a)) + + primitive_dictionary['wedosetMotorB'] = self.wedo_setmotorb + palette.add_block('wedosetMotorB', + style = 'basic-style-1arg', + label = _('Motor B'), + default = 30, + prim_name = 'wedosetMotorB', + help_string = _('set the value for Motor B')) + self._parent.lc.def_prim( + 'wedosetMotorB', 1, + lambda self, b: primitive_dictionary['wedosetMotorB'](b)) + + def wedo_select(self, i): + ''' Select current device ''' + if self.wedo_count() == 0: + self.active_wedo = None + self._parent.showlabel( + 'status', _('WeDo is unavailable')) + return + if type(i) == unicode: + i = str(i.encode('ascii', 'replace')) + if type(i) == float or type(i) == str: + i = int(i) + if type(i) != int: + i = 1 + if i < 0: + i = -i + if i < 0 or i > self.wedo_count() - 1: + self._parent.showlabel( + 'status', _('WeDo %d is unavailable; defaulting to 1') % (i)) + i -= 1 # Userspace counts from 1; internally, we count from 0 + if i > self.wedo_count() - 1: + i = 0 + self.active_wedo = i + + def wedo_count(self): + ''' How many devices are available? ''' + n = len(self.WeDos) + for wedo in self.WeDos: + if wedo.dev is None: + n -= 1 + return n + + def wedo_find(self): + ''' Find an available device ''' + for wedo in self.WeDos: + if wedo.dev is not None: + return self.WeDos.index(wedo) + return None + + def wedo_gettilt(self): + if self.active_wedo is None: + self.active_wedo = self.wedo_find() + if self.active_wedo is None: + self._parent.showlabel('status', _('WeDo is unavailable')) + return -1 + tilt = self.WeDos[self.active_wedo].getTilt() + if tilt == UNAVAILABLE: + # Should we look for tilt on another WeDo? + self._parent.showlabel( + 'status', _('%s is unavailable on WeDo %d') % ( + _('tilt'), self.active_wedo + 1)) + return -1 + else: + return tilt + + def wedo_getdistance(self): + if self.active_wedo is None: + self.active_wedo = self.wedo_find() + if self.active_wedo is None: + self._parent.showlabel('status', _('WeDo is unavailable')) + return 0 + distance = self.WeDos[self.active_wedo].getDistance() + if distance == UNAVAILABLE: + # Should we look for distance on another WeDo? + self._parent.showlabel( + 'status', _('%s is unavailable on WeDo %d') % ( + _('distance'), self.active_wedo + 1)) + return 0 + else: + return distance + + def wedo_getmotora(self): + if self.active_wedo is None: + self.active_wedo = self.wedo_find() + if self.active_wedo is None: + self._parent.showlabel('status', _('WeDo is unavailable')) + return 0 + speed = self.WeDos[self.active_wedo].getMotorA() + if speed == UNAVAILABLE: + self._parent.showlabel( + 'status', _('%s is unavailable on WeDo %d') % ( + _('Motor A'), self.active_wedo + 1)) + return 0 + else: + return speed + + def wedo_getmotorb(self): + if self.active_wedo is None: + self.active_wedo = self.wedo_find() + if self.active_wedo is None: + self._parent.showlabel('status', _('WeDo is unavailable')) + return 0 + speed = self.WeDos[self.active_wedo].getMotorB() + if speed == UNAVAILABLE: + self._parent.showlabel( + 'status', _('%s is unavailable on WeDo %d') % ( + _('Motor B'), self.active_wedo + 1)) + return 0 + else: + return speed + + def wedo_setmotora(self, speed): + if self.active_wedo is None: + self.active_wedo = self.wedo_find() + if self.active_wedo is None: + self._parent.showlabel('status', _('WeDo is unavailable')) + return + status = self.WeDos[self.active_wedo].setMotorA(speed) + if status == UNAVAILABLE: + self._parent.showlabel( + 'status', _('%s is unavailable on WeDo %d') % ( + _('Motor A'), self.active_wedo + 1)) + + def wedo_setmotorb(self, speed): + if self.active_wedo is None: + self.active_wedo = self.wedo_find() + if self.active_wedo is None: + self._parent.showlabel('status', _('WeDo is unavailable')) + return + status = self.WeDos[self.active_wedo].setMotorB(speed) + if status == UNAVAILABLE: + self._parent.showlabel( + 'status', _('%s is unavailable on WeDo %d') % ( + _('Motor B'), self.active_wedo + 1)) + + def start(self): + ''' Each time program starts, scan for devices and reset status ''' + for wedo in self.WeDos: + wedo.dev = None + self.active_wedo = None + device_list = scan_for_devices() + if len(device_list) > 0: + self.status = True + if self.active_wedo is None: + self.active_wedo = 0 + else: + self.status = False + for i, dev in enumerate(device_list): + if i < len(self.WeDos): + self.WeDos[i].dev = dev + self.WeDos[i].number = i + self.WeDos[i].init_device() # Reinitial device + else: + self.WeDos.append(WeDo(device=dev)) + self.WeDos[i].number = i + + def stop(self): + if self._status: + for wedo in self.WeDos: + if wedo.dev is not None: + wedo.setMotors(0, 0) + + def goto_background(self): + pass + + def return_to_foreground(self): + pass + + def quit(self): + if self._status: + for wedo in self.WeDos: + if wedo.dev is not None: + wedo.setMotors(0, 0) -- cgit v0.9.1