- Pengembangan driver USB sering dianggap sebagai pekerjaan tingkat kernel, tetapi pada praktiknya tingkat kesulitannya mirip dengan pemrograman soket dan dapat diimplementasikan juga di ruang pengguna
- Dengan libusb, semua hal mulai dari enumerasi perangkat, transfer kontrol, hingga kirim-terima data bisa dilakukan tanpa menulis kode kernel
- Komunikasi USB terdiri dari empat jenis transfer: Control, Bulk, Interrupt, Isochronous, serta arah IN/OUT, dan setiap endpoint bekerja sebagai jalur satu arah
- Dengan protokol Fastboot pada perangkat Android sebagai contoh, artikel ini mendemonstrasikan lewat kode proses pertukaran perintah dan respons melalui endpoint Bulk
- Bahkan di ruang pengguna, driver USB yang lengkap dapat diimplementasikan, dan semua protokol USB berbagi struktur dasar yang sama
Pengenalan
- Driver untuk perangkat USB sering terasa sulit karena ada anggapan bahwa kita harus menangani kode kernel, padahal kenyataannya kompleksitasnya setara dengan aplikasi yang menggunakan soket
- Pengembang yang tidak memiliki banyak pengalaman hardware pun dapat mempelajari cara menangani USB di ruang pengguna
- Memang ada banyak materi yang membahas detail kerja USB, tetapi bagi pemula materi seperti itu sering sulit diakses
- Menggunakan USB tidak memerlukan pengetahuan setingkat sistem embedded, dan bisa didekati seperti soket jaringan
Perangkat USB
- Sebagai contoh digunakan smartphone Android dalam mode bootloader
- Mudah didapat, protokolnya sederhana, dan cocok untuk eksperimen karena OS tidak memiliki driver bawaan untuknya
- Cara masuk ke mode bootloader berbeda untuk tiap perangkat, tetapi umumnya bisa dilakukan dengan kombinasi tombol daya dan volume
Enumerasi perangkat secara manual
- Enumeration adalah proses saat host meminta informasi perangkat agar perangkat bisa mengidentifikasi dirinya, dan proses ini berjalan otomatis ketika perangkat dihubungkan
- Untuk perangkat standar, driver dimuat otomatis berdasarkan kelas USB, sedangkan perangkat khusus vendor menggunakan
VID(Vendor ID) danPID(Product ID) - Di Linux, informasi perangkat dapat dilihat dengan perintah
lsusb- Contoh:
ID 18d1:4ee0 Google Inc. Nexus/Pixel Device (fastboot) 18d1adalah VID milik Google, dan4ee0adalah PID untuk bootloader Nexus/Pixel
- Contoh:
- Dengan perintah
lsusb -t, kita bisa memeriksa kelas dan status driver- Jika tampil
Class=Vendor Specific Class,Driver=[none], berarti OS tidak memuat driver
- Jika tampil
- Di Windows, informasi yang sama bisa dilihat melalui Device Manager atau USB Device Tree Viewer
Enumerasi perangkat dengan libusb
- Dengan library libusb, kita dapat berkomunikasi dengan perangkat USB di ruang pengguna tanpa menulis kode kernel
libusb_hotplug_register_callback()dapat digunakan untuk menyiapkan callback agar dijalankan saat perangkat dengan kombinasiVID:PIDtertentu terhubung- Setelah program berjalan, saat perangkat disambungkan akan muncul pesan
"Device plugged in!" - Di Linux ini bekerja secara bawaan, dan bila perlu driver kernel bisa dilepas dengan
libusb_detach_kernel_driver() - Di Windows dibutuhkan driver
Winusb.sys, dan jika belum ada dapat diganti secara manual menggunakan alat Zadig
Berkomunikasi dengan perangkat
- Komunikasi pertama dengan perangkat USB dilakukan melalui endpoint Control (alamat 0x00)
- Dengan
libusb_control_transfer(), kita dapat mengirim permintaan standar (GET_STATUS) untuk membaca status perangkat- Contoh respons:
01 00→ byte pertama berarti Self-Powered, byte kedua berarti tidak mendukung Remote Wakeup
- Contoh respons:
- Setelah itu kita bisa mengambil deskriptor perangkat melalui permintaan GET_DESCRIPTOR
- Data yang dikembalikan mencakup informasi perangkat seperti
idVendor,idProduct,bDeviceClass, dan lainnya
- Data yang dikembalikan mencakup informasi perangkat seperti
- Dengan perintah
lsusb -v, semua deskriptor (perangkat, konfigurasi, antarmuka, endpoint, dll.) dapat dilihat secara rinci- Contoh: antarmuka
Android Fastbootmemiliki endpoint Bulk IN (0x81) dan Bulk OUT (0x02)
- Contoh: antarmuka
Endpoint
- Endpoint adalah konsep yang mirip dengan port jaringan, yaitu jalur tempat perangkat mengirim dan menerima data
- Jenis dan arah setiap endpoint didefinisikan di dalam deskriptor
-
Jenis transfer Control
- Ada satu pada setiap perangkat dan alamatnya selalu
0x00 - Digunakan untuk konfigurasi awal dan permintaan informasi perangkat
- Tidak termasuk dalam antarmuka, melainkan merupakan bagian dari perangkat itu sendiri
- Ada satu pada setiap perangkat dan alamatnya selalu
-
Jenis transfer Bulk
- Digunakan untuk transfer data besar yang tidak real-time
- Contoh: Mass Storage, CDC-ACM (serial), RNDIS (ethernet)
- Bandwidth tinggi, tetapi prioritas rendah
-
Jenis transfer Interrupt
- Digunakan untuk transfer data kecil dengan latensi rendah
- Dipakai pada keyboard, mouse, dan perangkat serupa untuk melakukan polling input tombol dengan cepat
- Ini bukan interupsi hardware sungguhan; host yang meminta secara berkala
-
Jenis transfer Isochronous
- Digunakan untuk data besar yang sensitif terhadap waktu (streaming audio, video)
- Jika terjadi jeda, penurunan kualitas akan langsung terlihat
- Di
libusb, jenis ini ditangani secara asinkron
-
Arah IN / OUT
- USB memiliki arsitektur yang berpusat pada host, sehingga perangkat tidak mengirim data sebelum menerima permintaan
IN: arah saat host menerima dataOUT: arah saat host mengirim data- Jika bit paling signifikan (MSB) pada alamat endpoint bernilai
1, berarti IN; jika0, berarti OUT - Maksimal 127 endpoint kustom dapat digunakan (
0x00khusus untuk Control) - Endpoint bersifat satu arah, dan dapat dibentuk sebagai pasangan IN/OUT seperti pada antarmuka Fastboot
Protokol Fastboot
- Fastboot adalah protokol komunikasi bootloader Android dengan struktur mengirim string perintah lalu menerima kode status 4 byte beserta data
- Contoh:
Host: "getvar:version"→Client: "OKAY0.4"Host: "getvar:nonexistant"→Client: "OKAY"
- Contoh:
- Contoh kode pengiriman perintah Fastboot menggunakan libusb
- Antarmuka 0 diambil dengan
libusb_claim_interface() - Perintah
"getvar:version"dikirim ke endpoint Bulk OUT (0x02) - Respons diterima dari endpoint Bulk IN (0x81)
- Contoh output:
Request: getvar:version Response: OKAY0.4 OKAYadalah status berhasil,0.4adalah versi Fastboot
- Antarmuka 0 diambil dengan
Penutup
- Tanpa menulis kode kernel, kita tetap bisa mengimplementasikan driver USB yang lengkap di ruang pengguna
- Semua driver USB mengikuti prinsip dasar yang sama; yang berbeda hanyalah protokolnya
- Bahkan untuk protokol yang kompleks (seperti MTP), struktur dasarnya tetap sama dan dapat didekati dengan konsep yang mirip komunikasi soket
1 komentar
Komentar Hacker News
Timing-nya pas sekali. Sebentar lagi saya akan mengambil MOTU MIDI Express XT dari Guitar Center setempat
Karena ini perangkat bekas, secara hukum harus ditahan selama periode tertentu, jadi saya masih menunggu. Masalahnya, perangkat ini tidak memakai MIDI-over-USB standar melainkan protokol proprietari, jadi di sistem saya seperti Linux, OpenBSD, atau Haiku, perangkat ini tidak bisa langsung dipakai lewat USB
Untuk sekarang saya hanya butuh routing sederhana antara modul synth dan controller, jadi tidak masalah. Tapi akan bagus kalau bisa dibuat bekerja juga di sisi PC
Sudah ada driver Linux yang sudah ada, tapi kestabilannya belum jelas dan dukungan XT juga masih meragukan. Masalah kernel panic katanya sudah diperbaiki, tetapi masih ada issue tersisa
Jadi saya berencana membuat sendiri driver user-space berbasis LibUSB. Kalau bisa mengekspos port MIDI dan menambahkan tooling untuk routing, sepertinya akan sangat berguna
Kalau ingin mencoba hal seperti ini dengan Go, saya sudah membuat library go-usb yang memungkinkan akses USB tanpa cgo
Dengan ini saya juga mengembangkan go-uvc untuk menangani perangkat UVC
Saya juga baru-baru ini sedang mengimplementasikan sistem usbip dengan cara yang mirip di Macbook M3
Hanya saja ada batasan di macOS terbaru. Untuk perangkat USB yang dikenali sistem, Anda tidak bisa membangun driver user-space berbasis libusb kecuali mematikan fitur keamanan secara manual
Pendekatan ini pada akhirnya membuat driver USB juga berperan sebagai kode aplikasi. Artinya, ini lebih dekat ke library+program daripada driver
Misalnya saya penasaran bagaimana caranya jika ingin menghubungkan perangkat USB-Ethernet sebagai adaptor jaringan OS
Kalau saya membaca tulisan ini beberapa tahun lalu, rekayasa balik fitur laptop akan jauh lebih mudah. Terutama program kontrol LED keyboard masih menjadi salah satu proyek favorit saya sampai sekarang
Ini benar-benar panduan pemula yang berguna. Menangani API perangkat keras level rendah itu sulit tetapi memuaskan. Berkat lapisan abstraksi pada OS modern, hal ini jadi lebih mudah, tetapi memahami lapisan di bawahnya tetap penting
Kode C++-nya terlihat aneh. Saya belum pernah melihat keyboard yang bisa langsung mengetik karakter panah
->. Itu adalah sintaks trailing return type di C++ modern"->". Font-nya saja yang merendernya sebagai panahSaya penasaran apakah perangkat USB mendukung DMA. Apakah hanya bisa lewat host, atau perangkat juga bisa mengakses memori secara langsung
Dulu saya pernah mencoba membuat perangkat USB sederhana, tetapi hampir tidak ada informasi tentang cara menulis descriptor. Kebanyakan saran hanya “cari perangkat yang mirip lalu salin dan modifikasi”. Saya jadi meragukan apakah USB benar-benar standar yang bagus
Jika saya diminta “tulis sendiri driver perangkat USB”, saya akan mengembalikan perangkat itu dan terlebih dahulu memeriksa apakah bisa ditangani sebagai port COM virtual