Kernel API #
--
김도집 2005-09-22 14:20:31
driver_register/device_unregister 함수에서 사용하는 자료형은 <linux/device.h>에 선언되어 있다.
struct device_driver {
const char *name;
struct bus_type *bus;
struct completion unloaded;
struct kobject kobj;
struct klist klist_devices;
struct klist_node knode_bus;
struct module *owner;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
int (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state, u32 level);
int (*resume) (struct device *dev, u32 level);
};
필드 | 설명 |
name | 디바이스 드라이버의 이름을 문자열 형으로 지정한다 (필수) |
bus | 보통은 &platform_bus_type으로 지정한다 (필수) |
unloaded | |
kobj | 내부적으로 드라이버 객체를 관리하기 위한 것으로 직접적으로 사용하지 않는다 (임의 지정하지 않는다) |
klist_devices | |
knode_bus | |
owner | |
probe() | 디바이스의 초기화 루틴이다 (필수) |
remove() | 디바이스가 제거될 때 호출되는 루틴이다 (필수) |
shutdown() | |
suspend() | 절전모드로 들어갈 때 호출된다 (필수) |
resume() | 절점모들 빠져나올 때 호출된다 (필수) |
위 테이블에서 foo()와 같이 ()가 붙은 필드는 함수(포인터)를 의미한다.
1.1.2 driver_register #
버스를 갖는 드라이버를 등록할 때 사용하는 함수이다.
함수의 원형은 다음과 같다:
int driver_register(struct device_driver *drv);
관련함수: driver_unregister
static struct device_driver foo_driver = {
.name = "foo”,
.bus = &platform_bus_type,
.probe = foo_probe,
.remove = foo_remove,
.suspend = foo_suspend,
.resume = foo_resume,
};
static int __init foo_init(void) {
int err;
err = driver_register(&foo_driver);
return err;
}
1.1.3 driver_unregister #
driver_register()를 통해 등록된 드라이버를 해제할 때 사용한다.
함수의 원형은 다음과 같다:
void driver_unregister(struct device_driver *drv)
관련함수: driver_register
1.1.4 dev_set_drvdata #
사용자 데이터를 device 구조체의 driver_data 필드를 통해 참조할 수 있도록 설정하는 wrapper 함수이다.
함수 원형은 다음과 같다:
void dev_set_drvdata(struct device *dev, void *data)
관련함수: dev_get_drvdata
1.1.5 dev_get_drvdata #
device 구조체의 driver_data 필드의 참조 주소를 가져오는 wrapper 함수이다.
함수 원형은 다음과 같다:
void * dev_get_drvdata(struct device *dev)
관련함수: dev_set_drvdata
1.2.1.1 schedule_timeout #
적어도 일정 시간(timeout)동안 휴면상태로 있다가 다시 실행 가능 상태가 된다. 실행 가능 상태가 된다는 말은 바로 실행된다는 것이 아니라 스케줄러에 의해 조건이 충족되면 실행된다는 의미이다. 즉, 스케줄링에 따라 일정 시간 보다 더 오랫 동안 잠정적인 휴면 상태에 있을 수도 있다.
함수 원형은 다음과 같다:
signed long __sched schedule_timeout(signed long timeout)
timeout은 jiffies 단위 값으로 적어도 timeout 동안 휴면 상태에 있게 된다.
timeout동안 휴면 상태에 있을 때 task의 상태는 다음과 같을 수 있다:
상태를 나타내는 매크로 | 설명 |
TASK_UNINTERRUPTIBLE | timeout 이후에만 깨어난다 |
TASK_INTERRUPTIBLE | 시그널 받거나 timeout이 되면 깨어난다 |
timeout을 통해 깨어나게 되면 0을 반환하고 TASK_INTERRUPTIBLE 상태에서 시그널을 받아 깨어나게 되면 남은 jiffies 값을 반환한다.
관련함수: set_current_state
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2);
2 jifffies 이후에 실행 가능한 상태가 되며 그때까지 휴면 상태로 있는다. 휴면 상태에서 시그널을 받는 경우에도 깨어난다.
1.2.1.2 set_current_state #
현 task의 상태를 변경한다. 이는 <linux/sched.h>헤더 파일에 정의되어 있다.
void set_cureent_state(int state)
state 값은 다음과 같다:
state | 설명 |
TASK_RUNNING | 실행 가능한 상태 |
TASK_INTERRUPTIBLE | 시그널을 받거나 wakeup 조건을 만족하면 깨어날 수 있는 휴면 상태 |
TASK_UNINTERRUPTIBLE | wakeup 조건을 만족할때만 깨어날 수 있는 휴면 상태 |
커널은 필수적으로 하나의 타이머 인터럽트를 갖게 된다. 이는 스케줄링 및 타이머 등의 기준이 된다. 이때 타이머 인터럽트가 한 번 발생할 때마다 이를 tick이라 하며 하나의 tick마다 jiffies 값이 1씩 증가하게 된다.
jiffies는 커널 전역 변수로 원형은 다음과 같다:
volatile unsigned long jiffies;
커널 타이머 관련 함수는 <linux/timer.h>에 정의되어 있다.
struct timer_list {
struct list_head entry;
unsigned long expires;
spinlock_t lock;
unsigned long magic;
void (*function)(unsigned long);
unsigned long data;
struct tvec_t_base_s *base;
};
1.3.2.2 TIMER_INITIALIZER #
TIMER_INITIALIZER(function, expires, data)
static struct timer_list foo_timer =
TIMER_INITIALIZER(foo_timer_function, 0, 0);
void init_timer(struct timer_list *timer)
void add_timer(struct timer_list *timer)
int del_timer(struct timer_list *timer)
SMP머신에서 del_timer_sync()를 사용한다.
int mod_timer(struct timer_list *timer, unsigned long expires)
메모리 영역은 사용자 영역과 커널 영역으로 나뉜다. 일반적으로 사용자 영역의 경우 page out(swap out)된 경우가 있을 수 있다. 이런 메모리 공간을 커널 영역에서 직접 접근하는 경우 page fault 등이 생길 수 있는데, 커널 공간의 일부 처리 중에는 이러한 것이 문제가 될 수 있다.
반대로 커널 영역의 메모리는 사용자 영역의 프로세스에서 직접 접근할 수 없다.
따라서 서로 다른 영역을 메모리를 접근하기 위해서는 이에 필요한 적절한 API를 이용해야 한다.
사용자 영역의 메모리에 있는 데이터를 커널 영역의 메모리 영역으로 복사할 때 사용하는 함수이다. 이 함수는 아키텍처에 의존적인 함수로 <asm/uaccess.h> 헤더 파일에 선언되어 있다.
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
매개변수 | 설명 |
to | 커널 영역의 메모리 주소 |
from | 사용자 영역의 메모리 주소 |
n | 바이트 단위의 데이터 크기 |
성공하면 0을 반환하고 실패하면 0이 아닌 값을 반환한다.
관련함수: copy_to_user
함수 내부적으로 access_ok를 통해 사용자의 메모리 영역을 검사하므로 별도의 access_ok를 호출할 필요는 없다.
unsigend long copy_to_user(void __user *to, const void *from, unsigned long n)
사용자 영역의 데이터를 커널 영역으로 복사할 때 사용한다. 이는 아키텍처 의존적인 함수로 <asm/uacess.h>에 선언되어 있다.
int get_user(x, void *from)
x는 char, short, int 형으로 넘겨지는 데이터 형에 따라 복사 할 데이터의 길이(바이트 단위)가 결정된다.
커널 영역의 데이터를 사용자 영역으로 복사한다. 이는 아키텍처 의존적인 함수로 <asm/uaccess.h>에 선언되어 있다.
int put_user(x, void *to)
사용자 영역의 메모리가 유효한지 검사한다. <asm/uaccess.h>에 선언되어 있다.
int access_ok(int type, void *addr, unsigned long size);
매개변수 | 설명 |
type | 어떤 유효 검사를 할지를 결정한다. 다음 표를 참조 |
addr | 유효 검사를 시작할 주소 |
size | 유효 검사를 할 영역의 바이트 단위 크기 |
type 매개 변수는 매크로로 선언되어 있는데, 다음과 같다:
type | 설명 |
VERIFY_READ | 읽기에 대한 유효 검사 |
VERIFY_WRIT | 쓰기에 대한 유효 검사 |
다른 함수와 달리 유효하다면 0이 아닌 값을 반환하고 유효하지 않다면 0을 반환한다.
copy_from_user/copy_to_user 또는 get_user/put_user 등의 함수는 access_ok를 사용할 필요가 없다. __get_user 등과 같이 더 저 수준의 함수를 직접 호출하여 사용할 때만 사용하게 된다.
다음은 access_ok를 사용한 예이다.
static inline unsigned long copy_from_user(void *to, const void __user *from,
unsigned long n)
{
if (access_ok(VERIFY_READ, from, n))
n = __arch_copy_from_user(to, from, n);
else
memzero(to, n);
return n;
}
io 메모리와 관련된 함수는 <asm/io.h>에 선언되어 있다.
void __iomem *ioremap(unsigned long offset, unsigned long size)
void iounmap(volatile void __iomem *addr)
unsigned char readb(const volaltile void __iomem *addr)
unsigned short readw(const volaltile void __iomem *addr)
unsigned int readl(const volaltile void __iomem *addr)
void writeb(unsigned char b, const volaltile void __iomem *addr)
void writew(unsigned short b, const volaltile void __iomem *addr)
void writel(unsigned int b, const volaltile void __iomem *addr)
snd_card_t는 <sound/core.h>에 정의 되어 있다. 이는 strcut _snd_card 의 typedef형이다.
struct _snd_card {
char driver[16]; /* 드라이버 이름 */
char shortname[32]; /* 사운드 카드의 짧은 이름 */
char longname[80]; /* 사운드 카드의 이름 */
void *private_data; /* 사운드 카드에 대한 추가 정보 */
void (*private_free) (snd_card_t *card); /* 추가 정보을 위해 할당받은 자원 */
/* 반환을 위한 콜백 */
위 구조체의 필드는 위 명시된 내용 외에도 무수히 많다. 사용자가 꼭 알아야 하는 것들만 나열했다.
사운드 카드의 구조체를 할당 받는다.
snd_card_t *snd_card_new(int idx, const char *xid,
struct module *module, int extra_size)
매개변수 | 설명 |
idx | 카드의 인덱스(주소),0에서 (SNDRV_CARDS-1) |
xid | 카드 이름 (문자열) |
module | locking을 소유한 상위 모듈 |
extra_size | 사운드카드 구조체 이후 추가 할당받을 메모리 크기 |
성공하면 snd_card_t형의 포인터를 반환하고 실패하면 NULL을 반환한다.
snd_card_t *card;
card = snd_card_new(-1, id, THIS_MODULE, sizeof(foo_bar));
if (card == NULL)
return -ENOMEM;
1.5.1.3 snd_card_register #
사운드 카드를 등록한다.
int snd_card_register(snd_card_t *card)
성공하면 0을 반환하고, 실패하면 0이 아닌 값을 반환한다.
관련함수: snd_card_free
사운드카드 구조체의 자원을 반환한다.
int snd_card_free(snd_card_t *card)
성공하면 0을 반환하고, 실패하면 0이 아닌 값을 반환한다.
관련함수: snd_card_new, snd_card_register
전원 관리 함수는 CONFIG_PM 이 정의된 경우에만 유효 하도록 작성한다.
#ifdef CONFIG_PM
static int snd_foo_suspend(...)
{
...
}
#endif
snd_card_set_pm_callback()
snd_power_change_state()
snd_pcm_suspend_all()
전원 상태 매크로 | 설명 |
SNDRV_CTRL_POWER_D0 | Full On 상태 |
SNDRV_CTRL_POWER_D1 | 일부만 On 상태 |
SNDRV_CTRL_POWER_D2 | 일부만 On 상태 |
SNDRV_CTRL_POWER_D3 | Off 상태 |
SNDRV_CTRL_POWER_D3hot | 전원은 공급되지만 Off 상태 |
SNDRV_CTRL_POWER_D3cold | 전원 공급도 없는 Off 상태 |
1.5.1.5.1 snd_card_set_pm_callback #
전원 관리를 위한 콜백을 등록한다.
int snd_card_set_pm_callback(snd_card_t *card,
int (*suspend)(snd_card_t *, pm_message_t),
int (*resume)(snd_card_t *),
void *private_data)
필드 | 설명 |
card | 사운드카드 구조체 |
suspend | 절전 모드 진입시 호출되는 콜백 |
resume | 절전 모드 해제시 호출되는 콜백 |
private_data | 콜백에 넘길 매개 변수의 포인터 |
항상 0을 반환한다.
이 함수는 매개변수 card의 필드 중 pm_suspend, pm_resume, pm_private_data 를 넘겨주는 매개 변수의 값으로 설정한다.
snd_card_set_pm_callback(card, snd_foo_suspend, snd_foo_resume, foo);
다음의 자료형은 <sound/pcm.h> 파일에 정의되어 있다.
typedef struct _snd_pcm_ops {
int (*open)(snd_pcm_substream_t *substream);
int (*close)(snd_pcm_substream_t *substream);
int (*ioctl)(snd_pcm_substream_t *substream, unsigned int cmd, void *arg);
int (*hw_params)(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params);
int (*hw_free)(snd_pcm_substream_t *substream);
int (*prepare)(snd_pcm_substream_t *substream);
int (*trigger)(snd_pcm_substream_t *substream, int cmd);
snd_pcm_uframes_t (*pointer)(snd_pcm_substream_t *substream);
int (*copy)(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos,
void __user *buf, snd_pcm_ufreams_t count);
struct page *(*page)(snd_pcm_substream_t *substream, unsigned long offset);
int (*mmap)(snd_pcm_substream_t *substream, struct vm_area_struct *vma);
int (*ack)(snd_pcm_substream_t *substream);
} snd_pcm_ops_t;
필드 | 설명 |
open | |
close | |
ioctl | |
hw_parames | |
hw_free | |
prepare | |
trigger | |
pointer | |
새로운 PCM 디바이스를 위한 자원을 할당받는다. 이러한 역할을 하는 함수가 snd_pcm_new 함수이다.
함수의 원형은 다음과 같다:
int snd_pcm_new(snd_card_t *card, char *id, int device,
int playback_count, int capture_count,
snd_pcm_t **rpcm)
매개변수 | 설명 |
card | 사운드 카드 데이터의 포인터 |
id | ID 문자열 |
device | 장치 인덱스 (0부터 시작한다) |
playback_count | 재생할 때 스트림의 개수 |
capture_count | 녹음할 때 스트림의 개수 |
rpcm | 새 pcm 디바이스를 위한 자원을 저장하기 위한 포인터 |
성공하면 0을 반환하고, 실패하면 음의 에러 코드 값을 반환한다.
사용 예를 보면 다음과 같다:
int err;
snd_pcm_t *pcm;
if ((err = snd_pcm_new(foo->card, "FOO ACM", device, 1, 1, &pcm)) < 0)
return err;
}
1.5.1.6.3 snd_pcm_lib_preallocate_pages_for_all #
스트림을 위한 연속된 메모리를 할당받기 위한 사전 작업을 처리한다.
함수의 원형은 다음과 같다:
int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, int type, void *data,
size_t size, size_t max)
매개변수 | 설명 |
pcm | pcm 디바이스 포인터 |
type | SNDRV_DMA_TYPE_로 시작하는 DMA 타입 |
data | DMA 타입에 따른 데이터 |
size | 사전 할당을 받아야 하는 메모리의 바이트 단위의 크기 |
max | 최대로 허용하는 사전 할당 크기 |
성공하면 0을 반환하고 실패하면 에러코드인 음수를 반환한다.
type 필드에 사용할 수 있는 것들은 다음과 같다:
type | 설명 |
SNDRV_DMA_TYPE_UNKNOWN | 정의 되지 않은 타입 |
SNDRV_DMA_TYPE_CONTINUOUS | DMA가 아닌 연속된 메모리 |
SNDRV_DMA_TYPE_DEV | 디바이스 내의 연속된 메모리 |
SNDRV_DMA_TYPE_DEV_SG | 디바이스 내의 연속된 SG-버퍼 |
SNDRV_DMA_TYPE_SBUS | 연속된 SBUS |
다음은 사용예이다:
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINOUS,
snd_dma_continues_data(GFP_KERNEL),
128 * 1024, 128 * 1024);
enum 매크로 | 설명 |
SNDRV_PCM_STREAM_PLAYBACK | |
SNDRV_PCM_STREAM_CAPTURE | |