2011년 6월 15일 수요일

[번역] V4L2 - 드라이버 개발자 가이드

[ 출처 : http://www.thedirks.org/v4lw/v4l2dwg.htm ]

Video for Linux Two - Driver Writer's Guide

Bill Dirks - December 23, 1999

소개

 모든 기능을 제공하는 API spec이 있는데 왜 이 문서가 필요 한가? 하면 API spec이 단지 사용자 모드의 호출만 다루고 있고 커널 모드 코드에만 연관된 V4L2의 측면만 다루고 있기 때문이다. 이 문서의 목적은 어떻게 V4L2 드라이버 모듈을 시스템에 이식하는 방법과 V4L2 커널모드 API에 대한 문서, 디바이스 드라이버 구현 이슈에 대한 논쟁, 그리고 드라이버 개발자에게 유용한 다른 종류의 추가적인 문서를 제공하기 위해서이다.

 이 문서는 읽는이가 V4L2의 사용자 모드 API spec과 리눅스 드라이버 프로그래밍에 관련된 주제에 익숙하다는 가정하에 작성되었다. Allessandro Rubini의 Linux Device Drivers (ISBN 1-56592-292-1)이 기초적인 참조도서가 될 수 있을 것이다.

V4L2 드라이버와 videodev

 V4L2는 두 개의 계층으로 이루어진 드라이버 시스템이다. 상위 계층은 videodev 모듈이다. videodev가 초기화 될때, char major device 81으로 등록되고 char device 드라이버의  method 함수 모음을 등록한다. 모든 V4L2 드라이버들은 실제로는 videodev의 client에 해당되고 videodev는 V4L2 드라이버의 method 함수를 이용하여 client 드라이버들을 호출한다. V4L2 드라이버가 초기화 될때, V4L2 드라이버의 method 함수, minor device number, 그리고 다른 세세한 것들을 포함하는 구조체를 videodev에 전달해서 videodev에서 다루어져야할 모든 디바이스들이 등록된다. V4L2 method 함수들은 Linux의 전형적인 char 타입의 driver의 형태와 유사하지만, V4L2 드라이버를 위해 특별히 추가되는 다른 매겨변수들을 가지고 있다. 응용프로그램이 드라이버를 호출하면, 그 제어권이 먼저 videodev의 method함수에 넘어가고 method 함수는 같이 넘어오는 파일이나 inode의 구조체의 포인터를 해당 V4L2 구조체의 포인터로 변경하고 그 구조체로 부터 V4L2 드라이버의 method 함수를 호출한다. 이와 같이, videodev는 V4L2 드라이버들을 감싸고 있는 얇은 막처럼 작동한다. videodev는 모듈형태로 구현되고 모든 V4L2 드라이버들 또한 모듈이다.

 videodev 모듈은 또한 V4L2 드라이버 작성자이 유용하게 찾아 볼 수 있는 도움함수들을 여럿 포함하고 있다. 예를 들면, 몇 가지 단순한 queue 관리, file 구조체의 포인터를  그에 해당하는 V4L2 구조체로 변경하고 메모리 mapping(할당?) 도움함수 등이 그에 해당한다.

드라이버 등록과 method 함수의 호출

 드라이버가 초기화를 될때 시스템이 앞으로 다루어야 할 디바이스를 일일이 탐색하고, 각 디바이스를 위해서 각각의 독자적인 struct v4l2_device 구조체를 설정하고  이 구조체의 포인터는 v4l2_register_device()에 전달된다. 그리고 v4l2_register_device()는 넘어온 minor 숫자가 사용 가능한지 확인할 것이고, struct v4l2_device의 initialize() method 함수를 호출한다. initialize() 함수에는 v4l2_register_device()에 전달되었던 디바이스 포인터도 넘겨진다. 만약 initialize() 함수가 성공적으로 실행되면, 드바이스의 등록이 완성된다. 그럼 videodev는 내부의 테이블에 이 디바이스 포인터를 저장하게 될 것이고, 이 드라이버가 응용프로그램에 의해서 호출될때 그에 해당하는 method 함수들을 호출한다.

 v4l2_register_device()를 호출하기전에 해당 드라이버는 name, type, minor fields와 open() method 함수를 반드시 채워야한다. 하지만, 다른 method 함수들과 fields들은 선택사항이다. 여기서, type field는 struct v4l2_capability구조체의 type field와 동일한 값으로 사용되고, 그 값은 V4L2_TYPE_의 첨두어로 이루어지는 형태를 띄게된다. method 함수들의 field들은 드바이스가 등록된 이후에도 변경이 가능하다. 그리고 드라이버가 등록 해제될때, 드라이버 포인터와 함께 v4l2_unregister_device()함수를 호출한다.

struct v4l2_device
char name[32] 이 디바이스를 위한 정형화된 이름 (이 name은 /proc/videodev 파일에 나타나게 될 것이다.) int type V4L2 디바이스의 타입 (이것 또한 /proc/videodev에 나타난다.) int minor 디바이스의 minor number int (*open)() 새로운 file descriptor가 open 될때 호출됨 int (*close)() file descriptor의 마지막 close(release)시 호출됨 int (*read)() read()시 호출됨 int (*write)() write()시 호출됨 int (*ioctl)() ioctl()시 호출됨 int (*mmap)() mmap()시 호출됨 int (*poll)() select()시 호출됨 int (*initialize)() 디바이스 등록시 호출됨 void *priv 드라이버에 의해서 특수한 목적으로 사용되어 질 수 있음 int busy videodev에 의해서 관리되는 이 디바이스의 open count(몇번 open되었는지?)

 디자인 노트 : 일반적으로 드라이버들은 내부적으로  struct v4l2_device 구조체로 시작되는 훨씬 넓은 영역의 드바이스 구조체를 설정할 것이고, 그 뒷 부분에는 특별한 디바이스에 관련되는 모든 드라이버와 디바이스의 상태 정보가 위치하게 된다. 이런 더 넓은 영역의 구조체는 struct v4l2_device의 포인터가 호출되는 모든 곳에서 적절한 cast 연산자를 통해서  사용되어 질 수 있다.

Minor Device Numbers

 V4L2 디바이스를 위해서, minor 숫자값은 드라이버가 모듈 형태로 적재될때 매개변수로 주어져야 한다. 한편, 드라이버는 이 매개변수를 위한 변수를 할당하고 매개변수형태로 넘어오는 값으로 설정한다. (video capture 디바이스들을 위한 매개변수는 unit_video와 codec을 위한 unit_codec, 등등이 있다.) 그리고 각각의 struct v4l2_device 구조체의 minor field는 채워지고 v4l2_register_device() 함수를 호출한다. 만약 이 minor 숫자값이 이미 사용중이 있으면 드라이버 등록은 실패하게 될 것이다.

 static int unit_video[MAX_DEVICES] = {0, 1, 2, 3, };   
 MODULE_PARM(unit_video, "1-"__MODULE_STRING(MAX_DEVICES)"i");   
 ...   
 mydevice[i].minor = unit_video[i];   
 if (v4l2_register_device(&mydevice[i]))   
 { /* handle error */   
 }  

V4L2 드라이버의 method 함수 호출

 다음은 V4L2 드라이버의 method 함수들의 리스트, 간단한 설명이 첨부된 각각의 함수, 그리고 그에 관련된 노트들이다.

- Method: int initialize(struct v4l2_device *v)
 드라이버 등록시 v4l2_register_device()에서 아무런 문제가 발생하지 않는다면 호출되는 method 함수이다. 이 함수에서 드라이버는 open() method 함수가 호출되기 이전에 이루어져야할 몇 가지 초기화 과정을 수행할 수 있다. 그리고 만약 에러가 있으면 음수값을 되돌려 준다. 이 에러는 드라이버의 등록을 취소하게 된다.

- Method: int open(struct v4l2_device *v, int flags, void **idptr)
 디바이스가 파일시스템에서(/dev/video 같은 경로) open되서 file descriptor를 수립할때 호출되는 method 함수이다. open 호출을 할때 디바이스 등록시 사용되었던 디바이스 포인터가 전달된다. flags 매개변수는 응용프로그램에서 호출되는 open()함수에 넘겨지게 되는 flags값과 동일한 값을 갖는다. V4L2 디바이스들은 같은 응용프로그램에 의해서 혹은, 하나 이상의 응용프로그램에 의해서 상관 없이 동시에 한번 이상 open되어 질 수 있다. idptr 매개변수는 (struct file 구조체에 있는 private_data field같이) 이러한 특수한 open 호출을 구별 할 수 있도록 하는 ID 값을 가르키는  포인터이다. open 호출후, 다른 모든 method 함수 호출에 이 ID 값이 전달 되어 진다. 그래서 드라이버는 이러한 open호출을 위해서 *idptr 포인터가 ID 값을 가르키도록 할 필요가 있다. 이 값은 '0'(NULL) 값을 제외한 어떤 값이든 될 수 있다. *idptr 포인터의 데이터형은 (void *)이다. 그리고 *idptr가 디바이스 포인터에 해당하는 v값을 포함한 각각의 open 상황을 알려주는 모든 정보를 가지고 있는 드라이버의 내부 구조체를 가르키는 포인터로 설정되도록 되어질 것이다. 이 method 호출이 성공하면 '0'값을 되돌려 주고 문제가 발생하면 음수값을 되돌려 준다. 이 되돌려진 값은 응용프로그램에도 그대로 전달된다. 마지막으로 드라이버는 open 호출이 성공적으로 이루어지면 MOD_INC_USE_COUNT 매크로를 수행하여야 한다.

 no-I/O(비 입출력?)의 open이 가능하고 합리적이라면 그러한 open을 지원하는 것은 강력하게 추천된다. 이것을 지원하기 위해선, 각각의 open 호출을 위한 독자적인 포인터값을 갖도록 *idptr를 설정해서 다중 open을 지원해야만 하고, open() 호출시 넘겨지는 flags 파라미터에서 O_NOIO 플래그값이 설정되어 있는지 살펴보아야 한다. 만약 이 플래그값이 설정되어 있다면, 이러한 open 함수는 no-I/O(비 입출력 방식) 형태로 설정되어야 한다. no-I/O open 호출들과 관련된 남아있는 문제들은 API spec에서 승인된 리스트에 없는 어떤 동작들을 올바르게 종료하는 것과 관련되어 있다. 허용되지 않는 형태를 갖는 ioctl과 read 호출은 -EPERM 값을 되돌려 주어야 하고, mmap() 호출에 대해선 -ENODEV, poll() 호출에 대해선 POLLERR 값을 되돌려 주어야 한다.

- Method: void close(void *id)
 이 함수는 디바이스 파일의 file descriptor가 닫힐때 호출된다. 그리고 이 file descriptor를 생성할 때 호출 되었던 open에서 설정된 ID 값이 전달 된다. 디바이스는 멈추게 되고, 아무런 리턴값도 없다. 그져 videodev 드라이버가 응용프로그램에게 '0'값을 되돌려 줄 뿐이다. 그리고 드라이버는 MOD_DEC_USE_COUNT 매크로를 수행해야만 한다.

- Method: long read(void *id, char *buf, unsigned long count, int noblock)
 이 함수는 응용프로그램이 read()를 호출할때 호출된다. open시 전달되었던 ID값과 요구되는 데이터의 바이트수와 버퍼가 전달된다. 그리고 요구되는 데이터가 준비되지 않았을때 더 이상의 read 호출이 금지될지 말지에 대한 변수(noblock)도 전달된다. 리턴 값은 읽은 데이터의 바이트 수이거나 음수일 경우 에러 코드가 된다. 이 리턴값은 응용프로그램에게도 전달된다.

- Method: long write(void *id, const char *buf, unsigned long count, int noblock)
 이 함수는 응용프로그램이 write()를 호출할때 호출된다. open시 전달되었던 ID값과 요청한 데이터의 바이트수와 버퍼가 전달된다. 그리고 데이터를 수용할 준비가 되어 있지 않았을 때 더 이상의 write 호출이 금지될지 말지에 대한 변수(noblock)도 전달된다. 리턴값은 쓰여진 데이터의 바이트 수이거나 음수일 경우 에러코드이다. 이 리턴 값은 응용프로그램에게도 전달된다.

- Method: int ioctl(void *id, unsigned int cmd, void *arg)
 이 함수는 응용프로그램이 ioctl()을 호출할때 호출된다. open시 전달되었던 ID값과 ioctl의 코드와 그 코드에 해당하는 명령어를 위한 매개변수도 전달된다. 리턴값이 0이면 성공이고 음수이면 에러이다. 이 값은 응용프로그램에게도 전달된다. API 스펙에는 존재하지만, 드라이버가 지원하지 않는 ioctl 코드에 대해서는 -EINVAL 값을 되돌려 주어야 하고, 정의 되지 않은 모든 ioctl코드에 대해선 -ENOIOCTLCMD 값을  되돌려 주어야 한다.

 주의해야 할것은 arg에 해당하는 매개변수는 커널메모리 영역을 지정하는 포인터이어야하고 응용프로그램으로 부터 전달되는 사용자 영역을 가르키는 원래의 포인터를 그냥 사용하면 안된다. 그래서 videodev는 필요에 따라서 copy_from_user()와 copy_to_user()를 수행해서 사용자 영역과 커널영역 사이의 메모리 관리를 수행한다. arg가 버퍼일때 그 버퍼의 크기가 V4L2_MAX_IOCTL_SIZE (256bytes)나 VIDIOC_S_WIN에 해당하는 ioctl 호출시 전달되었던 clips 매개변수 값보다 커지면 예외처리가 되어야한다.

 드라이버가 VIDIOC_QUERYCTRL에 해당하는 ioctl호출에 응답하고 id field가 해당 범위 밖의 값을 갖게 되면 -EINVAL 값을 반드시 리턴해야 하는 작업들은 중요한다. 만약 여러분의 드라이버가 어떤 독자적인 제어를 하지 않는데 id 필드가 V4L2_CID_PRIVATE_BASE의 값을 갖는다면 -EINVAL을 되돌려 주어야 한다. 사전에 정의된 control ID를 위해서 v4l2_queryctr의 category나 group field들을 설정할 필요는 없다. videodev가 알아서 그것을 하게 될 것이다. videodev는 드라이버가 호출되기 전에 이 부분들을 설정하게 되고 나중에 드라이버가 필요하다면 이 초기값을 변경할 수 있다.

 원래의 API에 맞게 작성된 응용프로그램과 하위 호환성을 유지하기 위해선 videodev는 이전의 ioctrl 코드들과와 매개변수를 V4L2 ioctl 호출에 맞도록 해석할 것이다. 하지만, videodev는 맨 처음 ioctl 호출을 드라이버에 요청한다. 그리고 만약 드라이버가 -ENOIOCTLCMD 값을 되돌려 주면 videodev는 이것을 동일한 기능을 하는 새로운 ioctl 호출로 해석하려고 할 것이다. 그러므로 드라이버가 어떤 이유로 요구되는 오래된 ioctl 호출을 다루도록 하는 것이 가능하다.

- Method: int mmap(void *id, struct vm_area_struct *vma)
 이 함수는 응용프로그램이 mmap()을 호출할때 호출된다. open시 전달되었던 ID값과 가상메모리 영역의 구조체가 전달된다. V4L2 명세서는 모든 V4L2 드라이버들이 따라야 할 필요가 있는 buffer mapping 기능을 위한 mmap() 호출을 수행하기 위해서 일반적인 용도의 프로토콜을 정의한다. 리턴값 0은 성공을 음수값은 에러를 나타낸다. 이 리턴값은 곧바로 응용프로그램에 전달된다. 잘못된 매개변수에 대해서는 -EINVAL을 리턴하고 할당되지 않은 버퍼에 대해서는 -ENODEV를 리턴해야한다.

 V4L2의 memory mapping 프로토콜은 많은 버퍼들이 개별적으로 mapping되는 것을 허용한다, 그리고 그것들은 독립적인 버퍼모음들로 구조화되는 버퍼들을 가지게된다. v4l2_buffer 구조체에서 type field는 해당하는 버퍼모음을 구분하고, index 필드는 각 개별 버퍼를 구분하는데 사용된다. v4l2_buffer 구조체가 mmap method 함수에 직접적으로 전달하는 되는 것은 불가능하다. 그래서 우리는 버퍼가 mapping되었는지 확인하기 위해서 vma->vm_offset을 사용한다. 여러분들은 mapping 되어 질 수 있는 각각의 버퍼를 위해 유일한 값으로 video_buffer 구조체의 offset field을 설정할 수 도 있다. 응용프로그램은  mmap()을 호출할때 vma->vm_offset 필드를 통해서 여러분에게 그 값을 알려 줄 수 있을 것이다. 그럼 여러분의 mmap 핸들러는 어떤 버퍼가 mapping 되어 졌는지 결정하기 위해서 vm_offset을 살펴보게 될 것이다. 그래서 여러분들은 이러한 목적의 버퍼들을 위한 넘버링 방식을 고안해야할 필요가 있다.  주의할 것은: offset 값들은 버퍼의 길이에 기초해서 설정되지 말아야 한다 그렇지 않으면 커널은  vma의 제어를 문제 상황으로 몰고 갈 수 도 있다. 시스템의 RAM에 위치하는 버퍼들은 mmap에서 할당되어질 것을 추천한다. 시스템 메모리상의 버퍼들은 할당될때 응용프로그램인 이전에 남아 있던 데이터에 대한 정보를 얻을 수 없도록 0으로 초기화되어져야 한다.

 V4L2 드라이버는 모듈형태이기 때문에, 여러분은 open과 close method 함수들을 구현해야한다. open 함수에서  vma->vm_ops method 함수를 호출하고 MOD_INC_USE_COUNT 매크로를 사용한다. 그리고 close 함수에서 MOD_DEC_USE_COUNT 매크로를 사용해야한다. 시스템은 절대로 자동으로 open을 호출하기 않기 때문에 mmap에서 open을 호출할 필요가 있다. close가 호출 될때 할당되었던 시스템 메모리 버퍼를 다시 되돌려 주어야 한다. 그리고 기억할 것은 버퍼가 mapping 되었을때 V4L2_BUF_FLAG_MAPPED 플래그를 설정해 주고 mapping을 해제할때는 이 플래그 값을 지워야한다라는 것이다. munmap()의 호출에 실패해 이것이 불가능하여 streaming이 계속 활성화된 상태라면, 여러분은  하드웨어를 안전하게 정지시키고 버퍼를 할당 해제 시키기 이전에 버퍼를 queue에서의 대기상태에서 해제 시켜야 한다.

 videodev의 mmap의 외각부는 V4L2 드라이버를 호출하기 이전에 vma->vm_file을 설정할 것이다. videodev는 또한 mmap의 호출이 성공하여 V4L2 드라이버의 mmap method 함수가 정상적으로 종료된후 해당 file의 usage count를 증가 시킬 것이다. 그레서 드라이버 자체가 file의 usage count를 증가 시키지는 말아야 한다.

 버퍼 공간의 크기는 일반적으로 데이터의 format를 어떻게 설정하느냐에 따라 달라진다. 예를 들면, video capture 버퍼는 받아올 영상의 format에 따라 달라지는 것이 대표적이다. 일반적으로, 여러분들이 버퍼가 mapping 되는 동안 이러한 format를 변경하려는 시도는 실패하게 되어 있다. 예외가 있다면 v4l과의 하위 호완성을 위해서 VIDIOC_S_FMT 호출이 video capture 버퍼가 mapping 되는 동안 허용되는 정도이다. 비록 이러한 호출이 버퍼의 크기보다 큰 공간을 필요로 하는 format으로 설정할 수 는 없지만 말이다. 버퍼의 format은 그 format에 얼매여 있는 모든 버퍼들이 mapping 해제 된 후에 변경 되어 질 수 있다. 그리고 버퍼의 format이 변경된 후에는 응용 프로그램이 VIDIOC_REQBUFS의 호출을 다시 해야만 한다. 그래서 여러분들은 버퍼 format의 변경이 또한 이전의 VIDIOC_REQBUFS의 효과를 해제하도록 만들어야만 한다.

 만약 여러분들이 응용프로그램들이 추가적으로 메모리 버퍼나 메모리 영역의 형태를 mapping하거나, 혹은 다중 데이터 stream을 갖도록 하고자 한다면, 이런 인터페이스는 당연하게 확장성을 가지고 있다; 그져 버퍼의 type field에 더 많은 값을 추가하면 된다. V4L2_BUF_TYPE_PRIVATE_BASE에서부터 시작하는 이러한 값들은 드라이버의 사적 목적의 버퍼 타입을 위해 예약 되어 있다.

- Method: int poll(void *id, struct file *file, poll_table *table)
 이 함수는 응용프로그램이 select()를 호출할때 호출된다. 넘겨지는 ID 값은 open시 전달되었던 ID값에 해당한다. struct file 구조체의 포인터와 poll_table 타입의 포인터도 넘겨진다. 뒤의 두 포인터 file과 table은 poll_wait()시스템 호출을 위해서 필요하다. 리턴값은 상태에 따라 정의된 POLL*로 시작하는 플래그값이 된다. no-I/O  open일 경우 POLLERR를 리턴한다.

----------------------------------------------------------------------------------

V4L2 Video Capture Drivers

  video capture 드라이버의 특징에 관한 몇 가지 사항들

Non-capturing open. capture device (영상을 획득하는 디바이스)에서 non-capturing open(영상을 획득하지 않는 open) 호출을 지원하는 것은 강력하게 추천한다. 이러한 방식은 우리가 영상을 획득하는 프로그램의 옆에서 따로 실행될 수 있는 일반적인 video control panel형태의(영상을 획득하거나 하지 않는) 응용프로그램을 만들수 있도록 해준다. VIDIOC_S_PARM의 호출이 capture 모드나 프레인 당 시간 같은 것을 변경할 수 없다.

Video for Linux 하위 호환성. 원래의 API는 V4L2와는 다르게 mmap을 사용해왔다. 원래의 API에서는 단 한번의 mmap 호출이 하나의 연속된 넓은 메모리 공간에 모든 버퍼들이 연속적으로 배치되도록 mapping 해왔다. 이러한 관점에서 videodev의 이런 하위 호완성을 갖는 코드가  v4l 드라이버 같이 V4L2 드라이버 함수를 만드는 것은 불가능한다. 그래서 여러분이 여러분의 드라이버가 v4l API의 mmap을 사용하는 응용프로그램과 잘 동작하도록 하고자 한다면, 한번의 mmap 호출에서 모든 요구되는 버퍼들을 한꺼번에 mapping하는 것을 지원할 필요가 있다. 이러한 것은 V4L2_BUF_TYPE_CAPTURE의 플래그 값을 갖는 video capture 버퍼에 대해서 만 사용되어 진다. 이러한 호완성 담당하는 계층은 VIDIOC_REQBUFS의 ioctl 호출에서 v4l2_requestbuffers의 type field를 V4L2_BUF_REQ_CONTIG 플래그로 설정할 것이다. 만약
여러분이 이러한 여러 버퍼들의 연속적인 mapping을 지원한다면, VIDIOC_REQBUFS 호출이 완료될때 type field에 이 플래그 값이 설정되어 있는지 확인해야 한다. 이러한 방식으로 요청되는 버퍼들의 개수는 대부분 두 개일 가능성이 높다. 주의사항: CONTIG 플래그 값이 v4l2_buffer 구조체에 설정되지 말아야 한다, 혹은 그렇지 않다면 어떤 곳.... v4l2_requestbuffers의 구조체에만 설정되어야 한다. 그리고 그 다음 mmap을 호출할때 모든 버퍼들을 위해 충분히 큰 메모리 블럭을 동적 할당한다. vma_close의 호출은 한 번에 모든 버퍼들을 mapping 해제하고 그들의 동적할당 메모리를 해제한다. vm_offset field는 연속적인 메모리 공간을 위한 mapping이 사용될때 '0'값이 될 것이다. v4e: CONTIG 플래그 값이 v4l2_buffer 구조체에 설정되지 말아야한다. 혹은 그렇지 않다면 어떤 곳.... v4l2_requestbuffers의 구조체에만 설정되어야 한다. l2cap.c 샘플 capture 드라이버가 이 모든 것들을 설명하고 있다.

여러분이 V4L2_BUF_REQ_CONTIG 플래그 값을 지원하지 않는다면, v4l을 사용하는 응용프로그램은 단지 하나의 capture 버퍼만 mapping 할 수 있게 될것이고, 이것은 사용자가 기대하는 capture 성능을 만족하기에 충분하지 않을 것이다.

----------------------------------------------------------------------------------

V4L2 Kernel-Mode API

 Videodev는 V4L2 드라이버에 대한 함수의 모음을 외부에 제공한다. 모든 함수와 데이터 구조체는 v4l2_라는 첨두어를 가지고 있다. 다음 섹션들은 간단한 설명을 겯들인 각 함수를 나열한다.

드라이버 등록과 device 구조체들

int v4l2_register_device(struct v4l2_device *v)
device 구조체 포인터를 등록하고, minor device number를 할당한다.

void v4l2_unregister_device(sMemory Management
truct v4l2_device *v)
해당 디바이스를 등록해제한다.

void v4l2_version(int *major, int *minor)
videodev.o 모듈과 함께 컴파일 되었던 V4L2_MAJOR_VERSION와 V4L2_MINOR_VERSION 값을 되돌려 준다.

struct v4l2_device *v4l2_device_from_minor(int minor)
minor device number에 해당하는 the V4L2 device structure pointer를 되돌려 준다.

struct v4l2_device *v4l2_device_from_file(struct file *file)
Linux file 구조체에 해당하는 V4L2 device 구조체 포인터를 되돌려준다. vma->vm_ops  handlers에서 유용하게 사용할 수 있다.

void *v4l2_openid_from_file(struct file *file)
Linux file 구조체에 해당하는 open ID pointer를 되돌려 준다.

메모리 관리

unsigned long v4l2_vmalloc_to_bus(void *virt)
 vmalloc()으로 할당된 메모리의 가상 주소에 해당하는 bus 주소(실제의 하드웨어 주소?)를 되돌려 준다. vmalloc()으로 할당된 메모리를 DMA 버퍼로 사용하기 위해서 유용하게 사용된다.

unsigned long v4l2_vmalloc_to_page(void *virt) [kernel 2.2.x]
struct page *v4l2_vmalloc_to_page(void *virt) [kernel 2.4.x]
 vmalloc()로 할당된 buffer를 지정하는 pointer에 해당하는 page를 되돌려 준다. vmalloc()으로 할당된 buffer로 메모리 매핑을 할때 vma->vm_ops->nopage method 함수에서 유용하게 사용된다. 이 함수로 부터의 리턴값은 vma->vm_ops->nopage 함수로 부터의 리턴값으로 사용하기에 적합하다.

Queue 관리

 Queues는 struct v4l2_queue 타입의 root node와 struct v4l2_q_node 타입의 element node로 구성된다. 예를 들면, device 구조체에서 struct v4l2_queue 구조체를 'root' node가 되도록 할당한다. 이 root node를 지정하는 포인터는 queue에 추가/제거 등의 작업을 할때 queue 함수에 전달된다. queue의 동작들은 IRQ-safe, read-write spin locks등에 의해서 보호되기 때문에 인터럽트 루틴이나 다중 프로세서 환경에서 호출될때도 올바르게 작동한다. 안전한 초기 상태에서 queue를 사용하기 이전에 v4l2_q_init() 함수를 호출하는 것은 좋은 생각이다.

void v4l2_q_init(struct v4l2_queue *q)
 queue를 텅빈 상태로 초기화 한다.

void v4l2_q_add_head(struct v4l2_queue *q, struct v4l2_q_node *node)
 queue의 head에 node를 추가한다.

void v4l2_q_add_tail(struct v4l2_queue *q, struct v4l2_q_node *node)
 queue의 tail에 node를 추가한다.

void *v4l2_q_del_head(struct v4l2_queue *q)
 queue의 head에서 node를 제거하고 이 node의 포인터를 되돌려준다. queue가 텅빈 상태에 있다면 NULL 값을 되돌려준다.

void *v4l2_q_del_tail(struct v4l2_queue *q)
 queue의 tail에서 node를 제거하고 이 node의 포인터를 되돌려준다. queue가 텅빈 상태에 있다면  NULL 값을 되돌려준다.

void *v4l2_q_peek_head(struct v4l2_queue *q)
 queue에 아무런 변경도 만들지 않고 queue의 head에 위치한 node의 포인터를 되돌려준다. queue가 텅빈 상태에 있다면 NULL값을 되돌려준다.

void *v4l2_q_peek_tail(struct v4l2_queue *q)
 queue에 아무런 변경도 만들지 않고 queue의 tail에 위치한 node의 포인터를 되돌려준다. queue가 텅빈 상태에 있다면 NULL 값을 되돌려준다.

void *v4l2_q_yank_node(struct v4l2_queue *q, struct v4l2_q_node *node)
 특정 node를 위해서 queue를 검색하고 그 node가 존재하면 queue로 부터 제거하고 이 node를 되돌려준다. 만약 이 node가 존재 하지 않다면 NULL을 되돌려준다.

int v4l2_q_last(struct v4l2_queue *q)
 queue에 단 하나의 항목만 존재하면 1을 되돌려주고 하나 이상의 항목이 있으면 0을 그 이외의 경우 -1을 되돌려준다.

Master Clock 함수들

 이 함수들은 버퍼들의 timestamp을 사용하고 획득하기 위한 것들이다. timestamp의 데이터 타입은 stamp_t 이고, 이 타입은 signed 64-bit integer 이다. timestamp은 nano 초를 기본 단위로 사용한다.(2^63 nano초는 292년 이상이다.) 나는 SGI의 Unadjusted System Time과 같은 것이 커널에 clock으로 추가되기를 희망하지만, 당분간 time-of-day clock이 기본 시간 체계로 사용될 것이다.

 드라이버들은 현재 시각을 얻기 위해서 v4l2_masterclock_gettime() 함수를 항상 호출해야만 한다.

 v4l2_masterclock_register() 함수의 목적은 새로운 time base 함수를 교체가 가능하도록 하기 위함이다. 이것은 사용하고 싶어하는 add-in card에 있는 clock과 같은 다른 time base가 있을때 사용되어 질 수 있다.

int v4l2_masterclock_register (struct v4l2_clock *clock)
 master clock 함수를 등록한다. 단 하나의 master clocgk이 전체 시스템에 대해서 한번에 등록되어 질 수 있다. clock이 등록되면 0을 되돌려주고, 다른 clock이 이미 등록되어 있다면 -1을 되돌려준다.

void v4l2_masterclock_unregister(struct v4l2_clock *clock)
 master clock 함수를 등록 해제한다.

void v4l2_masterclock_gettime(stamp_t *curr)
 master clock에 따른 현재 시각을 얻어 온다.

Time Stamps의 동작

unsigned long v4l2_timestamp_divide (stamp_t t, unsigned long p_100ns)
 t 변수에 표현되는 시간간격을 (100ns 단위로 되어 있는) p_100ns의해서 표현되는 시간 간격으로 나누고 그 몫의 값에 가장 가까운 값을 갖는 정수로 근사화한 값을 되돌려 준다. 경고: x86시스템에서 나눈 몫이 232보다 큰 값을 가지면 OOPS(엉망인) 상태가 되어 버릴 것이다.

unsigned long v4l2_timestamp_correct(stamp_t *t, unsigned long p_100ns)
 t 변수에 있는 시간간격을 (100 nano 초 단위인) p_100ns에 의해서 표현되는 시간 간격의  배수로 근사하도록 변경해서 정확하게 만든다. 리턴값은 그 배수가 된다.

비디오 표준 구조체들의 동작

 이것들은 v4l2_standard 구조체를 해석하고 구축하기 위해 추천되는 함수들이 있다. 여러분이 공표된 표준 포멧만 지원하고자 한다면, 이러한 함수들은 대부분의 모든 작업을 할 수 있고, 여러분은 아마도 절대로 여러분의 코드에서 id 혹은 (여러분이 RF 신호를 다룬다면) transmission을 제외한 v4l2_standard field를 건드릴 필요가 없다.

unsigned int v4l2_video_std_fps (struct v4l2_standard *vs)
 framerate.numerator와 framerate.denominator field들을 정수에 근사한 값을 갖는 frames-per-second 값으로 변경한다.

unsigned int v4l2_video_std_tpf(struct v4l2_standard *vs)
 framerate.numerator와 framerate.denominator field들을 100ns 단위로 표현된 time per frame 값으로 변경한다.

int v4l2_video_std_confirm(struct v4l2_standard *vs)
 v4l2_standard 구조체를 검사하고 numeric field의 타당성를 확인하고 format이 인지 가능한 표준중 하나라면 name 필드를 설정한다. format이 확실하게 옮바르지 않다면 음수 값을 되돌려 주고, 인지 가능한 표준에 대해선 V4L2_STD_*의 값을 되돌려 준다. 그리고 나머지 모든 경우에 대해서 0값을 되돌려 준다. 드라이버가 비 표준 format을 지원해야 한다면 약간의 추가적인 test를 수행해야 할지도 모른다.

int v4l2_video_std_construct(struct v4l2_standard *vs, __u32 id, __u32 transmission)
 id와 transmission의 값들을 얻어오고 v4l2_standard 구조체의 모든 field들을 채워 넣는다. 성공하면 id값을 되돌려 주고 그렇지 않은 경우 에러 코드를 되돌려 준다.

기타 등등

u32 v4l2_math_div6432 (u64 a, u32 d, u32 *r)
 64-bit 숫자를 32-bit 숫자로 나누고  나눈 몫을 되돌려 준다. 그리고 r이 NULL이 아니라면 r변수에 나머지를 넘겨주고 d가 0일 때 나눗셈 오류가 발생한다. 나눈 몫이 2^32보다 크면 프로세서에 따라서 나눗셈 오류가 발생할 수도 있다. [이 함수는 i386의 __asm__ 코드를 포함한다. 아마 다른 32-bit 아키텍쳐도 마찬가지일 것이다. Alpha나 다른 64-bit machine에서도 아마도 동작할 것이다. 나는 i386코드로 쓰여진 것만 가지고 있다.]

첨언: 그 동안 개발 번역에 혼동을 드린 것에 미안하게 생각합니다. 아직도 약간 개발 번역이기 때문에 문제가 되는 것에 대한 덧글을 환영합니다.

댓글 없음:

댓글 쓰기