모든 브랜치를 보고 싶을 때

git branch -av

 

특정 브랜치/commit 으로 바꾸고 싶을 때

git checkout [branch 명/commit number]

 

내가 있는 경로의 git remote 저장소를 알고 싶을 때

git remote -v

 

gerrit push 명령어

git push refs/heads/[branch 명]


// <예시>
// master 브랜치에 push 하고 싶을 경우
// $ git push refs/heads/master

gerrit push 가 안될 경우?
→ commit message 에 Change-Id 가 없을 가능성이 큼.
→ 관련한 명령어는 추후 추가 예정. 보통은 error 메세지에 나온 명령어 그대로 입력하고 git commit --amend 로 commit 하면 Change-Id 가 추가 되어 다시 push 를 하면 push 가 됨.

 

Commit 한 뒤에 Commit 한 파일을 수정하고 싶을 때

// 수정하고 싶은 사항 수정 후, git status 로 수정한 파일 확인
git add [수정한 파일]
git commit --amend

 

 

N개 Commit 을 patch 파일로 만들고 싶을 경우

git format-patch -[N]

// <예시>
// 최신 commit 부터 3개 commit 을 patch 파일 생성하고 싶을 경우

// $ git format-patch -3

* patch 파일과 diff 파일의 차이
: git diff 로 생성판 파일은 파일을 수정한 내용만 있고 git format-patch 로 생성한 파일은 내부를 보면 소스 코드 수정 사항과 함께 commit 까지 포함되어 있다. commit message 까지 같이 전달하고 싶으면 git format-patch 로 patch 파일을 생성하여 전달하면 되고, 소스 코드 수정 사항만 내보내고 싶으면 git diff 로 diff 파일을 만들어 전달하면 된다.

 

staged 에 수정한 사항을 diff 로 저장하고 diff 파일을 적용할 때

// test.diff 에 수정 사항 저장
git diff > test.diff

// test.diff 를 적용
git apply test.diff

 

commit 한 사항을 patch 파일로 저장하고 patch 파일을 적용할 때

// commit 2개를 patch 파일로 만들기
git format-patch -2

// test.patch 파일을 git에 적용하기
git am test.patch

 

모든 수정 사항을 초기화하고 HEAD 로부터 commit N 개 아래 상태로 돌리고 싶을 때

git reset --hard HEAD~[N]

 // <예시>
 // 최신 commit 에서부터 2개 이전 commit 으로 돌아가고 싶을 때
 // git reset --hard HEAD~2

* 신규로 생성한 파일은 지워지지 않는다.

 

CPU 32 bit 의 경우 사용 가능한 가상 주소 범위는 이론 상 4 GB

=> 2^32 = 4 * 1024 * 1024 * 1024 = 4GB
기본적으로 User 가 3 GB, Kernel 이 1 GB 할당을 받는다. 옵션에 따라 변경은 가능하다. (64 bit 의 경우, 128 테라, 128 테라)

커널은 1G 를 어떻게 사용하는가?
- 선매핑용 주소 (896G) : 커널 내부에서 사용하는 지역 변수나 동적 할당 변수가 저장됨. DMA 주소 포함
- 후매핑용 주소 (128M) : 나중에 물리 메모리를 연결해서 사용할 용도로 남겨둠. vmalloc, pkmap area 주소, fixmap area 주소

 

CPU 64 bit 의 경우 사용 가능한 가상 주소 범위는 이론 상 16 * 1024 * 1024 TB

=> 2^64 = 16 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 = 16 * 1024 * 1024 TB

 

 

4 Level Page Table

가상 주소 9 bit (PGD-Global) + 9 bit (PUD-Upper) + 9 bit (PMD-Middle) + 12 bit (Page Table) = 가상 주소 48 bit 사용

  • PGD: Page Global Directory
  • PUD: Page Upper Directory
  • PMD: Page Middle Directory

 

 

 

 

Page Table Entry 가 커버하는 메모리 사이즈

arch/x86/include/asm/page_table.h 파일에서 다음을 확인할 수 있다.

/* PAGE_SHIFT determines the page size */
#define PAGE_SHIFT              12

위의 의미는 Page Table Entry 하나가 커버하는 크기는 2^12 = 4 * 1024 = 4KB 인 것을 확인할 수 있다.

 

arch/x86/include/asm/pgtable_64_types.h 에서 다음을 확인 할 수 있다.

/*
 * PMD_SHIFT determines the size of the area a middle-level
 * page table can map
 */
#define PMD_SHIFT       21
#define PTRS_PER_PMD    512

PMD 가 하나 할당 될 경우 PMD_SHIFT 는 9 + 12 bit = 21 bit
따라서 Page Middle Directory 의 하나의 entry 가 커버하는 크기는 2^21 = 2 * 1024 * 1024 = 2 MB 커버 가능

 

부팅 직후 Board 에서 확인할 수 있는 방법

/sys/kernel/debug/page_tables/current_kernel 내용에서 pmd 개수를 알아 낼 수 있다.

 

'Linux' 카테고리의 다른 글

MMAP  (0) 2022.12.18
Page Fault 관련 정리  (0) 2022.12.15
리눅스 프로그램과 커널 프로그램  (0) 2022.12.09
리눅스 운영체제의 구성  (0) 2022.12.09
커널 분석 및 디버깅 툴; uftrace  (0) 2022.12.09

mmap 이란 가상 주소 공간(VMA; Virtual Memory Address) 할당 하는 것을 말한다. 파일과 가상 주소를 맵핑할 수도 있다. malloc() 함수도 기본적으로 heap(VMA) 에서 가상 주소 공간을 할당하지만 더 큰 할당이 필요할 때는 내부적으로 mmap 을 통해서 가상 주소 공간을 할당한다.

 

task_struct : Task (혹은 Process) 관리 목적의 커널 자료 구조
ㄴ mm_struct : task_struct 구조체의 하위 자료 구조. 가상 메모리 관리 목적의 커널 자료 구조.
    ㄴ vma: 가상 메모리 공간을 관리하는 자료구조 (예시: stack, heap, code .. 등등)

* Anonymous Page 란? 파일과 매핑이 안된 VMA / Page (예: stack, heap ..)

 

mmap 사용 예시

file 을 열어서 읽고 쓰는 것과 비슷하게 다음 과정을 거치면 메모리를 할당 받아 사용할 수 있다.

  • open()
  • mmap() : malloc 과 거의 동일. 요구한 만큼의 용량 만큼 메모리를 할당하고 할당된 메모리 포인터를 반환.
  • memset()
  • munmap() : 메모리 할당 해제
  • close()

* pagecache 란? 파일과 매핑이 된 Page

 

* 메모리 상에 유지하고 있는 disk cache 버리는 방법

echo 3 > /proc/sys/vm/drop_caches

proc 의 man 페이지를 통해서 proc 사용 방법을 확인할 수 있다.

 

mmap 과 read 시스템콜 비교

  • read 시스템콜의 경우, read를 할 때마다 시스템콜이 발생하여 user mode -> kernel mode 로 넘어가야 하는 overhead 발생
  • mmap 의 경우, 가상 메모리와 물리 메모리가 바로 연결되어 있음. 배열로 된 페이지 테이블을 참고하여 바로 물리 메모리에 접근할 수 있어 속도가 빠름

 

 

'Linux' 카테고리의 다른 글

4-Level Page Table  (0) 2022.12.18
Page Fault 관련 정리  (0) 2022.12.15
리눅스 프로그램과 커널 프로그램  (0) 2022.12.09
리눅스 운영체제의 구성  (0) 2022.12.09
커널 분석 및 디버깅 툴; uftrace  (0) 2022.12.09

강의 들으면서 핵심 내용만 간단하게 정리 진행 중....

 

 

 

 

Memory 관련 Linux Register

CR0: Real mode vs Protection mode 구분 가능 (?)

CR2: 페이지폴트를 일으킨 가상 주소 저장 

CR3:현재 프로세스의 페이지테이블의 주소 (Vritual Adress - Phsical Address 매핑 테이블)

참고할만 한 자료: https://blackcon.tistory.com/117

 

Page Fault

x86 의 경우 Page Fault error code 는 다음과 같다.

arch/x86/include/asm/traps.h

/*
 * Page fault error code bits:
 *
 *   bit 0 ==    0: no page found   1: protection fault
 *   bit 1 ==    0: read access     1: write access
 *   bit 2 ==    0: kernel-mode access  1: user-mode access
 *   bit 3 ==               1: use of reserved bit detected
 *   bit 4 ==               1: fault was an instruction fetch
 *   bit 5 ==               1: protection keys block access
 */

arm 의 경우는 다르며 arch/arm64/include/asm/traps.h 에는 없음. 정리된 주석이 없어 더 살펴봐야 함

 

 

Page Fault 발생 시점의 Stack Trace

page fault가 발생한 지점의 stack trace

exec 하면서 Page fault 가 날 수도 있고, 여러 경우가 있음

 

Page Fault Handler 주요 흐름

Page Fault Handling 이라 함은 페이지 변환 테이블을 수정하는 것이다. 다음과 같은 일을 한다.

(1) 물리 메모리 (Page Frame) 할당
(2) Virtual Address 와 Physical Adrdress 연결
(3) Page Table 한 요소 (entry) flag 세팅
 *PTE: Page Table Entry

Interrupt 나 Exception 이 호출될 때 Entry 에서 시작한다. Page Fault 라는 Exception이 시작하는 부분은 x86 의 경우 arch/x86/entry/entry_64.S 에서 시작한다.

arch/x86/entry/entry_64.S 의 한 부분이 다음과 같다.

idtentry page_fault             do_page_fault           has_error_code=1        read_cr2=1

page_fault 가 호출되면 c로 된 do_page_fault 함수를 호출하고 has_error_code와 read_cr2 (cr2는 fault 가 났을 때 가상 메모리 주소)를 인자로 받아간다.

mm/fault.c 에서 do_page_fault 함수를 확인할 수 있다.

dotraplinkage void
do_page_fault(struct pt_regs *regs, unsigned long error_code, unsigned long address)
{
        enum ctx_state prev_state;

        prev_state = exception_enter();
        trace_page_fault_entries(regs, error_code, address);
        __do_page_fault(regs, error_code, address);
        exception_exit(prev_state);
}
NOKPROBE_SYMBOL(do_page_fault);

arm 의 경우에는 entry 가 아닌 다른 진입점이 있을 것으로 예상함. do_page_fault 는 있으나 .S 파일이 없음

 

__do_page_fault 는 arch/x86/mm/fault.c에서 다음과 같다.

static noinline void
__do_page_fault(struct pt_regs *regs, unsigned long error_code,
		unsigned long address)
{


...

unlikely 란 if 의 결과 값을 예상하고 미리 분기를 하는 함수. 강의 내용이 커널 3점대 버전으로 진행되는 것 같다. 5점 대 버전에서는 x86 에 상단 내용이 없음.

 

주된 내용만 살펴보면 page fault 핸들링은 다음과 같이 동작한다.

do_user_addr_fault()   : arch/x86/mm/fault.c
  -> handle_mm_fault()    :   mm/memory.c
      -> __handle_mm_fault()    :   mm/memory.c
          -> handle_pte_fault()     :   mm/memory.c
               -> ptep_set_access_flags()   :  arch/x86/mm/pgtable.c
                    -> set_pte() “페이지 테이블 한요소(pte) 수정”

 

 

task_struct 구조체: task (process) 관리 목적의 커널 자료 구조

ㄴ mm_struct : task_struct 하위 구조체. 가상 메모리 관리 목적의 커널 자료 구조.

    ㄴ vma : mm_struct 의 하위. 가상 메모리 공간을 관리하는 자료 구조. stack 공간, heap 공간, code 공간.

 

 

'Linux' 카테고리의 다른 글

4-Level Page Table  (0) 2022.12.18
MMAP  (0) 2022.12.18
리눅스 프로그램과 커널 프로그램  (0) 2022.12.09
리눅스 운영체제의 구성  (0) 2022.12.09
커널 분석 및 디버깅 툴; uftrace  (0) 2022.12.09

리눅스 프로그램

  • c 언어 파일을 컴파일 하면 ELF 파일이 생성
  • ELF 란 Excutable and Linkage Format
  • 바이너리 분석 프로그램
    • readelf
    • objdump

 

 

 

'Linux' 카테고리의 다른 글

4-Level Page Table  (0) 2022.12.18
MMAP  (0) 2022.12.18
Page Fault 관련 정리  (0) 2022.12.15
리눅스 운영체제의 구성  (0) 2022.12.09
커널 분석 및 디버깅 툴; uftrace  (0) 2022.12.09

(1) Core

  • Process management (PM): task, scheduler, ..
  • Memory Management (MM): Virtual Memory, Paging..
  • IRQ / Exception 처리, locking
    • entry: 진입점. 프로그램에서 진입점은 main 함수

 

(2) I/O 처리

  • 네트워크 (L4: TCP / L3: IP / L2: Device Driver)
    • TCP 통신: Port 번호
    • IP: IP 주소
    • Device Driver: MAC 주소
  • 스토리지 (VFS / FS / Block)
    • Virtual File System (VFS): open/read/write/close 으로 USB/마우스 등 기기 control 가능
    • File System (FS)
    • Block
  • 디바이스 드라이버

 

(3) 기타: security, tools, sounds ...

'Linux' 카테고리의 다른 글

4-Level Page Table  (0) 2022.12.18
MMAP  (0) 2022.12.18
Page Fault 관련 정리  (0) 2022.12.15
리눅스 프로그램과 커널 프로그램  (0) 2022.12.09
커널 분석 및 디버깅 툴; uftrace  (0) 2022.12.09

 

uftrace

- User 함수 뿐만 아니라 라이브러리, 커널까지 디버깅할 수 있는 Tracing Tool

명령어 예시

  • uftrace record -K 2 [프로그램 명]
    • record: 함수 추적하는 옵션
    • -K: 커널까지 추적하는 옵션
    • 2: 함수 depth. 최대 2개까지 호출 되는 것을 볼 수 있음
  • uftrace replay
    • 프로그램이 재시작이 되진 않음
    • record 한 결과를 토대로 함수에 대한 호출 흐름을 보여줌

 

 

 

'Linux' 카테고리의 다른 글

4-Level Page Table  (0) 2022.12.18
MMAP  (0) 2022.12.18
Page Fault 관련 정리  (0) 2022.12.15
리눅스 프로그램과 커널 프로그램  (0) 2022.12.09
리눅스 운영체제의 구성  (0) 2022.12.09

include/sound/soc-dpcm.h 참고

 

enum 정리

snd_soc_dpcm_update : 실행할 runtime_update 값. 업데이트를 안할 것인지, BE 혹인 FE 를 업데이트할 것인지 나타냄
 - SND_SOC_DPCM_UPDATE_NO
 - SND_SOC_DPCM_UPDATE_BE
 - SND_SOC_DPCM_UPDATE_FE

snd_soc_dpcm_link_state : 새로운 링크를 만들 것인지, 만들어진 링크를 분해할 것인지 나타냄
 - SND_SOC_DPCM_LINK_STATE_NEW
 - SND_SOC_DPCM_LINK_STATE_FREE

snd_soc_dpcm_state : PCM 프론트엔트에서 PCM 백엔드의 상태를 나타냄
 - SND_SOC_DPCM_STATE_NEW
 - SND_SOC_DPCM_STATE_OPEN
 - SND_SOC_DPCM_STATE_HW_PARAMS
 - SND_SOC_DPCM_STATE_PREPARE
 - SND_SOC_DPCM_STATE_START
 - SND_SOC_DPCM_STATE_STOP
 - SND_SOC_DPCM_STATE_PAUSED
 - SND_SOC_DPCM_STATE_SUSPEND
 - SND_SOC_DPCM_STATE_HW_FREE
 - SND_SOC_DPCM_STATE_CLOSE

snd_soc_dpcm_trigger : trigger 가 발생했을 때 어떤 trigger 가 발생했는지 나타냄
 - SND_SOC_DPCM_TRIGGER_PRE
 - SND_SOC_DPCM_TRIGGER_HOST
 - SND_SOC_DPCM_TRIGGER_BESPOKE

 

Dynamic PCM link 구조체

이 구조체는 FE DAI 와 BE DAI 를 runtime 에 연결하고, link 상태와 hw_params 정보를 저장한다.

struct snd_soc_dpcm {
        /* FE and BE DAIs*/
        struct snd_soc_pcm_runtime *be;
        struct snd_soc_pcm_runtime *fe;

        /* link state */
        enum snd_soc_dpcm_link_state state;

        /* list of BE and FE for this DPCM link */
        struct list_head list_be;
        struct list_head list_fe;

        /* hw params for this link - may be different for each link */
        struct snd_pcm_hw_params hw_params;
#ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_state;
#endif
};
struct snd_soc_dpcm_runtime {
        struct list_head be_clients;
        struct list_head fe_clients;

        int users;
        struct snd_pcm_runtime *runtime;
        struct snd_pcm_hw_params hw_params;

        /* state and update */
        enum snd_soc_dpcm_update runtime_update;
        enum snd_soc_dpcm_state state;

        int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */
};

 

궁금증 .. dpcm API 들도 snd_soc_pcm_runtime 을 쓰는데, snd_soc_dpcm_runtime 은 언제 쓰이는지...? 

 -> 쫓아가봤는데 쓰이질 않음..

 

DPCM APIs

/* can this BE stop and free */
int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
                struct snd_soc_pcm_runtime *be, int stream);

/* can this BE perform a hw_params() */
int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
                struct snd_soc_pcm_runtime *be, int stream);

/* is the current PCM operation for this FE ? */
int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream);

/* is the current PCM operation for this BE ? */
int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe,
                struct snd_soc_pcm_runtime *be, int stream);

/* get the substream for this BE */
struct snd_pcm_substream *
        snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream);

/* get the BE runtime state */
enum snd_soc_dpcm_state
        snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream);

/* set the BE runtime state */
void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream,
        enum snd_soc_dpcm_state state);

ASoC Drivers

  • ASoC Machine Driver : Board driver 라고도 불리며, platform 과 DAI, CODEC 을 연결하는 드라이버. 
  • ASoC Platform Driver : ASoC Platform 드라이버는 DMA 드라이버, SoC DAI 드라이버와 DSP 드라이버로 나뉩니다. Platform 드라이버는 오직 SoC CPU 를 위한 드라이버이며, 특정 보드에 국한되지 않는 드라이버입니다.
    • ASoC DMA Driver
    • ASoC DAI Driver : SoC Controller 에서 찾을 수 있는 Digital Audio Interface 드라이버.
    • ASoC DSP Driver : DSP 드라이버. DPCM 구조 사용.
  • ASoC CODEC Driver : 코덱 드라이버.

 

 

 

 

 

 

DPCM analysis based on Kernel Document

 

 

Description

 

Dynamic PCM 은 타겟이 부팅된 후에 PCM audio 와 다양한 오디오 디바이스를 라우팅할 수 있게 해줍니다.

예를 들어, pcm0 는 I2S DAI0, I2S DAI1 에 연결할 수 있습니다. 여러 개의 ALSA PCM 을 여러개의 DAI에 연결할 수 있도록 해 SoC DSP 드라이버에 유용합니다.

DPCM 라우팅은 ALSA mixer 설정에 의해 결정됩니다.

DPCM 은 DSP 내부 audio path 를 나타내는 DAPM 그래프와 믹서 설정을 사용하여 각 ALSA PCM 에 사용되는 패치를 결정합니다.

DPCM은 기존의 모든 컴포넌트 코덱, 플랫폼 및 DAI 드라이버를 변경하지 않고 재사용할 수 있습니다.

 

 

 

 - Phone Audio System with SoC based DSP

 

스마트폰의 오디오 서브시스템입니다.

| Front End PCMs    |  SoC DSP  | Back End DAIs | Audio devices |

                    *************
PCM0 <------------> *           * <----DAI0-----> Codec Headset
                    *           *
PCM1 <------------> *           * <----DAI1-----> Codec Speakers
                    *   DSP     *
PCM2 <------------> *           * <----DAI2-----> MODEM
                    *           *
PCM3 <------------> *           * <----DAI3-----> BT
                    *           *
                    *           * <----DAI4-----> DMIC
                    *           *
                    *           * <----DAI5-----> FM
                    *************

 

스마트폰은 블루투스, FM 디지털 라디오, 스피커, 헤드셋 잭, 디지털 마이크와 모뎀을 지원합니다.

이 사운드 카드는 4개의 DSP front end (FE) ALSA PCM 디바이스를 사용할 수 있으며, 6개의 back end (BE) DAI 를 사용할 수 있습니다.

각 FE PCM 은 모든 BE DAI 에 연결할 수 있습니다. FE PCM 디바이스는 1개 이상의 BE DAI 와 연결할 수 있습니다. (최대 BE: 8개)

 

 

 

 

 

 

 - Example - DPCM Switching playback from DAI0 to DAI1

 

오디오가 헤드셋을 통해 재생되는 것을 예시로 들어보겠습니다. 시간이 흐른 뒤, 유저는 헤드셋을 제거하고 스피커로 오디오를 연속해서 듣습니다.

PCM0 에서 헤드셋에 오디오가 재생되는 그림은 다음과 같습니다.

 

                    *************
PCM0 <============> *           * <====DAI0=====> Codec Headset
                    *           *
PCM1 <------------> *           * <----DAI1-----> Codec Speakers
                    *   DSP     *
PCM2 <------------> *           * <----DAI2-----> MODEM
                    *           *
PCM3 <------------> *           * <----DAI3-----> BT
                    *           *
                    *           * <----DAI4-----> DMIC
                    *           *
                    *           * <----DAI5-----> FM
                    *************

 

헤드셋이 잭에서 제거 되고 스피커가 사용될 때 모습입니다.

 

                    *************
PCM0 <============> *           * <----DAI0-----> Codec Headset
                    *           *
PCM1 <------------> *           * <====DAI1=====> Codec Speakers
                    *   DSP     *
PCM2 <------------> *           * <----DAI2-----> MODEM
                    *           *
PCM3 <------------> *           * <----DAI3-----> BT
                    *           *
                    *           * <----DAI4-----> DMIC
                    *           *
                    *           * <----DAI5-----> FM
                    *************

 

 

오디오 드라이버 프로세스는 다음과 같습니다.

  1. Machine Driver 가 헤드셋 잭이 제거되는 이벤트를 받음
  2. Machine Driver 혹은 Audio HAL 이 헤드셋 path 를 disable
  3. disable 이 된 후에 DPCM 은 헤드셋과 연결된 DAI0 를 PCM stop trigger 와 hw_free(), shutdown() 오퍼레이션 수행
  4. Machine Driver 혹은 Audio HAL 은 스피커 path 를 enable
  5. DPCM 은 스피커 path 가 enable 된 후에 스피커와 연결된 DAI1 을 startup(), hw_params(), prepare() 를 수행하고 start trigger 발생 시킴

 

이 예시에서는 Machine Driver 혹은 Audio HAL 이 라우팅을 바꿀 수 있고, DPCM 은 link up / down 하기 위해 DAI PCM 오퍼레이션을 관리합니다.

오디오 재생은 이렇게 link 가 변하는 동안에도 멈추지 않습니다.

 

 

 

DPCM Machine Driver

 

이번 목차에서는 DPCM 의 Machine Driver 에 대해 알아보겠습니다.

DPCM 의 Machine Driver 는 다음 3가지를 제외하고는 기존 드라이버와 유사합니다.

  1. FE, BE DAI link 를 정의
  2. FE, BE PCM operation 을 정의
  3. Widget Graph Connection 을 정의

 

 

 

 

다음 그림을 예시로 FE DAI link 와 BE DAI link를 설정하는 방법을 설명하겠습니다.

 

| Front End PCMs    |  SoC DSP  | Back End DAIs | Audio devices |

                    *************
PCM0 <------------> *           * <----DAI0-----> Codec Headset
                    *           *
PCM1 <------------> *           * <----DAI1-----> Codec Speakers
                    *   DSP     *
PCM2 <------------> *           * <----DAI2-----> MODEM
                    *           *
PCM3 <------------> *           * <----DAI3-----> BT
                    *           *
                    *           * <----DAI4-----> DMIC
                    *           *
                    *           * <----DAI5-----> FM
                    *************

 

위 그림은 총 4개의 FE DAI link 와 6개의 BE DAI link 가 존재합니다.

다음과 같이 FE DAI link 를 선언합니다.

 

FE DAI link 예시

Machine Driver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
static struct snd_soc_dai_link machine_dais[] = {
      {
              .name = "PCM0 System",
              .stream_name = "System Playback",
              .cpu_dai_name = "System Pin",
              .platform_name = "dsp-audio",
              .codec_name = "snd-soc-dummy",
              .codec_dai_name = "snd-soc-dummy-dai",
              .dynamic = 1,
              .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
              .dpcm_playback = 1,
      },
      .....< other FE and BE DAI links here >
};
  • dynamic = 1
    • Dynamic PCM 이라는 뜻
  • dpcm_playback = 1
    • PCM 을 Playback 으로 사용할 때 선언. dpcm_capture 와 함께 선언 가능.
  • dpcm_capture = 1
    • PCM 을 Capture 로 사용할 때 선언. dpcm_playback 와 함께 선언 가능.
  • .codec_name = "snd-soc-dummy",
    • BE 가 dynamic 하게 routing 에 따라 바뀌므로 dummy 로 선언
  • .codec_dai_name = "snd-soc-dummy-dai",
    • BE 가 dynamic 하게 routing 에 따라 바뀌므로 dummy 로 선언
  • .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
    • 다른 component 보다 앞에 혹은 뒤에 trigger 될 것인지 선언
    • 첫번째 인자는 Playback, 두번째 인자는 Capture
    • 종류
      • SND_SOC_DPCM_TRIGGER_PRE : FE 가 먼저 trigger
      • SND_SOC_DPCM_TRIGGER_POST :  BE 가 먼저 trigger
      • SND_SOC_DPCM_TRIGGER_BESPOKE : FE 와 BE 동시에 trigger (dai 쪽에 bespoke_trigger 가 구현이 되어야 함)
sound/soc/soc-pcm.c
2774     if (rtd->dai_link->dynamic) {
2775         rtd->ops.open       = dpcm_fe_dai_open;
2776         rtd->ops.hw_params  = dpcm_fe_dai_hw_params;
2777         rtd->ops.prepare    = dpcm_fe_dai_prepare;
2778         rtd->ops.trigger    = dpcm_fe_dai_trigger;
2779         rtd->ops.hw_free    = dpcm_fe_dai_hw_free;
2780         rtd->ops.close      = dpcm_fe_dai_close;
2781         rtd->ops.pointer    = soc_pcm_pointer;
2782         rtd->ops.ioctl      = soc_pcm_ioctl;
2783     } else {
2784         rtd->ops.open       = soc_pcm_open;
2785         rtd->ops.hw_params  = soc_pcm_hw_params;
2786         rtd->ops.prepare    = soc_pcm_prepare;
2787         rtd->ops.trigger    = soc_pcm_trigger;
2788         rtd->ops.hw_free    = soc_pcm_hw_free;
2789         rtd->ops.close      = soc_pcm_close;
2790         rtd->ops.pointer    = soc_pcm_pointer;
2791         rtd->ops.ioctl      = soc_pcm_ioctl;
2792     }
sound/soc/soc-pcm.c
2774     if (rtd->dai_link->dynamic) {
2775         rtd->ops.open       = dpcm_fe_dai_open;
2776         rtd->ops.hw_params  = dpcm_fe_dai_hw_params;
2777         rtd->ops.prepare    = dpcm_fe_dai_prepare;
2778         rtd->ops.trigger    = dpcm_fe_dai_trigger;
2779         rtd->ops.hw_free    = dpcm_fe_dai_hw_free;
2780         rtd->ops.close      = dpcm_fe_dai_close;
2781         rtd->ops.pointer    = soc_pcm_pointer;
2782         rtd->ops.ioctl      = soc_pcm_ioctl;
2783     } else {
2784         rtd->ops.open       = soc_pcm_open;
2785         rtd->ops.hw_params  = soc_pcm_hw_params;
2786         rtd->ops.prepare    = soc_pcm_prepare;
2787         rtd->ops.trigger    = soc_pcm_trigger;
2788         rtd->ops.hw_free    = soc_pcm_hw_free;
2789         rtd->ops.close      = soc_pcm_close;
2790         rtd->ops.pointer    = soc_pcm_pointer;
2791         rtd->ops.ioctl      = soc_pcm_ioctl;
2792     }

 

 

 

 

 

BE DAI link 예시

Machine Driver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static struct snd_soc_dai_link machine_dais[] = {
      .....< FE DAI links here >
      {
              .name = "Codec Headset",
              .cpu_dai_name = "ssp-dai.0",
              .platform_name = "snd-soc-dummy",
              .no_pcm = 1,
              .codec_name = "rt5640.0-001c",
              .codec_dai_name = "rt5640-aif1",
              .ignore_suspend = 1,
              .ignore_pmdown_time = 1,
              .be_hw_params_fixup = hswult_ssp0_fixup,
              .ops = &haswell_ops,
              .dpcm_playback = 1,
              .dpcm_capture = 1,
      },
      .....< other BE DAI links here >
};
  • .platform_name = "snd-soc-dummy",
    • FE DAI Link 가 routing에 따라 바뀌므로 dummy 로 설정
  • .no_pcm = 1,
    • BE DAI link 라는 것을 표시
  • .ignore_suspend = 1,
    .ignore_pmdown_time = 1,
    • hostless 모드에서 사용 (ex. BT Call)
  • .dpcm_playback = 1,
    .dpcm_capture = 1,
    • playback 으로 사용할 것인지, capture 로 사용할 것인지 표시

 

 

cf) hostless 모드란?

.ignore_suspend = 1,
.ignore_pmdown_time = 1,

 

BT Call 과 같이 Host CPU 가 데이터를 전송하지 않는 모드

                    *************
PCM0 <------------> *           * <----DAI0-----> Codec Headset
                    *           *
PCM1 <------------> *           * <----DAI1-----> Codec Speakers
                    *   DSP     *
PCM2 <------------> *           * <====DAI2=====> MODEM
                    *           *
PCM3 <------------> *           * <====DAI3=====> BT
                    *           *
                    *           * <----DAI4-----> DMIC
                    *           *
                    *           * <----DAI5-----> FM
                    *************

 

이 모드는 BT 와 MODEM 이 동작 중이더라도 host CPU 는 sleep 할 수 있다.

 

 

 

 - FE / BE PCM Operations

 

BE dai link 에서는 fixup callback 이 필요합니다.

fixup callback 은 machine driver 에서 FE hw params 에 따라 DAI 구성을 설정하는 데에 사용됩니다. 즉, DSP 는 SRC 나 ASRC 를 FE 나 BE 에서 수행할 수 있습니다.

예를 들어, DSP 가 모든 FE hw params 를 DAI0, 48K, 16 bit, stereo channel 로 바꾼다고 하면, 모든 FE hw params 가 DAI0 로 고정되어야 하며, DAI 는 현재 FE 구성이 어떻든 요구된 사항으로 운영되어야 합니다. 

fixup callback 이 호출되는 시점은 dpcm_fe_dai_hw_params 내부에서, FE hw_params 와 BE hw_params 가 호출되기 이전에 호출됩니다.

 

Machine Driver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static int dai0_fixup(struct snd_soc_pcm_runtime *rtd,
                      struct snd_pcm_hw_params *params)
{
      struct snd_interval *rate = hw_param_interval(params,
                      SNDRV_PCM_HW_PARAM_RATE);
      struct snd_interval *channels = hw_param_interval(params,
                                              SNDRV_PCM_HW_PARAM_CHANNELS);
 
      /* The DSP will covert the FE rate to 48k, stereo */
      rate->min = rate->max = 48000;
      channels->min = channels->max = 2;
 
      /* set DAI0 to 16 bit */
      snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
                                  SNDRV_PCM_HW_PARAM_FIRST_MASK],
                                  SNDRV_PCM_FORMAT_S16_LE);
      return 0;
}

 

다른 PCM operation 들은 표준 DAI link 에서 사용하는 PCM Operation 과 동일합니다.

 

 

 

 

 - Widget graph connections

 

 

 

Widget

 

- Desciption

Widget 은 DAPM 커널 문서에서 참고할 수 있습니다.

DAPM 에서의 Widget 은 전력에 영향을 주는 모든 component 를 말합니다. (ex, CODEC, Audio Interface, SRC 등)

DAPM 에는 4개의 Domain 이 있고 이에 따라 4 종류의 Widget 이 있습니다.

  • CODEC Bias domain
  • Platform / Machine domain
  • Path domain : mixer 와 mux 세팅이 유저에 의해 바뀔 때 자동으로 세팅되게 해주는 Widget. alsamixer 나 amixer 에 사용.
  • Stream domain :  스트림 재생 / 녹음이 시작되고 멈추는 것을 enable / disable 할 수 있는 Widget

 

 

 

- DAPM 매크로를 이용한 Widget 선언 예시

드라이버 소스 코드에서 다음과 같이 DAPM 에서 제공하는 매크로를 이용하여 Widget 을 등록할 수 있습니다.

 

  • Stream Domain Widget 예시

 

Widget 등록 매크로 예시
1
2
3
4
5
6
7
/* Stream Domain Widget Define */
#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert)
#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert)
 
/* Stream Domain Widget Example */
SND_SOC_DAPM_AIF_IN("DAI0 RX", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("DAI0 TX", NULL, 0, SND_SOC_NOPM, 0, 0),

 

 

  • Path Domain Widget 예시
    3가지 input 중에 wm8731 output 으로 어떤 widget 을 선택할건지 MIXER 를 등록하는 예시를 들겠습니다.
    • Line Bypass Input
    • DAC (HiFi Playback)
    • Mic Sidetone Input
include/sound/soc-dapm.h
1
2
#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert)
#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, wcontrols, wncontrols)

 

Machine Driver
1
2
3
4
5
6
7
8
/* Output Mixer */
static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
};
 
SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls, ARRAY_SIZE(wm8731_output_mixer_controls)),

 

 

 

 

 

- Device Tree 를 이용한 DAPM Widget 선언 예시

디바이스 트리를 사용한 예시입니다.

Device Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
sound {
    compatible = "simple-audio-card";
    simple-audio-card,name = "VF610-Tower-Sound-Card";
    simple-audio-card,format = "left_j";
    simple-audio-card,bitclock-master = <&dailink0_master>;
    simple-audio-card,frame-master = <&dailink0_master>;
 
    simple-audio-card,widgets =
        "Microphone", "Microphone Jack",
        "Headphone", "Headphone Jack",
        "Speaker", "External Speaker";
 
    simple-audio-card,cpu {
        sound-dai = <&sh_fsi2 0>;
    };
 
    dailink0_master: simple-audio-card,codec {
        sound-dai = <&ak4648>;
        clocks = <&osc>;
    };
};

 

DAPM Widget 은 string 이 한 짝을 이뤄 동작합니다. (Document/devictree/bindings/widgets.txt 참고)

  "template-wname", "user-supplied-wname"

 

짝을 이루어 선언하지 않을 경우, 동작하지 않습니다.

첫번째 요소는 template widget name 을 나타내고, 두번째 요소는 user 가 정하는 widget name 입니다.

 

 

 

- Widget 이 실제 등록되는 장소

Widget은 snd_soc_card 구조체에 linked list 형태로 등록되어 연결이 되어 있고, Component 드라이버에서도 Widget list를 확인할 수 있습니다.

snd_soc_card 구조체에 선언된 Widget 들과 snd_soc_component_driver 에 등록된 Widget 은 동일하며, card 를 등록할 때 component 에 있는 Widget 들을 가져다 card 에 저장합니다.

 

 

 

 

 

 

 

 

 

 

 

Routing

DAPM 은 초기화 타이밍에 오디오 라우팅 맵을 참조하여 BE DAI link 를 연결합니다.

 

 

 

만일 BE CODEC 이나 BE DAI 가 dummy 일 경우, 반드시 아래가 드라이버가 반드시 선언이 되어야 합니다.

Machine Driver
1
2
3
4
5
/* BE for codec Headset -  DAI0 is dummy and managed by DSP FW */
static const struct snd_soc_dapm_route audio_map[] = {
    {"DAI0 CODEC IN", NULL, "AIF1 Capture"},
    {"AIF1 Playback", NULL, "DAI0 CODEC OUT"},
};

첫번째 요소는 sink, 두번째 요소는 control, 세번째 요소는 source 입니다.

 

include/sound/soc-dapm.h
1
2
3
4
5
6
7
8
9
struct snd_soc_dapm_route {
    const char *sink;
    const char *control;
    const char *source;
 
    /* Note: currently only supported for links where source is a supply */
    int (*connected)(struct snd_soc_dapm_widget *source,
             struct snd_soc_dapm_widget *sink);
};

 

 

 

다음과 같이 디바이스 트리에서 routing 을 선언할 수도 있습니다.

routing 은 string 을 한 쌍으로 선언해야 합니다.

첫번째 요소는 sink, 두번째 요소는 source 를 의미합니다.

Device Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sound {
    compatible = "simple-scu-audio-card";
 
    simple-audio-card,name = "rsnd-ak4643";
    simple-audio-card,format = "left_j";
    simple-audio-card,bitclock-master = <&sndcodec>;
    simple-audio-card,frame-master = <&sndcodec>;
 
    simple-audio-card,convert-rate = <48000>;
 
    simple-audio-card,prefix = "ak4642";
    simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
            "DAI0 Capture", "ak4642 Capture";
 
    sndcpu: simple-audio-card,cpu {
        sound-dai = <&rcar_sound>;
    };
 
    sndcodec: simple-audio-card,codec {
        sound-dai = <&ak4643>;
        system-clock-frequency = <11289600>;
    };
};

위의 예시는 DAI0 Playback 에서 ak4642 로 음원을 재생하고, ak4642 에서 DAI0 로 음원을 녹음하는 예시를 나타냅니다.

 

 

하나의 Mixer 에 Line Input, DAC, MIC 와 같이 3 가지 input 이 들어갈 때의 디바이스 트리 예시입니다.

Device Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
sound {
    compatible = "simple-scu-audio-card";
 
    simple-audio-card,name = "rsnd-ak4643";
    simple-audio-card,format = "left_j";
    simple-audio-card,bitclock-master = <&sndcodec>;
    simple-audio-card,frame-master = <&sndcodec>;
 
    simple-audio-card,convert-rate = <48000>;
 
    simple-audio-card,prefix = "ak4642";
    simple-audio-card,routing =
            "Output Mixer", "Line Input",
            "Output Mixer", "DAC",
            "Output Mixer", "Mic Bias";
 
    sndcpu: simple-audio-card,cpu {
        sound-dai = <&rcar_sound>;
    };
 
    sndcodec: simple-audio-card,codec {
        sound-dai = <&ak4643>;
        system-clock-frequency = <11289600>;
    };
};

 

ASRC 의 경우 디바이스 트리나 소스 코드에 widget 을 사용하지 않고 probe 시에 snd_soc_dapm_route 에 source 와 sink 를 직접 넣어줍니다.

 

 

 

 

 

 

 

 

 

 

Writing a DPCM DSP driver

 

DPCM DSP 드라이버는 코덱 클래스 드라이버로부터 요소들이 결합된 표준 플랫폼 클래스 ASoC 드라이버와 매우 비슷합니다. DSP driver는 다음이 구현되어야 합니다.

  1. FE PCM DAIs - i.e. struct snd_soc_dai_driver
  2. DAPM widgets
  3. DAPM graph showing DSP audio routing from FE DAIs to BEs
  4. Mixers for gains, routing, etc.
  5. DMA configuration
  6. BE AIF widgets.

 

 

6번 아이템은 DSP 외부의 오디오와 라우팅 하는데에 중요합니다. AIF 는 각 BE 와 각 스트림 방향이 정의되어야 합니다. 즉, BE DAI0 는 다음과 같아야 합니다.

Machine Driver
1
2
SND_SOC_DAPM_AIF_IN("DAI0 RX", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("DAI0 TX", NULL, 0, SND_SOC_NOPM, 0, 0),

BE AIF 는 DSP 그래프와 다른 컴포넌트 드라이버 (ex. codec graph) 와 연결되는데 사용됩니다.

 

 

 

 

 

Hostless PCM streams

 

Hostless CPU 는 host CPU 에서 스트림이 라우팅되지 않는 것을 말합니다. 아래는 헤드셋으로 통화할 때 예시입니다.

 

                    *************
PCM0 <------------> *           * <----DAI0-----> Codec Headset
                    *           *
PCM1 <------------> *           * <====DAI1=====> Codec Speakers/Mic
                    *   DSP     *
PCM2 <------------> *           * <====DAI2=====> MODEM
                    *           *
PCM3 <------------> *           * <----DAI3-----> BT
                    *           *
                    *           * <----DAI4-----> DMIC
                    *           *
                    *           * <----DAI5-----> FM
                    *************

 

이 케이스에서는 PCM 데이터가 DSP 를 통해 라우팅 되고, host CPU 는 컨트롤하는데에만 사용되고 런타임에는 sleep 할 수 있습니다.

 

Hostless link 를 제어하는 방법은 2가지가 있습니다.

  1. 링크를 CODEC ↔ CODEC 스타일로 구성하는 방법.
    • 이 경우에는 DAPM graph 로 상태를 enable / disable 할 수 있습니다.
      즉, mixer control 를 통해서 연결 / 연결 해제를 지원해야 합니다.
  2. DAPM 그래프 상 FE 와 BE 사이에 가상 연결을 하는 방법.
    • FE 에 mixer control 를 등록합니다.
      이 방법은 DAI link 간 더 많은 control 할 수 있게 해주지만, user space 에서 링크를 제어하기 위해 더 많은 코드가 필요합니다.
      만일 HW 가 PCM operation 간의 fine grained 가 필요할 경우가 아니면 CODEC ↔ CODEC 을 추천합니다.

 

 

 

DAPM 그래프 내에서 path 를 enable 할 때 DAI link 가 enable 됩니다.

machine 드라이버는 DAI link 에 추가적은 parameter 를 세팅합니다.

 

CODEC ↔ CODEC link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static const struct snd_soc_pcm_stream dai_params = {
  .formats = SNDRV_PCM_FMTBIT_S32_LE,
  .rate_min = 8000,
  .rate_max = 8000,
  .channels_min = 2,
  .channels_max = 2,
};
 
static struct snd_soc_dai_link dais[] = {
  < ... more DAI links above ... >
  {
      .name = "MODEM",
      .stream_name = "MODEM",
      .cpu_dai_name = "dai2",
      .codec_dai_name = "modem-aif1",
      .codec_name = "modem",
      .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
              | SND_SOC_DAIFMT_CBM_CFM,
      .params = &dai_params,
  }
  < ... more DAI links here ... >
}

 

위 예시의 dai_params 는 DAPM 이 유효한 path 을 감지할 때 DAI 의 hw_parms() 오퍼레이션에서 사용됩니다. 그리고 link 를 start operation 을 호출합니다.

이 후에 DAPM 은 Path 가 유효하지 않아 졌을 때, 적절한 PCM operation 을 찾아서 DAI 를 disable 합니다.

 

 

 

Hostless FE

 

PCM data 를 아예 읽거나 쓰지 않는 DAI link 의 경우 FE 에 의해 enable 됩니다.

두 개의 DAI link 에 연결되는 가상의 path 와 연결되는 새로운 FE 를 만드는 것을 뜻합니다.

DAI link 들은 FE PCM 이 시작할 때 시작 해야 하고, FE PCM 이 멈출 때 멈추어야 합니다.

단, FE PCM 은 이 구성에서는 read / write 를 할 수 없습니다.

 

 

4장

이벤트와 리퀘스트

 허브는 주기적으로 하향 포트에 연결된 디바이스의 장착과 탈착을 감지. 주기적으로 폴링을 하거나 인핸스드 슈퍼스피드 패킷을 수신하여 디바이스의 장/탈착을 감지한다.

 과정
 1. 호스트는 새 디바이스에 대한 정보를 얻기 위해 디바이스 허브로 리퀘스트
 2. 허브는 디바이스와 호스트 간에 통신 경로를 설정
 3. 호스트는 통신 경로에 제어 전송(표준 USB 리퀘스트 포함)을 보내어 디바이스 열거 시도
 4. 디바이스는 각 리퀘스트에 해당하는 정보로 응답하거나 요청된 기타 동작 수행
* 모든 USB 디바이스는 제어 전송, 표준 리퀘스트, 엔드포인트 0을 지원해야 함

 

설정 상태 얻기

 

디바이스의 상태 (USB 2.0 기준)

  • 전원 공급됨 (Powered)
  • 기본 (Default)
  • 주소 (Address)
  • 설정됨 (Configured)
  • 장착됨 (Attached)
    : 허브가 디바이스의 VBUS 선에 전원을 공급하지 않은 상태. VBUS에 전원이 없으면 호스트와 디바이스가 통신을 할 수 없기 때문에 디바이스가 연결되지 않은 것과 마찬가지.
  • 서스펜드 (Suspend)
    : 최소 3ms 동안 SOF 마커를 포함한 버스 활동이 없는 상태

 

일반적인 USB 2.0 순서

  1. 시스템이 새 디바이스를 갖는다.
    : 사용자가 USB 포트에 디바이스를 장착하거나 디바이스를 장착한 상태에서 시스템 전원을 넣음.
     허브가 포트에 전원을 공급하면 디바이스는 Powerd 상태가 됨 (디바이스는 버르소부터 100mA까지 전류를 얻을 수 있음)
  2. 허브가 디바이스를 감지한다.
    : 허브는 각 포트의 신호선(D+와 D-) 전압을 감시한다. 허브의 각 신호선은 14.25~24.8kΩ의 풀다운 저항이 있다. 디바이스는 풀스프드 디바이스에서는 D+에, 로우스피드 디바이스에서는 D-에 900~1575 Ω 의 풀업 저항이 있다. 디바이스는 VBUS가 0.8V 이상인 것을 검출한 이후 1초 내에 연결돼야 하는데, 전력이 약하거나 모두 소모된 배터리를 상요하는 디바이스는 제외
  3. 호스트가 새 디바이스 정보를 얻는다.
    : 각 허브는 인터럽트 엔드포인트를 이용해 발생한 이벤트가 허브인지 포트인지 보고.
     호스트는 발생한 이벤트에 대해 자세히 알아내기 위해 Get Port Status 리퀘스트를 허브로 전송
     허브는 호스트로부터 반환된 정보를 통해 새 디바이스를 호스트에 장착했음을 감지
  4. 허브는 디바이스가 풀스피드인지 로우스피드인지 감지
    : 신호선 전압을 검사해 디바이스가 로우스피드인지 풀스피드인지 판단
  5. 허브가 디바이스를 리셋
    : 호스트가 새 디바이스 장착을 알아채면 Set Port Feature 리퀘스트를 허브로 보내 포트 리셋을 요청
  6. 호스트는 풀스피드 디바이스가 하이스피드도 지원하는지 확인
  7. 허브는 디바이스와 버스 사이의 신호 경로를 설정
  8. 호스트는 Get Descriptor 리퀘스트를 보내 기본 파이프의 최대 패킷 크기를 알아낸다.
  9. 호스트가 주소를 할당한다.
    : Set Address 리퀘스트
  10. 호스트는 디바이스 기능에 관한 정보를 읽어온다.
    : Get Descriptor 리퀘스트
  11. 호스트가 디바이스에 추가 정보를 요청
  12. 호스트가 디바이스 드라이버를 할당하고 메모리로 가져온다.
    : 윈도우 호스트는 INF 파일을 이용해 최적의 드라이버를 식별. INF 파일은 USB 클래스용 시스템 파일 또는 제조자 제공 파일이다. 이 안에는 디바이스 Vendor ID, Product ID가 들어있다.
  13. 호스트 디바이스 드라이버가 컨피규레이션을 선정
    : Set Configuration 리퀘스트 전송. 디바이스가 요청 받은 Configuration을 수신하면 요청받은 컨피규레이션을 활성화. 디바이스는 설정됨 (Configured) 상태로 전환되고 인터페이스 활성화

*OS나 OS 버전에 달라질 수 있음

 

 

디바이스 제거

사용자가 버스에서 디바이스를 제거하면 허브는 디바이스를 장착했던 포트를 비활성화한다. 그 후 허브가 호스트에게 이벤트가 발생했음을 알리고, 호스트가 디바이스 제거를 알아챈 후 Get Port Status 리퀘스트를 보내 어떤 이벤트가 발생했는지 확인. 이제 디바이스가 장치 관리자에서 사라진다.

 

 

성공적인 열거를 위한 팁

열거를 성공하지 못하면 디바이스와 호스트는 통신 작업을 수행할 수 없다. 칩 제조사는 대부분 예제 코드를 제공한다. 

  • 이벤트는 정해진 순서로 발생하지 않을 수 도 있다.
  • 제어 전송은 재빠르게 이루어져야 하며 유사시에는 신속하게 포기해야한다.
  • 호스트가 요청한 데이터보다 많이 보내면 안된다.
  • 필요하면 ZLP(zero-length data packet)를 보낸다.
  • 지원하지 않는 리퀘스트는 STALL로 응답
  • 주소를 너무 빨리 지정하면 안된다.
  • 서스펜드 상태에 대한 준비를 해야한다.
  • 다른 호스트 컨트롤러 환경에서도 테스트해야 한다.

 

 

 

2 디스크립터

2-1 디스크립터 유형

2-2 디바이스

2-3 디바이스 한정자

2-4 컨피규레이션

2-5 다른 속도 컨피규레이션

2-6 인터페이스 연관 디스크립터

2-7 인터페이스 디스크립터

2-8 엔드포인트

2-9 슈퍼스피드 엔드포인트 짝

2-10 슈퍼스피드 플러스 등시성 엔드포인트 짝

2-11 문자열

2-12 바이너리 오브젝트 스토어와 디바이스 기능

2-13 OTG 디스크립터

2-14 마이크로소프트 OS 디스크립터

2-15 USB 2.0용 디스크립터로 업데이트

2-16 USB 3.1용 디스크립터로 업데이트

 

 

 

 

'USB' 카테고리의 다른 글

[USB] USB 장치  (0) 2020.11.01
[USB] Window USB Descriptor  (0) 2020.11.01

+ Recent posts