00 USB gadget nedir: host, device ve OTG
USB protokolünde her bağlantıda bir "host" ve bir "device" vardır. Masaüstü bilgisayarınız genellikle host'tur; fare, klavye veya telefon ise device. Linux USB Gadget framework, Linux çalıştıran bir SoC'nin device rolünü üstlenmesini sağlar.
Host, Device ve OTG rolleri
Host: USB iletişimini yöneten taraftır; veri transferini başlatır, bağlı cihazları yönetir. Tipik örnekler: PC, dizüstü bilgisayar. Device (peripheral): Host'un yönetiminde çalışan taraf; komutları bekler, veriyi sunar. Tipik örnekler: fare, USB seri port, USB disk. OTG (On-The-Go): Bağlantıya göre host ya da device rolü üstlenebilen cihazlar. BeagleBone Black'in micro-USB portu OTG desteklidir.
| Rol | Linux subsystem | Örnek |
|---|---|---|
| Host | USB Host Controller (XHCI/EHCI/OHCI) | PC'nin USB portu |
| Device | USB Device Controller (UDC) + Gadget | RPi Zero, BeagleBone, STM32 |
| OTG | USB OTG + role switching | BeagleBone Black, i.MX6 |
Linux USB Device Controller (UDC) mimarisi
USB Gadget framework birkaç katmandan oluşur. En altta UDC sürücüsü bulunur; bu, SoC'nin USB device donanımıyla doğrudan iletişim kurar. Üstünde USB Gadget core (gadget.ko) katmanı yer alır ve farklı gadget fonksiyonlarına ortak bir arayüz sağlar. En üstte ise kullanıcı alanından yapılandırılan configfs gadget API bulunur.
Uygulama (configfs) → Gadget core → UDC driver (dwc2/dwc3/chipidea) → USB hardware → Host PC
Yaygın UDC sürücüleri
| Sürücü | SoC/Donanım | Kernel modülü |
|---|---|---|
| DWC2 | RPi Zero/Zero W, Allwinner, Rockchip | dwc2.ko |
| DWC3 | RPi 4/5, Qualcomm, i.MX8 | dwc3.ko |
| ChipIdea | i.MX6, i.MX7, SAMA5 | ci_hdrc.ko |
| Musb | TI AM335x (BeagleBone), OMAP | musb.ko |
| dummy_hcd | Test/geliştirme (sanal) | dummy_hcd.ko |
# Mevcut UDC'leri listele
ls /sys/class/udc/
# fe800000.usb (RPi Zero: dwc2)
# ci_hdrc.0 (i.MX6: ChipIdea)
# musb-hdrc.0.auto (BeagleBone: musb)
# UDC bilgisi
cat /sys/class/udc/fe800000.usb/state
# configured (host bağlı ve gadget aktif)
# not attached (host bağlı değil)
# Test için sanal UDC yükle
sudo modprobe dummy_hcd
ls /sys/class/udc/
Bu bölümde
- USB Gadget: Linux SoC'nin USB device (peripheral) rolünü üstlenmesi
- UDC sürücüsü: DWC2 (RPi Zero), DWC3 (RPi 4), ChipIdea (i.MX6), Musb (BeagleBone)
- /sys/class/udc/: mevcut UDC'leri listeler; state ile bağlantı durumu görülür
- dummy_hcd: gerçek donanım olmadan gadget geliştirme ve test için
01 configfs gadget API
configfs, gadget yapılandırmasını sysfs benzeri bir dosya sistemi hiyerarşisi üzerinden yapmanızı sağlar. Dizin oluşturup dosyalara yazmak yeterlidir — modül yüklemek veya özel araç gerekmez.
configfs hiyerarşisi
/sys/kernel/config/usb_gadget/
└── mygadget/ ← gadget ismi (serbest)
├── idVendor ← USB vendor ID
├── idProduct ← USB product ID
├── bcdDevice ← cihaz versiyon numarası
├── bcdUSB ← USB spec versiyon (0x0200 = USB 2.0)
├── strings/
│ └── 0x409/ ← Dil kodu (0x409 = İngilizce)
│ ├── manufacturer
│ ├── product
│ └── serialnumber
├── configs/
│ └── c.1/ ← Konfigürasyon 1
│ ├── MaxPower ← mA cinsinden güç
│ └── acm.0 → ../../functions/acm.usb0 ← symlink
└── functions/
└── acm.usb0/ ← Function instance
Temel gadget oluşturma
# libcomposite modülünü yükle
sudo modprobe libcomposite
# configfs bağlı değilse bağla
sudo mount -t configfs none /sys/kernel/config
# Gadget dizini oluştur
cd /sys/kernel/config/usb_gadget/
sudo mkdir mygadget
cd mygadget
# Cihaz kimlik bilgileri
sudo bash -c 'echo 0x1d6b > idVendor' # Linux Foundation
sudo bash -c 'echo 0x0104 > idProduct' # Multifunction Composite Gadget
sudo bash -c 'echo 0x0100 > bcdDevice' # v1.0.0
sudo bash -c 'echo 0x0200 > bcdUSB' # USB 2.0
# Dize tanımlayıcılar (İngilizce)
sudo mkdir strings/0x409
sudo bash -c 'echo "MyCompany" > strings/0x409/manufacturer'
sudo bash -c 'echo "My USB Gadget" > strings/0x409/product'
sudo bash -c 'echo "SERIAL001" > strings/0x409/serialnumber'
# Konfigürasyon oluştur
sudo mkdir configs/c.1
sudo bash -c 'echo 250 > configs/c.1/MaxPower' # 250 mA
sudo mkdir configs/c.1/strings/0x409
sudo bash -c 'echo "Config 1" > configs/c.1/strings/0x409/configuration'
UDC'ye bağlama ve kaldırma
# Function eklendikten sonra UDC'ye bağla (gadget etkinleşir)
UDC_NAME=$(ls /sys/class/udc | head -1)
sudo bash -c "echo $UDC_NAME > /sys/kernel/config/usb_gadget/mygadget/UDC"
# Gadget'ı devre dışı bırak (UDC'den ayır)
sudo bash -c 'echo "" > /sys/kernel/config/usb_gadget/mygadget/UDC'
# Gadget durumunu kontrol et
cat /sys/class/udc/fe800000.usb/state
# configured
Bu bölümde
- configfs: mkdir + echo ile gadget yapılandırması; özel araç gerektirmez
- Hiyerarşi: usb_gadget/mygadget/{idVendor,idProduct,strings,configs,functions}
- libcomposite: tüm gadget function'ları için temel modül; önce yüklenmeli
- UDC dosyasına yazma: gadget'ı etkinleştirir; boş yazma devre dışı bırakır
02 ACM: CDC seri konsol
ACM (Abstract Control Model), CDC (Communications Device Class) altında tanımlı bir USB seri port protokolüdür. Host bilgisayarda /dev/ttyACM0 olarak görünür; Linux üzerinde /dev/ttyGS0 ile erişilir.
ACM function oluşturma
# ACM modülünü yükle
sudo modprobe usb_f_acm
# Gadget temel yapısını oluştur (önceki bölümden)
cd /sys/kernel/config/usb_gadget/mygadget
# ACM function instance oluştur
sudo mkdir functions/acm.usb0
# Konfigürasyona bağla (symlink)
sudo ln -s functions/acm.usb0 configs/c.1/
# UDC'ye bağla
sudo bash -c "echo $(ls /sys/class/udc | head -1) > UDC"
# Host'ta /dev/ttyACM0 görünmeli
# Embedded Linux'ta /dev/ttyGS0 oluşur
ls /dev/ttyGS*
# /dev/ttyGS0
ACM üzerinden konsol erişimi
getty ile /dev/ttyGS0'a login konsolu bağlanabilir. Böylece host PC, USB kablosu üzerinden SSH benzeri terminal erişimi elde eder.
# Manuel getty başlatma
sudo getty -L ttyGS0 115200 vt100 &
# systemd ile kalıcı konsol
sudo systemctl enable serial-getty@ttyGS0.service
sudo systemctl start serial-getty@ttyGS0.service
# Host bilgisayardan bağlanma (minicom)
minicom -D /dev/ttyACM0 -b 115200
# Host bilgisayardan bağlanma (screen)
screen /dev/ttyACM0 115200
# Host bilgisayardan bağlanma (picocom)
picocom -b 115200 /dev/ttyACM0
ACM üzerinden veri transferi
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
int main(void)
{
int fd = open("/dev/ttyGS0", O_RDWR | O_NOCTTY);
if (fd < 0) { perror("open"); return 1; }
struct termios tty;
tcgetattr(fd, &tty);
cfmakeraw(&tty);
cfsetspeed(&tty, B115200);
tcsetattr(fd, TCSANOW, &tty);
const char *msg = "Hello from USB gadget!\r\n";
write(fd, msg, strlen(msg));
char buf[64];
ssize_t n;
while ((n = read(fd, buf, sizeof(buf))) > 0) {
write(fd, buf, n); /* Echo back */
}
close(fd);
return 0;
}
Bu bölümde
- ACM: USB CDC seri port; host'ta /dev/ttyACM0, embedded Linux'ta /dev/ttyGS0
- mkdir functions/acm.usb0 + ln -s → configs/c.1/: ACM function ekleme
- serial-getty@ttyGS0.service: USB üzerinden terminal login
- minicom/screen/picocom -D /dev/ttyACM0: host'tan bağlanma
03 CDC NCM: Ethernet üzeri USB
CDC NCM (Network Control Model), USB kablosu üzerinden Ethernet iletişimi sağlar. Host bilgisayarda yeni bir ağ arayüzü görünür; IP adresi atanıp SSH bağlantısı kurulabilir.
NCM function oluşturma
# Gerekli modüller
sudo modprobe usb_f_ncm
# Gadget temel yapısını oluştur
cd /sys/kernel/config/usb_gadget/mygadget
# NCM function instance
sudo mkdir functions/ncm.usb0
# MAC adresleri (host ve device tarafı)
sudo bash -c 'echo "48:6f:73:74:50:43" > functions/ncm.usb0/host_addr'
sudo bash -c 'echo "42:61:64:55:53:42" > functions/ncm.usb0/dev_addr'
# Konfigürasyona bağla
sudo ln -s functions/ncm.usb0 configs/c.1/
# UDC'ye bağla
sudo bash -c "echo $(ls /sys/class/udc | head -1) > UDC"
# Ağ arayüzü görünmeli
ip link show usb0
# 3: usb0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
IP adresi yapılandırması
# Embedded Linux tarafında IP ata
sudo ip addr add 192.168.7.2/24 dev usb0
sudo ip link set usb0 up
# Host Linux tarafında (enp0s20f0u1 veya benzeri görünür)
sudo ip addr add 192.168.7.1/24 dev enp0s20f0u1
sudo ip link set enp0s20f0u1 up
# Host'tan embedded'e SSH
ssh root@192.168.7.2
# systemd-networkd ile otomatik IP (embedded taraf)
cat <<'EOF' | sudo tee /etc/systemd/network/usb0.network
[Match]
Name=usb0
[Network]
Address=192.168.7.2/24
EOF
sudo systemctl enable systemd-networkd
sudo systemctl start systemd-networkd
RNDIS: Windows uyumluluğu için alternatif
Windows bilgisayarlar NCM yerine genellikle RNDIS (Microsoft'un tescilli USB Ethernet protokolü) bekler. Modern Windows 10/11 NCM'yi de destekler; ama maksimum uyumluluk için RNDIS tercih edilebilir.
# RNDIS modülü
sudo modprobe usb_f_rndis
# RNDIS function (ncm yerine)
sudo mkdir functions/rndis.usb0
sudo bash -c 'echo "48:6f:73:74:50:43" > functions/rndis.usb0/host_addr'
sudo bash -c 'echo "42:61:64:55:53:42" > functions/rndis.usb0/dev_addr'
sudo ln -s functions/rndis.usb0 configs/c.1/
Bu bölümde
- NCM: USB üzerinden full-duplex Ethernet; host'ta yeni ağ arayüzü (usb0/enp0s*) görünür
- host_addr/dev_addr: her iki taraftaki MAC adresi; çakışma olmaması için rastgele atanmalı
- ip addr add 192.168.7.2/24 dev usb0 + SSH: USB kablosuyla doğrudan SSH erişimi
- RNDIS: Windows uyumluluğu için; usb_f_rndis modülü ile aynı yapılandırma
04 Mass Storage gadget
Mass Storage gadget, embedded Linux cihazını bir USB disk gibi gösterir. Dosya, imaj veya gerçek block device paylaşılabilir. Host bilgisayar yeni bir USB sürücü olarak tanır.
Mass Storage function oluşturma
# Modül yükle
sudo modprobe usb_f_mass_storage
# Backing dosyası oluştur (1 GB FAT32 imajı)
dd if=/dev/zero of=/storage.img bs=1M count=1024
mkfs.vfat /storage.img
# Mass storage function oluştur
cd /sys/kernel/config/usb_gadget/mygadget
sudo mkdir functions/mass_storage.usb0
# LUN (Logical Unit Number) yapılandır
# lun.0 otomatik oluşturulur
sudo bash -c 'echo /storage.img > functions/mass_storage.usb0/lun.0/file'
sudo bash -c 'echo 0 > functions/mass_storage.usb0/lun.0/ro' # Yazılabilir
sudo bash -c 'echo 1 > functions/mass_storage.usb0/lun.0/removable'
# Konfigürasyona bağla
sudo ln -s functions/mass_storage.usb0 configs/c.1/
# UDC'ye bağla
sudo bash -c "echo $(ls /sys/class/udc | head -1) > UDC"
Gerçek block device paylaşma
# Block device veya partition doğrudan kullan
sudo bash -c 'echo /dev/mmcblk0p1 > functions/mass_storage.usb0/lun.0/file'
# Sadece-okunur (ro=1: host değiştiremez)
sudo bash -c 'echo 1 > functions/mass_storage.usb0/lun.0/ro'
# CDROM modu (iso imajı için)
sudo bash -c 'echo 1 > functions/mass_storage.usb0/lun.0/cdrom'
sudo bash -c 'echo /firmware.iso > functions/mass_storage.usb0/lun.0/file'
Birden fazla LUN
# LUN sayısını artır
sudo bash -c 'echo 2 > functions/mass_storage.usb0/stall'
# İkinci LUN dizini oluştur ve yapılandır
sudo mkdir functions/mass_storage.usb0/lun.1
sudo bash -c 'echo /data.img > functions/mass_storage.usb0/lun.1/file'
sudo bash -c 'echo 0 > functions/mass_storage.usb0/lun.1/ro'
# Host iki ayrı USB disk görür:
# /dev/sdb — lun.0 (storage.img)
# /dev/sdc — lun.1 (data.img)
Çalışma sırasında dosya değiştirme
# Önce medyayı çıkar (host'u bilgilendir)
sudo bash -c 'echo 1 > functions/mass_storage.usb0/lun.0/forced_eject'
# Yeni dosya ata
sudo bash -c 'echo /new_storage.img > functions/mass_storage.usb0/lun.0/file'
# LUN özelliklerini görüntüle
ls /sys/kernel/config/usb_gadget/mygadget/functions/mass_storage.usb0/lun.0/
# cdrom file forced_eject inquiry_string nofua removable ro
Bu bölümde
- Mass Storage: embedded cihazı USB disk gibi gösterir; dosya imajı veya block device paylaşır
- lun.0/file: backing store; dosya imajı (.img) veya blok cihaz (/dev/mmcblk0p1)
- lun.0/ro: 1=salt okunur; lun.0/cdrom: 1=CD-ROM modu (ISO imajları için)
- forced_eject: sıcak medya değişimi; önce eject sonra yeni dosya ata
05 HID gadget: klavye ve fare
HID (Human Interface Device) gadget, embedded Linux cihazını klavye, fare veya özel HID cihazı olarak sunar. Rubber Ducky benzeri otomasyon, test otomasyon ve özel giriş cihazları için kullanılır.
HID function oluşturma
# Modül yükle
sudo modprobe usb_f_hid
# HID function oluştur
cd /sys/kernel/config/usb_gadget/mygadget
sudo mkdir functions/hid.usb0
# Klavye için HID parametreleri
sudo bash -c 'echo 1 > functions/hid.usb0/protocol' # 1=keyboard, 2=mouse
sudo bash -c 'echo 1 > functions/hid.usb0/subclass' # 1=boot interface
sudo bash -c 'echo 8 > functions/hid.usb0/report_length' # 8 bayt rapor
# Klavye report descriptor (standart boot keyboard)
sudo bash -c 'echo -ne "\x05\x01\x09\x06\xa1\x01\x05\x07\x19\xe0\x29\xe7\x15\x00\x25\x01\x75\x01\x95\x08\x81\x02\x95\x01\x75\x08\x81\x03\x95\x05\x75\x01\x05\x08\x19\x01\x29\x05\x91\x02\x95\x01\x75\x03\x91\x03\x95\x06\x75\x08\x15\x00\x25\x65\x05\x07\x19\x00\x29\x65\x81\x00\xc0" > functions/hid.usb0/report_desc'
# Konfigürasyona bağla
sudo ln -s functions/hid.usb0 configs/c.1/
sudo bash -c "echo $(ls /sys/class/udc | head -1) > UDC"
# Host'ta /dev/hidg0 oluşur
ls /dev/hidg*
Tuş gönderme: /dev/hidg0'a yazma
HID klavye raporları 8 baytlıktır: modifier (Shift/Ctrl/Alt), reserved, keycode[1..6]. Her tuş için bir "basma" ve bir "bırakma" raporu gönderilir.
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
/* HID klavye raporu: 8 bayt */
struct hid_keyboard_report {
uint8_t modifier; /* Ctrl/Shift/Alt/GUI bitleri */
uint8_t reserved;
uint8_t keycode[6]; /* Maksimum 6 eş zamanlı tuş */
};
/* USB HID tuş kodları (USB HID Usage Tables) */
#define KEY_A 0x04
#define KEY_RETURN 0x28
#define KEY_SPACE 0x2C
#define MOD_LSHIFT 0x02
static int hid_fd;
void send_key(uint8_t modifier, uint8_t keycode)
{
struct hid_keyboard_report report = {0};
report.modifier = modifier;
report.keycode[0] = keycode;
/* Tuşa bas */
write(hid_fd, &report, sizeof(report));
/* Tuşu bırak (tüm sıfır) */
memset(&report, 0, sizeof(report));
write(hid_fd, &report, sizeof(report));
usleep(10000); /* 10ms bekleme */
}
void type_string(const char *str)
{
for (; *str; str++) {
uint8_t mod = 0, key = 0;
if (*str >= 'a' && *str <= 'z') {
key = KEY_A + (*str - 'a');
} else if (*str >= 'A' && *str <= 'Z') {
key = KEY_A + (*str - 'A');
mod = MOD_LSHIFT;
} else if (*str == ' ') {
key = KEY_SPACE;
} else if (*str == '\n') {
key = KEY_RETURN;
}
if (key) send_key(mod, key);
}
}
int main(void)
{
hid_fd = open("/dev/hidg0", O_RDWR | O_NONBLOCK);
if (hid_fd < 0) { perror("open hidg0"); return 1; }
type_string("Hello World\n");
close(hid_fd);
return 0;
}
Fare HID
# Fare için farklı HID parametreleri
sudo mkdir functions/hid.usb1
sudo bash -c 'echo 2 > functions/hid.usb1/protocol' # mouse
sudo bash -c 'echo 1 > functions/hid.usb1/subclass'
sudo bash -c 'echo 4 > functions/hid.usb1/report_length' # 4 bayt fare raporu
/* Fare raporu: buttons | X_rel | Y_rel | wheel */
struct mouse_report {
uint8_t buttons; /* bit0=sol, bit1=sag, bit2=orta */
int8_t x; /* Göreli X (-127..127) */
int8_t y; /* Göreli Y (-127..127) */
int8_t wheel;
};
void move_mouse(int fd, int8_t dx, int8_t dy)
{
struct mouse_report r = { .x = dx, .y = dy };
write(fd, &r, sizeof(r));
}
void click_left(int fd)
{
struct mouse_report r = { .buttons = 0x01 };
write(fd, &r, sizeof(r)); /* Bas */
r.buttons = 0;
write(fd, &r, sizeof(r)); /* Bırak */
}
Bu bölümde
- HID gadget: protocol=1 (klavye), protocol=2 (fare); report_length ile rapor boyutu
- report_desc: standart USB HID Usage Tables'tan alınan binary tanımlayıcı
- /dev/hidg0: 8 baytlık klavye raporu yaz (modifier + reserved + 6x keycode)
- Her tuş: press raporu + release raporu (tümü sıfır)
06 Composite gadget: birden fazla function
Composite gadget, tek bir USB bağlantısında birden fazla function sunar. ACM + NCM + Mass Storage kombinasyonu, tek kabloyla seri konsol, SSH ve depolama erişimi sağlar.
Composite gadget oluşturma
#!/bin/bash
# composite_gadget.sh — Tam composite gadget kurulum scripti
GADGET_DIR=/sys/kernel/config/usb_gadget
GADGET_NAME=composite
# Modülleri yükle
modprobe libcomposite
modprobe usb_f_acm
modprobe usb_f_ncm
modprobe usb_f_mass_storage
# configfs bağla
mount -t configfs none /sys/kernel/config 2>/dev/null || true
# Gadget oluştur
mkdir -p "$GADGET_DIR/$GADGET_NAME"
cd "$GADGET_DIR/$GADGET_NAME"
# USB cihaz kimliği
echo 0x1d6b > idVendor
echo 0x0104 > idProduct
echo 0x0200 > bcdUSB
# Dize tanımlayıcılar
mkdir -p strings/0x409
echo "EmbeddedDev" > strings/0x409/manufacturer
echo "Composite USB Gadget" > strings/0x409/product
echo "$(hostname)-usb" > strings/0x409/serialnumber
# Konfigürasyon
mkdir -p configs/c.1/strings/0x409
echo "Composite Config" > configs/c.1/strings/0x409/configuration
echo 500 > configs/c.1/MaxPower
# Function 1: ACM (seri konsol)
mkdir -p functions/acm.usb0
ln -sf functions/acm.usb0 configs/c.1/
# Function 2: NCM (Ethernet)
mkdir -p functions/ncm.usb0
echo "48:6f:73:74:50:43" > functions/ncm.usb0/host_addr
echo "42:61:64:55:53:42" > functions/ncm.usb0/dev_addr
ln -sf functions/ncm.usb0 configs/c.1/
# Function 3: Mass Storage
mkdir -p functions/mass_storage.usb0
echo /storage.img > functions/mass_storage.usb0/lun.0/file
echo 0 > functions/mass_storage.usb0/lun.0/ro
ln -sf functions/mass_storage.usb0 configs/c.1/
# UDC'ye bağla (en son adım)
UDC=$(ls /sys/class/udc | head -1)
echo "$UDC" > UDC
echo "Composite gadget etkin: $UDC"
echo " /dev/ttyGS0 — ACM seri konsol"
echo " usb0 — NCM Ethernet"
echo " /dev/sdb — Mass Storage (host'ta)"
İkinci konfigürasyon (iConfiguration)
USB cihazlar birden fazla konfigürasyon sunabilir. Host, cihazı başlatırken hangi konfigürasyonu aktif edeceğini seçer. Farklı özellik kombinasyonları farklı konfigürasyonlarda sunulabilir.
# İkinci konfigürasyon: yalnızca ACM (güç tasarruflu mod)
mkdir -p configs/c.2/strings/0x409
echo "Low Power Config" > configs/c.2/strings/0x409/configuration
echo 100 > configs/c.2/MaxPower
# Yalnızca ACM function ekle
ln -sf functions/acm.usb0 configs/c.2/
# Host iki seçenek görür; genellikle c.1 seçilir
Temizleme: gadget'ı kaldırma
#!/bin/bash
# teardown_gadget.sh
GADGET_DIR=/sys/kernel/config/usb_gadget/composite
# UDC'den ayır
echo "" > "$GADGET_DIR/UDC"
# Symlink'leri kaldır
rm -f "$GADGET_DIR"/configs/c.*/acm.usb0
rm -f "$GADGET_DIR"/configs/c.*/ncm.usb0
rm -f "$GADGET_DIR"/configs/c.*/mass_storage.usb0
# Dize dizinlerini kaldır
rmdir "$GADGET_DIR"/configs/c.*/strings/*
rmdir "$GADGET_DIR"/configs/c.*
# Function dizinlerini kaldır
rmdir "$GADGET_DIR"/functions/acm.usb0
rmdir "$GADGET_DIR"/functions/ncm.usb0
rmdir "$GADGET_DIR"/functions/mass_storage.usb0
# Gadget dize dizinleri ve gadget'ı kaldır
rmdir "$GADGET_DIR"/strings/*
rmdir "$GADGET_DIR"
echo "Gadget kaldırıldı"
Bu bölümde
- Composite gadget: birden fazla function instance + tek konfigürasyon = tek USB bağlantıda çoklu özellik
- Sıra önemlidir: UDC'ye bağlama (echo UDC) en son yapılır; tüm function'lar hazır olmalı
- İkinci konfigürasyon (configs/c.2): farklı özellik setleri için
- Temizleme: UDC'den ayır → symlink'leri sil → function'ları kaldır → gadget dizinini sil
07 FunctionFS: userspace USB function
FunctionFS, USB gadget function'larını tamamen kullanıcı alanında yazmayı sağlar. Özel protokoller, vendor-specific class'lar veya mevcut class'larla uyumlu ama özel davranışlı cihazlar için kullanılır.
FunctionFS hiyerarşisi
FunctionFS, bir FUSE benzeri dosya sistemidir. Kullanıcı programı bu dosya sistemini bağlar, endpoint yapılandırmasını yazar ve ardından endpoint dosyaları üzerinden veri okuyup yazar.
# Modül yükle
sudo modprobe usb_f_fs
# FunctionFS function oluştur
cd /sys/kernel/config/usb_gadget/mygadget
sudo mkdir functions/ffs.usb0
# Konfigürasyona bağla
sudo ln -s functions/ffs.usb0 configs/c.1/
# FunctionFS bağla
sudo mkdir /dev/ffs-usb0
sudo mount -t functionfs usb0 /dev/ffs-usb0
# Kullanıcı programı /dev/ffs-usb0/ep0 yazarak başlar
# ep0: kontrol endpoint (descriptor'lar)
# ep1, ep2: bulk/interrupt endpoint'ler
Minimal FunctionFS uygulaması
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/usb/functionfs.h>
/* Descriptor: 2 bulk endpoint (IN + OUT) */
static const struct {
struct usb_functionfs_descs_head_v2 header;
__le32 fs_count;
__le32 hs_count;
struct { struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor_no_audio bulk_out;
struct usb_endpoint_descriptor_no_audio bulk_in; } __attribute__((packed)) fs;
struct { struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor_no_audio bulk_out;
struct usb_endpoint_descriptor_no_audio bulk_in; } __attribute__((packed)) hs;
} __attribute__((packed)) descriptors = {
.header = {
.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
.length = cpu_to_le32(sizeof(descriptors)),
.flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC),
},
.fs_count = cpu_to_le32(3),
.hs_count = cpu_to_le32(3),
.fs = {
.intf = {
.bLength = sizeof(descriptors.fs.intf),
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
},
.bulk_out = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 1 | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(64),
},
.bulk_in = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 2 | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(64),
},
},
/* hs aynı, wMaxPacketSize=512 */
};
int main(void)
{
int ep0 = open("/dev/ffs-usb0/ep0", O_RDWR);
/* Descriptor'ları yaz */
write(ep0, &descriptors, sizeof(descriptors));
/* String'leri yaz (boş) */
static const struct usb_functionfs_strings_head strings = {
.magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
.length = cpu_to_le32(sizeof(strings)),
.str_count = 0,
.lang_count = 0,
};
write(ep0, &strings, sizeof(strings));
/* UDC'ye bağla (main'den önce shell'de yapılmış olmalı) */
int ep_out = open("/dev/ffs-usb0/ep1", O_RDONLY);
int ep_in = open("/dev/ffs-usb0/ep2", O_WRONLY);
char buf[512];
ssize_t n;
while ((n = read(ep_out, buf, sizeof(buf))) > 0) {
write(ep_in, buf, n); /* Echo back */
}
return 0;
}
Bu bölümde
- FunctionFS: USB function tamamen userspace'te; kernel modülü gerektirmez
- ep0: kontrol endpoint; descriptor ve string yapıları buraya yazılır
- ep1/ep2: bulk/interrupt endpoint'ler; read()/write() ile veri akışı
- Kullanım alanı: vendor-specific protokoller, özel class cihazları, hızlı prototipleme
08 Pratik: bootup script, udev ve debug
Raspberry Pi veya BeagleBone'da gadget'ı açılışta otomatik başlatma, udev kuralıyla tetikleme ve lsusb/dmesg/udc-core ile sorun giderme.
Raspberry Pi Zero için ACM gadget kurulumu
# /boot/config.txt dosyasına ekle
echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt
# /boot/cmdline.txt dosyasında rootwait'ten sonra ekle
# modules-load=dwc2,g_cdc
# Örnek: "console=serial0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait modules-load=dwc2,g_cdc"
# Tek function için g_serial (ACM) yeterli
echo "modules-load=dwc2,g_serial" | sudo tee -a /boot/cmdline.txt
# /usr/local/bin/usb-gadget-init.sh
cat <<'SCRIPT' | sudo tee /usr/local/bin/usb-gadget-init.sh
#!/bin/bash
set -e
modprobe libcomposite
mount -t configfs none /sys/kernel/config 2>/dev/null || true
GADGET=/sys/kernel/config/usb_gadget/pi_gadget
mkdir -p "$GADGET"
cd "$GADGET"
echo 0x1d6b > idVendor
echo 0x0104 > idProduct
echo 0x0200 > bcdUSB
mkdir -p strings/0x409
echo "Raspberry Pi Foundation" > strings/0x409/manufacturer
echo "Pi Zero USB Gadget" > strings/0x409/product
echo "$(cat /proc/cpuinfo | grep Serial | cut -d' ' -f2)" > strings/0x409/serialnumber
mkdir -p configs/c.1/strings/0x409
echo "CDC Composite" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower
# ACM
mkdir -p functions/acm.usb0
ln -sf functions/acm.usb0 configs/c.1/
# NCM
mkdir -p functions/ncm.usb0
echo "48:6f:73:74:00:01" > functions/ncm.usb0/host_addr
echo "42:61:64:00:00:01" > functions/ncm.usb0/dev_addr
ln -sf functions/ncm.usb0 configs/c.1/
# UDC
UDC=$(ls /sys/class/udc | head -1)
echo "$UDC" > UDC
SCRIPT
sudo chmod +x /usr/local/bin/usb-gadget-init.sh
# systemd unit dosyası
cat <<'UNIT' | sudo tee /etc/systemd/system/usb-gadget.service
[Unit]
Description=USB Gadget Initialization
After=sysinit.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/usb-gadget-init.sh
[Install]
WantedBy=multi-user.target
UNIT
sudo systemctl enable usb-gadget.service
sudo systemctl start usb-gadget.service
Debug: lsusb, dmesg ve udc-core
# Host bilgisayarda: cihaz tanınıyor mu?
lsusb
# Bus 001 Device 003: ID 1d6b:0104 Linux Foundation Multifunction Composite Gadget
# Cihaz detayı
lsusb -v -d 1d6b:0104
# Embedded tarafta: kernel logları
dmesg | grep -i usb | tail -20
# [ 5.123] dwc2 fe980000.usb: bound driver configfs-gadget
# [ 5.456] dwc2 fe980000.usb: new device is full-speed
# UDC durumu
cat /sys/class/udc/fe800000.usb/state
# configured
# Gadget function'larını listele
ls /sys/kernel/config/usb_gadget/pi_gadget/functions/
# acm.usb0 ncm.usb0
# Host'ta ACM görünüyor mu?
ls /dev/ttyACM*
# /dev/ttyACM0
# dmesg host tarafında
dmesg | tail -10
# cdc_acm 1-1.3:1.0: ttyACM0: USB ACM device
# cdc_ncm 1-1.3:1.2 usb0: register 'cdc_ncm'
# USB trace (detaylı debug)
sudo modprobe usbmon
sudo tcpdump -i usbmon1 -w usb_capture.pcap
BeagleBone Black kurulumu
# BeagleBone Black: musb driver, OTG portu
ls /sys/class/udc/
# musb-hdrc.0.auto
# Debian tabanlı BBB'de g_multi modülü hali hazırda yüklü olabilir
lsmod | grep g_
# g_multi komposit CDC gadget
# Manuel configfs yaklaşımı (g_multi yerine)
sudo modprobe musb_hdrc # UDC driver
sudo modprobe libcomposite
# Gadget oluştur (önceki örneklerden aynı komutlar)
# UDC adı farklı:
echo "musb-hdrc.0.auto" > /sys/kernel/config/usb_gadget/mygadget/UDC
Bu bölümde
- RPi Zero: config.txt'e dtoverlay=dwc2 + cmdline.txt'e modules-load=dwc2 eklenmeli
- systemd one-shot service: gadget script'ini açılışta çalıştırır; After=sysinit.target
- lsusb -v -d vendor:product: host'ta gadget'ın tanınıp tanınmadığını ve descriptor'ları gösterir
- dmesg (her iki taraf) + cat /sys/class/udc/*/state: bağlantı sorunlarını teşhis eder