Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX USB GADGET 2026

USB Gadget
Linux'u USB Cihazı Yap

Raspberry Pi, BeagleBone veya herhangi bir UDC destekli SoC'yi USB seri port, ethernet adaptörü, mass storage veya HID cihazına dönüştür — configfs API ile.

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.

RolLinux subsystemÖrnek
HostUSB Host Controller (XHCI/EHCI/OHCI)PC'nin USB portu
DeviceUSB Device Controller (UDC) + GadgetRPi Zero, BeagleBone, STM32
OTGUSB OTG + role switchingBeagleBone 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ımKernel modülü
DWC2RPi Zero/Zero W, Allwinner, Rockchipdwc2.ko
DWC3RPi 4/5, Qualcomm, i.MX8dwc3.ko
ChipIdeai.MX6, i.MX7, SAMA5ci_hdrc.ko
MusbTI AM335x (BeagleBone), OMAPmusb.ko
dummy_hcdTest/geliştirme (sanal)dummy_hcd.ko
bash — UDC varlığını kontrol et
# 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

bash — gadget oluşturma adımları
# 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

bash — UDC bağlama
# 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

bash — ACM gadget kurulumu
# 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.

bash — getty ile USB konsol
# 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

C — /dev/ttyGS0 üzerinden veri gönderme
#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

bash — NCM gadget kurulumu
# 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ı

bash — IP adresi atama ve SSH
# 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.

bash — RNDIS function
# 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

bash — mass storage kurulumu
# 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

bash — SD kart bölümünü 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

bash — çoklu 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

bash — sıcak 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

bash — HID klavye kurulumu
# 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.

C — HID klavye tuş gönderme
#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

bash + C — fare gadget
# 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
C — fare hareketi gönderme
/* 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

bash — ACM + NCM + Mass Storage composite
#!/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.

bash — ikinci konfigürasyon ekleme
# İ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

bash — gadget temizleme scripti
#!/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.

bash — FunctionFS kurulumu
# 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ı

C — FunctionFS echo server
#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

bash — RPi Zero: config.txt + cmdline.txt
# /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
bash — systemd service ile otomatik başlatma
# /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

bash — debug komutları
# 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

bash — BeagleBone Black OTG gadget
# 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