Programmation avec PyUSB 1.0

Du traducteur :
Ceci est une traduction du manuel de programmation avec PyUSB 1.0
Ce guide a été écrit par les développeurs PyUSB, mais en parcourant rapidement les commits, je crois que walac est l'auteur principal.

Laisse moi me présenter


PyUSB 1.0 est une bibliothèque Python qui fournit un accès facile à l' USB . PyUSB offre diverses fonctions:

  • 100% écrit en Python:
    Contrairement aux versions 0.x écrites en C, la version 1.0 est écrite en Python. Cela permet aux programmeurs Python sans expérience C de mieux comprendre le fonctionnement de PyUSB.
  • Neutralité de la plateforme:
    La version 1.0 inclut un schéma de backend frontal. Il isole l'API des détails d'implémentation spécifiques au système. L'interface IBackend relie ces deux couches. PyUSB est livré avec des backends intégrés pour libusb 0.1, libusb 1.0 et OpenUSB. Vous pouvez écrire votre backend vous-même si vous le souhaitez.
  • Portabilité:
    PyUSB devrait fonctionner sur n'importe quelle plate-forme avec Python> = 2.4, ctypes et au moins l'un des backends intégrés pris en charge.
  • Simplicité:
    L'interaction avec un périphérique USB n'a jamais été aussi facile! L'USB est un protocole complexe et PyUSB a de bons préréglages pour les configurations les plus courantes.
  • Support d'engrenage isochrone:
    PyUSB prend en charge les transferts isochrones si le backend sous-jacent les prend en charge.

Bien que PyUSB rend la programmation USB moins pénible, ce tutoriel suppose que vous avez une connaissance minimale du protocole USB. Si vous ne savez rien de l'USB, je recommande l' excellent livre USB Complete de Jan Axelson.

Assez parlé, écrivons le code!


Qui est qui


Pour commencer, donnons une description des modules PyUSB. Tous les modules PyUSB sont sous usb , avec les modules suivants:
ModuleLa description
noyauLe module USB principal.
utilFonctions auxiliaires.
contrôleDemandes de gestion standard.
héritageCouche de compatibilité version 0.x.
backendUn sous-package contenant des backends intégrés.

Par exemple, pour importer un module principal , entrez les informations suivantes:

>>> import usb.core >>> dev = usb.core.find() 

Eh bien, commençons


Ce qui suit est un programme simple qui envoie la chaîne «test» à la première source de données trouvée (point final OUT):

  import usb.core import usb.util #    dev = usb.core.find(idVendor=0xfffe, idProduct=0x0001) #   ? if dev is None: raise ValueError('Device not found') #   .  ,   #    dev.set_configuration() #    cfg = dev.get_active_configuration() intf = cfg[(0,0)] ep = usb.util.find_descriptor( intf, #     custom_match = \ lambda e: \ usb.util.endpoint_direction(e.bEndpointAddress) == \ usb.util.ENDPOINT_OUT) assert ep is not None #   ep.write('test') 

Les deux premières lignes importent les modules du package PyUSB. usb.core est le module principal et usb.util contient des fonctions d'assistance. La commande suivante recherche notre appareil et renvoie une instance de l'objet s'il le trouve. Sinon, renvoie Aucun . Ensuite, nous définissons la configuration que nous utiliserons. Remarque: l'absence d'arguments signifie que la configuration souhaitée a été définie par défaut. Comme vous le verrez, de nombreuses fonctionnalités PyUSB ont des paramètres par défaut pour la plupart des appareils courants. Dans ce cas, la première configuration trouvée est définie.

Ensuite, nous recherchons le point final auquel nous sommes intéressés. Nous le recherchons dans la première interface que nous avons. Après avoir trouvé ce point, nous lui envoyons des données.

Si nous connaissons à l'avance l'adresse du point de terminaison, nous pouvons simplement appeler la fonction d' écriture de l'objet périphérique:

 dev.write(1, 'test') 

Ici, nous écrivons la chaîne «test» au point d'arrêt à l'adresse 1 . Toutes ces fonctions seront mieux discutées dans les sections suivantes.

Qu'est-ce qui ne va pas?


Chaque fonction dans PyUSB lève une exception en cas d'erreur. Outre les exceptions Python standard , PyUSB définit usb.core.USBError pour les erreurs liées à l'USB.

Vous pouvez également utiliser les fonctions de journal PyUSB. Il utilise le module de journalisation . Pour l'utiliser, définissez la variable d' environnement PYUSB_DEBUG avec l'un des niveaux de journalisation suivants: critique , erreur , avertissement , info ou débogage .

Par défaut, les messages sont envoyés à sys.stderr . Si vous le souhaitez, vous pouvez rediriger les messages du journal vers un fichier en définissant la variable d'environnement PYUSB_LOG_FILENAME . Si sa valeur est le chemin d'accès correct au fichier, les messages y seront écrits, sinon ils seront envoyés à sys.stderr .

Où es tu


La fonction find () du module principal est utilisée pour rechercher et numéroter les périphériques connectés au système. Par exemple, supposons que notre appareil possède un ID de fournisseur avec une valeur de 0xfffe et un ID de produit de 0x0001. Si nous devons trouver cet appareil, nous le ferons:

 import usb.core dev = usb.core.find(idVendor=0xfffe, idProduct=0x0001) if dev is None: raise ValueError('Our device is not connected') 

C'est tout, la fonction renverra l'objet usb.core.Device qui représente notre appareil. Si le périphérique n'est pas trouvé, il renverra Aucun . En fait, vous pouvez utiliser n'importe quel champ de la classe Device Descriptor de votre choix. Par exemple, que faire si nous voulons savoir si une imprimante USB est connectée au système? C'est très simple:

 #      ,   if usb.core.find(bDeviceClass=7) is None: raise ValueError('No printer found') 

7 est le code de la classe d'imprimante selon la spécification USB. Oh, attendez, que faire si je veux numéroter toutes les imprimantes disponibles? Pas de problème:

 #     ... printers = usb.core.find(find_all=True, bDeviceClass=7) # Python 2, Python 3,     import sys sys.stdout.write('There are ' + len(printers) + ' in the system\n.') 

Qu'est-il arrivé? Eh bien, il est temps pour une petite explication ... find a un paramètre appelé find_all et par défaut à False. Quand c'est faux [1] , find renverra le premier appareil qui correspond aux critères spécifiés (nous en reparlerons bientôt). Si vous transmettez une valeur vraie au paramètre, find renverra à la place une liste de tous les périphériques correspondant aux critères. C'est tout! C'est simple, non?

Avons-nous fini? Non! Je n'ai pas encore tout dit: de nombreux appareils mettent en fait leurs informations de classe dans le Descripteur d' interface au lieu du Descripteur d' appareil. Donc, pour vraiment trouver toutes les imprimantes connectées au système, nous devrons passer par toutes les configurations, ainsi que toutes les interfaces, et vérifier si l'une des interfaces est définie sur bInterfaceClass 7. Si vous êtes un programmeur comme moi, vous pouvez vous demander: existe-t-il un moyen plus simple de mettre cela en œuvre? Réponse: oui, il l'est. Pour commencer, regardons le code prêt à l'emploi pour trouver toutes les imprimantes connectées:

 import usb.core import usb.util import sys class find_class(object): def __init__(self, class_): self._class = class_ def __call__(self, device): #  ,   if device.bDeviceClass == self._class: return True # ,   ,   # ,     for cfg in device: # find_descriptor:  ? intf = usb.util.find_descriptor( cfg, bInterfaceClass=self._class ) if intf is not None: return True return False printers = usb.core.find(find_all=1, custom_match=find_class(7)) 

Le paramètre custom_match accepte tout objet appelé qui reçoit l'objet périphérique. Il doit renvoyer true pour un appareil approprié et false pour un appareil inapproprié. Vous pouvez également combiner custom_match avec des champs d'appareil si vous le souhaitez:

 #   ,    : printers = usb.core.find(find_all=1, custom_match=find_class(7), idVendor=0xfffe) 

Ici, nous nous intéressons aux imprimantes du fournisseur 0xfffe.

Décrivez-vous


Ok, nous avons trouvé notre appareil, mais avant d'interagir avec lui, nous aimerions en savoir plus. Eh bien, vous savez, les configurations, les interfaces, les points de terminaison, les types de flux de données ...
Si vous avez un périphérique, vous pouvez accéder à n'importe quel champ du descripteur de périphérique en tant que propriétés d'objet:

 >>> dev.bLength >>> dev.bNumConfigurations >>> dev.bDeviceClass >>> # ... 

Pour accéder aux configurations disponibles dans l'appareil, vous pouvez itérer l'appareil:

 for cfg in dev: sys.stdout.write(str(cfg.bConfigurationValue) + '\n') 

De la même manière, vous pouvez itérer la configuration pour accéder aux interfaces, ainsi qu'itérer les interfaces pour accéder à leurs points de contrôle. Chaque type d'objet a des champs du descripteur correspondant comme attributs. Jetez un oeil à un exemple:

 for cfg in dev: sys.stdout.write(str(cfg.bConfigurationValue) + '\n') for intf in cfg: sys.stdout.write('\t' + \ str(intf.bInterfaceNumber) + \ ',' + \ str(intf.bAlternateSetting) + \ '\n') for ep in intf: sys.stdout.write('\t\t' + \ str(ep.bEndpointAddress) + \ '\n') 

Vous pouvez également utiliser des index pour un accès aléatoire aux descripteurs, comme ici:

 >>> #      >>> cfg = dev[1] >>> #      >>> intf = cfg[(0,0)] >>> #    >>> ep = intf[2] 

Comme vous pouvez le voir, les indices sont comptés à partir de 0. Mais attendez! Il y a quelque chose d'étrange dans la façon dont j'accède à l'interface ... Oui, vous avez raison, l'index de configuration prend une série de deux valeurs, dont la première est l'index d'interface et la seconde est un paramètre alternatif. En général, pour accéder à la première interface, mais avec le second paramètre, nous écrirons cfg [(0,1)] .

Il est maintenant temps d'apprendre une manière puissante de rechercher des descripteurs - une fonction find_descriptor utile. Nous l'avons déjà vu dans l'exemple de recherche d'imprimante. find_descriptor fonctionne presque de la même manière que find , à deux exceptions près:

  • find_descriptor reçoit comme premier paramètre le descripteur source que vous recherchez.
  • Il n'y a pas de paramètre backend dedans [2] .

Par exemple, si nous avons un descripteur de configuration cfg , et que nous voulons trouver tous les paramètres alternatifs pour l'interface 1, nous le ferons:

 import usb.util alt = usb.util.find_descriptor(cfg, find_all=True, bInterfaceNumber=1) 

Notez que find_descriptor se trouve dans le module usb.util . Il accepte également le paramètre custom_match décrit précédemment.

Nous traitons avec plusieurs appareils identiques

Parfois, vous pouvez connecter deux appareils identiques à un ordinateur. Comment pouvez-vous les distinguer? Les objets de périphérique sont livrés avec deux attributs supplémentaires qui ne font pas partie de la spécification USB, mais sont très utiles: les attributs de bus et d' adresse . Tout d'abord, il convient de dire que ces attributs proviennent du backend et que le backend peut ne pas les prendre en charge - dans ce cas, ils sont définis sur Aucun . Cependant, ces attributs représentent le numéro et l'adresse du bus de périphérique et, comme vous l'avez peut-être deviné, peuvent être utilisés pour distinguer deux périphériques avec les mêmes valeurs d'attribut idVendor et idProduct .

Comment dois-je travailler?


Après la connexion, les périphériques USB doivent être configurés à l'aide de quelques requêtes standard. Lorsque j'ai commencé à étudier la spécification USB , j'ai été découragé par les descripteurs, les configurations, les interfaces, les paramètres alternatifs, les types de transfert et tout ça ... Et pire que tout, vous ne pouvez pas simplement les ignorer: l'appareil ne fonctionne pas sans définir la configuration, même si elle en est une! PyUSB essaie de vous rendre la vie aussi simple que possible. Par exemple, après avoir reçu votre objet appareil, tout d'abord, avant d'interagir avec lui, vous devez envoyer une demande set_configuration . Le paramètre de configuration de cette requête qui vous intéresse est bConfigurationValue . La plupart des appareils n'ont pas plus d'une configuration, et le suivi de la valeur de configuration à utiliser est ennuyeux (bien que la plupart du code que j'ai vu soit codé en dur). Par conséquent, dans PyUSB, vous pouvez simplement envoyer une demande set_configuration sans arguments. Dans ce cas, il installera la première configuration trouvée (si votre appareil n'en a qu'une, vous n'avez pas du tout à vous soucier de la valeur de configuration). Par exemple, supposons que vous ayez un périphérique avec un descripteur de configuration et que son champ bConfigurationValue soit 5 [3] , les requêtes suivantes fonctionneront de la même manière:

 >>> dev.set_configuration(5) #  >>> dev.set_configuration() #  ,   5 -  #  >>> cfg = util.find_descriptor(dev, bConfigurationValue=5) >>> cfg.set() #  >>> cfg = util.find_descriptor(dev, bConfigurationValue=5) >>> dev.set_configuration(cfg) 

Ouah! Vous pouvez utiliser l'objet Configuration comme paramètre pour set_configuration ! Oui, il a également une méthode définie pour se configurer dans la configuration actuelle.

Une autre option dont vous avez besoin ou n'aurez pas besoin de configurer est l'option de changer les interfaces. Chaque périphérique ne peut avoir qu'une seule configuration activée à la fois, et chaque configuration peut avoir plusieurs interfaces, et vous pouvez utiliser toutes les interfaces en même temps. Vous comprenez mieux ce concept si vous considérez l'interface comme un périphérique logique. Par exemple, imaginons une imprimante multifonction, qui est à la fois une imprimante et un scanner. Afin de ne pas compliquer (ou du moins de le rendre aussi simple que possible), supposons qu'il n'a qu'une seule configuration. Parce que nous avons une imprimante et un scanner, la configuration a 2 interfaces: une pour l'imprimante et une pour le scanner. Un périphérique avec plusieurs interfaces est appelé périphérique composite. Lorsque vous connectez votre imprimante multifonction à votre ordinateur, le système d'exploitation charge deux pilotes différents: un pour chaque périphérique «logique» que vous avez [4] .

Qu'en est-il des paramètres d'interface alternatifs? C'est une bonne chose que vous ayez demandé. Une interface a un ou plusieurs paramètres alternatifs. Une interface avec un seul paramètre alternatif est considérée comme n'ayant aucun paramètre alternatif [5] . Paramètres alternatifs pour les interfaces en tant que configuration pour les appareils, c'est-à-dire que pour chaque interface, vous ne pouvez avoir qu'un seul paramètre alternatif actif. Par exemple, la spécification USB suggère qu'un appareil ne peut pas avoir de point de contrôle isochrone dans sa configuration alternative principale [6] , de sorte que le dispositif de diffusion en continu doit avoir au moins deux paramètres alternatifs, un second paramètre ayant un point de contrôle isochrone. Mais, contrairement aux configurations, les interfaces avec une seule configuration alternative n'ont pas besoin d'être configurées [7] . Vous sélectionnez un autre paramètre d'interface à l'aide de la fonction set_interface_altsetting :

 >>> dev.set_interface_altsetting(interface = 0, alternate_setting = 0) 

Avertissement

La spécification USB indique que l'appareil est autorisé à renvoyer une erreur s'il reçoit une demande SET_INTERFACE pour une interface qui n'a pas de paramètres alternatifs supplémentaires. Donc, si vous n'êtes pas sûr que l'interface possède plusieurs paramètres alternatifs ou qu'elle accepte la demande SET_INTERFACE, la méthode la plus sûre consiste à appeler set_interface_altsetting à l'intérieur du bloc try-except, comme ici:

 try: dev.set_interface_altsetting(...) except USBError: pass 

Vous pouvez également utiliser l'objet Interface en tant que paramètre de fonction, l' interface et les paramètres de remplacement sont automatiquement hérités des champs bInterfaceNumber et bAlternateSetting . Un exemple:

 >>> intf = find_descriptor(...) >>> dev.set_interface_altsetting(intf) >>> intf.set_altsetting() # !        

Avertissement

L'objet Interface doit appartenir au descripteur de configuration actif.

Parle-moi chérie


Et maintenant, il est temps pour nous de comprendre comment interagir avec les périphériques USB. L'USB propose quatre types de flux de données: transfert en masse, transfert d'interruption, transfert isochrone et transfert de contrôle. Je n'ai pas l'intention d'expliquer le but de chaque fil et les différences entre eux. Par conséquent, je suppose que vous avez au moins une connaissance de base des flux de données USB.

Le flux de données de contrôle est le seul flux dont la structure est décrite dans la spécification, le reste envoie et reçoit simplement des données brutes d'un point de vue USB. Par conséquent, vous disposez de diverses fonctions pour travailler avec des flux de contrôle et les autres flux sont traités par les mêmes fonctions.

Vous pouvez accéder au flux de données de contrôle à l'aide de la méthode ctrl_transfer . Il est utilisé à la fois pour les flux sortants (OUT) et entrants (IN). Le sens du flux est déterminé par le paramètre bmRequestType .

Les paramètres ctrl_transfer coïncident presque avec la structure de la demande de contrôle. L'exemple suivant montre comment organiser un flux de données de contrôle. [8] :

 >>> msg = 'test' >>> assert dev.ctrl_transfer(0x40, CTRL_LOOPBACK_WRITE, 0, 0, msg) == len(msg) >>> ret = dev.ctrl_transfer(0xC0, CTRL_LOOPBACK_READ, 0, 0, len(msg)) >>> sret = ''.join([chr(x) for x in ret]) >>> assert sret == msg 

Cet exemple suppose que notre appareil comprend deux demandes de contrôle utilisateur qui agissent comme un canal de bouclage. Ce que vous écrivez avec le message CTRL_LOOPBACK_WRITE , vous pouvez le lire avec le message CTRL_LOOPBACK_READ .

Les quatre premiers paramètres - bmRequestType , bmRequest , wValue et wIndex - sont les champs de la structure standard du flux de contrôle. Le cinquième paramètre est soit les données transférées pour le flux de données sortant, soit le nombre de données lues dans le flux entrant. Les données transmises peuvent être n'importe quel type de séquence, qui peut être fournie en tant que paramètre à l'entrée de la méthode __init__ pour la matrice . Si aucune donnée n'est transférée, le paramètre doit être défini sur Aucun (ou 0 dans le cas d'un flux de données entrant). Il existe un autre paramètre facultatif indiquant le délai d'expiration de l'opération. Si vous ne le réussissez pas, le délai d'expiration par défaut sera utilisé (plus à ce sujet plus tard). Dans le flux de données sortant, la valeur de retour est le nombre d'octets réellement envoyés au périphérique. Dans le flux entrant, la valeur de retour est un tableau avec les données lues.

Pour les autres flux, vous pouvez utiliser les méthodes d' écriture et de lecture , respectivement, pour écrire et lire des données. Vous n'avez pas à vous soucier du type de flux - il est automatiquement détecté par l'adresse du point de contrôle. Voici notre exemple de bouclage, à condition que nous ayons un tuyau de bouclage au point d'arrêt 1:

 >>> msg = 'test' >>> assert len(dev.write(1, msg, 100)) == len(msg) >>> ret = dev.read(0x81, len(msg), 100) >>> sret = ''.join([chr(x) for x in ret]) >>> assert sret == msg 

Les premier et troisième paramètres sont les mêmes pour les deux méthodes - il s'agit de l'adresse du point de contrôle et du délai d'expiration, respectivement. Le deuxième paramètre est la donnée transmise (écriture) ou le nombre d'octets à lire (lecture). Les données renvoyées seront soit une instance d'un objet tableau pour la méthode de lecture , soit le nombre d'octets écrits pour la méthode d' écriture .

Avec les versions beta 2, au lieu du nombre d'octets, vous pouvez passer en lecture ou ctrl_transfer un objet tableau vers lequel les données seront lues. Dans ce cas, le nombre d'octets à lire sera la longueur du tableau multipliée par la valeur de array.itemsize .

Dans ctrl_transfer , le paramètre timeout est facultatif. Lorsque le délai est omis, la propriété Device.default_timeout est utilisée comme délai opérationnel.

Contrôlez-vous


En plus des fonctions de flux de données, le module usb.control fournit des fonctions qui incluent des demandes de contrôle USB standard, et le module usb.util a une fonction get_string pratique qui affiche spécifiquement les descripteurs de ligne.

Sujets supplémentaires


Derrière chaque grande abstraction se cache une grande réalisation


Auparavant, il n'y avait que libusb . Puis vint libusb 1.0 et nous avons eu libusb 0.1 et 1.0. Après cela, nous avons créé OpenUSB et maintenant nous vivons dans la tour de Babel de la bibliothèque USB [9] . Comment PyUSB gère-t-il cela? Eh bien, PyUSB est une bibliothèque démocratique, vous pouvez choisir la bibliothèque que vous souhaitez. En fait, vous pouvez écrire votre propre bibliothèque USB à partir de zéro et dire à PyUSB de l'utiliser.

La fonction find a un autre paramètre, dont je ne vous ai pas parlé. Il s'agit du paramètre backend . Si vous ne le transférez pas, l'un des backends intégrés sera utilisé. Un backend est un objet hérité de usb.backend.IBackend , responsable de l'introduction de fichiers indésirables USB spécifiques au système d'exploitation. Comme vous l'avez peut-être deviné, les libusb 0.1, libusb 1.0 et OpenUSB intégrés sont des backends.

Vous pouvez écrire votre propre backend et l'utiliser. Héritez juste d' IBackend et activez les méthodes nécessaires. Vous devrez peut-être consulter la documentation usb.backend pour comprendre comment cela se fait.

Ne sois pas égoïste


Python possède ce que nous appelons la gestion automatique de la mémoire . Cela signifie que la machine virtuelle décidera quand décharger les objets de la mémoire. Sous le capot, PyUSB gère toutes les ressources de bas niveau avec lesquelles vous devez travailler (approbation de l'interface, réglage de l'appareil, etc.) et la plupart des utilisateurs n'ont pas à s'en soucier. Mais, en raison de la nature non définie de la destruction automatique des objets par Python, les utilisateurs ne peuvent pas prédire quand les ressources allouées seront libérées. Certaines applications doivent allouer et libérer des ressources de manière déterministe. Pour de telles applications, le module usb.util fournit des fonctions d'interaction avec la gestion des ressources.

Si vous souhaitez demander et libérer des interfaces manuellement, vous pouvez utiliser les fonctions claim_interface et release_interface .La fonction claim_interface demandera l'interface spécifiée si le périphérique ne l'a pas encore fait. Si l'appareil a déjà demandé une interface, il ne fait rien. Juste release_interface libérera l'interface spécifiée, si elle est demandée. Si l'interface n'est pas demandée, elle ne fait rien. Vous pouvez utiliser l'interrogation manuelle de l'interface pour résoudre le problème de sélection de configuration décrit dans la documentation de libusb . Si vous souhaitez libérer toutes les ressources allouées par l'objet périphérique (y compris les interfaces demandées), vous pouvez utiliser la fonction dispose_resources

. Il libère toutes les ressources allouées et place l'objet périphérique (mais pas dans le matériel du périphérique lui-même) dans l'état dans lequel il a été renvoyé après avoir utilisé la fonction find .

Définition manuelle de la bibliothèque


En général, un backend est un wrapper sur une bibliothèque partagée qui implémente une API pour accéder à USB. Par défaut, les utilisations de back - end ctypes disposent find_library () . Sous Linux et d'autres systèmes d'exploitation de type Unix, find_library essaie d'exécuter des programmes externes (tels que / sbin / ldconfig , gcc et objdump ) afin de trouver le fichier de bibliothèque.

Sur les systèmes dans lesquels ces programmes sont manquants et / ou le cache de bibliothèque est désactivé, cette fonction ne peut pas être utilisée. Pour surmonter ces limitations, PyUSB vous permet de soumettre la fonction personnalisée find_library () au backend.

Un exemple d'un tel scénario serait:

 >>> import usb.core >>> import usb.backend.libusb1 >>> >>> backend = usb.backend.libusb1.get_backend(find_library=lambda x: "/usr/lib/libusb-1.0.so") >>> dev = usb.core.find(..., backend=backend) 

Notez que find_library est un argument de la fonction get_backend () dans lequel vous fournissez la fonction chargée de trouver la bonne bibliothèque pour le backend.

Règles de la vieille école


Si vous écrivez une application en utilisant les anciennes API PyUSB (0. quelque chose-là), vous vous demandez peut-être si vous devez mettre à jour votre code pour utiliser la nouvelle API. Eh bien, vous devriez le faire, mais ce n’est pas nécessaire. PyUSB 1.0 est fourni avec le module de compatibilité usb.legacy . Il inclut l'ancienne API basée sur la nouvelle API. "Eh bien, devrais-je simplement remplacer ma ligne USB d' importation par Import USB.legacy en tant qu'USB pour faire fonctionner ma demande?", Demandez-vous. La réponse est oui, cela fonctionnera, mais ce n'est pas nécessaire. Si vous exécutez votre application sans modification, cela fonctionnera car la ligne USB d' importation importe tous les symboles publics de usb.legacy. Si vous rencontrez un problème - vous avez probablement trouvé un bug.

Aidez-moi s'il vous plaît


Si vous avez besoin d'aide, ne m'écrivez pas d'e-mail , il existe une liste de diffusion pour cela. Les instructions d'abonnement se trouvent sur le site Web de PyUSB .

[1] Quand j'écris Vrai ou Faux (avec une majuscule), je veux dire les valeurs correspondantes du langage Python. Et quand je dis vrai (vrai) ou faux (faux), je veux dire toute expression Python qui est considérée comme vraie ou fausse. (Cette similitude s'est produite dans l'original et aide à comprendre les concepts de vrai et de faux en traduction. - Remarque. ) :

[2] Voir la documentation spécifique pour le backend.

[3] La spécification USB n'impose aucune valeur spécifique à la valeur de configuration. Il en va de même pour les numéros d'interface et les autres paramètres.

[4] En fait, tout est un peu plus compliqué, mais ce n'est qu'une explication pour nous.

[5] Je sais que cela semble bizarre.

[6] En effet, s'il n'y a pas de bande passante pour les flux de données isochrones pendant la configuration de l'appareil, elle peut être numérotée avec succès.

[7] Cela ne se produit pas pour la configuration car le périphérique est autorisé à être dans un état non configuré.

[8] Dans PyUSB, les flux de données de contrôle accèdent au point de contrôle 0. Très très très rarement, un appareil dispose d'un point de contrôle de contrôle alternatif (je n'ai jamais rencontré un tel appareil).

[9] Ce n'est qu'une blague, ne la prenez pas au sérieux. De grands choix valent mieux que pas de choix.

Source: https://habr.com/ru/post/fr430528/


All Articles