'Developer's Infos/Linux Programming'에 해당되는 글 9건

  1. 2011.11.06 df 소스
  2. 2011.10.12 PCI 드라이버(PCI-Driver)
  3. 2011.10.10 pci-driver
  4. 2011.10.10 add_wait_queue()/remove_wait_queue()/schedule() 설명 및 사용법
  5. 2011.10.10 __attribute__
/*
* 파일시스템 정보 얻어오기
* /proc/mounts 정보와 statfs를 이용한다. 
*/
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <sys/vfs.h>
#include <sys/stat.h>
#include <sys/types.h>

const char *MMOUNT = "/proc/mounts";

struct f_size
{
    long blocks;
    long avail; 
};

typedef struct _mountinfo 
{
    FILE *fp;                // 파일 스트림 포인터    
    char devname[80];        // 장치 이름
    char mountdir[80];        // 마운트 디렉토리 이름
    char fstype[12];        // 파일 시스템 타입
    struct f_size size;        // 파일 시스템의 총크기/사용율 
} MOUNTP;

MOUNTP *dfopen()
{
    MOUNTP *MP;

    // /proc/mounts 파일을 연다.
    MP = (MOUNTP *)malloc(sizeof(MOUNTP));
    if(!(MP->fp = fopen(MMOUNT, "r")))
    {
        return NULL;
    }
    else
        return MP;
}

MOUNTP *dfget(MOUNTP *MP)
{
    char buf[256];
    char *bname;
    char null[16];
    struct statfs lstatfs;
    struct stat lstat; 
    int is_root = 0;

    // /proc/mounts로 부터 마운트된 파티션의 정보를 얻어온다.
    while(fgets(buf, 255, MP->fp))
    {
        is_root = 0;
        sscanf(buf, "%s%s%s",MP->devname, MP->mountdir, MP->fstype);
         if (strcmp(MP->mountdir,"/") == 0) is_root=1;
        if (stat(MP->devname, &lstat) == 0 || is_root)
        {
            if (strstr(buf, MP->mountdir) && S_ISBLK(lstat.st_mode) || is_root)
            {
                // 파일시스템의 총 할당된 크기와 사용량을 구한다.        
                statfs(MP->mountdir, &lstatfs);
                MP->size.blocks = lstatfs.f_blocks * (lstatfs.f_bsize/1024); 
                MP->size.avail  = lstatfs.f_bavail * (lstatfs.f_bsize/1024); 
                return MP;
            }
        }
    }
    rewind(MP->fp);
    return NULL;
}

int dfclose(MOUNTP *MP)
{
    fclose(MP->fp);
}

int main()
{
    MOUNTP *MP;
    if ((MP=dfopen()) == NULL)
    {
        perror("error");
        return 1;
    }

    while(1)
    {
        while(dfget(MP))
        {
            printf("%-14s%-20s%10lu%10lu\n", MP->mountdir, MP->devname, 
                                MP->size.blocks,
                                MP->size.avail);
        }
        printf("=========================\n\n");
        sleep(1);
    }
}

[출처] df 소스|작성자 포도조아


'Developer's Infos > Linux Programming' 카테고리의 다른 글

PCI 드라이버(PCI-Driver)  (0) 2011.10.12
pci-driver  (0) 2011.10.10
add_wait_queue()/remove_wait_queue()/schedule() 설명 및 사용법  (0) 2011.10.10
__attribute__  (0) 2011.10.10
[Linux] asmlinkage  (0) 2011.09.30
Posted by 삼성동고양이

1 PCI 드라이버의 구조 


2가지 종류의 PCI 드라이버가 존재한다: 신식 (new-style) 드라이버는 장치를 검색 (probe) 하는 대부분의 과정을 PCI 레이어에게 넘겨주었고 장치의 온라인 삽입과 제거를 지원한다. (즉 PCI, hot-pluggable PCI, CardBus 를 하나의 드라이버로 지원한다) 다른 하나는 구식 (old-style) 드라이버인데 모든 장치 검색 과정을 스스로 처리한다. 만약 꼭 그래야 할 이유가 없다면, 새로 작성하는 코드에서 구식의 방식을 이용하여 장치 검색을 수행하지 않도록 하자. 장치가 검색되고 나면 드라이버는 (구식이던 신식이던 간에) 장치를 동작시키고 싶어한다. 그러기 위해서는 다음 과정이 필요하다:

  • 장치를 활성화한다
  • 장치의 설정 공간에 접근한다
  • 장치가 제공하는 자원 (주소와 IRQ 번호) 를 알아낸다
  • 이 자원들을 할당한다
  • 장치와 통신한다 

이들은 대부분 다음 절에서 설명한다. 나머지 부분은 주석이 잘 달려있는 <linux/pci.h> 코드를 보게 될 것이다.

만약 PCI subsystem 이 설정되지 않았다면 (CONFIG_PCI 가 n 값을 가짐) 다음에 소개하는 대부분의 함수들은 내용이 없는 인라인 함수 혹은 드라이버 상의 많은 ifdef 들을 피하기 위한 적절한 에러코드를 리턴하는 함수로 정의될 것이다.

2 신식 (New-style) 드라이버 


신식 드라이버는 초기화 과정에서 단지 다음과 같은 멤버를 포함하는 드라이버를 표현하기 위한 구조체 (struct pci_driver) 의 포인터를 인수로 pci_register_driver 함수를 호출한다.

name 드라이버 이름
id_table 드라이버가 처리하는 디바이스 ID 의 테이블의 포인터. 대부분의 드라이버에서는 이 테이블을 MODULE_DEVICE_TABLE(pci,...) 매크로를 이용해서 공개 (export) 해야 한다. 시스템이 알고있는 모든 PCI 장치들의 probe() 함수를 호출할 때는 NULL 로 설정하라..?
probe ID 테이블과 매치되고 아직 다른 드라이버에 의해 처리되지 않은 모든 장치들에 대한 장치 검색 함수의 포인터 (기존에 존재하던 장치나 이후에 새롭게 추가된 장치에 대하여 pci_register_driver 함수가 실행되는 과정에서 호출됨). 이 함수는 장치를 나타내는 pci_dev 구조체의 포인터와 장치와 매치되는 ID 테이블의 엔트리의 포인터를 인자로 받는다. 장치가 드라이버를 수락하면 (accepted) 0 을 반환하고 그렇지 않으면 (음수값인) 에러 코드를 반환한다. 이 함수는 언제나 process context 에서 호출되기 때문에 sleep 할 수 있다.
remove 이 드라이버에 의해 처리되던 장치가 제거될 때에 호출되는 함수의 포인터 (드라이버의 등록해제 (deregistration) 과정이나 hot-pluggable 슬롯에서 기계적으로 제거한 경우에 호출됨). 이 함수는 언제나 process context 에서 호출되기 때문에 sleep 할 수 있다.
save_state 장치가 대기 모드 (suspend) 로 들어가기 전에 상태 정보를 저장한다.
suspend 장치를 절전 상태 (low-power state) 로 만든다.
resume 장치를 절전 상태에서 깨어나게 한다
enable_wake wake event 를 만들어서 장치를 절전 상태로부터 활성화 시킨다.
(PCI 전원 관리와 그에 관련된 함수들은 Documentation/power/pci.txt 를 참고하기 바란다)

ID 테이블은 마지막이 모두 0 인 엔트리로 끝나는 struct pci_device_id 의 배열이다. 이 구조체는 다음과 같은 멤버들을 가진다:

vendor, device 매치시킬 제품 공급자와 장치 ID (혹은 PCI_ANI_ID 값을 가짐)
subvendor, subdevice 매치시킬 subsystem 의 제품 공급자와 장치 ID (혹은 PCI_ANI_ID 값을 가짐)
class, class_mask 매치시킬 장치의 클래스. class_mask 는 비교시 어떤 비트들을 검사해야 하는지를 나타냄
driver_data 드라이버마다 자체적으로 사용하는 데이터
대부분의 드라이버에서는 driver_data 를 사용할 필요가 없다. driver_data 를 사용하는 가장 좋은 예로는 동일한 장치 타입의 정적인 리스트의 인덱스로 사용하는 것이다 (포인터로 사용하는 것이 아니다).

시스템이 알고있는 모든 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> 에 정의된 매크로 들을 이용하여) 적절히 표시해 주기 바란다:

__init 초기화 코드. 드라이버가 초기화 된 이후에는 사라진다.
__exit 종료 코드. 모듈로 컴파일 되지 않은 드라이버에서는 무시된다.
__devinit 장치 초기화 코드. 커널이 CONFIG_HOTPLUG 가 설정되지 않은 상태로 컴파일 되었다면 __init 와 동일하다. 그렇지 않으면 일반 함수이다.
devexit exit 와 동일하다.
팁: 

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 다른 흥미로운 함수들 


pci_fiind_slot() 주어진 버스와 슬롯 번호에 대응하는 pci_dev 를 찾는다.
pci_set_power_state() PCI Power Management state (0=D0 ... 3=D3) 를 설정한다.
pci_find_capability() 장치의 capability 리스트로 부터 특정한 capability 를 찾는다.
pci_module_init() 올바른 pci_driver 의 초기화와 에러 처리를 도와주는 인라인 함수이다.
pci_resource_start() 주어진 PCI 영역에 해당하는 버스의 시작 주소를 리턴한다.
pci_resource_end() 주어진 PCI 영역에 해당하는 버스의 마지막 주소를 리턴한다.
pci_resource_len() PCI 영역의 바이트 길이를 리턴한다.
pci_set_drvdata() pci_dev 에 대한 private driver data 포인터를 설정한다.
pci_get_drvdata() pci_dev 에 대한 private driver data 포인터를 얻어온다.
pci_set_mwi() Memory-Write-Invalidate 트랜잭션을 활성화한다.
pci_clear_mwi() Memory-Write-Invalidate 트랜잭션을 비활성화한다.

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 에 호환되지 않기 때문에 더이상 커널에 존재하지 않는 것들이다.

pcibios_present(), pci_present() 예전부터, PCI subsystem 과 통신하려고 할 때 그것이 존재하는지 테스트할 필요가 없었다. 만약 존재하지 않는다면 PCI 장치의 리스트는 비어 있을 것이고 장치를 검색하는 모든 함수들은 단지 NULL 을 리턴할 것이다.
pcibios_(read|write)_* 대응되는 pci_(read|write)_* 함수들로 대체되었다.
pcibios_find_* 대응되는 pci_find_* 함수들로 대체되었다.
pci_for_each_dev() pci_find_device() 로 대체되었다.
pci_for_each_dev_reverse() pci_find_device_reverse() 로 대체되었다.
pci_for_each_bus() pci_find_next_bus() 로 대체되었다.
pci_find_device() pci_get_device() 로 대체되었다.
pci_find_subsys() pci_get_subsys() 로 대체되었다.
pcibios_find_class() pci_find_class() 로 대체되었다.
pci_(read|write)_*_nodev() pci_bus_(read|write)_*() 로 대체되었다.


출처 :  http://wiki.kldp.org

'Developer's Infos > Linux Programming' 카테고리의 다른 글

df 소스  (0) 2011.11.06
pci-driver  (0) 2011.10.10
add_wait_queue()/remove_wait_queue()/schedule() 설명 및 사용법  (0) 2011.10.10
__attribute__  (0) 2011.10.10
[Linux] asmlinkage  (0) 2011.09.30
Posted by 삼성동고양이
PCI 의 세계는 광대하고 (별로 즐겁지 않겠지만) 놀라움으로 가득하다. 각각의 PCI 장치들은 서로 다른 요구사항과 버그들을 가진다. 이때문에 리눅스 커널의 PCI 지원 레이어는 우리가 바라는 만큼 간단하지가 않다. 이 짧은 글은 잠재적인 모든 PCI 드라이버 개발자들에게 PCI handling 이라는 깊은 숲속에서 자신 만의 길을 찾아내도록 도와주려고 한다.




1 PCI 드라이버의 구조 


2가지 종류의 PCI 드라이버가 존재한다: 신식 (new-style) 드라이버는 장치를 검색 (probe) 하는 대부분의 과정을 PCI 레이어에게 넘겨주었고 장치의 온라인 삽입과 제거를 지원한다. (즉 PCI, hot-pluggable PCI,CardBus 를 하나의 드라이버로 지원한다) 다른 하나는 구식 (old-style) 드라이버인데 모든 장치 검색 과정을 스스로 처리한다. 만약 꼭 그래야 할 이유가 없다면, 새로 작성하는 코드에서 구식의 방식을 이용하여 장치 검색을 수행하지 않도록 하자. 장치가 검색되고 나면 드라이버는 (구식이던 신식이던 간에) 장치를 동작시키고 싶어한다. 그러기 위해서는 다음 과정이 필요하다:

  • 장치를 활성화한다
  • 장치의 설정 공간에 접근한다
  • 장치가 제공하는 자원 (주소와 IRQ 번호) 를 알아낸다
  • 이 자원들을 할당한다
  • 장치와 통신한다 

이들은 대부분 다음 절에서 설명한다. 나머지 부분은 주석이 잘 달려있는 <linux/pci.h> 코드를 보게 될 것이다.

만약 PCI subsystem 이 설정되지 않았다면 (CONFIG_PCI 가 n 값을 가짐) 다음에 소개하는 대부분의 함수들은 내용이 없는 인라인 함수 혹은 드라이버 상의 많은 ifdef 들을 피하기 위한 적절한 에러코드를 리턴하는 함수로 정의될 것이다.

2 신식 (New-style) 드라이버 


신식 드라이버는 초기화 과정에서 단지 다음과 같은 멤버를 포함하는 드라이버를 표현하기 위한 구조체 (struct pci_driver) 의 포인터를 인수로 pci_register_driver 함수를 호출한다.

name 드라이버 이름
id_table 드라이버가 처리하는 디바이스 ID 의 테이블의 포인터. 대부분의 드라이버에서는 이 테이블을 MODULE_DEVICE_TABLE(pci,...) 매크로를 이용해서 공개 (export) 해야 한다. 시스템이 알고있는 모든 PCI 장치들의 probe() 함수를 호출할 때는 NULL 로 설정하라..?
probe ID 테이블과 매치되고 아직 다른 드라이버에 의해 처리되지 않은 모든 장치들에 대한 장치 검색 함수의 포인터 (기존에 존재하던 장치나 이후에 새롭게 추가된 장치에 대하여 pci_register_driver 함수가 실행되는 과정에서 호출됨). 이 함수는 장치를 나타내는 pci_dev 구조체의 포인터와 장치와 매치되는 ID 테이블의 엔트리의 포인터를 인자로 받는다. 장치가 드라이버를 수락하면 (accepted) 0 을 반환하고 그렇지 않으면 (음수값인) 에러 코드를 반환한다. 이 함수는 언제나 process context 에서 호출되기 때문에 sleep 할 수 있다.
remove 이 드라이버에 의해 처리되던 장치가 제거될 때에 호출되는 함수의 포인터 (드라이버의 등록해제 (deregistration) 과정이나 hot-pluggable 슬롯에서 기계적으로 제거한 경우에 호출됨). 이 함수는 언제나 process context 에서 호출되기 때문에 sleep 할 수 있다.
save_state 장치가 대기 모드 (suspend) 로 들어가기 전에 상태 정보를 저장한다.
suspend 장치를 절전 상태 (low-power state) 로 만든다.
resume 장치를 절전 상태에서 깨어나게 한다
enable_wake wake event 를 만들어서 장치를 절전 상태로부터 활성화 시킨다.
(PCI 전원 관리와 그에 관련된 함수들은 Documentation/power/pci.txt 를 참고하기 바란다)

ID 테이블은 마지막이 모두 0 인 엔트리로 끝나는 struct pci_device_id 의 배열이다. 이 구조체는 다음과 같은 멤버들을 가진다:

vendor, device 매치시킬 제품 공급자와 장치 ID (혹은 PCI_ANI_ID 값을 가짐)
subvendor, subdevice 매치시킬 subsystem 의 제품 공급자와 장치 ID (혹은 PCI_ANI_ID 값을 가짐)
class, class_mask 매치시킬 장치의 클래스. class_mask 는 비교시 어떤 비트들을 검사해야 하는지를 나타냄
driver_data 드라이버마다 자체적으로 사용하는 데이터
대부분의 드라이버에서는 driver_data 를 사용할 필요가 없다. driver_data 를 사용하는 가장 좋은 예로는 동일한 장치 타입의 정적인 리스트의 인덱스로 사용하는 것이다 (포인터로 사용하는 것이 아니다).

시스템이 알고있는 모든 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> 에 정의된 매크로 들을 이용하여) 적절히 표시해 주기 바란다:

__init 초기화 코드. 드라이버가 초기화 된 이후에는 사라진다.
__exit 종료 코드. 모듈로 컴파일 되지 않은 드라이버에서는 무시된다.
__devinit 장치 초기화 코드. 커널이 CONFIG_HOTPLUG 가 설정되지 않은 상태로 컴파일 되었다면 __init 와 동일하다. 그렇지 않으면 일반 함수이다.
devexit exit 와 동일하다.
팁: 

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 다른 흥미로운 함수들 


pci_fiind_slot() 주어진 버스와 슬롯 번호에 대응하는 pci_dev 를 찾는다.
pci_set_power_state() PCI Power Management state (0=D0 ... 3=D3) 를 설정한다.
pci_find_capability() 장치의 capability 리스트로 부터 특정한 capability 를 찾는다.
pci_module_init() 올바른 pci_driver 의 초기화와 에러 처리를 도와주는 인라인 함수이다.
pci_resource_start() 주어진 PCI 영역에 해당하는 버스의 시작 주소를 리턴한다.
pci_resource_end() 주어진 PCI 영역에 해당하는 버스의 마지막 주소를 리턴한다.
pci_resource_len() PCI 영역의 바이트 길이를 리턴한다.
pci_set_drvdata() pci_dev 에 대한 private driver data 포인터를 설정한다.
pci_get_drvdata() pci_dev 에 대한 private driver data 포인터를 얻어온다.
pci_set_mwi() Memory-Write-Invalidate 트랜잭션을 활성화한다.
pci_clear_mwi() Memory-Write-Invalidate 트랜잭션을 비활성화한다.

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 에 호환되지 않기 때문에 더이상 커널에 존재하지 않는 것들이다.

pcibios_present(), pci_present() 예전부터, PCI subsystem 과 통신하려고 할 때 그것이 존재하는지 테스트할 필요가 없었다. 만약 존재하지 않는다면 PCI 장치의 리스트는 비어 있을 것이고 장치를 검색하는 모든 함수들은 단지 NULL 을 리턴할 것이다.
pcibios_(read|write)_* 대응되는 pci_(read|write)_* 함수들로 대체되었다.
pcibios_find_* 대응되는 pci_find_* 함수들로 대체되었다.
pci_for_each_dev() pci_find_device() 로 대체되었다.
pci_for_each_dev_reverse() pci_find_device_reverse() 로 대체되었다.
pci_for_each_bus() pci_find_next_bus() 로 대체되었다.
pci_find_device() pci_get_device() 로 대체되었다.
pci_find_subsys() pci_get_subsys() 로 대체되었다.
pcibios_find_class() pci_find_class() 로 대체되었다.
pci_(read|write)_*_nodev() pci_bus_(read|write)_*() 로 대체되었다.



 출처 : http://wiki.kldp.org/wiki.php
 

'Developer's Infos > Linux Programming' 카테고리의 다른 글

df 소스  (0) 2011.11.06
PCI 드라이버(PCI-Driver)  (0) 2011.10.12
add_wait_queue()/remove_wait_queue()/schedule() 설명 및 사용법  (0) 2011.10.10
__attribute__  (0) 2011.10.10
[Linux] asmlinkage  (0) 2011.09.30
Posted by 삼성동고양이

1
vim:ft=cpp
2
3 @ 개요 4 wait_queue 설명 5 6 @ wait_queue 7 어떤 일이 발생할때 깨어날 task(process)를 저장하는 queue. 8 9 동일한 커널함수(코드)가 여러 task에 의해 실행될수 있어 어떤 조건을 10 만족할때까지 여러개의 task를 보류했다가 조건 발생시 task를 실행하기 위해 사용. 11 12 A constext에서 schdule()를 호출하게 되면 context는 다른 task로 넘어가게 되는데 13 A를 어떤일이 발생하면 다시 실행(TASK_RUNNING)으로 만들기 위해 wait queue를 사용한다. 14 15 A context에서 schedule()를 호출하기전 자신을 깨어날 조건 (TASK_INTERRUPTIBLEE등) 16 으로 변경한후 wait_queue 에 추가한다. 그리고 schedule()를 호출하여 context를 17 다른 task로 넘긴다. 다른 task실행중 A task가 깨어날 조건 (interrupt등)이 발생되면 18 관련함수(코드)가 실행되고 이곳에서 wake_up_xxx()함수를 호출하여 wait_queue의 19 task를 깨운다(TASK_RUNNING으로 변경). 20 그러면 A task의 schedule()아래 코드부터 다시 실행된다. 21 이때 주의할것이 22 - A task의 schedule()아래에서 remove_wait_queue()를 호출하여 23 wait_queue에서 자신을 (A task)를 제거해야 한다. 24 25 - add_wait_queue(), remove_wait_queue(), wake_up_xxx()에 인자로 넘어가는 26 wait_queue는 동일한 instance이다. 27 28 @ 용법 29 30 - wait 시작하는 함수 31 DECLARE_WAITQUEUE(wait, current); 32 add_wait_queue(&gQName, &wait); 33 set_current_state(TASK_INTERRUPTIBLEE); 34 schedule_timeout(timeout); 35 current->state = TASK_RUNNING; 36 remove_wait_queue(&gQName, &wait); 37 38 void tty_wait_until_sent(struct tty_struct * tty, long timeout) 39 { 40 DECLARE_WAITQUEUE(wait, current); 41 42 ... 43 add_wait_queue(&tty->write_wait, &wait); 44 if (!timeout) 45 timeout = MAX_SCHEDULE_TIMEOUT; 46 do { 47 ... 48 set_current_state(TASK_INTERRUPTIBLE); 49 ... 50 timeout = schedule_timeout(timeout); 51 } while (timeout); 52 if (tty->driver.wait_until_sent) 53 tty->driver.wait_until_sent(tty, timeout); // <- wait에서 깨어나서 해야할 일 54 stop_waiting: 55 current->state = TASK_RUNNING; 56 remove_wait_queue(&tty->write_wait, &wait); 57 } 58 59 - wait를 깨우는 함수 60 wake_up_interruptible(&gQName); 61 62 /* 63 * This can be called through the "tq_scheduler" 64 * task-list. That is process synchronous, but 65 * doesn't hold any locks, so we need to make 66 * sure we have the appropriate locks for what 67 * we're doing.. 68 */ 69 void do_tty_hangup(void *data) 70 { 71 ... 72 wake_up_interruptible(&tty->write_wait); 73 wake_up_interruptible(&tty->read_wait); 74 ... 75 } 76 77 @ wake_up_interruptible() 78 wait_queue list에서 TASK_INTERRUPTIBLEE로 설정된 79 task를 TASK_RUNNING로 변경 하고 add_to_runqueue()호출 80 81 #define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) 82 #define wake_up_all(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE) 83 #define wake_up_sync(x) __wake_up_sync((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) 84 #define wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) 85 #define wake_up_interruptible_all(x) __wake_up((x),TASK_INTERRUPTIBLE) 86 #define wake_up_interruptible_sync(x) __wake_up_sync((x),TASK_INTERRUPTIBLE | TASK_EXCLUSIVE) 87 88 void __wake_up(wait_queue_head_t *q, unsigned int mode) 89 { 90 __wake_up_common(q, mode, 0); 91 } 92 93 static inline void __wake_up_common(wait_queue_head_t *q, unsigned int mode, const int sync) 94 { 95 struct list_head *tmp, *head; 96 struct task_struct *p; 97 unsigned long flags; 98 ... 99 list_for_each(tmp, head) { 100 unsigned int state; 101 wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list); 102 103 #if WAITQUEUE_DEBUG 104 CHECK_MAGIC(curr->__magic); 105 #endif 106 p = curr->task; 107 state = p->state; 108 if (state & (mode & ~TASK_EXCLUSIVE)) { 109 #if WAITQUEUE_DEBUG 110 curr->__waker = (long)__builtin_return_address(0); 111 #endif 112 if (sync) 113 wake_up_process_synchronous(p); 114 else 115 wake_up_process(p); 116 if (state & mode & TASK_EXCLUSIVE) 117 break; 118 } 119 } 120 wq_write_unlock_irqrestore(&q->lock, flags); 121 out: 122 return; 123 } 124 125 /* 126 * Wake up a process. Put it on the run-queue if it's not 127 * already there. The "current" process is always on the 128 * run-queue (except when the actual re-schedule is in 129 * progress), and as such you're allowed to do the simpler 130 * "current->state = TASK_RUNNING" to mark yourself runnable 131 * without the overhead of this. 132 */ 133 inline void wake_up_process(struct task_struct * p) 134 { 135 unsigned long flags; 136 137 /* 138 * We want the common case fall through straight, thus the goto. 139 */ 140 spin_lock_irqsave(&runqueue_lock, flags); 141 p->state = TASK_RUNNING; 142 if (task_on_runqueue(p)) 143 goto out; 144 add_to_runqueue(p); 145 reschedule_idle(p, flags); // spin_unlocks runqueue 146 147 return; 148 out: 149 spin_unlock_irqrestore(&runqueue_lock, flags); 150 } 151 152 /* 153 * Careful! 154 * 155 * This has to add the process to the _beginning_ of the 156 * run-queue, not the end. See the comment about "This is 157 * subtle" in the scheduler proper.. 158 */ 159 static inline void add_to_runqueue(struct task_struct * p) 160 { 161 list_add(&p->run_list, &runqueue_head); 162 nr_running++; 163 }

[출처] wait Queue|작성자 달려라


'Developer's Infos > Linux Programming' 카테고리의 다른 글

PCI 드라이버(PCI-Driver)  (0) 2011.10.12
pci-driver  (0) 2011.10.10
__attribute__  (0) 2011.10.10
[Linux] asmlinkage  (0) 2011.09.30
[Shared Memory] shmget() 공유 메모리 생성  (0) 2011.09.06
Posted by 삼성동고양이

__attribute__는 GCC 컴파일러에 있는 메커니즘으로 컴파일러에게 추가적인 에러 체킹 같은 것을 하도록 지시하는 것이다.

형식 : __attribute__(x)
괄호 안의 x에는 가능한 몇 가지들이 있는데 특히, packed는 채우라는 의미이다:
typedef struct
{
    char a;
    int b;
} myST;

위 구조체의 sizeof(myST)는 8이다.
상식적으로 char =1byte, int = 4byte이므로 5가 나와야 하는데.. 
이런 문제 때문에 구조체를 가지고 작업할 경우 예상치 못한 버그로 고생을 가끔한다.
이런 현상이 생기는 이유는 현재 사용하고 있는 컴퓨터가 32비트이기 때문이다. 즉, CPU가 메모리 어드레스를 지정할 때 4바이트(32비트/8) 단위가 내부적으로 가장 최적화되어 빠른 데이터 접근을 가능하게 한다. 4바이트 배수 단위가 아닌 경우 당연히 접근은 가능하지만 속도 차이가 있어 디폴트로 저런 접근을 하도록 해 둔 것이다.

그럼, 메모리에 저 구조체가 잡힐 경우 어떻게 존재하느냐는 4바이트 단위로 채워지기 때문에 char a;는 1바이트이지만 4바이트를 할당해서 거기다 char a;를 담아둔다. 다음 int b;는 4바이트므로 그냥 4바이트를 할당한다. 이렇게 해서 8바이트의 크기가 필요한 것이다.

첫 char a;에서 1바이트를 제외한 나머지 3바이트는 어떤 걸로 채워질까? 의미없는 값이 채워진다. 그냥 공간만 예약되는 것이다. 그래서 이런 현상을 방지할 수 있도록 컴파일러는 __attribute__ (packed) 메커니즘을 두어서 우리가 예상한 (1바이트+4바이트) 크기가 되도록 빌드한다:
typedef struct
{
    char a;
    int b;
__attribute__((packed)) myST;

결국, 위 처럼 사용하게되면 컴파일러는 구조체 멤버의 실제 크기 만큼 할당한다. 그래서 정확한 5바이트가 나오게 된다. 물론, 속도는 약간 더 늦어진다.

여기에서 __attribute__라는 의미는 컴파일러에게 특성을 주겠다는 것을 의미하며 괄호 안은 여러 가지가 가능한데 여기서는 그 중 한가지인 구조체 정렬을 사용하겠다는 의미인 packed를 사용했다.

packed의 의미는 꽉 채우다라는 것인데 이 기능을 사용하지 않았을 경우 char a;는 실제 1바이트 외에 나머지 3바이트가 자리만 차지한 채 남아있다. 이런 비어있는 것들을 제거해서 구조체 멤버들이 빈 자리없이 꽉 채우라는 단어 의미이다. 이런 것을 구조체 정렬(alignment)이라고 한다.

위는 unix/linux의 GCC에서 사용하는 것이고 윈도우 상의 비주얼 씨의 경우는
#pragma pack(1)

처럼 사용한다. 괄호 안의 의미는 1바이트 단위까지 체크해서 빈 자리가 없도록 차곡차곡 채우라는 것이다. 4가 올 수도 있겠지만 그렇게 되면 가장 작은 단위가 4가되어 char 형 같은 경우 역시 4로 채워지게 되어 낭패이다. 따라서 확실한 경우 아니면 위 처럼 사용하면 된다.

출처: 지식IN(edson)

'Developer's Infos > Linux Programming' 카테고리의 다른 글

pci-driver  (0) 2011.10.10
add_wait_queue()/remove_wait_queue()/schedule() 설명 및 사용법  (0) 2011.10.10
[Linux] asmlinkage  (0) 2011.09.30
[Shared Memory] shmget() 공유 메모리 생성  (0) 2011.09.06
PCI Driver Flow  (0) 2011.09.05
Posted by 삼성동고양이
이전버튼 1 2 이전버튼