24 poin oleh GN⁺ 2026-04-10 | 1 komentar | Bagikan ke WhatsApp
  • 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) dan PID (Product ID)
  • Di Linux, informasi perangkat dapat dilihat dengan perintah lsusb
    • Contoh: ID 18d1:4ee0 Google Inc. Nexus/Pixel Device (fastboot)
    • 18d1 adalah VID milik Google, dan 4ee0 adalah PID untuk bootloader Nexus/Pixel
  • Dengan perintah lsusb -t, kita bisa memeriksa kelas dan status driver
    • Jika tampil Class=Vendor Specific Class, Driver=[none], berarti OS tidak memuat driver
    Iklan
  • 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 kombinasi VID:PID tertentu 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
  • Setelah itu kita bisa mengambil deskriptor perangkat melalui permintaan GET_DESCRIPTOR
    • Data yang dikembalikan mencakup informasi perangkat seperti idVendor, idProduct, bDeviceClass, dan lainnya
  • Dengan perintah lsusb -v, semua deskriptor (perangkat, konfigurasi, antarmuka, endpoint, dll.) dapat dilihat secara rinci
    • Contoh: antarmuka Android Fastboot memiliki endpoint Bulk IN (0x81) dan Bulk OUT (0x02)
Iklan

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
  • 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 data
    • OUT: arah saat host mengirim data
    • Jika bit paling signifikan (MSB) pada alamat endpoint bernilai 1, berarti IN; jika 0, berarti OUT
    • Maksimal 127 endpoint kustom dapat digunakan (0x00 khusus untuk Control)
    • Endpoint bersifat satu arah, dan dapat dibentuk sebagai pasangan IN/OUT seperti pada antarmuka Fastboot
Iklan

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 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
      
    • OKAY adalah status berhasil, 0.4 adalah versi Fastboot

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

 
GN⁺ 2026-04-10
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

    • Masa tunggu di Guitar Center bukan semata untuk memeriksa apakah barang itu curian. Secara hukum mereka juga wajib menahan penjualan untuk jangka waktu tertentu seperti pawn shop, agar pemilik asli masih punya kesempatan untuk mengambilnya kembali
    • Saya juga memakai perangkat yang sama dan sudah memaketkan driver itu ke AUR. Binary blob-nya tidak berfungsi, tetapi untuk dipakai sebagai router MIDI sederhana sudah cukup
  • 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

    • Untuk Rust, saya merekomendasikan nusb
  • 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

    • Override driver hanya perlu disesuaikan pada satu lapisan saja, jadi masih bisa dimitigasi
  • 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

    • Perangkat yang terstandarisasi biasanya memakai USB/CDC/ECM atau RNDIS sehingga dikenali otomatis. Akses dari user space justru berguna untuk perangkat non-standar. Di Windows, ini bisa diimplementasikan secara portabel dengan libusb tanpa tanda tangan driver
    • Di Linux, Anda bisa membuat perangkat tun/tap untuk berkomunikasi dengan kernel dari user space, atau harus menjalankan subsistem lain juga di user space
  • 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 ligature pada font pemrograman. Kalau disalin, aslinya terlihat sebagai ->. Itu adalah sintaks trailing return type di C++ modern
    • Sebagian developer menyukai font ligature. Dua karakter digabung menjadi satu glif
    • Jika Anda mengatur tombol Compose, Anda bisa mengetik “→” dengan keyboard apa pun
    • Pada akhirnya itu hanya "->". Font-nya saja yang merendernya sebagai panah
  • Saya penasaran apakah perangkat USB mendukung DMA. Apakah hanya bisa lewat host, atau perangkat juga bisa mengakses memori secara langsung

    • Perangkat USB tidak mengakses memori host secara langsung seperti PCIe atau FireWire. Sebaliknya, kontroler XHCI yang melakukan DMA, dan kebanyakan kontroler perangkat mendukung DMA antara RAM internalnya sendiri dan USB
    • Semua transfer diprakarsai oleh host. Walaupun terlihat seperti perangkat lebih dulu mengirim data, sebenarnya host yang memintanya. DMA langsung akan menjadi risiko keamanan yang besar
  • 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

    • Saya juga dulu menganggap descriptor itu misterius, tetapi akhirnya sadar bahwa itu hanyalah struct biner tetap. Selama field dan endpoint yang ditentukan tiap kelas USB sesuai, perangkat akan dikenali
    • USB lumayan baik, tetapi dari sisi kelistrikan, USB 1/2 bukan sinyal diferensial sejati
    • Materi tutorial memang hampir tidak ada, tetapi untuk standar buatan perusahaan besar, ini sebenarnya cukup masuk akal. Hanya saja opsinya terlalu banyak, jadi Anda harus membaca banyak spesifikasi terkait
  • Jika saya diminta “tulis sendiri driver perangkat USB”, saya akan mengembalikan perangkat itu dan terlebih dahulu memeriksa apakah bisa ditangani sebagai port COM virtual