La lecture et l'écriture à l'USB (HID) interrompre les points de terminaison sur Mac
Je suis tente de communiquer avec un plutôt périphérique USB spécifique et de développer à la fois Windows et Mac code pour le faire.
Le périphérique est un périphérique USB avec une interface HID (classe 3) avec deux points d'extrémité, une entrée d'interruption et une interruption de la production. La nature de l'appareil est telle que les données sont envoyées à partir de l'appareil sur l'entrée d'extrémité seulement lorsque des données sont demandées à partir de l'hôte: l'hôte envoie des données qui le périphérique répond sur son entrée d'interruption de point de terminaison. L'obtention de données de l'appareil (l'écriture) est beaucoup plus simple...
Le code pour Windows est plutôt simple: je reçois une poignée de l'appareil, puis en appel, soit ReadFile ou WriteFile. Apparemment, une grande partie de la sous-jacentes du comportement asynchrone est une abstraction de l'extérieur. Il semble bien fonctionner.
Sur Mac, cependant, c'est un peu collante. J'ai essayé un certain nombre de choses, aucun qui ont été pleinement réussie, mais ici sont les deux choses qui semble la plus prometteuse...
1.) Tenter d'obtenir l'accès à l'appareil (USB) via IOUSBInterfaceInterface, itérer sur les systèmes d'extrémité pour déterminer l'entrée et la sortie des points de terminaison, et (espérons-le) de l'utilisation de ReadPipe et WritePipe de communiquer. Malheureusement, je suis incapable d'ouvrir l'interface une fois que je l'ai, avec la valeur de retour (kIOReturnExclusiveAccess) en notant que quelque chose a déjà l'appareil ouvert exclusivement. J'ai essayé d'utiliser IOUSBinterfaceInterface183, de sorte que je pouvais l'appeler USBInterfaceOpenSeize, mais que les résultats dans le même retourner la valeur de l'erreur.
--- mise à jour 7/30/2010 ---
Apparemment, Apple IOUSBHIDDriver correspond au début de l appareil et c'est ce qui est probablement la prévention de l'ouverture de la IOUSBInterfaceInterface. Depuis le creusement du sujet il me semble que la façon la plus courante pour prévenir la IOUSBHIDDriver à partir de la correspondance est d'écrire un code-moins kext (extension du noyau) avec une sonde de score. Cela permettrait de match au début, la prévention de la IOUSBHIDDriver de l'ouverture de l'appareil, et devrait, en théorie, permettez-moi d'ouvrir l'interface et à écrire et à lire, pour les effets directement. C'est OK, je préfèrerais ne pas avoir à installer quelque chose sur l'ordinateur de l'utilisateur. Si quelqu'un connaît une solide alternative je serais reconnaissant pour l'information.
2.) Ouvrir l'appareil comme un IOHIDDeviceInterface122 (ou plus tard). À lire, j'ai mis en place un async port, source de l'événement et de la méthode de rappel à appeler lorsque les données sont prêtes lorsque les données sont envoyées à partir de l'appareil sur l'entrée d'interruption de point de terminaison. Cependant, pour écrire les données que l'appareil a besoin — pour initialiser une réponse je ne peux pas trouver un moyen. Je suis perplexe. setReport généralement écrit pour le contrôle de l'ordinateur d'extrémité, plus j'ai besoin d'une écriture qui ne vous attendez pas à une réponse directe, pas de blocage.
J'ai regardé autour en ligne et ont essayé beaucoup de choses, mais aucun d'entre eux me donne le succès. Tous les conseils? Je ne peux pas utiliser une grande partie de l'Apple HIDManager code depuis beaucoup de qui est de 10,5+ et mon application doit fonctionner sur 10.4 ainsi.
Vous devez vous connecter pour publier un commentaire.
J'ai maintenant un travail Mac pilote pour un périphérique USB qui nécessite la communication par le biais de l'interruption des points de terminaison. Voici comment j'ai fait:
Finalement, la méthode qui a bien fonctionné pour moi a été l'option 1 (noté ci-dessus). Comme indiqué, j'ai eu des problèmes d'ouverture de la COM-style IOUSBInterfaceInterface de l'appareil. Il est devenu évident au fil du temps que c'était dû à la HIDManager la capture de l'appareil. J'ai été incapable d'arracher le contrôle de l'appareil de l'HIDManager une fois qu'il a été capturé (même pas le USBInterfaceOpenSeize appel ou de l'USBDeviceOpenSeize appels fonctionne).
De prendre le contrôle de l'appareil, j'avais besoin de le saisir avant que le HIDManager. La solution à ce problème est d'écrire un sans code de kext (extension du noyau). Un kext est essentiellement un module qui se trouve dans System/Library/Extensions qui contient (en général) un fichier plist (propriété de la liste) et (occasionnellement) un noyau pilote de niveau, entre autres éléments. Dans mon cas, je voulais seulement le plist, qui permettrait de donner les instructions pour le noyau par rapport à ce qu'il corresponde. Si les données donne une plus grande sonde score que le HIDManager alors je pourrais essentiellement la capture de l'appareil et de l'utilisation d'un espace utilisateur conducteur de communiquer avec elle.
Le kext plist écrit, avec quelques détails spécifiques modifié, est comme suit:
La idVendor et idProduct valeurs donnent le kext de la spécificité et de l'augmentation de sa sonde score suffisamment.
Afin d'utiliser le kext, les éléments suivants doivent être faites (dont mon installateur pour les clients):
sudo chown root:wheel DemiUSBDevice.kext
)sudo cp DemiUSBDevice.kext /System/Library/Extensions
)sudo kextload -vt /System/Library/Extensions/DemiUSBDevice.kext
)sudo touch /System/Library/Extensions
)À ce stade, le système devrait utiliser le kext pour garder la HIDManager à partir de la capture de mon appareil. Maintenant, que faire avec elle? Comment écrire et lire?
Voici quelques simplifiée des extraits de mon code, moins toute erreur de manipulation, qui illustrent la solution. Avant de pouvoir faire quoi que ce soit avec l'appareil, l'application a besoin de savoir quand le dispositif d'attache (et se détache). Notez que ce n'est que pour les fins de l'illustration — certaines de ces variables sont au niveau de la classe, certains sont globales, etc. Voici le code d'initialisation qui définit l'attacher/détacher des événements jusqu':
Il y a deux méthodes qui sont utilisés comme des callbacks dans ce code d'initialisation: device_detach_callback et device_attach_callback (tous deux déclaré à des méthodes statiques). device_detach_callback est simple:
device_attach_callback est là que la magie se produit. Dans mon code j'ai cette répartis en plusieurs méthodes, mais ici, je vais vous le présenter comme un grand monolithique méthode...:
À ce point, nous devrions avoir les numéros de l'interruption des systèmes d'extrémité et ouverte IOUSBInterfaceInterface de l'appareil. Asynchrone de l'écriture de données peut être fait en appelant quelque chose comme:
où les données sont un char tampon de données pour écrire, le dernier paramètre est facultatif dans le contexte de l'objet pour passer dans le rappel, et device_write_completion est une méthode statique de la forme générale suivante:
de lecture à partir de l'interruption de point de terminaison est la même:
où device_read_completion est de la forme suivante:
Noter que pour recevoir ces rappels à l'exécution de la boucle doit être en cours d'exécution (voir ce lien pour plus d'informations sur le CFRunLoop). Une façon d'y parvenir est d'appeler
CFRunLoopRun()
après l'appel de la async lire ou écrire des méthodes à quel point le thread principal bloque lors de l'exécution de la boucle s'exécute. Après la manipulation de votre callback, vous pouvez appelerCFRunLoopStop(CFRunLoopGetCurrent())
pour arrêter l'exécution de la boucle et de la main d'exécution au thread principal.Une autre solution (ce que je fais dans mon code) est de faire passer un objet de contexte (appelé "demande" dans l'exemple de code suivant) dans la WritePipeAsync/ReadPipeAsync méthodes de cet objet contient un booléen indicateur d'achèvement (appelé "is_done" dans cet exemple). Après l'appel de la lecture/écriture de la méthode, au lieu d'appeler
CFRunLoopRun()
, quelque chose comme ce qui suit peut être exécutée:Cela a pour avantage, si vous avez d'autres threads qui utilisent l'exécution de la boucle, vous ne serez pas quitter prématurément si un autre thread arrêter l'exécution de la boucle...
J'espère que cela est utile pour les gens. J'ai dû tirer à partir de nombreuses sources incomplets pour résoudre ce problème, ce qui exige un travail considérable pour le faire fonctionner...
Après la lecture de cette question à quelques reprises et y réfléchir un peu, j'ai pensé à une autre solution pour l'émulation de blocage comportement de la lecture, mais en utilisant le HID manager au lieu de la remplacer.
Un blocage de la fonction de lecture d'enregistrer une entrée de rappel pour l'appareil, l'enregistrement de l'appareil sur l'exécution de la boucle, puis bloc en appelant CFRunLoopRun(). L'entrée de rappel pouvez ensuite copier le rapport dans une mémoire tampon partagée et appel CFRunLoopStop(), ce qui provoque CFRunLoopRun() pour retourner, ce qui déblocage read(). Ensuite, read() peut retourner le rapport de l'appelant.
La première question à laquelle je pense est le cas où l'appareil est d'ores et déjà prévue sur une course en boucle. La planification et alors unscheduling l'appareil en fonction de lecture peut avoir des effets négatifs. Mais que serait un problème si l'application tente d'utiliser à la fois synchrone et asynchrone des appels sur le même appareil.
La deuxième chose qui vient à l'esprit est le cas où le code d'appel a déjà de l'exécution de la boucle en cours d'exécution (le Cacao et les applications Qt par exemple). Mais, la documentation de CFRunLoopStop() semble indiquer que les appels imbriqués CFRunLoopRun() sont gérés correctement. Donc, il devrait être ok.
Voici un peu de code simplifié pour aller avec ça. J'ai juste mis en œuvre quelque chose de semblable dans mon HID Bibliothèque et il semble fonctionner, même si je n'ai pas testé beaucoup.
Je suis tombé sur cette même kIOReturnExclusiveAccess. Au lieu de la combattre (la construction d'un kext, etc). J'ai trouvé l'appareil et utilisé l'api POSIX est.
une fois devPath est défini, vous pouvez appeler d'ouvrir et de lire/écrire..