PCI Drivers
|
How to Write Linux PCI Drivers
이 문서는 /usr/src/linux-2.6.5/Documentation/pci.txt 문서를 번역한 것입니다.
번역이 매끄럽지 못한 부분들은 원문을 참고하셔서 이해하시길 바랍니다.
저자 Martin Mares <
mj_at_ucw.czmj_at_ucw.cz > on 2000.02.07
번역 김남형 <
pastime_at_ece.uos.ac.krpastime_at_ece.uos.ac.kr > on 2004.05.05
PCI 의 세계는 광대하고 (별로 즐겁지 않겠지만) 놀라움으로 가득하다.
각각의 PCI 장치들은 서로 다른 요구사항과 버그들을 가진다.
이때문에 리눅스 커널의 PCI 지원 레이어는 우리가 바라는 만큼 간단하지가 않다.
이 짧은 글은 잠재적인 모든 PCI 드라이버 개발자들에게 PCI handling 이라는 깊은 숲속에서
자신 만의 길을 찾아내도록 도와주려고 한다.
1. PCI 드라이버의 구조 ¶2가지 종류의 PCI 드라이버가 존재한다:
신식 (new-style) 드라이버는 장치를 검색 (probe) 하는 대부분의 과정을 PCI 레이어에게 넘겨주었고
장치의 온라인 삽입과 제거를 지원한다. (즉 PCI, hot-pluggable PCI, CardBus 를 하나의 드라이버로 지원한다)
다른 하나는 구식 (old-style) 드라이버인데 모든 장치 검색 과정을 스스로 처리한다.
만약 꼭 그래야 할 이유가 없다면, 새로 작성하는 코드에서 구식의 방식을 이용하여 장치 검색을 수행하지 않도록 하자.
장치가 검색되고 나면 드라이버는 (구식이던 신식이던 간에) 장치를 동작시키고 싶어한다.
그러기 위해서는 다음 과정이 필요하다:
만약 PCI subsystem 이 설정되지 않았다면 (CONFIG_PCI 가 n 값을 가짐)
다음에 소개하는 대부분의 함수들은 내용이 없는 인라인 함수
혹은 드라이버 상의 많은 ifdef 들을 피하기 위한 적절한 에러코드를 리턴하는 함수로 정의될 것이다.
2. 신식 (New-style) 드라이버 ¶신식 드라이버는 초기화 과정에서 단지 다음과 같은 멤버를 포함하는
드라이버를 표현하기 위한 구조체 (struct pci_driver) 의 포인터를 인수로 pci_register_driver 함수를 호출한다.
ID 테이블은 마지막이 모두 0 인 엔트리로 끝나는 struct pci_device_id 의 배열이다. 이 구조체는 다음과 같은 멤버들을 가진다:
시스템이 알고있는 모든 PCI 장치에게 probe() 가 호출되도록 하려면 테이블의 엔트리를 {PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID} 로 만들면 된다.
/sys/bus/pci/drivers/{driver}/new_id 파일에 쓰는 것으로 런타임에 디바이스 드라이버에게 새로운 PCI ID 가 추가될 수 있다. 일단 추가되면, 드라이버는 이것을 지원하는 모든 장치를 검색 (probe) 할 것이다.
echo "vendor device subvendor subdevice class class_mask driver_data" > \
/sys/bus/pci/drivers/{driver}/new_id
(위의 모든 필드들은 (0x 을 생략한 형태의) 16 진수로 넘겨진다.)
사용자는 단지 필요한 필드만을 넘길 필요가 있다; vendor, device, subvendor, subdevice 필드는 기본값으로 PCI_ANY_ID (FFFFFFFF) 를 가지고 class 와 classmask 는 0 을, driver_data 필드는 OUL 을 각각 기본값으로 가진다. 디바이스 드라이버는 인자로 넘겨진 driver_data 필드를 위해서 다음 함수를 반드시 호출해야 한다.
pci_dynids_set_use_driver_data(pci_driver *, 1) 그렇지 않으면 해당 필드로는 단지 0 이 넘겨진다.
드라이버가 제거될 (exit) 때는, 단지 pci_unregister_driver() 함수를 호출하고 PCI 레이어는 자동적으로 이 드라이버에게 처리되고 있던 모든 장치에 대해서 remove hook 함수를 호출한다.
초기화 (initialization) 함수와 제거 (clean-up) 함수는 (<linux/init.h> 에 정의된 매크로 들을 이용하여) 적절히 표시해 주기 바란다:
팁:
module_init()/module_exit() 함수들 (그리고 오직 여기서만 호출되는 모든 초기화 함수들) 은 __init/exit 로 표시 (mark) 되어야 한다. struct pci_driver 는 이러한 태그들로 표시되면 안된다. ID 테이블 배열은 __devinitdata 로 표시되어야 한다. probe() 와 remove() 함수들 (그리고 오직 여기서만 호출되는 모든 초기화 함수들) 은 __devinit/exit 로 표시되어야 한다. 만약 드라이버가 hotplug 를 지원하는 드라이버가 아니라고 확신한다면 그냥 __init/exit 와 __initdata/exitdata 만을 사용하라.
__devexit 로 표시된 함수에 대한 포인터는 반드시 __devexit_p(함수이름) 을 사용하여 생성되어야 한다. 이 매크로는 함수이름을 생성하거나 __devexit 로 표시된 함수가 버려질 (discard) 경우 NULL 을 반환한다.
3. 수동으로 PCI 장치를 검색하는 방법 (구식 드라이버) ¶pci_register_driver() 인터페이스를 이용하지 않는 PCI 드라이버의 경우에는 다음과 같은 방식으로 수동으로 (manually) PCI 장치를 검색할 수 있다:
제품 공급자와 장치 ID 검색:
struct pci_dev *dev = NULL;
while (dev = pci_find_device(VENDOR_ID, DEVICE_ID, dev))
configure_device(dev);
클래스 ID 검색 (동일한 방식으로 루프를 이용):
pci_find_class(CLASS_ID, dev)
제품 공급자/장치 ID 및 subsystem 의 제품 공급자/장치 ID 검색:
pci_find_subsys(VENDOR_ID, DEVICE_ID, SUBSYS_VENDOR_ID, SUBSYS_DEVICE_ID, dev)
VENDOR_ID 나 DEVICE_ID 위치에는 와일드 카드로 PCI_ANY_ID 라는 상수를 이용할 수 있다. 이 경우에는 예를 들어 '특정 공급자가 제공하는 모든 장치' 와 같은 검색이 가능하게 된다.
이 함수들은 hotplug 에 대해서 안전하지 않다는 (not hotplug-safe) 것을 주의하기 바란다. 위의 각 함수들에 대해서 hotplug 를 안전하게 지원하는 버전은 pci_get_device(), pci_get_class(), pci_get_subsys() 이다. 이 함수들은 자신이 반환하는 장치에 해당하는 pci_dev 구조체의 참조 횟수 (reference count) 를 증가시킨다. 해당 장치를 사용하지 않을 경우에는 (아마도 모듈 언로드시) pci_dev_put() 함수를 이용하여 반드시 이 장치의 참조 횟수를 감소시켜야 한다.
4. 장치 활성화하기 ¶검색한 장치를 이용해서 어떤 작업을 수행하기 전에, pci_enable_device() 함수를 이용해 장치를 활성화 시킬 필요가 있다.
이 함수는 I/O 와 메모리 영역을 활성화시키고, 필요한 경우 잃은 자원들을 할당하며, 장치가 절전 상태에 있었던 경우에는 깨어나도록 한다.
이 함수가 실패할 수도 있다는 사실에 주의하기 바란다.
만약 장치를 buf mastering mode 로 사용하고 싶은 경우에는, pci_set_master() 함수를 호출해서
PCI_COMMAND 레지스터의 bus master 비트를 활성화하고, BIOS 에 의해 어떤 bogus 로 설정된 경우?? 레이턴시 타이머 값을 수정한다.
만약 PCI Memory-Write-Invalidate 트랜잭션을 사용하고 싶은 경우에는 pci_set_mwi() 함수를 호출하라.
이 함수는 Mem-Wr-Inval 의 PCI_COMMAND 비트를 활성화하고, cache inline size 레지스터가 적절하게 설정되도록 해준다.
모든 아키텍처에서 Memory-Write-Invalidate 를 지원하지는 않기 때문에, pci_set_mwi() 의 리턴값을 체크해야 한다.
5. PCI 설정 공간에 접근하는 방법 ¶struct pci_dev * 로 표현되는 장치의 설정 공간에 접근하기 위해서 pci_(read/write)_config_(byte|word|dword) 함수를 사용할 수 있다.
이 함수들은 모두 성공인 경우에 0 을 리턴하고, 실패인 경우 pcibios_strerror 에 의해 문자열로 변환될 수 있는 에러 코드 (PCIBIOS_... 의 형태) 를 리턴한다.
대부분의 드라이버에서 올바른 PCI 장치에 접근하는 경우에는 실패하지 않을 것이다.
만약 가능한 struct pci_dev 가 없는 경우에는 pci_bus(read|write)_config(byte|word|dword) 함수를 이용해서 해당 버스상의 장치와 function ? 에 접근할 수 있다.
만약 config header 의 standard portion 의 필드에 접근하는 경우에는 <linux/pci.h> 에 선언된 심볼들을 이용하기 바란다.
만약 Extended PCI Capability 레지스터에 접근할 필요가 있는 경우에는 단지 특정한 capability 를 위해 pci_find_capability() 를 호출하라.
그러면 이 함수는 해당하는 레지스터 블럭을 찾아줄 것이다.
6. 주소와 인터럽트 ¶메모리와 포트 주소와 인터럽트 번호는 설정 공간으로 부터 읽혀져서는 안된다.
대신에 커널에 의해 매핑된 struct pci_dev 의 값을 사용해야 한다.
장치의 메모리에 접근하는 방법은 Documentation/IO-mapping.txt 를 참조하기 바란다.
다른 누군가가 같은 장치를 사용하지 않음을 나타내기 위해
I/O 영역에 대해서 request_region() 함수를, 메모리 영역에 대해서 request_mem_region() 함수를 호출할 필요가 있다.
모든 인터럽트 핸들러는 SA_SHIRQ 플래그와 함께 등록되어 있어야 하며, IRQ 와 장치를 매핑하기 위해서 devid 를 사용해야 한다
(모든 PCI 인터럽트는 공유된다는 것을 기억하라).
7. 다른 흥미로운 함수들 ¶
8. 그 밖의 힌트들 ¶(드라이버에서 사용자에게 어떤 카드가 발견되었는지를 보여주고 싶은 경우와 같이) 사용자에게 PCI 슬롯 네임을 보여줄 때에는
pci_name(pci_dev) 를 사용하기 바란다.
PCI 장치를 참조할 경우에는 항상 struct pci_dev 의 포인터를 이용하라.
모든 PCI 레이어 함수들은 이 정보를 사용하며, 이것이 오직 올바른 것이다.
(semantics 가 상당히 복잡한 multiple primary bus 를 사용하는 시스템에서와 같이) 아주 특별한 경우가 아니라면
bus/slot/function 번호를 사용하지 말자.
만약 PCI bus mastering DMA 를 사용하고자 할 경우에는, Documentation/DMA-mapping.txt 를 읽어보기 바란다.
당신이 사용하는 장치에서 Fast Back to Back write 를 켜려고 (turn on) 시도하지 마라.
버스상의 모든 장치들이 이를 지원해야 하므로, 이것은 플랫폼과 일반적인 코드 (generic code) 로 처리할 필요가 있는 것이지
하나의 드라이버 차원에서 다루어 져서는 안된다.
9. 사용되지 않는 함수들 ¶오래된 드라이버를 새로운 PCI 인터페이스로 포팅하려고 할 때 주의해야 할 몇가지 함수들이 있따.
이들은 hotplug 나 PCI domain 이나 locking 에 호환되지 않기 때문에 더이상 커널에 존재하지 않는 것들이다.
|
You will soon meet a person who will play an important role in your life. |











