Presentation is loading. Please wait.

Presentation is loading. Please wait.

27. DMA와 PCI 디바이스 김진홍 jhkim3624@etri.re.kr 2015.11.15.

Similar presentations


Presentation on theme: "27. DMA와 PCI 디바이스 김진홍 jhkim3624@etri.re.kr 2015.11.15."— Presentation transcript:

1 27. DMA와 PCI 디바이스 김진홍

2 1. DMA 2. PCI 목차

3 1. DMA

4 개요 DMA 등장 배경 기존의 문제점 프로세서는 하드웨어에 데이터를 써넣거나 읽음으로써 제어함 1. DMA 2. PCI 해결
프로세서에서 메모리에 있는 데이터를 하드웨어에 써 넣을때 메모리 장치에서 데이터를 프로세서의 레지스터로 읽음 프로세서의 레지스터 내용을 하드웨어에 써넣음 각 과정을 처리하는 명령을 읽고 해석 데이터 전송과 읽고 쓰기 위한 하드웨어 주소를 관리해야 함 => 프로세스 효율을 저하시킴 해결 DMA

5 개요 DMA 란 구성 1. DMA 2. PCI 대부분 채널로 구성됨 하나의 DMAC에서 여러 채널을 관리 함
채널은 데이터를 전송하는 기본 단위 전송 데이터의 소스 주소 전송 데이터의 목적지 주소 전송 데이터의 크기 하나의 DMAC에서 여러 채널을 관리 함 채널이 있는 이유는 DMAC가 관리할 수 있는 하드웨어 장치의 수를 의미 함

6 리눅스에서의 DMA 처리 개요 DMA 처리와 관련된 함수나 구조체를 리눅스에서 완벽하게 표준화 할 수 없음 1. DMA
2. PCI 개요 DMA 처리와 관련된 함수나 구조체를 리눅스에서 완벽하게 표준화 할 수 없음 최소한의 공통된 스펙에 관련된 함수만 제공 공통 함수군을 벗어난 처리는 디바이스 드라이버에서 직접 처리 해야 함

7 리눅스에서의 DMA 처리 DMA 소유권 등록과 해제 DMA 채널에 대한 소유권 등록 1. DMA
2. PCI DMA 소유권 등록과 해제 DMA 채널에 대한 소유권 등록 디바이스드라이버가 DMA를 사용하기 위해 가장 먼저 할일 디바이스 파일이 열렸을때(open()) 처리 함 int request_dma(unsigned int dmanr, const char * device_id); dmanr : 사용하려는 DMA 채널 지정 device_id : DMA 채널을 소유한 것을 proc에 표시하기 위해 사용하는 문자열 DMA 채널에 대한 소유권 해제 디바이스 파일이 닫혔을때(release()) 처리 함 void free_dma(unsigned int dmanr);

8 리눅스에서의 DMA 처리 디바이스 파일이 열렸을 때의 DMA 처리 주의사항 1. DMA 디바이스 파일 open시 절차
2. PCI 디바이스 파일이 열렸을 때의 DMA 처리 주의사항 open()시 request_dma() 함수를 통해 단순히 소유권 등록만 하면 안됨. DMA를 위한 버퍼가 필요함 DMA 처리 종료시 발생하는 인터럽트를 처리해야 함 디바이스 파일 open시 절차 DMA 버퍼 할당 DMA 인터럽트 핸들러 등록 DMA 채널 소유권 등록 int xxx_open(struct inode *inode, struct file *filp) { : xxx_dma_buff = xxx_alloc_dma_buffer(); : request_irq(XXX_IRQ, dma_interrupt, …); request_dma(XXX_DMA_CHANNEL, XXX_DEV_NAME); : return 0; }

9 리눅스에서의 DMA 처리 디바이스 파일이 닫혔을 때의 DMA 처리 open() 처리의 역순 1. DMA 2. PCI
인터럽트 핸들러 해제 버퍼 해제 int xxx_release ( struct inode *inode, struct file *filp) { : free_dma(XXX_DMA_CHANNEL); free_irq(XXX_IRQ, …); : xxx_free_dma_buffer(xxx_dma_buff); //할당된 dma 버퍼를 해제 함 //커널에서 제공하지 않아 개발자가 직접 작성해야 함 : return 0; }

10 리눅스에서의 DMA 처리 DMA 버퍼 할당과 해제 1. DMA 2. PCI DMA 전송은 메모리와 I/O 하드웨어간의 전송
디바이스 드라이버에서 vmalloc말고 kmalloc으로 할당해야 함 DMA에 사용되는 메모리는 물리적으로 연속된 공간을 가져야 함 kmalloc()함수에 GFP_DMA 옵션을 줌 __get_free_pages() 함수에 GFP_DMA 옵션을 줌 __get_dma_pages() 많이 사용됨 __get_free_pages()의 매크로 함수 할당 dma_buffer = (char *) __get_dma_pages(GFP_KERNEL, get_order(XXX_DMA_BUFFER_SIZE)); XXX_DMA_BUFFER_SIZE는 할당 하고자 하는 DMA 버퍼 크기 해제 if(dma_buffer) { free_pages((unsigned long) dma_buffer, get_order(XXX_DMA_BUFFER_SIZE)); }

11 리눅스에서의 DMA 처리 DMA 버퍼 할당과 해제 커널 2.6 dma용 버퍼 할당 함수 1. DMA 2. PCI
void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag); DMA 용 버퍼를 할당함 dev : 이 메모리를 소유하는 디바이스 객체의 주소 size : 할당하고자 하는 메모리의 크기 dma_handle : 할당된 메모리의 물리주소 flag : 부가적으로 주고자 하는 메모리의 속성 ( kmalloc 속성 참고) void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle); 위와 같음 cpu_addr에 전달되어야 하는 매개변수 : dma_alloc_coherent에서 할당한 주소값

12 리눅스에서의 DMA 처리 DMA 전송 시작 DMA 데이터를 전송 코드의 형태는 정형화 되어 있음 1. DMA 2. PCI
int xxx_dma_start(int channel, int mode, unsigned char *dma_buff, unsigned int count) { unsigned long flags; : flags=claim_dma_lock( ); disable_dma(channel); clear_dma_ff(channel); set_dma_mode(channel, mode); set_dma_addr(channel, virt_to_bus(dma_buff)); set_dma_count(channel, count); enable_dma(channel); release_dma_lock(flags); : return 0; } DMA 시작을 위한 I/O 하드웨어에 관련된 처리 하드웨어마다 다름

13 리눅스에서의 DMA 처리 DMA 전송 시작(계속) 내부 함수 1. DMA 2. PCI
unsigned long claim_dma_lock(void); 다른 드라이버와의 경쟁을 방지하기 위해 잠금 void disable_dma(unsigned int dmanr); DMA 전송 시작시 혹시 모를 DMA 동작을 금지 시킴. 매개변수값은 금지시키고자 하는 채널 void clear_dma_ff(unsigned int dmanr); DMA의 플립플롭(flip-flop)을 클리어 플립플롭은 16비트 데이터 전송을 8비트 버스에서 처리할 때 사용됨 void set_dma_mode(unsigned int dmanr, char mode); 전송 방향을 설정하기 위해 사용 매개변수는 mode는 보통 다음 중 하나 DMA_MODE_READ : I/O 하드웨어에서 메모리로 전송 DMA_MODE_WRITE : 메모리에서 I/O 하드웨어로 전송

14 리눅스에서의 DMA 처리 DMA 전송 시작(계속) 내부 함수 1. DMA 2. PCI
void set_dma_addr(unsigned int dmanr, unsigned int a); DMA 버퍼의 주소를 지정 dmanr : 채널 지정, a : 주소 지정(가상 주소 불가능) void set_dma_count(unsigned int dmanr, unsigned int count); DMA로 전송되는 데이터의 크기를 지정 전송 단위는 바이트이며, 채널이 16비트 채널이면 반드시 짝수여야 함 void enable_dma(unsigned int dmanr); 전송을 위해 DMAC을 활성화 시킴 void release_dma_lock(unsigned long flags); lock 되었던것을 해제함 int get_dma_residue(unsigned int dmanr); 현재 DMA에 전송중인지를 확인

15 DMA와 MMAP DMA와 MMAP 1. DMA 2. PCI DMA를 사용한다
하드웨어 <-> 프로세서 간의 데이터 전송이 많다는 것을 의미 함(비디오 데이터, 사운드 데이터 등) 응용 프로그램 <-> 디바이스드라이버 사이에도 많은 데이터 전달이 발생한다는 의미도 됨 따라서 응용 프로그램 <-> 디바이스드라이버간에 데이터 전달을 위해 mmap을 지원하는 것이 좋음 user -> kernel, kernel -> user 간의 copy 과정의 딜레이를 제거 디바이스 파일이 열릴 때 DMA용 버퍼를 할당 함 DMA용 버퍼를 mmap() 함수를 이용해 User 영역으로 매핑하는 것을 지원해야 함

16 DMA와 MMAP DMA와 MMAP 가상 메모리 1. DMA 유저 유저 영역 응용 프로그램 2. PCI 호출 물리 메모리
mmap (0, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0x0b0000) 호출 xxx_mmap() 커널 영역 유저 응용 프로그램 DMAC 물리 메모리 I/O 장치 DMA Device Driver file_operation 내부의 mmap() = xxx_mmap() DAM 버퍼 -> mmap을 통해 유저에게 매핑 0x0b0000 0x0b1000

17 2. PCI

18 개요 PCI란 프로세서와 가까운 확장 슬롯에 부착된 디바이스간의 고속 데이터 전송 버스에 대한 규약 장점 1. DMA
PnP 지원 PC이외에 ARM, PowerPC, MIPS, SH등에서도 사용 됨 디바이스 드라이버 작성 PCI 버스에 연결된 디바이스 사용시 필요한 내용은 커널에서 사전에 처리 함 PCI 버스에 대한 스펙을 알필요 없음

19 PnP와 디바이스 환경 설정 공간 PnP 구현 절차
1. DMA 2. PCI PnP 구현 절차 시스템 전원이 켜지면 PCI 버스에 연결된 디바이스들은 버스와 연결이 끊어져 있음 환경 설정 영역만 프로세서에서 접근 가능 운영체제는 PCI 버스에 연결된 디바이스의 환경설정영역을 차례로 검사 함 디바이스가 필요한 I/O 주소 공간과 인터럽트 번호 등을 파악하여 내부적으로 저장 OS가 내부 정보를 정리하고, 각 디바이스의 I/O 주소와 인터럽트 번호를 내부적으로 할당 함 OS가 정리된 I/O주소와 인터럽트 번호를 환경 설정영역에 써넣음 프로세서는 해당 I/O 주소로 접근 할 수 있고, 주변 장치는 인터럽트 번호에 해당하는 인터럽트를 발생 시킬 수 있음 PCI 주변 장치들은 환경 설정 영역의 정보를 이용해 PnP를 구현함

20 PnP와 디바이스 환경 설정 공간 환경 설정 영역의 헤드 1. DMA 2. PCI 벤더 ID 디바이스 ID
offset : 0x00 디바이스 제조사를 나타내는 16bit ID 디바이스 ID offset : 0x02 디바이스 구분 16bit ID Revision ID offset : 0x08 버전을 나타냄 클래스 코드 0x09 디바이스의 종류와 기능을 나타냄 기본 클래스(0x0B) 하부 클래스(0x0A) 프로그래밍 인터페이스(0x09)

21 PnP와 디바이스 환경 설정 공간 환경 설정 영역의 헤드 1. DMA 2. PCI 헤더 타입 기준 주소 레지스터 인터럽트 라인
offset : 0x0E 디바이스 드라이버의 구분 PCI-PCI 브릿지 일반 디바이스 기준 주소 레지스터 offset : 0x10 ~ 0x24 크기 : 32bit 운영체제에서 드라이버가 요구하는 주소 크기와 종류를 얻거나 시스템에 할당된 선두주소를 지정하는 레지스터 접근하는 메모리 공간은 다음과 같이 나눔 I/O 영역 최대 접근 가능한 주소공간 크기 : 256 버스의 접근 속도가 매우 느리게 설계된 주소 공간 prefetch 32bit/64bit 메모리 영역 32bit/64bit 주소 지정이 가능한 메모리 영역을 지정 지정 가능한 최소 크기는 16byte 이상 none prefetch32bit/64bit 메모리 영역 제어용 레지스터인 경우 이 메모리 공간으로 설정 됨 인터럽트 라인 offset : 0x3C OS가 디바이스 드라이버에 할당한 인터럽트 번호 prefetch 하나의 주소를 지정하고 읽기를 시도할 때 해당 주소 뒤쪽의 데이터도 같이 읽어 들여 처리 속도를 향상 시킴

22 PnP와 디바이스 환경 설정 공간 lspci를 이용한 PCI 정보 검색 1. DMA 2. PCI 디바이스가 포함된 버스의 번호
각 디바이스가 장착된 슬롯의 번호 슬롯에 장착된 디바이스에서 내부 디바이스를 구별하는 번호

23 PnP와 디바이스 환경 설정 공간 proc 파일 시스템을 이용한 PCI 정보 검색 1. DMA 2. PCI
불안정한 디바이스의 드라이버를 작성할때 도움이 됨 raw value를 그대로 볼 수 있어 잘못된 점을 확인 가능

24 PnP와 디바이스 환경 설정 공간 sysfs 파일 시스템을 이용한 PCI 정보 검색(2.6) 1. DMA 2. PCI
proc 파일 시스템보다 더 많은 정보를 검색 할 수 있음

25 PnP와 디바이스 환경 설정 공간 sysfs 파일 시스템을 이용한 PCI 정보 검색(2.6) 1. DMA 2. PCI

26 PnP와 디바이스 환경 설정 공간 디바이스 드라이버에서 환경 설정 공간의 접근 1. DMA 2. PCI
접근하기 위해 사용하는 함수 int pci_bus_read_config_byte(struct pci_bus *bus, unsigned int devfn, int where, u8 *val); int pci_bus_read_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 *val); int pci_bus_read_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 *val); int pci_bus_write_config_byte(struct pci_bus *bus, unsigned int devfn, int where, u8 *val); int pci_bus_write_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 *val); int pci_bus_write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 *val); PCI 디바이스 정보가 담겨져 있는 pci_dev 구조체가 있는 경우 int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val); int pci_read_config_word(struct pci_dev *dev, int where, u8 *val); int pci_read_config_dword(struct pci_dev *dev, int where, u8 *val); int pci_write_config_byte(struct pci_dev *dev, int where, u8 *val); int pci_write_config_word(struct pci_dev *dev, int where, u8 *val); int pci_write_config_dword(struct pci_dev *dev, int where, u8 *val); int xxx_read_info(struct pci_dev *dev) { u8 deviceid; : pci_read_config_byte(dev, PCI_DEVICE_ID, &deviceid); return deviceid; }

27 PCI 디바이스의 디바이스 드라이버 일반 디바이스 드라이버와의 차이점 일반적인 디바이스 드라이버 PCI 디바이스 드라이버
1. DMA 2. PCI 일반 디바이스 드라이버와의 차이점 일반적인 디바이스 드라이버 제어하려는 하드웨어 주소와 인터럽트가 고정되어 있음 DMA를 다룸 PCI 디바이스 드라이버 제어하려는 하드웨어 주소와 인터럽트가 고정적이지 않음 초기 처리에 주소와 인터럽트를 얻는 부분이 추가됨 DMA가 없고 버스 마스터링 방식의 DMA처리

28 PCI 디바이스의 디바이스 드라이버 PCI 디바이스 검출 1. DMA 2. PCI
int pci_register_driver(struct pci_driver *drv); 을 사용하여 등록 제어 대상 디바이스가 있는 것을 알려면 벤더 ID와 디바이스 ID가 필요함 이 정보를 pci_register_driver()함수를 이용해 전달하기 위해 struct pci_device_id 구조체가 필요함 struct pci_device_id { __u32 vendor, device; //vendor : 검출하려는 PCI 디바이스의 벤더 ID, device : 검출하려는 PCI 디바이스의 디바이스 ID __u32 subvendor, subdevice; //subvendor : 검출하려는 pci 디바이스의 하부 시스템 벤더 ID, subdevice : 검출하려는 PCI 디바이스의 하부 시스템 디바이스 ID __u32 class, class_mask; //class : 디바이스의 클래스 코드, class_mask : 클래스에서 유효한 비트를 표시하는 마스크 kernel_ulong_t driver_data; //driver_data : 데이터 검출 시 드라이버에서 사용할 수 있는 정보를 지정하기 위해 준비된 것, 인덱스 번호 }; 예제 static struct pci_device_id xxx_pci_tbl[] __devinitdata = { {0x1233, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, XXX_INDEX1}; {0x1233, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0 ,0, XXX_INDEX2}; {0, } //테이블의 끝을 나타냄 }; MODULE_DEVICE_TABLE(pci, xxx_pci_tbl);

29 PCI 디바이스의 디바이스 드라이버 PCI 디바이스 검출 struct pci_driver 1. DMA 2. PCI
pci_register_driver() 함수를 사용하기 위한 ID 테이블이 준비 된후 PCI가 검출 되었을 때 처리해야 할 pci_driver 구조체가 준비 됨 struct pci_driver { struct list_head node; char *name; // PCI 디바이스를 제어하는 드라이버 이름 const struct pci_device_id *id_table; //디바이스 드라이버가 제어하려고 검출하는 PCI 디바이스의 ID 테이블 int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); // ID 테이블 목록과 일치하는 디바이스가 발견되면 호출됨 일치한 ID 정보는 id 매개변수에 전달, 디바이스와 관련된 pci정보는 dev에 전달 void (*remove) (struct pci_dev *dev); // 디바이스가 제거될때 호출됨 int (*suspend) (struct pci_dev *dev, u32 stats); //커널이 PCI 디바이스를 절전모드로 만들때 호출됨 int (*resume)(struct pci_dev *dev); //절전에서 깨어나게 할때 호출됨 int (*enable_wake) (struct pci_dev *dev, u32 state, int enable); struct device_driver driver; struct pci_dynids dynids; };

30 PCI 디바이스의 디바이스 드라이버 PCI 디바이스 검출 PCI 디바이스 검색과 등록 요청 1. DMA 2. PCI
static struct pci_driver xxx_pci_driver = { .name = XXX_DRIVER_NAME, .id_table = xxx_pci_tbl, .probe = xxx_probe, .remove = __devexit_p(xxx_remove), #ifdef CONFIG_PM .suspend = xxx_suspend, .resume = xxx_resume, #endif }; static int __init xxx_init_module(void) { return pci_register_driver(&xxx_pci_driver); } static void __exit xxx_cleanup_module(void) { pci_unregister_driver(&xxx_pci_driver); }

31 PCI 디바이스의 디바이스 드라이버 PCI 디바이스 검출 probe()와 remove() 처리 1. DMA 2. PCI
pci_register_driver() 함수를 호출해 ID테이블 리스트의 목록과 일치하는 디바이스가 발견 되면 pci_driver의 probe 필드에 등록된 함수 호출 int probe(struct pci_dev *dev, const struct pci_device_id *id); PCI 디바이스를 활성화 시킴 PCI 디바이스를 제어하기 위한 I/O와 메모리 주소를 얻음 I/O와 메모리 영역을 등록 메모리 영역을 커널공간에 매핑 PCI 디바이스를 제어하기 위한 IRQ 번호를 얻음 인터럽트 핸들러 등록 필요시 버스 마스터 모드 설정 디바이스 드라이버 등록 하드웨어 초기화

32 PCI 디바이스의 디바이스 드라이버 PCI 디바이스 검출 1. DMA 2. PCI
int probe(struct pci_dev *dev, const struct pci_device_id *id); PCI 디바이스를 활성화 PCI 디바이스에 접근하기 위해 디바이스 활성화 int pci_enable_device(struct pci_dev *dev); void pci_disable_device(struct pci_dev *dev); PCI 디바이스의 하드웨어 제어 주소 정보 주소정보는 커널 내의 데이터 구조에 존재함 다음 함수를 이용하여 획득 unsigned long pci_resource_start(struct pci_dev *dev, int bar); BAR(Base Address Register)에 해당하는 메모리의 시작주소를 얻어옴 unsigned long pci_resource_end(struct pci_dev *dev, int bar); BAR(Base Address Register)에 해당하는 메모리의 끝주소를 얻어옴 unsigned long pci_resource_len(struct pci_dev *dev, int bar); BAR(Base Address Register)에 해당하는 메모리의 크기를 얻어옴 unsigned long pci_resource_flags(struct pci_dev *dev, int bar); BAR(Base Address Register)에 해당하는 메모리의 속성값을 가져옴 IORESOURCE_IO, IORESOURCE_MEM

33 PCI 디바이스의 디바이스 드라이버 PCI 디바이스 검출 1. DMA 2. PCI
int probe(struct pci_dev *dev, const struct pci_device_id *id); 3. PCI 메모리 영역 등록과 해제 충돌 방지를 위해 PCI 디바이스의 메모리영역을 커널에 등록 int pci_request_region(struct pci_dev *, char *dev_name); void pci_release_regions(struct pci_dev *); 4. 메모리 영역 매핑 버스 주소를 획득하여 커널 주소공간에 매핑 버스 주소 획득 pci_resource_start(), pci_resource_end() 커널 주소공간 매핑 ioremap() 5. PCI 디바이스 IRQ 번호와 인터럽트 핸들러 등록 PCI 디바이스의 인터럽트 번호는 pci_dev -> irq request_irq()를 이용하여 등록 6. 버스 마스터링 모드 설정 버스 마스터링 방식의 DMA를 사용함 void pci_set_master(struct pci_dev *dev);

34 PCI 디바이스의 디바이스 드라이버 PCI 디바이스 검출 probe 함수의 예 1. DMA 2. PCI
int xxx_probe(struct pci_dev *dev, const struct pci_device_id *id) { : //PCI 디바이스 활성화 pci_enable_device(dev); // 메모리 주소 획득 및 등록과 매핑 xxx_hw_addr = pci_resource_start(dev, 0); xxx_addr_size = pci_reousrce_len(dev, 0); pci_request_regions(dev,XXX_DEV_NAME); xxx_vaddr = ioremap(xxx_hw_addr, xxx_addr_size); //인터럽트 등록 xxx_irq = dev->irq; request_irq(xxx_irq, xxx_interrupt, SA_INTERRUPT | SA_SHIRQ, XXX_DEV_NAME, &xxx_info); //버스 마스터링 모드 설정 pci_set_master(dev); //디바이스 드라이버 등록 register_chrdev(XX_DEV_MAJOR, XXX_DEV_NAME, &xxx_fops); //하드웨어 초기화 xxx_hardware_init(); : }

35 PCI 디바이스의 디바이스 드라이버 PCI 디바이스 검출(수동) 1. DMA 2. PCI
struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *from); struct pci_dev *pci_find_subsys (unsigned int vendor, unsigned int device, unsigned int ss_vendor, unsigned int ss_device const struct pci_dev *from); struct pci_dev *pci_find_class(unsigned int class, const struct pci_dev *from); struct pci_dev *pci_find_slot(unsigned int bus, unsigned int devfn); 각 함수는 다음과 같은 조건과 일치하는 대상을 찾음 pci_find_device : 벤더ID, 디바이스 ID pci_find_subsys : 벤더ID, 디바이스 ID, 하부 벤더 ID, 하부 디바이스 ID pci_find_class : 클래스 코드 pci_find_slot : 버스번호, 슬롯, function 번호 예제 struct pci_dev *dev = NULL; while(dev = pci_find_device(VENDOR_ID, DEVICE_ID, dev)){ //찾은 PCI 디바이스 정보가 dev에 넘어온다. : }

36 버스 마스터링 DMA 버스 마스터링 DMA 개요 구현 1. DMA 2. PCI
PCI 버스 스펙에는 DMA 방식이 규정되어 있지 않음 대신, PCI 디바이스가 버스 점유권을 획득한후 처리 구현 PCI 디바이스의 구조에 따라 처리 방식이 모두 다름 DMA와 같이 데이터를 전송하기 위해 버스 주소가 필요함 이와 관련된 함수의 이해가 필요함

37 Q&A

38


Download ppt "27. DMA와 PCI 디바이스 김진홍 jhkim3624@etri.re.kr 2015.11.15."

Similar presentations


Ads by Google