FSTWikiKernel Api2
Login:
Password:
Join
E D R S I H P RSS
FrontPage|FindPage|TitleIndex|RecentChanges

Kernel API 2 - Extension #

-- 김도집 2005-09-23 09:30:36

Kernel API에 대한 두번째 문서로 첫번째 문서는 Kernel API이다.

Contents

1 Kernel API 2 - Extension
1.1 device file system
1.1.1 /dev
1.1.2 udev
1.1.3 devfs
1.1.4 ndevfs
1.2 MTD
1.2.1 낸드 플래시
1.2.2 MTD 블록
1.2.2.1 MTD 블록 동작 메카니즘
1.3 ?FileSystem
1.3.1 드라이버 내에서 파일 연산
1.3.1.1 filp_open
1.3.1.2 filp_close
1.3.1.3 vfs_read
1.3.1.4 vfs_write
1.3.2 seq_file
1.3.2.1 single_open
1.3.2.2 single_release
1.3.2.3 seq_read
1.3.2.4 seq_lseek
1.3.2.5 seq_printf
1.3.3 procfs
1.3.3.1 create_proc_entry
1.3.3.2 remove_proc_entry
1.3.4 debugfs
1.3.4.1 debugfs_create_file
1.3.4.2 debugfs_create_dir
1.3.4.3 debugfs_remove
1.3.4.4 debugfs_create_u8
1.3.4.5 debugfs_create_u16
1.3.4.6 debugfs_create_u32
1.3.4.7 debugfs_create_bool
1.3.5 libfs
1.3.6 YAFFS/?YAFFS2
1.3.7 JFFS/?JFFS2
1.3.8 ?JFFS3
1.3.9 CRAMFS
1.3.10 DOS/FAT/VFAT
1.4 Serial
1.5 irda
1.6 tty
1.7 Input
1.7.1 event 값 사용하기
1.8 프레임버퍼
1.8.1 커서 숨기기
1.8.2 스크린 ON/OFF
1.9 사운드(Sound)
1.9.1 OSS
1.9.2 ALSA
1.10 ?Video4Linux
1.11 USB
1.11.1 USB 호스트
1.11.2 USB 클라이언트
1.11.3 USB 가젯
1.11.3.1 usb_gadget
1.11.3.1.1 usb_gadget_ops
1.11.3.2 usb_gadget_driver
1.11.3.3 usb_ep
1.11.3.3.1 usb_ep_ops
1.11.3.4 usb_gadget_register_driver
1.11.3.5 usb_gadget_unregister_driver
1.11.3.6 usb_ep_queue
1.11.3.7 usb_ep_dequeue
1.11.4 USB 가젯 드라이버
1.11.5 USB OTG
1.12 PCI
1.13 PCMCIA
1.14 SD/MMC

1.1 device file system #

1.1.1 /dev #

파일 시스템 상에 포함된 /dev 디렉토리 아래에 디바이스 노드들이 정적으로 선언되어 있는 것이다. 오늘날 devfs와 udev 등을 통해 /dev 아래 다바이스 노드가 정적으로 만들어지는 것이 아니라 동적으로 만들어지는 방식이 이용되고 있지만, 아직도 예전 방식인 /dev 아래 정적으로 노드를 만드는 경우도 많다.

1.1.2 udev #

LinuxApplicationDevelopment의 udev를 참고하라.

1.1.3 devfs #

리눅스 커널 2.3.46 이후 사용되어 커널 2.6.13부터 커널에서 제외되었다.

참고: [http]http://lwn.net/Articles/139595/

devfs에 대한 참고할 만한 것들은 다음과 같다.

1.1.4 ndevfs #

Greg KH가 제안한 nano device file system이다. 일명 ndevfs로 불리며, 정식으로 커널에 포함된 것은 아니다. 그러나 그 구조가 매우 간단하여 devfs를 대체해서 사용될 수 있을 것이다.

자세한 것은 NewLinuxKernel를 참고하라.

/!\ ndevfs는 kobject의 class에 기반하여 디바이스 노드를 등록 및 해제하도록 하고 있다. 이에 따라 블록 디바이스에 대한 노드는 생성이 되지 않는다. 장기적으로 블록 디바이스 역시 class 아래에 편입될 예정이긴 하지만, 그 전까지는 register_disk()와 del_gendisk()에 각각 블록 디바이스의 노드 생성과 해제를 추가해줄 필요가 있다.

1.2 MTD #

MTD는 메모리를 디스크처럼 사용하기 위한 인터페이스를 제공한다. 일반적으로 플래시 메모리를 위한 것으로 대표적으로 NOR, NAND, MDOC 등을 지원한다.

1.2.1 낸드 플래시 #

낸드 플래시는 페이지 크기에 따라 크게 두가지 타입이 있다.
  • Small page size : 512바이트
  • Large page size : 2K바이트

낸드 플래시가 대용량화 되면서 512바이트 크기의 페이지를 사용하는 경우 너무 많은 블록이 존재하게 되어 제한된 IO만으로는 모두 커버하기가 어렵게 되었다.이에 따라 한 페이지의 크기를 2K바이트로 크게 함으로써 이러한 제약사항을 극복하게 되었다.

경로: drivers/mtd/nand

다음은 <mtd/mtd-alib.h>에 선언되어 있는 nand_oobinfo 구조체이다.
struct nand_oobinfo {
  uint32_t useecc;
  uint32_t eccbytes;
  uint32_t oobfree[8][2];
  uint32_t eccpos[32];
};

필드설명
useecc일반적으로 MTD_NANDECC_AUTOPLACE
eccbytesECC의 크기(비트)
oobfreeECC 및 BADBLOCK 비트를 제외한 사용 가능한 비트. {시작비트, 크기}
eccposECC 비트의 비트 위치

예를 들어, ECC가 6비트이고 비트 0, 1, 2, 3, 6, 7에 위치하고 비트 8에서 비트 15까지 8비트는 다른 용도로 사용 가능하다면 다음과 같이 정의할 수 있다.
struct nand_oobinfo nand_oob_16 = {
  .useecc = MTD_NANDECC_AUTOPLACE,
  .eccbytes = 6,
  .eccpos = {0, 1, 2, 3, 6, 7},
  .oobfree = { {8, 8} }
};

1.2.2 MTD 블록 #

MTD 블록의 주번호는 31번이며, MTD의 각 블록마다 주 번호가 0부터 1씩 증가해서 붙게 된다. 디바이스 노드는 /dev/mtdblock0, /dev/mtdblock1, /dev/mtdbloc2와 같이 이름이 결정되게 되며, 각각 주번호는 31로 동일하며 부번호는 0, 1, 2가 된다.


1.2.2.1 MTD 블록 동작 메카니즘 #

mtd 블록의 등록 및 해제는 drivers/mtd/mtdblock.c의 mtdblock_add_mtd()함수와 mtdblock_remove_dev()함수를 통해 이뤄진다. 이들 함수는 다른 디바이스에서 호출해서 사용할 수 있는 함수는 아니면 mtdblock.c 파일내에서 내부적으로 호출된다.
  • mtdblock_add_mtd
  • mtdblock_remove_dev

하나의 MTD 블록 등록 과정을 살펴보자.

mtdblock_add_mtd()함수는 add_mtd_blktrans_dev()함수를 통해 mtd_blktrans_dev형의 dev를 등록하게 된다.

add_mtd_blktrans_dev()함수는 블록 디바이스의 등록 과정에 따라 alloc_disk()를 통해 gendisk형의 gd를 할당받게 된다. 이를 블록 디바이스에 필요한 값으로 설정한 후 add_disk()를 통해 새 블록 디바이스인 (논리) 디스크를 추가하게 된다.

이 후의 메카니즘은 블록 디바이스에서 자세히 다루니 이를 참고하자.

1.3 ?FileSystem #

1.3.1 드라이버 내에서 파일 연산 #

드라이버 내에서 파일을 연산을 해야 할 경우가 있다. 다음은 이런 경우에 대한 내용을 기술한다.

1.3.1.1 filp_open #

1.3.1.2 filp_close #

1.3.1.3 vfs_read #

1.3.1.4 vfs_write #

1.3.2 seq_file #

seq_file 인터페이스는 /proc 파일시스템을 위해 제안된 것이다. procfs를 직접 구현하는 경우 overflow와 같은 문제가 생길 수 있는 것을 방지하기 위한 안전한 인터페이스를 제공하는 것을 목적으로 한다.

seq_file에 대한 오퍼레이션을 위한 4개의 메소드를 제공한다. 이들 메소드는 seq_operations라는 구조체를 통해 정의한다. 이는 <linux/seq_file.h>에 정의되어 있다.
struct seq_operations {
        void * (*start) (struct seq_file *m, loff_t *pos);
        void (*stop) (struct seq_file *m, void *v);
        void * (*next) (struct seq_file *m, void *v, loff_t *pos);
        int (*show) (struct seq_file *m, void *v);
};
start

stop

next

show

위 seq_operations을 정의해서 사용하는 것보다는 single_open을 이용하여 show 함수만 사용자가 지정하는 방식으로 쉽게 사용할 수 있다.

일반적으로 seq_file을 사용하는 file_operations을 정의할 때 다음과 같이 선언한다(drivers/usb/gadget/omap_udc.c).
static struct file_operations proc_ops = {
  .open = proc_udc_open,
  .read = seq_read,
  .llseek = seq_lseek,
  .release = single_release,
};

이후 proc_udc_open 함수에서는 다음과 같이 한다.
static int proc_udc_open(struct inode *inode, struct file *file)
{
  return single_open(file, proc_udc_show, NULL);
}

실제 사용자가 보여줄 데이터는 proc_udc_show 함수에서 seq_printf문을 사용하여 구현하면 된다.
static int proc_udc_show(struct seq_file *s, void *_)
{
  ...
  seq_printf(s, "%s, version: " DRIVER_VERSION);
  ...
  seq_printf(s, "...\n");
  ...
  return 0;
}

1.3.2.1 single_open #

<linux/seq_file.h>

single_open을 사용하면 seq_file을 간단히 사용할 수 있다. 결국 여기서 사용자가 정의해 주어야 하는 부분은 show 함수이다.
int single_open(struct file *file, int (*show)(struct seq_file *, void *),
                void *data)

1.3.2.2 single_release #

single_open에 반대 기능의 함수이다.
int single_release(struct inode *inode, struct file *file)

1.3.2.3 seq_read #

ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)

1.3.2.4 seq_lseek #

loff_t seq_lseek(struct file *file, loff_t offset, int origin)
file_operations의 lseek과 같은 방식이다. 단, origin이 0이면 절대 위치, 1이면 상대적인 위치로 찾게 된다.

1.3.2.5 seq_printf #

seq_file에 데이터를 채울 때 사용하는데, printf와 동일한 방식으로 사용한다.
int seq_printf(struct seq_file *m, const char *f, ...)

single_open에서 show 함수를 사용자가 정의해서 등록하게 되는데, 이때 show 함수에서 사용하게 되는 것이 바로 seq_printf이다. 이 함수를 통해 사용자가 보여줄 파일의 포맷을 만들어 주면 된다.

1.3.3 procfs #

드라이버의 정보 등을 텍스트 기반으로 손쉽게 제공할 수 있는 파일시스템의 인터페이스가 /proc 이다. proc의 기본적은 구조체는 proc_dir_entry이다. 이는 <linux/proc_fs.h>에 정의되어 있다.

struct proc_dir_entry {
        unsigned int low_ino;
        unsigned short namelen;
        const char *name;
        mode_t mode;
        nlink_t nlink;
        uid_t uid;
        gid_t gid;
        unsigned long size;
        struct inode_operations * proc_iops;
        struct file_operations * proc_fops;
        get_info_t *get_info;
        struct module *owner;
        struct proc_dir_entry *next, *parent, *subdir;
        void *data;
        read_proc_t *read_proc;
        write_proc_t *write_proc;
        atomic_t count;         /* use count */
        int deleted;            /* delete flag */
        void *set;
};

1.3.3.1 create_proc_entry #

/proc 아래에 파일을 등록한다.
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
                                         struct proc_dir_entry *parent)

proc_dir_entry를 통해 proc_dir_entry를 생성하고, 생성된 entry 내의 proc_fops에 file_operations를 등록한다.

간단한 예를 보면 다음과 같다.
static const char proc_filename[] = "driver/xxx";

static struct file_operations proc_ops = {
  .open = proc_xxx_open,
  .read = seq_read,
  .llseek = seq_lseek,
  .release = single_release,
};

static void create_proc_file(void)
{
  struct proc_dir_entry *pde;

  pde = create_proc_entry (proc_filename, 0, NULL);
  if (pde)
    pde->proc_fops = &proc_ops;
}

proc과 관련하여 seq_file을 사용하는 내용에 대해서는 seq_file 절을 참고하자.

1.3.3.2 remove_proc_entry #

생성된 proc을 삭제할 때는 remvoe_proc_entry를 실행한다.
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)

1.3.4 debugfs #

커널 개발자를 위한 pseudo 파일 시스템이다. debugfs를 사용하기 위해서는 커널 configuration에서 debugfs를 선택해야만 한다.

debugfs에 대한 자료형 및 함수는 <linux/debugfs.h>에 선언되어 있다.

Debugfs를 이용해 dir 만들고 그 아래 debug와 관련된 debugfs 파일을 만든다. 이 때 file을 생성할 때 file_operations을 등록하는데, debugfs 파일과 관련된 연산을 처리하게 된다.

참고할 만한 자료는 다음과 같다.

1.3.4.1 debugfs_create_file #

<linux/debugfs.h>

fops로 파일 연산을 정의한다. 이는 만들어진 파일에 대한 연산을 담당하게 된다. 따라서 debugfs의 파일에 대한 기능 정의는 fops 연산자를 통해 하면 된다.

struct dentry *debugfs_create_file(const char *name, mode_t mode,
                                   struct dentry *parent, void *data,
                                   struct file_operations *fops);

1.3.4.2 debugfs_create_dir #

<linux/debugfs.h>

struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);

1.3.4.3 debugfs_remove #

<linux/debugfs.h>

void debugfs_remove(struct dentry *dentry);

1.3.4.4 debugfs_create_u8 #

<linux/debugfs.h>

struct dentry *debugfs_create_u8(const char *name, mode_t mode,
                                 struct dentry *parent, u8 *value);

1.3.4.5 debugfs_create_u16 #

<linux/debugfs.h>

struct dentry *debugfs_create_u16(const char *name, mode_t mode,
                                  struct dentry *parent, u16 *value);

1.3.4.6 debugfs_create_u32 #

<linux/debugfs.h>

struct dentry *debugfs_create_u32(const char *name, mode_t mode,
                                  struct dentry *parent, u32 *value);

1.3.4.7 debugfs_create_bool #

<linux/debugfs.h>

struct dentry *debugfs_create_bool(const char *name, mode_t mode,
                                   struct dentry *parent, u32 *value);

1.3.5 libfs #

1.3.6 YAFFS/?YAFFS2 #

1.3.7 JFFS/?JFFS2 #

1.3.8 ?JFFS3 #

1.3.9 CRAMFS #

1.3.10 DOS/FAT/VFAT #

1.4 Serial #

1.5 irda #

1.6 tty #

1.7 Input #

struct input_event {
        struct timeval time;
        unsigned short type;
        unsigned short code;
        unsigned int value;
};
  • time : event 발생 시간
  • type : event의 종류
    EV_KEY : keyboard event
    EV_ABS : mouse event
  • code : type is EV_KEY : A, B, C 등의 keyboard code값(kernel 측과 합의 필요)
    type is EV_ABS : 0-> X좌표 의미, 1-> Y좌표 의미, 값은 value에 저장
  • value: type is EV_KEY : 0-> down key event, 1-> up key event
    type is EV_ABS : code 0-> X좌표의 값, code 1-> Y좌표 의미, 이때 value의 값이 0이면 release event, value 값이 4055(0xFFF)이면 press event 이다.

1.7.1 event 값 사용하기 #

Input 시스템 중 event를 이용하여 키 값 또는 포인트 값을 읽어들일 수 있다. 다음은 사용자 프로그램의 간단한 예이다.
#include <stdint.h>
#include <linux/input.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#ifndef EV_SYN
#define EV_SYN 0
#endif
int main (int argc, char **argv)
{
        int fd, rd, i, j, k;
        struct input_event ev[64];
        if (argc < 2) {
                printf("Usage: evtest /dev/input/eventX\n");
                printf("Where X = input device number\n");
                printf("If X is 1, keypad, else if X is 2, touchpad\n");
                return 1;
        }
        if ((fd = open(argv[argc - 1], O_RDONLY)) < 0) {
                perror("evtest");
                return 1;
        }
        printf("Testing ... (interrupt to exit)\n");
        while (1) {
                rd = read(fd, ev, sizeof(struct input_event) * 64);
                if (rd < (int) sizeof(struct input_event)) {
                        printf("yyy\n");
                        perror("\nevtest: error reading");
                        return 1;
                }
                for (i = 0; i < rd / sizeof(struct input_event); i++)
                {
                        if(ev[i].type==EV_KEY)
                        {
                                printf("Keypad Event: time %ld.%06ld, code %d , value %d(%s)\n",
                ev[i].time.tv_sec, ev[i].time.tv_usec,
                ev[i].code,
                ev[i].value, ev[i].value ? "Down keypad" : "Up keypad");
                        }
                        else if(ev[i].type==EV_ABS)
                        {
                                switch(ev[i].value)
                                {
                                        case 0:
        printf("Touch Event: time %ld.%06ld, code %d , value %d(release)\n",
                ev[i].time.tv_sec, ev[i].time.tv_usec, ev[i].code, ev[i].value);
                                                break;
                                        case 4095:
        printf("Touch Event: time %ld.%06ld, code %d , value %d(press)\n",
                ev[i].time.tv_sec, ev[i].time.tv_usec, ev[i].code, ev[i].value);
                                                break;
                                        default :
        printf("Touch Event: time %ld.%06ld, code %d (%s), value %d\n",
                ev[i].time.tv_sec, ev[i].time.tv_usec,
                ev[i].code, ev[i].code ? "Y" : "X",
                ev[i].value);
                                        }

                        }
                }
        }
}

1.8 프레임버퍼 #

1.8.1 커서 숨기기 #

프레임 버퍼를 이용하여 화면을 출력하는 경우 커서를 숨길 필요가 있는 경우가 있다. 이에 따라 프레임 버퍼의 용도에 따라 두 가지 모드를 두고 있다. 텍스트 기반의 KD_TEXT와 그래픽 기반의 KD_GRAPHICS이다.
  • KD_TEXT
  • KD_GRAPHICS

다음은 KD_GRAPHICS 모드로 설정하는 사용자 프로그램의 간단한 예이다.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <linux/kd.h>

int main(int argc, char *argv[])
{
        int fd;

        fd = open("/dev/tty0", O_RDWR);
        if (fd < 0) {
                fprintf(stderr, "cannot open tty0\n");
                return -1;
        }

        ioctl(fd, KDSETMODE, KD_GRAPHICS);

        return 0;
}

1.8.2 스크린 ON/OFF #

다음은 프레임 버퍼 스크린의 ON/OFF를 위한 사용자 프로그램의 간단한 예이다.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <linux/fb.h>

const char ver[] = "blank v1.0 2004(C) Dojip Kim\n";

#define FB_DEV  "/dev/fb0"

typedef enum {
        OFF = 0,
        ON
} bl_mode_t;

static int blank(unsigned long arg);

int main(int argc, char **argv)
{
        bl_mode_t mode = ON;

        printf("%s", ver);

        if (argc > 1) {
                if (strncmp(argv[1], "-on", 3) == 0)
                        mode = ON;
                if (strncmp(argv[1], "-off", 4) == 0)
                        mode = OFF;
        } else {
                printf("usage: blank -on/-off\n");
                exit(1);
        }

        switch (mode) {
                case OFF:
                        blank(VESA_POWERDOWN);
                        break;
                case ON:
                        blank(VESA_NO_BLANKING);
                        break;
                default:
        }

        return 0;
}

static int blank(unsigned long arg)
{
        int fd = -1;

        fd = open(FB_DEV, O_RDWR);
        if (fd < 0) {
                fprintf(stderr, strerror(errno));
                return -1;
        }

        ioctl(fd, FBIOBLANK, arg);

        close(fd);

        return 0;
}

1.9 사운드(Sound) #

리눅스에서는 두가자 형태의 사운드 드라이버를 제공한다. OSS와 ALSA가 그것이다. OSS는 리눅스의 오래된 사운드 아키텍처이다. 리눅스의 사운드는 ALSA 기반으로 바뀌고 있다.

1.9.1 OSS #

1.9.2 ALSA #

ALSA에 대한 것은 ALSA를 참고하라.

1.10 ?Video4Linux #

1.11 USB #

USB는 1.1과 2.0이 있다. USB 1.1에서는 Low-speed와 Full-speed를 지원하고 2.0에서는 High-speed까지 지원한다.
  • Low-speed : 1.5Mbps
  • Full-speed : 12Mbps
  • High-speed : 480Mbps

USB는 VBUS(+5V), GND, D+, D- 네 개의 신호로 이뤄진다. D+와 D-는 differential signal로 서로 반대 극성을 갖는다. USB data는 NRZI 인코딩을 통해 실리게 된다. NRZI는 Non-return-to-zero, inverted의 약자로 1은 변화가 없고 0이 나올 때만 시그널 레벨이 변하게 된다. 다음은 01001101을 NRZI로 인코딩 하는데, 그 초기 값이 1인 경우의 예이다.
---> data flow direction
data: 01001101
NRZI: 00100011 (inital code: 1)

USB 프로토콜에서 볼 때 계층적인 구조를 갖는다. 각 계층은 그 계층을 표현하는 descriptor을 갖고 있다. Device descriptor, Configuration descriptor, Interface descriptor, Endpoint descriptor 순으로 상위 계층에서 하위 계층으로 이뤄져 있다.

USB에서 IN, OUT의 개념은 USB Host를 중심으로 한다. USB Host에서 Device로 가는 것이 OUT이고 Device에서 USB Host 쪽으로 가는 것이 IN이다.

리눅스에서 USB는 리눅스 코딩 스타일을 따르지 않고 USB 규약 상에서 정의한 내용대로 하고 있다. 이는 USB 규약과의 혼동을 최소화하기 위한 것이다. 이에 따라 USB 규약 상에서 정의한 내용은 <linux/usb_ch9.h>라는 헤더 상에 정의되어 있다. USB의 하위 레벨 드라이버를 작성하는 경우라면 이 헤더를 포함시켜야만 할 것이다.

리눅스는 USB 호스트 및 클라이언트 디바이스 드라이버를 제공한다. 또 하나의 드라이버 프레임을 제공하는데, 디바이스에 올라가는 USB 드라이버가 바로 그것이다. 가젯(gadget)이라고 하는데, USB 호스트가 있는 컴퓨터에서 실행되는 것이 아니라 실제 디바이스 상에서 실행되는 것이다.

  • USB 호스트 드라이버 (USB HOST)
  • USB 클라이언트 드라이버 (USB Slave)
  • USB 가젯 드라이버 (USB Gadget)

1.11.1 USB 호스트 #

1.11.2 USB 클라이언트 #

1.11.3 USB 가젯 #

USB 디바이스에 올라가는 USB 드라이버를 USB 가젯 드라이버라 한다. 가젯 드라이버의 계층 구조는 다음과 같다.
+-------------------------+
|    Additional Layer     |
+-------------------------+
|     USB Upper Layer     |
+-------------------------+
|    USB Gadget Layer     |
+-------------------------+
|  USB Controller Layer   |
+=========================+
|         Hardware        |
+-------------------------+
일반적으로 USB Device Controller가 있으면 Controller를 액세스 하기 위한 인터페이스가 필요할 것이다. 이는 USB Controller Layer 자체로 별도로 있기 보다는 Core 아키텍처 레벨 상에서 인터페이스가 구현이 된다. 이후 gadget 드라이버가 USB Gadget Layer에 해당하게 된다. 실질적인 USB Device Controller에 대한 기능을 통해 Endpoint를 사용 가능한 인터페이스를 만들어주게 된다. 여기서 그 상위 계층에서 사용할 수 있도록 usb_gadget_regisger_driver 및 usb_gadget_unregister_driver 심볼을 export하게 된다. 이 두 함수를 통해 상위 계층의 함수와 USB Gadget Layer와 함수 포인터로 연결되게 된다. Additional Layer에서는 POSIX와 같은 인터페이스를 제공하기도 한다. open/close/read/write와 같은 것이 그것이다.

리눅스 커널의 drivers/usb/gadget 아래에 해당하는 파일들이 있다.

일반적으로 가젯에서 제공하는 기능은 다음과 같다:
  • USB 시리얼
  • USB 이더넷
  • USB 저장매체
  • USB 가젯 파일시스템
  • 그외

1.11.3.1 usb_gadget #

<linux/usb_gadget.h>에 정의되어 있다.
struct usb_gadget {
        /* readonly to gadget driver */
        const struct usb_gadget_ops     *ops;
        struct usb_ep                   *ep0;
        struct list_head                ep_list;        /* of usb_ep */
        enum usb_device_speed           speed;
        unsigned                        is_dualspeed:1;
        unsigned                        is_otg:1;
        unsigned                        is_a_peripheral:1;
        unsigned                        b_hnp_enable:1;
        unsigned                        a_hnp_support:1;
        unsigned                        a_alt_hnp_support:1;
        const char                      *name;
        struct device                   dev;
};

1.11.3.1.1 usb_gadget_ops #
struct usb_gadget_ops {
        int     (*get_frame)(struct usb_gadget *);
        int     (*wakeup)(struct usb_gadget *);
        int     (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
        int     (*vbus_session) (struct usb_gadget *, int is_active);
        int     (*vbus_draw) (struct usb_gadget *, unsigned mA);
        int     (*pullup) (struct usb_gadget *, int is_on);
        int     (*ioctl)(struct usb_gadget *,
                                unsigned code, unsigned long param);
};

get_frame
호스트로 부터 받은 SOF(Start Of Frame) 패킷이 유효한지를 결정한다. 유효하다면 타임스탬프 값을 반환하고 그렇지 않다면 -?EL2NSYNC 값을 반환한다.

wakeup
서스펜드 모드인 경우 깨운다.

set_selfpowered
디바이스의 전원 공급을 USB 버스를 통해 받을 지, 자체적으로 공급 받을 지(selfpower)를 결정한다.

vbus_session
VBUS 세션을 감지할 때마다 그 동작 여부를 결정한다.

vbus_draw
전원 공급을 결정한다. 그 단위는 mA이다(이는 OTG에서 의미가 있다).

pullup
Pull-up을 설정한다.

ioctl
내부적으로 ioctl을 사용하기에 별도 지정하지 않는다.

1.11.3.2 usb_gadget_driver #

<linux/gadget.h>에 정의되어 있다.
struct usb_gadget_driver {
        char                    *function;
        enum usb_device_speed   speed;
        int                     (*bind)(struct usb_gadget *);
        void                    (*unbind)(struct usb_gadget *);
        int                     (*setup)(struct usb_gadget *,
                                        const struct usb_ctrlrequest *);
        void                    (*disconnect)(struct usb_gadget *);
        void                    (*suspend)(struct usb_gadget *);
        void                    (*resume)(struct usb_gadget *);

        // FIXME support safe rmmod     
        struct device_driver    driver;
};
위 필드 내용 중 speed, bind, unbidn, setup은 반드시 지정되어 있어야만 한다.

function
문자열로 이름을 지정한다.

speed
USB의 전송 속도를 결정한다. 이는 <linux/usbch9.h>에 정의되어 있는데, USB_SPEED_LOW, USB_SPEED_FULL, USB_SPEED_HIGH, USB_SPEED_VARIABLE 이 가능하다. 각각 1.2Mbps, 12Mbps, 480Mbps, 가변 속도 등을 지원한다.

bind
이는 모듈이 적재될 때 실행이 된다. 디바이스를 지원 가능한지를 판별하고 필요한 자원에 대해 초기화를 한다.

unbind
이는 모듈의 적재가 해제될 때 실행이 된다. 할당 받은 자원에 대해 반환처리를 하게 된다.

setup
하드웨어 드라이버에서 처리하지 않은 endpoint의 기능에 대한 모든 것을 처리하게 된다.

disconnect 디바이스의 연결이 끊길 때 실행이 된다. 연결이 이뤄지면서 할당 받은 것들을 반환하여 다른 연결이 이뤄지는 해당 자원을 사용할 수 있도록 한다.

suspend
USB에 연결된 상태에서 서스펜드 모드로 들어가기 위하여 USB 컨트롤러의 Pull-up을 disable로 설정하여 연결이 끊어진 상태로 만든다. 이후 서스펜드 모드로 설정하게 된다.

resume
Pull-up을 enable하여 USB의 연결 상태를 복원하고 서스펜드 모드에서 빠져 나오도록 wakeup 과정을 거치게 된다.

1.11.3.3 usb_ep #

<linux/gadget.h>에 정의되어 있다.
struct usb_ep {
        void                    *driver_data;

        const char              *name;
        const struct usb_ep_ops *ops;
        struct list_head        ep_list;
        unsigned                maxpacket:16;
};
1.11.3.3.1 usb_ep_ops #
struct usb_ep_ops {
        int (*enable) (struct usb_ep *ep,
                const struct usb_endpoint_descriptor *desc);
        int (*disable) (struct usb_ep *ep);

        struct usb_request *(*alloc_request) (struct usb_ep *ep,
                gfp_t gfp_flags);
        void (*free_request) (struct usb_ep *ep, struct usb_request *req);

        void *(*alloc_buffer) (struct usb_ep *ep, unsigned bytes,
                dma_addr_t *dma, gfp_t gfp_flags);
        void (*free_buffer) (struct usb_ep *ep, void *buf, dma_addr_t dma,
                unsigned bytes);
        // NOTE:  on 2.6, drivers may also use dma_map() and
        // dma_sync_single_*() to directly manage dma overhead. 

        int (*queue) (struct usb_ep *ep, struct usb_request *req,
                gfp_t gfp_flags);
        int (*dequeue) (struct usb_ep *ep, struct usb_request *req);

        int (*set_halt) (struct usb_ep *ep, int value);
        int (*fifo_status) (struct usb_ep *ep);
        void (*fifo_flush) (struct usb_ep *ep);
};

enable
Endpoint에 대한 description을 초기화하고 endpoint를 enable시킨다.

disable
Endpoint에 대한 description을 해제하고 endpoint를 disable시킨다.

alloc_request
usb_req를 위한 메모리 공간을 할당받고 초기화 한다.

free_request
할당 받은 메모리 공간을 반환한다.

alloc_buffer
버퍼를 위한 메모리 공간을 할당받는다.

free_buffer
할당 받은 버퍼 공간을 반환한다.

queue
request를 큐에 등록하고 등록된 request를 처리한다.

dequeue
request를 큐에서 제거하고 request를 완료한다.

set_halt
Endpoint의 동작을 중단한다.

1.11.3.4 usb_gadget_register_driver #

USB 가젯 드라이버는 USB 디바이스 컨트롤러를 위한 드라이버이다. 이 드라이버 내에서 sub_gadget_register_driver함수가 정의되고 이는 상위 드라이버를 등록할 때 사용된다. 예를 들어 파일 스토리지 드라이버는 자신을 USB gadget 드라이버의 기능 드라이버로 등록하기 위해서는 usb_gadget_regiser_driver를 통해 등록을 해야만 한다.

int usb_gadget_register_driver (struct usb_gadget_driver *driver)

관련함수: usb_gadget_unregister_driver

1.11.3.5 usb_gadget_unregister_driver #

USB 가젯 드라이버의 등록을 해제한다.

int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)

관련함수: usb_gadget_register_driver

1.11.3.6 usb_ep_queue #

USB I/O 요구를 큐에 넣는다. 이는 <linux/usb_gadget.h>에 인라인 함수로 정의되어 있다.
static inline int usb_ep_queue(struct usb_ep *ep, struct usb_request *req,
                               gfp_t gfp_flags);

gfp_flags에는 GFP_KERNEL을 일반적으로 지정한다.

이 함수는 usb_ep 구조체->usb_ep_ops 구조체 내의 queue 함수 포인터에 등록된 queue 함수를 실행하게 된다.

성공시엔 0을 반환하고 실패시엔 음수를 반환한다.

1.11.3.7 usb_ep_dequeue #

USB I/O 요구를 큐에서 제거한다(취소 또는 중단).
static inline int usb_ep_dequeue (struct usb_ep *ep, struct usb_request *req);

이 함수는 usb_ep 구조체->usb_ep_ops 구조체 내의 dequeue 함수 포인터에 등록된 dequeue 함수를 실행하게 된다.

성공시엔 0을 반환하고 실패시엔 음수를 반환한다.

1.11.4 USB 가젯 드라이버 #

USB 디바이스 컨트롤러 드라이버를 가젯 드라이버라고 했다. 이러한 가젯 드라이버를 작성하는 것은 만만한 작업이 아니다. 사실상 USB에 기본적인 지식이 있어야 하며, 컨트롤러에 대한 이해가 선행되어야 하기 때문이다.

여기서는 자세한 내용보다는 대략적인 그 흐름을 파악하는 정도에서 기술하도록 한다.

일반적으로 USB 가젯은 리눅스 디바이스 모델에서 보면 일반적으로 플랫폼 디바이스에 속하는 것으로 구현하고 있다. 이에 따라 paltform_device 구조체에 대한 정의는 보드 레벨의 아키텍처를 정의하는 곳에서 정의하고 있다.

USB 가젯 드라이버에서는 platform_driver 구조체형의 udc_driver 형태의 구조체를 정의하게 된다. 그 내용은 대략 다음과 같다.
static struct platform_driver udc_driver = {
  .probe   = xxx_udc_probe,
  .remove  = __exit_p(xxx_udc_remove),
  .suspend = xxx_udc_suspend,
  .resume  = xxx_udc_resume,
  .driver  = {
    .owner = THIS_MODULE,
    .name  = (char *)driver_name,
  },
};

이후 모듈 초기화 함수에서 platform_driver_register를 통해 등록하게 된다. 모듈 해제시엔 platform_driver_unregister를 통해 등록을 해제하게 된다.

udc_driver에서 핵심은 xxx_udc_probe 함수이다. 여기서 가젯 드라이버에 초기화가 이뤄진다고 할 수 있다. probe에서는 처리하는 것은 다음과 같다.
  • 리소스에 대한 획득 및 초기화를 한다.
  • USB 디바이스 컨트롤러를 사용하기 위한 클럭 등의 초기화 및 활성화를 한다.
  • USB 디바이스 컨트롤러에 대한 초기화를 하고 Endpoint 0에 대한 default 기능을 설정한다.
  • 그외 Endpoint에 대한 초기화를 한다.
  • USB 디바이스의 Pull-up를 disable 상태로 놓고 기반 IRQ 핸들러를 등록한다.

대략적인 처리는 위와 같다. 이후 udc 기능 드라이버에서 usb_gadget_register_driver를 통해 드라이버를 등록하면 관련 오퍼레이션에 대한 처리를 처리하게 된다.

1.11.5 USB OTG #

  • HNP (Host Negotiation Protocol)
  • SRP (Session Request Protocol)

1.12 PCI #

1.13 PCMCIA #

1.14 SD/MMC #


last modified 2006-05-09 11:06:17
EditText|FindPage|DeletePage|LikePages Valid XHTML 1.0! Valid CSS! powered by MoniWiki
0.1365 sec