ラゞコンマシンの䟋を䜿甚したUSBデバむスドラむバヌのリバヌス゚ンゞニアリング

自分でドラむブUSB CAR



画像



Linuxの愛奜家に察するWindowsの愛奜家の䞻匵の1぀は、このOSのハヌドりェアのドラむバヌがないこずです。 時間が経぀に぀れお、状況は改善されおいたす。 今ではすでに10幎前よりもはるかに優れおいたす。 しかし、お気に入りのディストリビュヌションで認識されないデバむスに出䌚うこずもありたす。 通垞、それは䜕らかの皮類のUSB呚蟺機噚です。



フリヌ゜フトりェアの利点は、この問題を自分で解決できるこずですプログラマヌの堎合。 もちろん、それはすべお機噚の耇雑さに䟝存したす。 3次元りェブカメラでは成功しないかもしれたせんが、倚くのUSBデバむスは非垞にシンプルであり、コアの深郚に飛び蟌んだり、Cで掘ったりする必芁はありたせん。 。



プロセスは本質的にリバヌス゚ンゞニアリングになりたす。 たず、デバむスを詳现に調査し、次にWindowsでドラむバヌず亀換するデヌタを保存し、それらの意味を理解しようずしたす。 自明でないプロトコルの堎合、経隓ず運の䞡方が必芁になる堎合がありたす。



USBの玹介



USB-ホスト制埡を備えたバス。 ホストPCは、どのデバむスがデヌタをい぀ワむダ経由で送信するかを決定したす。 非同期むベントキヌボヌドのボタンを抌すの堎合でも、ホストにすぐには送信されたせん。 各バスには最倧127個のデバむスハブ経由の堎合はさらに倚くが存圚する可胜性があるため、この操䜜スキヌムは制埡を容易にしたす。



USBには、むンタヌネットによく䌌た倚局プロトコルシステムもありたす。 通垞、最䜎レベルはシリコンに実装されたす。 トランスポヌト局は、トンネルパむプを介しお機胜したす。 ストリヌムトンネルは、さたざたなデヌタ、メッセヌゞトンネル-デバむスを管理するためのメッセヌゞを送信したす。 各デバむスは、少なくずも1぀のメッセヌゞトンネルをサポヌトしたす。 アプリケヌションたたはクラスの最高レベルには、USB倧容量蚘憶装眮フラッシュドラむブやヒュヌマンむンタヌフェむスデバむスHIDなどのプロトコルがありたす。



ワむダヌで



USBデバむスは、゚ンドポむントのセット、たたはI / Oバッファヌず考えるこずができたす。 それぞれにデヌタの方向入力たたは出力ず送信のタむプがありたす。 バッファのタむプは次のずおりです。割り蟌み、アむ゜クロナス、制埡、およびパケット。



割り蟌みは、リアルタむムで少しデヌタを送信したす。 ナヌザヌがキヌを抌すず、デバむスはホストが「そこでボタンを抌したしたか」ず尋ねるたで埅機したす。 ホストの速床が䜎䞋するこずはなく、これらのむベントは倱われたせん。 アむ゜クロナスはほが同じ方法で動䜜したすが、それほど難しくはありたせん-より倚くのデヌタを転送できたすが、重芁ではない堎合りェブカメラなどは倱われたす。



倧容量甚に蚭蚈されたバッチ。 チャネルを詰たらせないように、珟圚他のデヌタによっお占有されおいないすべおのスペヌスが䞎えられたす。 コントロヌラはデバむスの制埡に䜿甚され、芁求ず応答の圢匏が厳密に定矩されおいるのはコントロヌラのみです。 関連付けられたメタデヌタを持぀バッファのセットは、むンタヌフェむスず呌ばれたす。



USBデバむスのバッファ番号はれロです。これは、制埡デヌタに䜿甚されるトンネルのデフォルトの゚ンドポむントです。 しかし、デバむスが持っおいるバッファヌの数ずそのタむプをホストはどのようにしお知るのでしょうか このために、特別な芁求によっおデフォルトトンネルを介しお送信されるさたざたな蚘述子が䜿甚されたす。 それらはすべおの暙準、特定のクラスのデバむスに特化したもの、たたは独自のものです。



蚘述子は、lsusbなどのナヌティリティで衚瀺できる階局を構成したす。 最䞊郚にはデバむス蚘述子があり、ベンダヌIDVIDず補品IDPIDが含たれおいたす。 このペアはデバむスごずに固有であり、システム䞊で適切なドラむバヌが怜出されたす。 デバむスにはいく぀かの構成があり、それぞれに独自のむンタヌフェむスMFPのプリンタヌ、スキャナヌ、FAXなどがありたす。 ただし、通垞は、1぀のむンタヌフェヌスを持぀1぀の構成が定矩されたす。 それらは、察応する蚘述子によっお蚘述されたす。 各゚ンドポむントには、アドレス番号、方向入力たたは出力、および䌝送タむプを含む蚘述子がありたす。



クラス仕様には独自の蚘述子タむプがありたす。 USB HID仕様では、制埡バッファたたは割り蟌みを介しお送受信される「レポヌト」の圢匏でのデヌタ転送が想定されおいたす。 これらの蚘述子は、レポヌトの圢匏たずえば、「1フィヌルド8ビット長」ずその䜿甚方法「X方向のオフセット」を決定したす。 したがっお、HIDデバむスはそれ自䜓を蚘述し、ナニバヌサルドラむバヌLinuxではusbhidでサポヌトできたす。 それ以倖の堎合は、マりスごずに独自のドラむバヌを䜜成する必芁がありたす。



数癟ペヌゞの仕様を数段萜で説明しようずはしたせん。 www.beyondlogic.org/usbnutshellから無料でO'Reillyの本「USB in a Nutshell」に送信するこずに興味がありたす。 もっず良くしたしょう。



蚱可に぀いお



デフォルトでは、USBデバむスはルヌト内からのみアクセスできたす。 この方法でテストプログラムを実行しないようにするには、udevルヌルを远加したす。



SUBSYSTEM=="usb", ATTRS{idVendor}=="0a81", ATTRS{idProduct}=="0702", GROUP="INSERT_HERE", MODE="0660"
      
      







ナヌザヌが属するグルヌプの名前を挿入し、これを/lib/udev/rules.d/99-usbcar.rulesに远加したす。



ボンネットの䞋



USB経由でマシンがどのように芋えるか芋おみたしょう。 lsusbは、デバむスをカりントし、蚘述子をデコヌドするためのツヌルです。 usbutilsに含たれおいたす。



 [val@y550p ~]$ lsusb Bus 002 Device 036: ID 0a81:0702 Chesen Electronics Corp. Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub ...
      
      







マシンはデバむス036です確かに、切断しおlsusbを再床実行できたす。 IDフィヌルドは、VIDずPIDのペアです。 蚘述子を読み取るには、lsusb -vを実行したす。



 [val@y550p ~]$ lsusb -vd 0a81:0702 Bus 002 Device 036: ID 0a81:0702 Chesen Electronics Corp. Device Descriptor: idVendor 0x0a81 Chesen Electronics Corp. idProduct 0x0702 ... bNumConfigurations 1 Configuration Descriptor: ... Interface Descriptor: ... bInterfaceClass 3 Human Interface Device ... iInterface 0 HID Device Descriptor: ... Report Descriptors: ** UNAVAILABLE ** Endpoint Descriptor: ... bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt ...
      
      







暙準階局。 ほずんどのデバむスず同様に、構成ずむンタヌフェヌスは1぀だけです。 1぀の゚ンドポむント割り蟌みが発生する堎合がありたすデフォルトポむント0は䟋倖です。デフォルトポむント0は垞に存圚するため、リストに衚瀺されたせん。 bInterfaceClassフィヌルドは、これがHIDデバむスであるこずを報告したす。 これは良いこずです-HIDず通信するためのプロトコルは開いおいたす。 レポヌトの圢匏ず䜿甚法を理解するためにレポヌトの蚘述子を読むように思えたすが、それは問題です。 ただし、** UNAVAILABLE **ずマヌクされおいたす。 FAQ マシンはHIDデバむスであるため、usbhidドラむバヌはそれを自分自身に割り圓おたしたが、それをどうするかはわかりたせん。 圌がそれを管理するのを解く必芁がありたす。



たず、バスのアドレスを芋぀ける必芁がありたす。 再接続しお、dmesgを実行したす| grep usb、およびusb XY.Zで始たる最埌の行を芋おください。 X、Y、およびZは、ホスト䞊のポヌトを䞀意に識別する敎数です。 次に実行する



 [root@y550p ~]# echo -n XY.Z:1.0 > /sys/bus/usb/drivers/usbhid/unbind
      
      







1.0は、usbhidドラむバヌがリリヌスする構成ずむンタヌフェむスです。 すべおを結び぀けるには、/ sys / bus / usb / drivers / usbhid / bindに同じこずを曞きたす。



これで、レポヌト蚘述子フィヌルドから情報が埗られたす。



 Report Descriptor: (length is 52) Item(Global): Usage Page, data= [ 0xa0 0xff ] 65440 (null) Item(Local ): Usage, data= [ 0x01 ] 1 (null) ... Item(Global): Report Size, data= [ 0x08 ] 8 Item(Global): Report Count, data= [ 0x01 ] 1 Item(Main ): Input, data= [ 0x02 ] 2 ... Item(Global): Report Size, data= [ 0x08 ] 8 Item(Global): Report Count, data= [ 0x01 ] 1 Item(Main ): Output, data= [ 0x02 ] 2 ...
      
      







2぀のレポヌトが提䟛されたす。 1぀はデバむスから読み取り入力、2぀目は曞き蟌み出力です。 䞡方ずもバむトサむズです。 ただし、それらの䜿甚は明らかではありたせん。 比范のために、マりスのレポヌト蚘述子は次のようになりたすすべおではなく、䞻芁な行。



 Report Descriptor: (length is 75) Item(Global): Usage Page, data= [ 0x01 ] 1 Generic Desktop Controls Item(Local ): Usage, data= [ 0x02 ] 2 Mouse Item(Local ): Usage, data= [ 0x01 ] 1 Pointer Item(Global): Usage Page, data= [ 0x09 ] 9 Buttons Item(Local ): Usage Minimum, data= [ 0x01 ] 1 Button 1 (Primary) Item(Local ): Usage Maximum, data= [ 0x05 ] 5 Button 5 Item(Global): Report Count, data= [ 0x05 ] 5 Item(Global): Report Size, data= [ 0x01 ] 1 Item(Main ): Input, data= [ 0x02 ] 2
      
      







ここではすべおが明確です。 タむプラむタヌでは-それは明確ではなく、私たち自身でビットを䜿甚するこずを掚枬する必芁がありたす。



小ボヌナス



ほずんどのラゞコン玩具はシンプルで、同じ呚波数で動䜜する暙準的な受信機を䜿甚しおいたす。 したがっお、このプログラムを䜿甚しお、このマシン以倖の他のおもちゃを制埡できたす。



探偵の仕事



ネットワヌクトラフィックを分析する堎合、スニファヌが䜿甚されたす。 そしお、私たちの堎合、そのようなこずが䟿利になりたす。 商甚利甚のための特別なUSBモニタヌがありたすが、Wiresharkも私たちのタスクに適しおいたす。



WiresharkでUSBむンタヌセプトを蚭定したす。 たず、カヌネルでUSB監芖を有効にしたす。 usbmonモゞュヌルをダりンロヌドしたす。



 [root@y550p ~]# modprobe usbmon
      
      







特別なdebugfsファむルシステムをマりントしたす。



 [root@y550p ~]# mount -t debugfs none /sys/kernel/debug
      
      







/ sys / kernel / debug / usb / usbmonディレクトリが衚瀺されたす。これは、単玔なシェルツヌルを䜿甚しおトラフィックを蚘録するために䜿甚できたす。



 [root@y550p ~]# ls /sys/kernel/debug/usb/usbmon 0s 0u 1s 1t 1u 2s 2t 2u
      
      







暗号化された名前のファむルがありたす。 æ•Žæ•°-バス番号USBバスアドレスの最初の郚分; 0は、ホスト䞊のすべおのバスを意味したす。 s-統蚈、t-転送、u-URB進行䞭のトランザクションを衚すUSB芁求ブロック論理゚ンティティ。 バス2のすべおの転送を保存するには、次のように入力したす。



 [root@y550p ~]# cat /sys/kernel/debug/usb/usbmon/2t ffff88007d57cb40 296194404 S Ii:036:01 -115 1 < ffff88007d57cb40 296195649 C Ii:036:01 0 1 = 05 ffff8800446d4840 298081925 S Co:036:00 s 21 09 0200 0000 0001 1 = 01 ffff8800446d4840 298082240 C Co:036:00 0 1 > ffff880114fd1780 298214432 S Co:036:00 s 21 09 0200 0000 0001 1 = 00
      
      







蚓緎されおいない目では、ここでは䜕も明確ではありたせん。 Wiresharkがデヌタをデコヌドするのは良いこずです。



次に、元のドラむバヌで動䜜するWindowsが必芁です。 VirtualBoxにすべおをむンストヌルするのが最適ですUSBサポヌトが必芁なため、Oracle Extension Packを䜿甚。 VirtualBoxがデバむスを䜿甚できるこずを確認し、Windowsでマシンを制埡するKeUsbCarを実行したす。 Wiresharkを起動しお、ドラむバヌがデバむスに送信するコマンドを確認したす。 最初の画面で、usbmonXむンタヌフェヌスを遞択したす。Xはマシンが接続されおいるバスです。 ルヌトの倖郚からWiresharkを実行する堎合は、ノヌド/ dev / usbmon *に適切な暩限があるこずを確認しおください。



画像



KeUsbCarの[進む]ボタンをクリックしたす。 Wiresharkは、いく぀かの発信制埡パケットを傍受したす。 スクリヌンショットは必芁なものを瀺しおいたす。 パラメヌタヌから刀断するず、これはSET_REPORT芁求bmRequestType = 0x21、bRequest = 0x09であり、通垞はキヌボヌドの電球などのデバむスのステヌタスを倉曎するために䜿甚されたす。 確認したレポヌト蚘述子によるず、デヌタ長は1バむトで、レポヌト自䜓には0x01が含たれおいたすこれも匷調衚瀺されおいたす。



右ボタンを抌すず、同様のク゚リが実行されたす。 ただし、レポヌトにはすでに0x02が含たれおいたす。 これは移動の方向を意味するず掚枬できたす。 同様に、0x04が右逆、0x08が逆などであるこずがわかりたす。 ルヌルは単玔です。方向コヌドは、KeUsbCarむンタヌフェヌスのボタンの䜍眮で巊にシフトしたバむナリ単䜍です時蚈回りにカりントした堎合。



゚ンドポむント1からの定期的な割り蟌み芁求に泚意するこずもできたす0x81、0x80はこれが゚ントリポむントであるこずを意味し、0x01はそのアドレスです。 これは䜕ですか ボタンに加えお、KeUsbCarには充電むンゞケヌタがあるため、これはバッテリヌ情報である可胜性がありたす。 車がガレヌゞを離れない堎合、それらの倀は倉化したせん0x05。 それ以倖の堎合、割り蟌み芁求は発生したせんが、元に戻すず再開されたす。 そしお、どうやら、0x05は「充電」を意味したすおもちゃは簡単なので、充電レベルは送信されたせん。 バッテリヌが充電されるず、割り蟌みは0x857ビットが蚭定された0x05を返し始めたす。 どうやら、7ビットは「充電枈み」を意味したす。 0x05を構成するビット0ず2の動䜜はただ明確ではありたせん。



ほずんど本物のドラむバヌを曞く



以前はサポヌトされおいなかったデバむスでプログラムを動䜜させるこずは良いこずですが、堎合によっおはシステムの残りの郚分を動䜜させる必芁がありたす。 ぀たり、ドラむバヌを䜜成する必芁があり、これにはカヌネルレベルhttp://www.linuxvoice.com/be-a-kernel-hacker/でのプログラミングが必芁であり、今ではほずんど必芁ありたせん。 しかし、USBに぀いお話しおいる堎合は、おそらくそれなしでも実行できたす。



USBネットワヌクカヌドがある堎合、TUN / TAPを䜿甚しおPyUSBプログラムをLinuxネットワヌクスタックに接続できたす。 TUN / TAPむンタヌフェヌスは、tun0やtap1のような名前を持぀通垞のネットワヌクむンタヌフェヌスのように機胜したすが、それらを通しお、すべおのパッケヌゞが/ dev / net / tunノヌドで利甚可胜になりたす。 pytunモゞュヌルを䜿甚するず、TUN / TAPを簡単に操䜜できたす。 パフォヌマンスは䜎䞋したすが、libusbを䜿甚しおCプログラムを曞き換えるこずができたす。



別の候補はUSBディスプレむです。 Linuxには、/ dev / fbXなどのフレヌムバッファヌにアクセスできるvfbモゞュヌルがありたす。 ioctlを䜿甚しおコン゜ヌルをコン゜ヌルにリダむレクトし、/ dev / fbXのコンテンツをUSBデバむスにアップロヌドできたす。 これも高速ではありたせんが、USB経由で3Dシュヌティングゲヌムをプレむする予定はありたせん。



コヌドを曞く



Windows甚ず同じプログラムを䜜りたしょう。 6本の矢印ず、マシンの充電䞭に点滅する充電レベル。 コヌドはgithubにありたすgithub.com/vsinitsyn/usbcar.py



LinuxでUSBを䜿甚するにはどうすればよいですか これは、libusbラむブラリを䜿甚しおナヌザヌ空間から実行できたす。 Cで曞かれおおり、USBの十分な知識が必芁です。 簡単な代替手段はPyUSBです。 ナヌザヌむンタヌフェむスには、PyGameを䜿甚したした。



github.com/walac/pyusbからPyUSB゜ヌスをダりンロヌドし、setup.pyでむンストヌルしたす。 libusbラむブラリもむンストヌルする必芁がありたす。 マシンを制埡する機胜を、USBCarずいう元の名前のクラスに入れたした。



 import usb.core import usb.util class USBCar(object): VID = 0x0a81 PID = 0x0702 FORWARD = 1 RIGHT = 2 REVERSE_RIGHT = 4 REVERSE = 8 REVERSE_LEFT = 16 LEFT = 32 STOP = 0
      
      







2぀のメむンPyUSBモゞュヌルをむンポヌトし、トラフィックを衚瀺するずきに蚈算したマシンを制埡するための倀を挿入したす。 VIDおよびPIDは、lsusb出力から取埗したマシンIDです。



 def __init__(self): self._had_driver = False self._dev = usb.core.find(idVendor=USBCar.VID, idProduct=USBCar.PID) if self._dev is None: raise ValueError("Device not found")
      
      







usb.core.find関数は、IDでデバむスを怜玢したす。 詳现に぀いおは、 github.com / walac / pyusb / blob / master / docs / tutorial.rstをご芧ください



  if self._dev.is_kernel_driver_active(0): self._dev.detach_kernel_driver(0) self._had_driver = True
      
      







lsusbで行ったように、カヌネルドラむバヌを解きたす。 0-むンタヌフェヌス番号。 プログラムを終了するず、アクティブであった堎合は、リリヌスでバむンドし盎す必芁がありたす。 したがっお、self._had_driverの初期状態を芚えおいたす。



  self._dev.set_configuration()
      
      







構成を実行したす。 このコヌドは、PyUSBがプログラマから隠しおいる次のコヌドず同等です。



  self._dev.set_configuration(1) usb.util.claim_interface(0) def release(self): usb.util.release_interface(self._dev, 0) if self._had_driver: self._dev.attach_kernel_driver(0)
      
      







このメ゜ッドは、プログラムが終了する前に呌び出す必芁がありたす。 䜿甚枈みのむンタヌフェむスを解攟し、カヌネルドラむバヌを接続し盎したす。



機械の動き



 def move(self, direction): ret = self._dev.ctrl_transfer(0x21, 0x09, 0x0200, 0, [direction]) return ret == 1
      
      







directionは、クラスの最初に定矩された倀の1぀です。 ctrl_transferは制埡コマンドを枡したす。 デヌタは文字列たたはリストずしお送信されたす。 このメ゜ッドは、曞き蟌たれたバむト数を返したす。 1バむトしかないため、この堎合はTrueを返し、そうでない堎合はFalseを返したす。



バッテリヌ状態の方法



 def battery_status(self): try: ret = self._dev.read(0x81, 1, timeout=self.READ_TIMEOUT) if ret: res = ret.tolist() if res[0] == 0x05: return 'charging' elif res[0] == 0x85: return 'charged' return 'unknown' except usb.core.USBError: return 'out of the garage'
      
      







readメ゜ッドは、゚ンドポむントアドレスず読み取るバむト数を受け入れたす。 転送のタむプぱンドポむントによっお決定され、蚘述子に保存されたす。 たた、プログラムがより速く動䜜するように、非暙準のタむムアりト時間を蚭定したす。 Device.readは、リストに倉換する配列を返したす。 最初のバむトをチェックしお、充電ステヌタスを刀断したす。 マシンがガレヌゞにない堎合、read呌び出しは倱敗し、usb.core.USBError゚ラヌがスロヌされたす。 この゚ラヌはたさにこれによるものず想定しおいたす。 その他の堎合、「䞍明」のステヌタスを返したす。



UIクラスは、ナヌザヌむンタヌフェむスをカプセル化したす。 䞻なものを芋おいきたしょう。 メむンルヌプはUI.main_loopにありたす。 背景を写真で蚭定し、マシンがガレヌゞにある堎合は充電レベルを衚瀺し、コントロヌルボタンを描画したす。 次に、むベントを埅ちたす-クリックの堎合、USBCar.moveを介しおマシンを指定の方向に移動したす。



GUIを含むプログラム党䜓は、200行匷を占めたす。 ドキュメントのないデバむスにずっおは悪くありたせん。



もちろん、かなり単玔なデバむスを具䜓的に取り䞊げたした。 しかし、䞖界には私たちのデバむスに䌌たデバむスがかなり倚くあり、倚くは私たちが取り䞊げたものずあたり倉わらないプロトコルを䜿甚しおいたす。 耇雑なデバむスのリバヌス゚ンゞニアリングは簡単な䜜業ではありたせんが、受信した電子メヌルを報告するデバむスなど、Linuxサポヌトにいく぀かの装身具を远加できるようになりたした。 これがあたり圹に立たない堎合は、少なくずも興味深いです。



All Articles