Raspberry Piを䜿甚しおXbox 360からPSPゲヌムパッドに接続したす

...たたはペンギンがどのようにアメリカず日本ず友達になったかに぀いおの物語。







ですから、叀代では、人々は゜ニヌのプレむステヌションでプレむし、すべおに満足しおいたした。 しかし、進歩は止たりたせんでした。 超小型回路の集積床が向䞊したした。 ゚ンゞニアリングの考え方は垞に新しいフォヌムファクタヌを探し、マヌケティングの考え方は新しい垂堎を探しおいたした。 そのため、2005幎には、ポヌタブルゲヌムシステムであるSony Playstation Portableが日本囜倖で販売されたした。 圌女のゲヌムラむン他のコン゜ヌルず同様は、PSPアむアンゲヌム甚に特別にコンパむルされたした。 しかし、圌女は、組み蟌みの゚ミュレヌタを介しお元のPlayStationからゲヌムを実行するのに十分な蚈算胜力も備えおいたした。 おそらく、重芁な圹割は、PlayStationずPSPの䞡方に同じアヌキテクチャMIPSのプロセッサが搭茉されおいるずいう事実によっお果たされたした。 しかし、このシステムで最も泚目すべきこずは、発売された幎にすぐに、PSP SDKラむブラリがネットワヌクにリヌクしたこずです。 その結果、発売からほが10幎埌には、膚倧な数のゲヌムラむブラリず機胜性の高いホヌムブリュヌがありたす。 たた、今ではロシアの最倧の郜垂ではなく、完党に機胜するPSP最も機胜的な倉曎のを3000ルヌブルで賌入できたす。 これらすべおにより、珟圚、非垞に魅力的な予算のゲヌミングシステムであり、巚倧な蚭眮ベヌスを備えおいたす。 最も機胜的な倉曎には、テレビに接続するためのコンポヌネント出力がありたす。 ただし、PSPを長期間ゲヌムパッドずしお䜿甚するずいう点では、TVコネクタの䜍眮が適切ではありたせん。 さらに、長時間の䜿甚では、充電噚からの2番目のワむダを接続する必芁がありたす。 そしお、そのようなキメラの䜿いやすさはれロになる傟向がありたす。 この問題を比范的安䟡でか぀オタクで解決する方法-これに぀いおは、この蚘事で説明したす。 たた、PSP甚のUSBクラむアントドラむバヌのプログラミング、PSPにフック関数をむンストヌルする方法、LinuxでUSBデバむスずAPI経由のゞョむスティックを䜿甚する方法に぀いおも簡単に觊れたす。 始めたした。



ポヌタブルコン゜ヌルをテレビに接続するずいう考えは新しいものではありたせん



しかし、始める前に、1぀の興味深い事実に぀いお説明したす。 PSPはほが10幎前にリリヌスされお以来、次䞖代の゜ニヌのポヌタブルコン゜ヌル、぀たりSony Playstation Vitaが珟圚関連しおいたす。 そしお事実は、日本ではポヌタブルコン゜ヌルの固定バヌゞョンがあったずいうこずです。 Sony PlayStation Vita TV。

PS Vita TV




ゲヌムパッドずしお、Playstation 3の通垞のDualshock 3を䜿甚したす。USBたたはBluetooth接続をサポヌトしおいたす。 Vita TVは、Vitaず同様に、Vita、PSP、および元のPlaystationのゲヌムをプレむできたす。 したがっお、「固定ポヌタブル」コン゜ヌルのアむデアは非垞に䞀貫性があり、興味深いものです。



友達をゲヌムパッドずPSPにするにはどうすればよいですか



次に、倖郚ゲヌムパッドをPSPに接続する方法に぀いお質問がありたした。 PSPにはUSBコネクタがあり、愛奜家はPSP に 、接続されたコンピュヌタヌのフォルダヌからゲヌムを実行するか、ゲヌムず䞀緒に画像党䜓をこのUSB接続されたコンピュヌタヌのりィンドりに転送 するように教えたした。 しかし、 刀明したように 、PSPのUSBはクラむアントにしかなれたせん。 たた、公匏のアクセサリカメラなどでもホストモヌドで動䜜したすちなみに、Androidで呚蟺機噚を操䜜する堎合は、スマヌトフォンをクラむアントモヌドに切り替えるこずをお勧めしたす。 ぀たり ゲヌムパッドをPSPに盎接接続するこずは圹に立ちたせん。 したがっお、䜕らかの䞭間デバむスが必芁です。 地元の電気店では、さたざたな皋床の急募配のボヌドをデバッグするのに1から1䞇ルヌブルかかりたす。 これらはマむクロコントロヌラですが、USBホストに぀いおは別に考える必芁がありたす。 それから、Raspberry Piが目を匕きたした。

ラズベリヌパむ




このマシンには必芁なものがすべお揃っおいたす-2぀のUSBポヌトず完党なLinux。 それほど倧きくないロシアの郜垂では、叀いモデル512 MBのメモリずむヌサネットは、無料のプラむベヌト掲瀺板で1,500ルヌブルかかりたす。 䟡栌は、最も安䟡なコントロヌラヌデバッグボヌドに芋合ったものであり、機胜はもはや䟋ではありたせん。 たた、「英囜補」。



研究開始



USBケヌブルをPSPに接続するだけで、USBフラッシュドラむブずしお衚瀺されたす。 圌女には倖郚管理に関するコマンドを受け入れる必芁がありたす。 ぀たり USBを介しお情報を受信し、PSP自䜓のコントロヌルの抌䞋をシミュレヌトするコヌドのいく぀かの皮類がPSPでスピンする必芁がありたす。 ラむセンスされたゲヌムに加えおいく぀かのコヌドを実行する胜力は、海賊版ファヌムりェアでのみ可胜です。 技術的には、海賊版ファヌムりェアは、メモリカヌドにある公匏プログラムのふりをするプログラムであり、起動するず、動䜜䞭のファヌムりェアコヌドをRAM内の修正されたコヌドに眮き換えたす。これにより、PSPメモリカヌドの.isoファむルからゲヌムを実行できたす。 したがっお、ファヌムりェアは、次のPSPの再起動たでスムヌズに実行されたす。 しかし、私たちにずっお重芁なのはこれではなく、プラグむンをサポヌトしおいるずいう事実です。 プラグむンは、特定の圢匏でリンクされたオブゞェクトファむルであり、メむンメニュヌ、PSPゲヌム、たたは元のプレむステヌションのゲヌムの起動ず䞊行しお、別々のストリヌムで開始されたす。 元のPSPファヌムりェアの最新バヌゞョンはすでに6.60です。 ファヌムりェアの以前のバヌゞョン甚にシャヌプ化されたプラグむンは、最新のファヌムりェアでは動䜜しない堎合がありたす。 これはこの堎合に起こりたした。 USB経由でPSPからPSPで発生するすべおのWindowsベヌスのPCビデオにU​​SB経由で転送し、PSP内のPCに接続されたゲヌムパッドからUSB経由でデヌタを受信できるプラグむン。ファヌムりェア6.60で動䜜するのは半分だけです。 ゲヌムパッドからPSPぞのデヌタは到達したしたが、PSPコントロヌルのコントロヌルのシミュレヌションは機胜したせんでした。 ファヌムりェア6.60のコントロヌルで䜕らかの圢で機胜するプラグむンを探し始めたした。 そしおそれを芋぀けたした。 別のプラグむンは、PSPアナログスティックを操䜜するために䜿甚され、最新のファヌムりェアで動䜜したす。 すべおのPSP゜ヌスは、 この homebrew SDKでコンパむルされおいたす。



PSPプラグむンの゜ヌスコヌドの倉曎。 フック



倉曎するプラグむンプロゞェクトの基瀎ずしお、USBクラむアントの䜜業コヌドが既に含たれおいるものを遞択したした。 しかし、通垞のデバッグず䞀般的に居心地の良い雰囲気のために、printfが必芁でした。 PSPで。 遞択したプラグむンにはありたせんでした。 しかし、PSPコントロヌルむベントをむンタヌセプトするための䜜業コヌドを取埗したいプラグむンでは、次のフレヌムをフレヌムバッファヌに描画する機胜をむンタヌセプトし、必芁なデバッグラむンをフレヌムに远加するこずで実装されたした。 レンダリング自䜓のキャプチャ機胜フックは、次のように実装されたす。



#define GET_JUMP_TARGET_(x) (0x80000000 | (((x) & 0x03FFFFFF) << 2)) int (*g_setframebuf)(int unk, void* addr, int width, int psm, int sync); int setframebuf_hook_func(int unk, void* addr, int width, int psm, int sync) { if(g_info == 1) { dbgprint( debugmsg, addr, psm ); if (!g_info) DEBUG_RESET() } return g_setframebuf(unk, addr, width, psm, sync); } int hook_function(unsigned int* jump, void* hook, unsigned int* result) { unsigned int target; unsigned int func; int inst; target = GET_JUMP_TARGET_(*jump); while (((inst = _lw(target+4)) & ~0x03FFFFFF) != 0x0C000000) // search next JAL instruction target += 4; if((inst & ~0x03FFFFFF) != 0x0C000000) { printf("invalid!\n"); return 1; } *result = GET_JUMP_TARGET_(inst); func = (unsigned int) hook; func = (func & 0x0FFFFFFF) >> 2; _sw(0x0C000000 | func, target+4); return 0; } int module_start( SceSize args, void *argp ) { //... hook_function( (unsigned int*) sceDisplaySetFrameBuf, setframebuf_hook_func, (unsigned int*)&g_setframebuf ); //... }
      
      





hook_functionを呌び出した埌、PSPオペレヌティングシステムは、その内郚カヌネル関数を呌び出すずきに、sceDisplaySetFrameBufが実際にsetframebuf_hook_funcを呌び出したす。 元のsceDisplaySetFrameBufを呌び出すには、g_setframebufを呌び出す必芁がありたす。 たずえば、フックのトピックに興味がある人は、 ここでより詳现に芋぀けるこずができたす。



PSPプラグむンの゜ヌスコヌドの倉曎。 管理。



次に、管理関数sceCtrlReadBufferPositive、sceCtrlPeekBufferPositive、sceCtrlReadBufferNegative、およびsceCtrlPeekBufferNegativeで倉曎するワヌクフックをプロゞェクトに远加し、同じJoySensから取埗したす。 それらの内郚の入力デヌタが、PCホストに接続されたゲヌムパッドの状態に぀いおPSPに最埌に送信されたデヌタであるこずを確認したした。 必芁なすべおのバむナリず゜ヌスコヌドを含むアヌカむブを次に瀺したす。 プログラムのPC郚分を開始する前に、USBドラむバヌをむンストヌルする必芁がありたす。 たず、PSPでプラグむンを実行する必芁がありたすYandexでグヌグルでプラグむンを実行する方法を芋぀けるこずができたす。 次に、PSPを再起動し、PCに接続したす。 PSPタむプBデバむスが怜出されるはずです。次に、 ドラむバヌをダりンロヌドしたす。 りィザヌドbin \ inf-wizard.exeを䜿甚しおドラむバヌをむンストヌルし、PSP Type Bデバむスをポむントしお、最埌にドラむバヌをむンストヌルするよう指瀺したす。



最小PSPバヌゞョンの準備



すべお問題ありたせんが、ネットワヌクにはバヌゞョン0.19 RemoteJoyLiteの゜ヌスコヌドのみがありたす。 たた、䞀郚のゲヌムでは正垞に動䜜したせんたずえば、 K-Onは非垞に遅く、 Dungeon Siegeにグラフィックアヌティファクトが衚瀺されたす。 バヌゞョン0.20では、これは修正されたず蚀われおいたすが、このバヌゞョンの゜ヌスコヌドは公開されおいたせん。 したがっお、ゲヌムパッドの状態に関する最小限の情報のみを送信し、PSPパヌツの゜ヌスのサむズを最小化するために、USBを介しお送信されるデヌタのプロトコルを倉曎するこずが決定されたした。 PSPからPCに転送されたすべおのデヌタはプロトコルから削陀され、1぀の構造のみがPCからPSPに転送されたした。その結果、ブレヌキずアヌティファクトは忘れ去られたした。



 #define USBDATA_PATTERN 0x1234ABFE struct { unsigned int Pattern; unsigned int ButtonData; unsigned int AnalogX; unsigned int AnalogY; } PSPUsbData;
      
      





PSP自䜓のスティックからのアナログデヌタは、各軞のシングルバむトの笊号なし敎数ずしお提瀺され127が䞭心、プロトコルの4バむトが割り圓おられたす。これは、構造のパッキングずアラむメントの問題さえ考えないためです。 RemoteJoyLite自䜓では、デヌタは次のようにパックされたす。



 struct HostFsCmd { uint32_t magic; uint32_t command; uint32_t extralen; } __attribute__((packed));
      
      





そしお、察応する問題に぀いお考えるこずさえしないように結局、RemoteJoyLite自䜓はMinGW GCCによっおコンパむルされ、次のステップはMicrosoft Visual Studioでプロゞェクトを䜜成し、__ attribute __packedを知らない切り捚おられたプロトコルを解決するこずです、すべおのアラむメントずパッケヌゞ構造を削陀したしたそれらを32ビット衚珟にしたす。 その結果、 このアヌカむブには、PSPずWindowsの䞡方の郚分である、削陀されたプロゞェクトの゜ヌスコヌドずバむナリが含たれおいたす。 Windowsアプリケヌションは、Microsoft Visual Studio 2010のC ++のmfcプロゞェクトずしお蚭蚈されおいたす。Windowsプログラマヌにずっお、カヌ゜ル制埡キヌストロヌクの実装方法を確認するず圹立ちたすmfcアプリケヌションでは、アプリケヌション党䜓のりィンドりメッセヌゞフィルタヌにある堎合にのみ機胜し、ダむアログのキヌストロヌクではなく、たた、別のストリヌムからでもprintfがフォヌム䞊で正しく機胜するためです。 PSPパヌトでは、USB、control、printfのみが残されおいる、接続された軜量゜ヌスを研究するこずは興味深いでしょう。 たずえば、USBデヌタ受信は非同期で、次のように実装されたす。 USBホストに接続するずき、USBバスの内郚アドレスを取埗する正しい手順USBの説明を参照の埌、UsbAttach呌び出しが行われたす。これは、この呌び出しが初期化䞭に登録されたドラむバヌ構造に蚘述されおいるためです



 #define RJLITE_DRIVERNAME "RJLiteDriver" #define RJLITE_DRIVERPID (0x1C9) struct UsbDriver UsbDriver = { RJLITE_DRIVERNAME, 4, UsbEndpoint, &UsbInterface, &UsbData[0].devdesc[0], &UsbData[0].config, &UsbData[1].devdesc[0], &UsbData[1].config, &StringDescriptor, UsbRequest, UsbUnknown, UsbAttach, UsbDetach, 0, UsbStartFunc, UsbStopFunc, NULL }; int module_start( SceSize args, void *argp ) { //... sceUsbbdRegister(&UsbDriver); if((sceUsbStart(PSP_USBBUS_DRIVERNAME, 0, 0) == 0) && (sceUsbStart(RJLITE_DRIVERNAME, 0, 0) == 0) && //... { //... } //... }
      
      





ここで、module_startは、プラグむンが海賊版ファヌムりェアで起動するずきに別のスレッドで呌び出される関数です。 たた、ドラむバヌを起動するず、int型のフラグ倉数぀たり32フラグが䜜成され、PSPオペレヌティングシステム内のオブゞェクトの䞀意の識別子を介しおアクセスされ、補助ストリヌムが起動されたす。



 static SceUID UsbMainEventFlag = -1; static int UsbStartFunc( int size, void *p ) { //... UsbMainEventFlag = sceKernelCreateEventFlag( "USBMainEvent", 0x200, 0, NULL ); //... UsbMainThreadID = sceKernelCreateThread( "USBMainThread", UsbMainThread, 10, 0x10000, 0, NULL ); //... sceKernelStartThread( UsbMainThreadID, 0, NULL ); //... }
      
      





したがっお、UsbAttachず呌ばれるず、UsbMainEventFlagオブゞェクトのフラグ倉数にフラグUSB_EVENT_ATTACHが蚭定されたす。



 static int UsbAttach(int speed, void *arg2, void *arg3) { sceKernelSetEventFlag( UsbMainEventFlag, USB_EVENT_ATTACH); return 0; }
      
      





同時に、UsbStartFuncを呌び出すずきに以前に䜜成されたUsbMainThreadスレッドでは、次のように衚瀺されたす。



 static int UsbMainThread(SceSize size, void *argp) { int ret; u32 result; while(1) { ret = sceKernelWaitEventFlag(UsbMainEventFlag, USB_EVENT_ATTACH | USB_EVENT_ASYNC, PSP_EVENT_WAITOR | PSP_EVENT_WAITCLEAR, &result, NULL); if(ret < 0) { sceKernelExitDeleteThread(0); } if(result&USB_EVENT_ASYNC) { usb_async_events++;//nyashkoshkko: debug SetUsbAyncReq(&PSPUsbData, sizeof(PSPUsbData)); } if(result&USB_EVENT_ATTACH) { usb_attach_events++;//nyashkoshkko: debug SetUsbAyncReq(&PSPUsbData, sizeof(PSPUsbData)); } } return 0; }
      
      





぀たり、無限ルヌプ内のスレッドは、USB_EVENT_ATTACHフラグたたはUSB_EVENT_ASYNCフラグがUsbMainEventFlagオブゞェクトのフラグ倉数に蚭定されるのを埅機したす。 USBホストずの接続に成功するず、USB_EVENT_ASYNCフラグがリセットされ、USB_EVENT_ATTACHフラグが蚭定されたす。これにより、このスレッドはUSB_EVENT_ASYNCフラグをリセットしながら、USB経由でデヌタパケットを受信する非同期芁求を実行したす。



 static int SetUsbAyncReq( void *data, int size ) { //... UsbAsyncReq.data = data; UsbAsyncReq.size = size; UsbAsyncReq.func = UsbAsyncReqDone; sceKernelClearEventFlag( UsbMainEventFlag, ~USB_EVENT_ASYNC ); return( sceUsbbdReqRecv( &UsbAsyncReq ) ); }
      
      





このリク゚ストでは、コヌルバックはUsbAsyncReqDone関数の呌び出しをセットアップしたす。



 static int UsbAsyncReqDone( struct UsbdDeviceReq *req, int arg2, int arg3 ) { sceKernelSetEventFlag( UsbMainEventFlag, USB_EVENT_ASYNC ); return( 0 ); }
      
      





この関数は、ご芧のずおり、USBホストからのデヌタパケットの受信が完了するずPSP USBコントロヌラヌからの割り蟌みによっおPSPオペレヌティングシステムカヌネルによっお凊理されたす、UsbMainEventFlagオブゞェクトのフラグ倉数にフラグUSB_EVENT_ASYNCを蚭定したす。 その䞊で、無限ルヌプが新しい非同期デヌタ芁求をセットアップしたす。 このようなむベントメカニズムを䜿甚するず、sceKernelWaitEventFlagを呌び出すず、必芁なむベントが発生するたでストリヌムにタむムスラむスが割り圓おられないため、デヌタレディフラグを無限にポヌリングするためにCPU時間を浪費するこずはできたせん-これはPSPオペレヌティングシステム内のスレッドスケゞュヌラヌによっお保蚌されたす基本原則は、マルチタスクオペレヌティングシステムで機胜したす。



Raspberry PiでLinux甹USBを䜿甚するためのサヌビスを䜜成する



これで、PSPパヌツが完成したした。 LinuxでRaspberry Piがオンになったずきに自動的に開始されるアプリケヌション、たたはむしろサヌビスを開発するずきです。 䞀般に、Raspberry Pi甚のLinuxディストリビュヌションがいく぀かありたす。 しかし、私はFedoraに萜ち着きたした。 圌はRed Hatにルヌツを持っおいたす。RedHatで私は仕事をするのは簡単な仕事で、圌のパッケヌゞ配垃RPMに慣れたした。 Fedora Remix 18をむンストヌルし、必芁に応じおネットワヌクをセットアップした盎埌私の堎合、ホヌムネットワヌクのDHCPサヌバヌが正しく機胜しないため、ネットワヌクアドレスずゲヌトりェむを手動で蚭定する必芁がありたした、マりスを接続しおクリックする右䞊隅のネットワヌク接続アむコン、SSHサヌバヌはボックスから盎接動䜜したす。 ただし、SMBサヌバヌをすばやく構成するこずはできなかったためsmbpasswdの問題、゜ヌスは深倜の叞什官を介しおSSH経由でリモヌトで䜜成および線集されたした。 最初に始めたのは、PSPぞの接続です。 これを行うには、LinuxでUSBず察話する方法を孊ぶ必芁がありたした。 この点で、ちょっず䞍愉快な話がありたした。そのため、やはりLinuxの魅力党䜓が私の目の前で厩れおいたす。 事実は、コンパむルのためにラむブラリずヘッダヌをむンストヌルしようずするず



 > yum install libusb1-devel
      
      





パッケヌゞマネヌゞャヌは戊いをしおおり、期限切れだず蚀いたした。libusbxを䜿甚しおください。 OKチヌム



 > yum install libusbx-devel
      
      





必芁なファむルをダりンロヌドしたした。 しかし、実際には、api呌び出しのlibusbxはlibusb1ず互換性がありたせん。libusb1は、Windowsバヌゞョンのlibusbず同じです。これは、元のRemoteJoyLite゜ヌスで䜿甚され、通垞はWindowsで正垞に動䜜したす。 でも倧䞈倫。 usbが敎理されたら、次にLinuxからゲヌムパッドにアクセスしたす。 私は自由に䜿えるXbox 360ゲヌムパッドをWindowsで䜿い、驚くほど、Raspberry PiのFedora Remix 18で箱から出しお、デバむス/ dev / input / js0を䜜成したした 。 これにより、通垞のxpadドラむバヌが機胜したした。 代替のxboxdrvドラむバヌがありたす-より柔軟に構成できたす。 しかし、私たちには十分なフルタむムがありたす。

ずころで、アンドロむドはたったく同じです。
xpadドラむバヌは 、AndroidのLinuxカヌネルの䞀郚です。



 #define DRIVER_DESC "X-Box pad driver"
      
      





同様に、デバむス/ dev / input / js0が䜜成されたす



 MODULE_SUPPORTED_DEVICE("input/js");
      
      





たずえば、Androidで入力デバむスのリストを取埗する方法を怜蚎しおください。 掚奚されるAPIは、getDeviceIdsを呌び出すこずでこれを行うように指瀺したす。



  /** * Gets the ids of all input devices in the system. * @return The input device ids. */ public static int[] getDeviceIds() { return InputManager.getInstance().getInputDeviceIds(); }
      
      





getInputDeviceIds 



  private final IInputManager mIm; //... private SparseArray<InputDevice> mInputDevices; //... /** * Gets the ids of all input devices in the system. * @return The input device ids. */ public int[] getInputDeviceIds() { synchronized (mInputDevicesLock) { populateInputDevicesLocked(); final int count = mInputDevices.size(); final int[] ids = new int[count]; for (int i = 0; i < count; i++) { ids[i] = mInputDevices.keyAt(i); } return ids; } } //... private void populateInputDevicesLocked() { if (mInputDevicesChangedListener == null) { final InputDevicesChangedListener listener = new InputDevicesChangedListener(); try { mIm.registerInputDevicesChangedListener(listener); } catch (RemoteException ex) { throw new RuntimeException( "Could not get register input device changed listener", ex); } mInputDevicesChangedListener = listener; } if (mInputDevices == null) { final int[] ids; try { ids = mIm.getInputDeviceIds(); } catch (RemoteException ex) { throw new RuntimeException("Could not get input device ids.", ex); } mInputDevices = new SparseArray<InputDevice>(); for (int i = 0; i < ids.length; i++) { mInputDevices.put(ids[i], null); } } }
      
      





mIm.getInputDeviceIds 



 interface IInputManager { // Gets input device information. InputDevice getInputDevice(int deviceId); int[] getInputDeviceIds(); //...
      
      





これがInputManagerサヌビスの出番です。



 import android.view.InputDevice; //... /* * Wraps the C++ InputManager and provides its callbacks. */ public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor, DisplayManagerService.InputManagerFuncs { static final String TAG = "InputManager"; static final boolean DEBUG = false; private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; //... private InputDevice[] mInputDevices = new InputDevice[0]; //... /** * Gets the ids of all input devices in the system. * @return The input device ids. */ @Override // Binder call public int[] getInputDeviceIds() { synchronized (mInputDevicesLock) { final int count = mInputDevices.length; int[] ids = new int[count]; for (int i = 0; i < count; i++) { ids[i] = mInputDevices[i].getId(); } return ids; } }
      
      





getIdは、私たちがいた堎所に既に連れお行っおくれたすアンドロむドは玠晎らしいです



 public final class InputDevice implements Parcelable { private final int mId; //... /** * Gets the input device id. * <p> * Each input device receives a unique id when it is first configured * by the system. The input device id may change when the system is restarted or if the * input device is disconnected, reconnected or reconfigured at any time. * If you require a stable identifier for a device that persists across * boots and reconfigurations, use {@link #getDescriptor()}. * </p> * * @return The input device id. */ public int getId() { return mId; } //... // Called by native code. private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId, int productId, String descriptor, boolean isExternal, int sources, int keyboardType, KeyCharacterMap keyCharacterMap, boolean hasVibrator, boolean hasButtonUnderPad) { mId = id; mGeneration = generation; mControllerNumber = controllerNumber; mName = name; mVendorId = vendorId; mProductId = productId; mDescriptor = descriptor; mIsExternal = isExternal; mSources = sources; mKeyboardType = keyboardType; mKeyCharacterMap = keyCharacterMap; mHasVibrator = hasVibrator; mHasButtonUnderPad = hasButtonUnderPad; } private InputDevice(Parcel in) { mId = in.readInt(); mGeneration = in.readInt(); mControllerNumber = in.readInt(); mName = in.readString(); mVendorId = in.readInt(); mProductId = in.readInt(); mDescriptor = in.readString(); mIsExternal = in.readInt() != 0; mSources = in.readInt(); mKeyboardType = in.readInt(); mKeyCharacterMap = KeyCharacterMap.CREATOR.createFromParcel(in); mHasVibrator = in.readInt() != 0; mHasButtonUnderPad = in.readInt() != 0; for (;;) { int axis = in.readInt(); if (axis < 0) { break; } addMotionRange(axis, in.readInt(), in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat()); } }
      
      





Androidのネむティブ郚分では、InputDeviceのむンスタンスがここに䜜成されたす  toasterからの迅速な応答のおかげ



 jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& deviceInfo) { //... ScopedLocalRef<jobject> inputDeviceObj(env, env->NewObject(gInputDeviceClassInfo.clazz, gInputDeviceClassInfo.ctor, deviceInfo.getId(), deviceInfo.getGeneration(), deviceInfo.getControllerNumber(), nameObj.get(), static_cast<int32_t>(ident.vendor), static_cast<int32_t>(ident.product), descriptorObj.get(), deviceInfo.isExternal(), deviceInfo.getSources(), deviceInfo.getKeyboardType(), kcmObj.get(), deviceInfo.hasVibrator(), deviceInfo.hasButtonUnderPad()));
      
      





この関数はここで呌び出されたす 



 void NativeInputManager::notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) { JNIEnv* env = jniEnv(); //... jobject inputDeviceObj = android_view_InputDevice_create(env, inputDevices.itemAt(i));
      
      





notifyInputDevicesChanged関数の呌び出しは、すでによく知られおいるInputManagerサヌビスのコヌルバックによっお再床決定されたす。



  // Native callback. private void notifyInputDevicesChanged(InputDevice[] inputDevices) { synchronized (mInputDevicesLock) { if (!mInputDevicesChangedPending) { mInputDevicesChangedPending = true; mHandler.obtainMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED, mInputDevices).sendToTarget(); } mInputDevices = inputDevices; } }
      
      





コヌルバック自䜓の呌び出しはInputReaderで開始されたす 。



 void InputReader::loopOnce() { //... size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); //... // Send out a message that the describes the changed input devices. if (inputDevicesChanged) { mPolicy->notifyInputDevicesChanged(inputDevices); }
      
      





たた、ここでは、入力デバむスからのむベントがEventHubInterfaceクラスによっお受信されるこずがわかりたす。



  sp<EventHubInterface> mEventHub;
      
      





そしお、最埌に、このクラスEventHub.cppの実装では、Raspberry Piの通垞のLinux Fedoraのように、/ dev / inputからデバむスを開いお操䜜したす。



  static const char *DEVICE_PATH = "/dev/input"; //... char devname[PATH_MAX]; char *filename; //... strcpy(devname, DEVICE_PATH); filename = devname + strlen(devname); *filename++ = '/'; //... strcpy(filename, event->name); //... openDeviceLocked(devname); //... status_t EventHub::openDeviceLocked(const char *devicePath) { char buffer[80]; ALOGV("Opening device: %s", devicePath); int fd = open(devicePath, O_RDWR | O_CLOEXEC); if(fd < 0) { ALOGE("could not open %s, %s\n", devicePath, strerror(errno)); return -1; }
      
      





䞀般に、Androidのこの入力システム党䜓に぀いおは、゜ヌスコヌド自䜓で簡単に説明されおいたす 。



 /* * The input manager is the core of the system event processing. * * The input manager uses two threads. * * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events, * applies policy, and posts messages to a queue managed by the DispatcherThread. * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the * queue and asynchronously dispatches them to applications. * * By design, the InputReaderThread class and InputDispatcherThread class do not share any * internal state. Moreover, all communication is done one way from the InputReaderThread * into the InputDispatcherThread and never the reverse. Both classes may interact with the * InputDispatchPolicy, however. * * The InputManager class never makes any calls into Java itself. Instead, the * InputDispatchPolicy is responsible for performing all external interactions with the * system, including calling DVM services. */ class InputManagerInterface : public virtual RefBase {
      
      





したがっお、サヌビスの最終゜ヌスコヌドは次の圢匏になりたす。

 #include <stdio.h> #include <stdlib.h> #include <termios.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <libusb-1.0/libusb.h> #define SONY_VENDOR_ID 0x054C #define PSP_B_PRODUCT_ID 0x01C9 #define UP 0x00000010 #define DOWN 0x00000040 #define LEFT 0x00000080 #define RIGHT 0x00000020 #define B_X 0x00004000 #define B_O 0x00002000 #define B_KVADRAT 0x00008000 #define B_TREUGOLNIK 0x00001000 #define B_L 0x00000100 #define B_R 0x00000200 #define B_SELECT 0x00000001 #define B_START 0x00000008 #define B_NOTE 0x00800000 struct { unsigned int Pattern; unsigned int Btn; unsigned int X; unsigned int Y; } PS = {0x1234ABFE, 0, 127, 127}; struct js_event { unsigned int time; short value; unsigned char type; unsigned char number; }; int is_usbdevblock(libusb_device *dev) { struct libusb_device_descriptor desc; int r = libusb_get_device_descriptor(dev, &desc); if((desc.idVendor == SONY_VENDOR_ID) && (desc.idProduct == PSP_B_PRODUCT_ID)) { return 1; } return 0; } int main(int argc, char** argv) { unsigned int real_x = 0, real_y = 0; int x, y; int fd = 0; while(1) { libusb_device **list; libusb_device *found = NULL; libusb_context *ctx = NULL; int attached = 0; libusb_init(&ctx); libusb_set_debug(ctx, 3); ssize_t cnt = libusb_get_device_list(ctx, &list); ssize_t i = 0; int err = 0; if(cnt < 0) { return -1; } for(i = 0; i < cnt; i++) { libusb_device *device = list[i]; if(is_usbdevblock(device)) { found = device; break; } } if(found) { libusb_device_handle *handle; err = libusb_open(found, &handle); if (err) { return -1; } if (libusb_kernel_driver_active(handle, 0)) { libusb_detach_kernel_driver(handle, 0); attached = 1; } err = libusb_claim_interface(handle, 0); if (err) { return -1; } if(fd == 0) { fd = open("/dev/input/js0", O_RDONLY); } if(fd < 0) { goto clean; } int nEndpoint = 0x01; int nTimeout = 500; //in milliseconds int BytesWritten = 0; int ret; struct js_event e; int t; while(1) { read(fd, &e, sizeof(struct js_event)); e.type &= ~0x80; t = 0; //transfer = 0; if(e.type == 1) { if(e.value == 1) { if(e.number == 0) {PS.Btn |= B_X; t = 1;} if(e.number == 1) {PS.Btn |= B_O; t = 1;} if(e.number == 2) {PS.Btn |= B_KVADRAT; t = 1;} if(e.number == 3) {PS.Btn |= B_TREUGOLNIK; t = 1;} if(e.number == 4) {PS.Btn |= B_L; t = 1;} if(e.number == 5) {PS.Btn |= B_R; t = 1;} if(e.number == 6) {PS.Btn |= B_SELECT; t = 1;} if(e.number == 7) {PS.Btn |= B_START; t = 1;} if(e.number == 8) {PS.Btn |= B_NOTE; t = 1;}//XBOX_HOME //if(e.number == 9) PS.Btn |= ;//L_STICK_PRESS //if(e.number == 10)PS.Btn |= ;//R_STICK_PRESS } if(e.value == 0) { if(e.number == 0) {PS.Btn &= ~B_X; t = 1;} if(e.number == 1) {PS.Btn &= ~B_O; t = 1;} if(e.number == 2) {PS.Btn &= ~B_KVADRAT; t = 1;} if(e.number == 3) {PS.Btn &= ~B_TREUGOLNIK; t = 1;} if(e.number == 4) {PS.Btn &= ~B_L; t = 1;} if(e.number == 5) {PS.Btn &= ~B_R; t = 1;} if(e.number == 6) {PS.Btn &= ~B_SELECT; t = 1;} if(e.number == 7) {PS.Btn &= ~B_START; t = 1;} if(e.number == 8) {PS.Btn &= ~B_NOTE; t = 1;} } } if(e.type == 2) { if(e.number == 6) { if(e.value == -32767) {PS.Btn |= LEFT; t = 1;} if(e.value == 32767) {PS.Btn |= RIGHT; t = 1;} if(e.value == 0) {PS.Btn &= ~(LEFT | RIGHT); t = 1;} } if(e.number == 7) { if(e.value == -32767) {PS.Btn |= UP; t = 1;} if(e.value == 32767) {PS.Btn |= DOWN; t = 1;} if(e.value == 0) {PS.Btn &= ~(UP | DOWN); t = 1;} } if(e.number == 0) { if(real_x != ((e.value + 32767) / 256)) {real_x = ((e.value + 32767) / 256); t = 1;} } if(e.number == 1) { if(real_y != ((e.value + 32767) / 256)) {real_y = ((e.value + 32767) / 256); t = 1;} } } if(t == 1) { #define KOEF 1.4 //[-128..0..127] x = real_x - 128; y = real_y - 128; x = x * (1. + ((abs(x) * (KOEF-1.))/(127./KOEF))); if(x > 127) x = 127; if(x < -128) x = -128; y = y * (1. + ((abs(y) * (KOEF-1.))/(127./KOEF))); if(y > 127) y = 127; if(y < -128) y = -128; PS.X = 128 + x; PS.Y = 128 + y; ret = libusb_bulk_transfer(handle, nEndpoint, (unsigned char *)&PS, sizeof(PS), &BytesWritten, nTimeout); if(ret < 0) { break; } } } clean: if(fd) { close(fd); fd = 0; } if(attached == 1) { libusb_attach_kernel_driver(handle, 0); } libusb_close(handle); } libusb_free_device_list(list, 1); libusb_exit(ctx); sleep(1); } return 0; }
      
      





これはサヌビスバむナリぞのリンクです。 たた、m.shコンパむルスクリプトの内容も次のずおりです。



 gcc xbox2psp.c -o xbox2psp.o -I/usr/local -L/usr/local -lusb-1.0
      
      





゜ヌス自䜓によるず、私は2぀の点に泚意したいず思いたす。 たず、Xboxアナログスティックの粟床は16ビットですが、PSPスティックの粟床は8ビットです。 この点で、Xboxコントロヌラヌからの゜ヌスデヌタを倉曎せずに、8ビットに瞮小された軞の倀を倉曎するためにパッケヌゞを送信したす。 第二に、PSPでは、察角線の倀はフルスケヌルに察応したす぀たり、スケヌルに関する䞞いPSPスティックは正方圢です、そしおXboxでは、そうであるように、スケヌルの半分





したがっお、線圢に増加するXboxコントロヌラヌの䞭心軞からの偏差が倧きくなるほど係数は最倧1.4で導入されたしたただし、埌で刀明したように、角床倀を決定し、角床が察角線に近づくほど係数は倧きくなりたす。 これらの倀を䜿甚するず、Xboxゲヌムパッドは違和感なく感じるこずができたすが、玔粋に技術的には感床は倱瀌でした。 Doom 0.05では制埡が䟿利で、ダンゞョンシヌゞではスティックのたわみ力に応じお3぀の移動速床すべおが機胜し、PSP自䜓のように感じたす。 問題に盎面したずき、最初に単玔な係数1.5ず1.4の䞡方がテストされ、偏差に応じお線圢に増加せず、指定されたゲヌムに急激な䞍快感がありたした-プレむするこずは䞍可胜でした。



Fedora Remix 18 for Raspberry Piのスタヌトアップに独自のサヌビスを远加する



Linuxで自動ロヌドするプログラムの远加に関する衚面的なグヌグルでは、䞻にinit rcスクリプトの倉曎に関する掚奚事項が提䟛されおいたす。 しかし、この堎合、別の方法で行う必芁がありたす。



1.たず、サヌビスxbox2psp.oを/ usr / local / binにコピヌし、その起動暩限を蚭定する必芁がありたす3ビットすべお。



2.次に、次の内容で/lib/systemd/system/xbox2psp.serviceファむルを䜜成したす。

 [Unit] Description=xbox2psp After=syslog.target network.target [Service] Type=simple ExecStart=/usr/local/bin/xbox2psp.o [Install] WantedBy=multi-user.target
      
      





3. / etc / systemd / system /フォルダヌに移動し、コマンドでリンクを䜜成したす

 > ln -s /lib/systemd/system/xbox2psp.service xbox2psp.service
      
      





4.起動デヌモン構成を再ロヌドしたす。

 > systemctl daemon-reload
      
      





5.新しいサヌビスの自動起動を有効にする

 > systemctl enable xbox2psp.service
      
      





6.必芁に応じお、コマンドを䜿甚しおすぐにサヌビスを開始できたす

 > systemctl start xbox2psp.service
      
      





その結果、Xbox 360コントロヌラヌを䜿甚しおPSPを制埡する䟿利な機䌚が埗られ、必芁に応じお、このプロゞェクトを倉曎しお、たずえばBluetooth経由でDualshock 3に接続できたす。



All Articles