Server

[RaspberryPI] 라즈베리 파이 dwc 커스텀 usb_hid_gadget 설정

하루이2222 2024. 8. 30. 22:39

본 포스팅은  tinypilot 도커 컨테이너화 과정의 일부로  tinypilot 에서 라즈베리 파이 의 usb 포트를 키보드, 마우스  처럼 주변 장치로 인식 하게 만들기 위한 과정을 담고 있음.

용어 정리

  1. dwc2 (DesignWare USB 2.0)
    • 정의: dwc2는 Synopsys사의 DesignWare USB 2.0 호스트/디바이스 컨트롤러 IP를 위한 Linux 커널 드라이버임.
    • 역할: 라즈베리 파이와 같은 장치가 USB Device 모드(즉, 주변 장치 모드)에서 작동할 수 있도록 지원함.
      • 호스트 모드: 라즈베리 파이가 USB 키보드, 마우스 등의 주변 장치를 연결해 사용할 수 있는 기본 모드.
      • 디바이스 모드: 라즈베리 파이가 USB 주변 장치로 동작할 수 있는 모드. 예를 들어, USB 키보드, 마우스, 가상 네트워크 어댑터 등으로 변신할 수 있음.
    • 용도: 라즈베리 파이가 USB 장치로 작동하도록 하려면 dwc2 모듈을 활성화해야 함. 이를 통해 라즈베리 파이를 USB 주변 장치처럼 사용할 수 있게 됨.
  2. g_ether (USB Gadget Ethernet)
    • 정의: g_ether는 Linux USB Gadget 프레임워크의 드라이버로, USB 포트를 통해 가상 네트워크 인터페이스를 제공함. 이를 통해 라즈베리 파이가 USB 이더넷 어댑터처럼 동작할 수 있음.
    • 역할: USB 포트를 사용하여 호스트 시스템과 네트워크 연결을 제공함. 라즈베리 파이와 호스트 컴퓨터 간에 네트워크 통신이 가능해짐.
    • 용도: g_ether를 사용하면 라즈베리 파이를 USB로 연결된 네트워크 인터페이스처럼 사용할 수 있으며, 이를 통해 네트워크 액세스, 파일 전송, 네트워크 디버깅 등을 수행할 수 있음.
    • 주의점: g_ether가 활성화되면, USB Device Controller(UDC)를 점유하게 되어 다른 USB Gadget 드라이버(예: HID 장치)를 사용할 수 없게 됨. 따라서 g_ether를 사용하려면 다른 USB Gadget 드라이버와의 충돌을 피하기 위해 적절히 관리해야 함.
  3. UDC (USB Device Controller)
    • 정의: UDC(USB Device Controller)는 USB 장치 모드에서 USB 통신을 관리하는 하드웨어 컨트롤러임.
    • 역할: USB 디바이스 모드에서 라즈베리 파이의 USB 포트를 관리하고, 호스트 시스템과의 데이터 통신을 담당함. USB Gadget 드라이버(g_ether, hid, 등)가 UDC를 통해 USB 연결을 설정하고 유지함.
    • 용도: 라즈베리 파이와 같은 장치에서 다양한 USB 장치 기능(HID, Mass Storage, Ethernet 등)을 수행할 수 있도록 함. 하지만, UDC는 한 번에 하나의 드라이버만 지원하므로, 여러 USB Gadget 드라이버를 동시에 사용하려면 이를 고려하여 적절히 관리해야 함.
    • 상태 관리: UDC가 "busy" 상태가 되면, 현재 다른 USB Gadget 드라이버가 UDC를 사용하고 있음을 의미함. 따라서 새로운 USB 기능을 추가하려면 "busy" 상태를 해제해야 함.

계층 구조에 대한 설명

  1. UDC (USB Device Controller)저급 계층
    • 정의: USB 하드웨어 컨트롤러로, USB 통신을 물리적으로 처리하는 장치.
    • 역할: USB 호스트(예: PC)와의 직접적인 데이터 전송 및 통신을 담당함.
    • 위치: USB 제어의 가장 저급 계층에서 작동하여 하드웨어 단에서 직접적인 USB 통신을 처리함.
  2. dwc2 (USB 컨트롤러 드라이버)중간 계층
    • 정의: UDC를 관리하고 제어하는 소프트웨어 드라이버.
    • 역할: UDC의 하드웨어 기능을 제어하고, USB 디바이스 모드에서의 동작을 가능하게 함.
    • 위치: USB 제어의 미들 계층에 위치하여, UDC를 직접 제어하는 역할을 수행함.
  3. g_ether (USB Gadget 드라이버)상위 계층(고급 계층)
    • 정의: USB Gadget 프레임워크의 드라이버로, USB를 통해 가상 네트워크 인터페이스를 제공함.
    • 역할: USB 이더넷 어댑터를 에뮬레이트하여 네트워크 연결을 설정하고, USB를 통해 네트워크 통신을 수행함.
    • 위치: 가장 상위 계층에 존재하며, dwc2를 통해 UDC와 상호작용하여 USB 장치 모드에서 동작함. 이는 HTTP, SMTP, SMB와 같은 네트워크 프로토콜이 TCP/IP 위에서 동작하는 방식과 유사한 개념임.

1. g_ether 모듈 제거

UDC는 동시에 하나의 USB Gadget 드라이버만 사용할 수 있음. g_ether 모듈이 로드되면, UDC는 가상 네트워크 인터페이스를 위한 USB 장치로 설정됨. 이 경우, UDC는 g_ether가 제어하고 있는 상태가 되어, 다른 USB Gadget 드라이버가 UDC를 사용할 수 없게 됨. 즉, g_ether가 활성화되어 있는 동안 UDC는 "busy" 상태로 표시됨.

g_ether 모듈은 우리가 설정할 usb_gadget 드라이버의 일종으로 활성화되어 있으면 커스컴 gadget을 설정할수 없음. 따라서 비활성화 가 필요함.

sudo modprobe -r g_ether

1.1 dwc2 모듈 설정

먼저, USB Gadget 기능을 위해 dwc2 모듈을 활성화함. /boot/firmware/config.txt/boot/firmware/cmdline.txt 파일을 편집하여 설정함.

1.2 /boot/firmware/config.txt 파일 수정

sudo nano /boot/firmware/config.txt

파일에 다음 내용을 추가함:

dtoverlay=dwc2

1.3 /boot/firmware/cmdline.txt 파일 수정

sudo nano /boot/firmware/cmdline.txt

rootwait 뒤에 modules-load=dwc2를 추가하여, dwc2 모듈만 로드되도록 설정함: g_ether를 설정하면 g_ehther 가 udc 를 점유하고 있어

... rootwait modules-load=dwc2 ...

1.4 라즈베리 파이 재부팅

설정을 적용하기 위해 라즈베리 파이를 재부팅함:

sudo reboot

1.5 udc 상태 확인

재부팅 후 udc가 정상적으로 활성화 되었는지 확인하기 위해 아래 명령을 사용하여 확인함.

ls /sys/class/udc

# 결과 
fe980000.usb # 이와같이 뜬다면 정상적으로 인식된것 이다.

2. USB Gadget 설정 스크립트 작성

USB Gadget을 설정하는 스크립트를 작성하여, 라즈베리 파이를 USB HID(주변창치) 장치로 구성함. 라즈베리 파이 의 usb포트 를 주변 장치로 인식하여 라즈베리파이의 usb와 연결된 서버에 키보드 마우스 캡쳐 정보를 전달하기 위함.

2.1 스크립트 작성

스크립트를 작성하여 USB Gadget을 설정함:

sudo nano /usr/local/bin/setup_usb_gadget.sh

스크립트 내용:

#!/bin/bash

# USB Gadget 설정 디렉토리
GADGET_DIR="/sys/kernel/config/usb_gadget/g1"
sudo modprobe libcomposite

# 기존 설정 제거
if [ -d "$GADGET_DIR" ]; then
  echo "Removing existing gadget configuration..."

  # UDC 설정 해제
  echo "" | sudo tee $GADGET_DIR/UDC || true
  sleep 1

  # 기능 및 구성 해제
  sudo find $GADGET_DIR -type f -exec sh -c 'echo "" > "$1"' sh {} \; || true
  sudo rm -rf $GADGET_DIR
fi

echo "Creating USB Gadget configuration..."

# USB Gadget 기본 설정
sudo mkdir -p $GADGET_DIR
cd $GADGET_DIR

echo 0x1d6b | sudo tee idVendor       # Linux Foundation Vendor ID
echo 0x0104 | sudo tee idProduct      # Multifunction Composite Gadget Product ID
echo 0x0100 | sudo tee bcdDevice      # Device release number
echo 0x0200 | sudo tee bcdUSB         # USB 2.0
sudo mkdir -p strings/0x409
echo "1234567890" | sudo tee strings/0x409/serialnumber
echo "My Manufacturer" | sudo tee strings/0x409/manufacturer
echo "My Composite HID Device" | sudo tee strings/0x409/product

# 첫 번째 HID 기능 설정 (키보드)
sudo mkdir -p functions/hid.usb0
echo 1 | sudo tee functions/hid.usb0/protocol    # 1 = Keyboard
echo 1 | sudo tee functions/hid.usb0/subclass    # Subclass (1 for Boot Interface)
echo 8 | sudo tee functions/hid.usb0/report_length

# 첫 번째 HID Report Descriptor 설정 (키보드)
echo -ne "\\x05\\x01\\x09\\x06\\xA1\\x01\\x05\\x07\\x19\\xE0\\x29\\xE7\\x15\\x00\\x25\\x01\\x95\\x08\\x75\\x01\\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" | sudo tee functions/hid.usb0/report_desc

# 두 번째 HID 기능 설정 (마우스)
MOUSE_FUNCTIONS_DIR="functions/hid.mouse"
mkdir -p "$MOUSE_FUNCTIONS_DIR"
echo 0 > "${MOUSE_FUNCTIONS_DIR}/protocol"  # 0 = 마우스 부트 프로토콜
echo 0 > "${MOUSE_FUNCTIONS_DIR}/subclass"  # 0 = 일반 마우스 장치
echo 5 > "${MOUSE_FUNCTIONS_DIR}/report_length"  # 리포트 길이 설정

# 마우스 HID Report Descriptor 작성
D=$(mktemp)
{
echo -ne \\x05\\x01      # USAGE_PAGE (Generic Desktop)
echo -ne \\x09\\x02      # USAGE (Mouse)
echo -ne \\xA1\\x01      # COLLECTION (Application)
                         #   8-buttons
echo -ne \\x05\\x09      #   USAGE_PAGE (Button)
echo -ne \\x19\\x01      #   USAGE_MINIMUM (Button 1)
echo -ne \\x29\\x08      #   USAGE_MAXIMUM (Button 8)
echo -ne \\x15\\x00      #   LOGICAL_MINIMUM (0)
echo -ne \\x25\\x01      #   LOGICAL_MAXIMUM (1)
echo -ne \\x95\\x08      #   REPORT_COUNT (8)
echo -ne \\x75\\x01      #   REPORT_SIZE (1)
echo -ne \\x81\\x02      #   INPUT (Data,Var,Abs)
                         #   x,y absolute coordinates
echo -ne \\x05\\x01      #   USAGE_PAGE (Generic Desktop)
echo -ne \\x09\\x30      #   USAGE (X)
echo -ne \\x09\\x31      #   USAGE (Y)
echo -ne \\x16\\x00\\x00 #   LOGICAL_MINIMUM (0)
echo -ne \\x26\\xFF\\x7F #   LOGICAL_MAXIMUM (32767)
echo -ne \\x75\\x10      #   REPORT_SIZE (16)
echo -ne \\x95\\x02      #   REPORT_COUNT (2)
echo -ne \\x81\\x02      #   INPUT (Data,Var,Abs)
                         #   vertical wheel
echo -ne \\x09\\x38      #   USAGE (wheel)
echo -ne \\x15\\x81      #   LOGICAL_MINIMUM (-127)
echo -ne \\x25\\x7F      #   LOGICAL_MAXIMUM (127)
echo -ne \\x75\\x08      #   REPORT_SIZE (8)
echo -ne \\x95\\x01      #   REPORT_COUNT (1)
echo -ne \\x81\\x06      #   INPUT (Data,Var,Rel)
                         #   horizontal wheel
echo -ne \\x05\\x0C      #   USAGE_PAGE (Consumer Devices)
echo -ne \\x0A\\x38\\x02 #   USAGE (AC Pan)
echo -ne \\x15\\x81      #   LOGICAL_MINIMUM (-127)
echo -ne \\x25\\x7F      #   LOGICAL_MAXIMUM (127)
echo -ne \\x75\\x08      #   REPORT_SIZE (8)
echo -ne \\x95\\x01      #   REPORT_COUNT (1)
echo -ne \\x81\\x06      #   INPUT (Data,Var,Rel)
echo -ne \\xC0           # END_COLLECTION
} >> "$D"
cp "$D" "${MOUSE_FUNCTIONS_DIR}/report_desc"

# 위의 mouse descriptor 는 기본적으로 사용되는 버튼 3개의 마우스 디스크립터를 사용하면 동작이 안됨. tintpilot은 ustreamer를 활용해 캡쳐된 화면을 스트리밍 하기 때문에 마우스의 좌표를 인식하는 위치 에 차이가 있음. 따라서 위의 descriptor 는 tinypilot 코어 에서 추출해서 사용함 

# 관련 링크 
ansible-role-tinypilot/files/init-usb-gadget at ed29bacf132625c26b24898c7a08376ba3d22c61 · tiny-pilot/ansible-role-tinypilot

# USB 구성 연결
sudo mkdir -p configs/c.1/strings/0x409
echo "Configuration 1" | sudo tee configs/c.1/strings/0x409/configuration
echo 250 | sudo tee configs/c.1/MaxPower

# 첫 번째 HID 장치 링크 (키보드)
sudo ln -s functions/hid.usb0 configs/c.1/

# 두 번째 HID 장치 링크 (마우스)
sudo ln -s functions/hid.mouse configs/c.1/

# UDC 활성화 및 상태 확인
UDC_DEVICE=$(ls /sys/class/udc | head -n 1)
if [ -z "$UDC_DEVICE" ]; then
  echo "UDC가 발견되지 않음. USB 장치가 올바르게 연결되었는지 확인하십시오."
  exit 1
fi
echo $UDC_DEVICE | sudo tee

 UDC

echo "USB HID Gadget 설정이 완료되었습니다."

위 스크립트의 gadget 설정 경로는 /sys 경로 임, 이는 재부팅시 초기화 되기 때문에 rc.d 에 지정해서 자동 실행 하거나 재부팅시 재실행 하여 설정해줘 야함.

2.2 스크립트 저장 및 실행

스크립트를 저장하고 실행 권한을 부여함:

sudo chmod +x /usr/local/bin/setup_usb_gadget.sh

스크립트를 실행함:

sudo /usr/local/bin/setup_usb_gadget.sh

2.3 최종 확인

최종적으로 tinypilot 이 가상 디바이스 파일에 접근하여 다비이스를 제어할수 있도록 해야함 따라서 가상 디바이스 파일이 정상적으로 /dev/hidg 경로에 생성 되었는지 확인

ls /dev/hidg**