'전체'에 해당되는 글 29건

  1. 2011.11.06 ls 소스
  2. 2011.11.06 df 소스
  3. 2011.10.12 PCI 드라이버(PCI-Driver)
  4. 2011.10.10 pci-driver
  5. 2011.10.10 add_wait_queue()/remove_wait_queue()/schedule() 설명 및 사용법
2011. 11. 6. 16:36
//*********************************************************************
//		My ls Ver 0.1.0
//2007   Made By Paek Jae-Dong
//E-mail: pjd@kw.ac.kr
//Blog: rabe.egloos.com
//*********************************************************************


#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <term.h>

#define PATH_LENGTH 	200

//옵션에대한 값을 지정
#define FLAG_ALL 	0x01
#define FLAG_INODE 	0x02
#define FLAG_LONG 	0x04
#define FLAG_RECUR	0x08
#define FILEINFO 	struct file_info

//파일길이를 지정
#define FILENAME_SIZE	256

struct file_info{
	char filename[FILENAME_SIZE+1];
	unsigned long inode;
	unsigned long mode;
	unsigned long nlink;
	unsigned long uid;
	unsigned long gid;
	unsigned long size;
	//마지막 수정시간 출력
	time_t atime;  
	//이 파일이 차지하는 block수
	unsigned long blocks;
	struct file_info *next;
};


void seek_dir(char *dir,int opt);	//디렉토리를 순회하는 함수
void print_long(FILEINFO *list_head,int opt);	//-l옵션이 있을때 화면에 파일정보를 출력함

//-l옵션이 있을때 파일정보를 링크드리스트에 저장하는 함수
void save_long(FILEINFO **list_head,struct stat *cur_stat,struct dirent *cur_dir,int opt);	

void print_normal(FILEINFO *list_head,int opt);	//-l 옵션이 없을때 화면에 파일정보를 출력하는 함수

//-l 옵션이 없을때 파일정보를 링크드리스트에 저장하는 함수
void save_normal(FILEINFO **list_head,struct stat *cur_stat,struct dirent *cur_dir,int opt);

void free_list(FILEINFO *list_head);	//링크드리스트의 메모리를 해제하는 함수

void sort_list(FILEINFO *list_head);	//링크드리스트에 저장된 자료를 파일이름순으로 정렬하는 함수
int long_len(unsigned long num);	//unsigned long 값을 입력받아 자리수를 리턴하는 함수

int main(int argc,char* argv[])
{
	int i,j;
	int opt=0;	//옵션을 저장하는 변수
	char path[PATH_LENGTH]="./"; // 실행시 path옵션을 안주었을때의 디폴트 경로값
	
	
	for(i=1;i<argc;i++)
	{
		for(j=1;argv[i][j] != '\0';j++)
		{
			if(argv[i][0] != '-')	//옵션이 아니고 path 정보일때
			{
				strncpy(path,argv[i],PATH_LENGTH-1);
				break;
			}
			switch(argv[i][j])	//실제 ls에서는 각 옵션의 대소문자를 구분함
			{
			case 'a':
				opt |= FLAG_ALL;
				break;
			case 'l':
				opt |= FLAG_LONG;
				break;
			case 'i':
				opt |= FLAG_INODE;
				break;
			case 'R':	
				opt |= FLAG_RECUR;
				break;
			}
		}
	}
	
	if(path[strlen(path)-1] != '/')
	{
		sprintf(path+strlen(path),"/");
	}
	printf("%c[1;31m",27);  //색변경
	printf("\ndirectory path:");
	seek_dir(path,opt);		//디렉토리를 순회함
	
	
	printf("%c[0m\n",27);  //색변경
	return 0;
}

int long_len(unsigned long num)	//화면에 정렬하여 출력하기위하여 칸수를 계산
{
	int nCount=0;
	while( num%10 || num/10 ){
		num /=10;
		nCount++;
	}
	
	return nCount;
}

void free_list(FILEINFO *list_head){
	FILEINFO *tmp_list;
	
	while(list_head != NULL){     //메모리 해제하기
		tmp_list = list_head;
		list_head = list_head->next;
		free(tmp_list);
	}
}

//링크드리스트에 들어있는자료를 파일이름순으로 정렬
void sort_list(FILEINFO *list_head){	
	FILEINFO *tmp_list_left;
	FILEINFO *tmp_list_right;
	
	FILEINFO tmp_list;
	FILEINFO *tmp_listp;
	
	
	if( list_head == NULL )
		return ;
	
	if( list_head->next == NULL)
		return ;
	
	tmp_list_left = list_head;
	tmp_list_right = list_head->next;
	
	
	while( tmp_list_left->next != NULL){
		
		while(tmp_list_right != NULL){
			
			//두개의 리스트를 서로 exchange하는 코드
			if( strcmp(tmp_list_left->filename,tmp_list_right->filename) >0 ){	
				memcpy(&tmp_list , tmp_list_left,sizeof(FILEINFO));
				memcpy(tmp_list_left , tmp_list_right,sizeof(FILEINFO));
				memcpy(tmp_list_right , &tmp_list,sizeof(FILEINFO));
				tmp_listp = tmp_list_left->next;
				tmp_list_left->next = tmp_list_right->next;
				tmp_list_right->next = tmp_listp;
			}
			
			tmp_list_right = tmp_list_right->next;
		}
		tmp_list_left = tmp_list_left->next;
		tmp_list_right = tmp_list_left->next;
	}
	
}

void seek_dir(char *dir,int opt)
{
	DIR *dp;
	struct dirent *entry;
	struct stat tmp_stat;
	FILEINFO *tmp_list;
	FILEINFO *list_head;
	
	list_head = NULL;
	
	printf("%c[1;31m",27);	//색변경
	printf("%s\n",dir);	//경로를 출력
	
	if((dp = opendir(dir)) == NULL){	//디렉토리 스트림을 Open함
		fprintf(stderr,"directory open error: %s\n",dir);
		return;
	}
	
	chdir(dir);
	
	while((entry = readdir(dp)) != NULL){	//디렉토리정보를 한개씩 읽어옴
		lstat(entry->d_name, &tmp_stat);
		if( opt & FLAG_LONG)	//옵션에 따라 파일정보를 저장하는 방식을 달리함
			save_long(&list_head,&tmp_stat,entry,opt);
		else
			save_normal(&list_head,&tmp_stat,entry,opt);
	}
	
	
	sort_list(list_head);	//읽어온 자료를 정렬함
	
	
	if( opt & FLAG_LONG)	//옵션에 따라 파일정보를 출력하는 방식을 달리함
		print_long(list_head,opt);
	else
		print_normal(list_head,opt);
	
	
	tmp_list = list_head;
	
	
	while( tmp_list != NULL) {
		if(S_ISDIR(tmp_list->mode)){
			
			// '.' 과 '..'은 재귀 에서 배제함
			if( strcmp(".",tmp_list->filename) == 0 || strcmp("..",tmp_list->filename) == 0 ){
				tmp_list = tmp_list->next;
				continue;
			}
			if( opt & FLAG_RECUR){          //재귀 옵션이 있을때만 실행
				
				printf("%c[1;31m",27);  //색변경
				printf("\n\ndirectory path:%s",dir);
				
				seek_dir(tmp_list->filename,opt);
			}
		}
		
		tmp_list = tmp_list->next;
		
	}
	chdir("..");
	
	closedir(dp);
	free_list(list_head);	//파일정보가 저장된 메모리를 해제함
}
void save_long(FILEINFO **list_head,struct stat *cur_stat,struct dirent *cur_dir,int opt)
{
	FILEINFO *cur_list=(*list_head);
	
	if( *list_head != NULL)
		while( cur_list->next != NULL)
			cur_list = cur_list->next;
        
        if( cur_dir->d_name[0] == '.' )
			if( !(opt & FLAG_ALL) )
				return; //all옵션이 없으면 .으로 시작하는 파일은 스킵힌다. 
			
			
			if( (*list_head) == NULL){   //리스트의 맨처음일때
                cur_list = (FILEINFO *)malloc(sizeof(FILEINFO));
                cur_list->next = NULL;
                *list_head = cur_list;
			}else{
                cur_list->next = (FILEINFO *)malloc(sizeof(FILEINFO));
				cur_list = cur_list->next;
                cur_list->next = NULL;
			}
			
			
			cur_list->inode = cur_stat->st_ino;
			cur_list->mode = cur_stat->st_mode;
			strcpy(cur_list->filename ,cur_dir->d_name);
			
			//hard-link 수
			cur_list->nlink = cur_stat->st_nlink;
			
			//User ID  
			cur_list->uid = cur_stat->st_uid;
			
			//Group ID
			cur_list->gid = cur_stat->st_gid;
			
			//File Size 
			cur_list->size = cur_stat->st_size;
			
			//마지막 수정시간 
			cur_list->atime = cur_stat->st_atime;   //시간값을 분석하여 구조체에 저장
			
			//이 파일이 차지하는 block수
			cur_list->blocks = cur_stat->st_blocks;
			
}               

void print_long(FILEINFO *list_head,int opt)
{
	FILEINFO *cur_list;
	unsigned long tmp_perm;
	struct tm *tm_ptr;
	int i;
	
	cur_list = list_head;
	
	while(cur_list != NULL){
		tmp_perm = cur_list->mode;
		
		
		//File Type에따라서 출력할 색을 달리함
		if(S_ISREG(cur_list->mode)){
			if(cur_list->mode & 01001001)	//실행파일인지 검사
				printf("%c[1;32m",27);
			else
				printf("%c[0m",27);
			
			printf("REG  ");
		}else if(S_ISDIR(cur_list->mode)){
			printf("%c[1;34m",27);
			printf("DIR  ");
		}else if(S_ISCHR(cur_list->mode)){
			printf("%c[1;37m",27);
			printf("CHR  ");
		}else if(S_ISBLK(cur_list->mode)){
			printf("%c[1;33m",27);
			printf("BLK  ");
		}else if(S_ISFIFO(cur_list->mode)){
			printf("%c[1;37m",27);		
			printf("FIFO ");
		}else if(S_ISLNK(cur_list->mode)){
			printf("%c[1;36m",27);
			printf("LNK  ");
		}else if(S_ISSOCK(cur_list->mode)){
			printf("%c[1;35m",27);
			printf("SOCK ");
		}
		
		//30->기본색 밝은 회색
		//31->밝은 빨강
		//32->실행파일 녹색
		//33-> 노랑
		//34->디렉토리 밝은파랑
		//35->연보라
		//36-> 밝은 CYAN 링크
		//37->밝은 흰색
		
		if(opt & FLAG_INODE)
			printf("%u ",(unsigned int)cur_list->inode);	
		
		//Permission 출력
		for(i=0;i<3;i++)
		{
			if(tmp_perm & S_IRUSR)
				printf("r");
			else
				printf("-");
			
			if(tmp_perm & S_IWUSR)
				printf("w");
			else
				printf("-");
			
			if(tmp_perm & S_IXUSR)
				printf("x");
			else
				printf("-");
			
			tmp_perm <<=3;
		}
		
		//hard-link 수 출력
		printf(" %2u",(unsigned int)cur_list->nlink);
		
		//User ID출력
		printf(" %5u",(unsigned int)cur_list->uid);
		
		//Group ID출력
		printf(" %5u",(unsigned int)cur_list->gid);
		
		//File Size 출력
		printf(" %12u",(unsigned int)cur_list->size);
		
		//마지막 수정시간 출력
		tm_ptr = gmtime(&cur_list->atime);	//시간값을 분석하여 구조체에 저장
		printf(" %02d/%02d/%2d %02d:%02d",tm_ptr->tm_year%100,tm_ptr->tm_mon,tm_ptr->tm_mday,tm_ptr->tm_hour,tm_ptr->tm_min);
		
		//이 파일이 차지하는 block수
		printf("%5u",(unsigned int)cur_list->blocks);
		
		//파일이름 출력
		printf(" %s",cur_list->filename);
		
		
		printf("\n");
		cur_list = cur_list->next;
	}
}

void save_normal(FILEINFO **list_head,struct stat *cur_stat,struct dirent *cur_dir,int opt)
{
	FILEINFO *cur_list=*list_head;
	
	if( *list_head != NULL)
		while( cur_list->next != NULL)
			cur_list = cur_list->next;
		
		
		if( cur_dir->d_name[0] == '.')
			if( !(opt & FLAG_ALL) )
				return; //all옵션이 없으면 .과 ..은 스킵한다.
			
			
			if( (*list_head) == NULL){	//리스트의 맨처음일때
				cur_list = (FILEINFO *)malloc(sizeof(FILEINFO));
				cur_list->next = NULL;
				*list_head = cur_list;
			}else{
				cur_list->next = (FILEINFO *)malloc(sizeof(FILEINFO));
				cur_list = cur_list->next;
				cur_list->next = NULL;
			}
			
			cur_list->inode	= cur_stat->st_ino;
			cur_list->mode = cur_stat->st_mode;
			strcpy(cur_list->filename ,cur_dir->d_name);
			
			
}

void print_normal(FILEINFO *list_head,int opt)
{
	FILEINFO *cur_list;
	int i,j;
	int col_length_name[15];	//정렬을위해 칸 너비를 저장하는 배열
	int col_length_inode[15];
	int col_num=0;
	int nCount;
	int index;
	int term_x,term_y;	//터미널의 크기를 읽어오는 함수
	
	for(i=0;i<15;i++){
		col_length_name[i] = 0;
		col_length_inode[i] = 0;
	}
	
	
	//터미널 화면의 크기 정보를 얻어옴
	setupterm(NULL,fileno(stdout),(int *)0);
	term_y = tigetnum("lines");	//현재 터미널의 크기를 얻어온다.
	term_x = tigetnum("cols");
	
	
	for(j=15;j>0;j--){
		cur_list = list_head;
		index  =0;
		nCount = 0;
		
		//파일 정보 출력시 세로정렬을 위한 최적의 칸 너비를 계산하는  코드부분
		while(cur_list != NULL){	
			if( col_length_name[index%j] < strlen(cur_list->filename) )
				col_length_name[index%j] = strlen(cur_list->filename);
			
			if( opt & FLAG_INODE){
				if( col_length_inode[index%j] < long_len(cur_list->inode) )
					col_length_inode[index%j] =long_len(cur_list->inode);
			}
			
			nCount = 0;
			for(i=0;i<j;i++){
				nCount += col_length_name[i]+2;
				if( opt & FLAG_INODE)
					nCount += col_length_inode[i]+1;
			}
			
			if(term_x < nCount)	
				break;
			
			nCount = 0;

			index ++;
			index = index %j;
			cur_list = cur_list->next;
		}
		if(cur_list == NULL){
			col_num = j;
			break;
		}

	}
	
	cur_list = list_head;
	index = 0;
	
	while(cur_list != NULL){
		if(S_ISREG(cur_list->mode)){
			if(cur_list->mode & 01001001) //실행파일인지 검사
				printf("%c[1;32m",27);
			else
				printf("%c[0m",27);
			
		}else if(S_ISDIR(cur_list->mode)){
			printf("%c[1;34m",27);
		}else if(S_ISCHR(cur_list->mode)){
			printf("%c[1;37m",27);
		}else if(S_ISBLK(cur_list->mode)){
			printf("%c[1;33m",27);
		}else if(S_ISFIFO(cur_list->mode)){
			printf("%c[1;37m",27);
		}else if(S_ISLNK(cur_list->mode)){
			printf("%c[1;36m",27);
		}else if(S_ISSOCK(cur_list->mode)){ 
			printf("%c[1;35m",27);
		}
		
		if( opt & FLAG_INODE )
			printf(" %*u",col_length_inode[index],(unsigned int)cur_list->inode);
		
		printf(" %*s ",col_length_name[index],cur_list->filename);
		cur_list = cur_list->next;
		if( index == col_num -1)
			printf("\n");
		index ++;
		index = index % col_num;
		
	}
}
Posted by 삼성동고양이
/*
* 파일시스템 정보 얻어오기
* /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 삼성동고양이
이전버튼 1 2 3 4 5 6 이전버튼