드라이버 프레임워크 기능에 대한 자세한 설명

기사의 틀은 이전 장을 기반으로 합니다.

https://blog.csdn.net/qq_52749711/article/details/132409329
는 거의 동일합니다. 여기의 이름은 변경되었지만 원래 이름은 그대로 유지됩니다.

module_init 입력 함수

유저 모드의 메인 함수와 마찬가지로 괄호 안의 함수를 호출하여 실행하며, 그 안의 함수가 진입 함수입니다.
예를 들면 다음과 같습니다.

static int  pin5_drv_init(void);//函数声明
module_init(pin5_drv_init)

함수에는 등록 드라이버와 같은 코드가 포함되어 있습니다.

module_exit 종료 함수

실행 후 실행해야 하는 기능, 원리는 위와 동일

module_exit(pin5_drv_exit);

이 함수에는 드라이버 등을 제거하기 위한 코드가 포함되어 있습니다.

레지스터_chrdev

register_chrdevfunction은 문자 장치 드라이버를 등록하는 데 사용되는 Linux 커널의 함수 중 하나입니다. 문자 장치는 터미널, 직렬 포트 등과 같은 문자 스트림과 상호 작용하는 장치입니다. 리눅스 커널에서 문자 장치 드라이버는 파일 연산 기능을 통해 문자 장치 읽기, 쓰기 등의 연산을 구현한다.

함수는 일반적으로 다음과 같이 선언됩니다.

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

매개변수 설명:

major: 메이저 장치 번호를 지정합니다.문자 장치의 경우 메이저 장치 번호는 장치 드라이버를 고유하게 식별하는 번호입니다. MAJOR(dev_t dev)주요 장치 번호는 매크로를 통해 얻을 수 있습니다 .
name: /proc/devices에 장치 이름을 표시하는 데 사용되는 장치 이름을 지정합니다.
fops: file_operations읽기, 쓰기, 열기, 닫기 등과 같은 문자 장치 드라이버의 작동 기능을 포함하는 구조에 대한 포인터입니다.

반환 값:

성공적으로 등록되면 할당된 주요 장치 번호가 반환됩니다.
등록에 실패하면 음수 값(일반적으로 오류 코드)이 반환됩니다.
이 함수를 사용하여 문자 장치 드라이버를 등록한 후 register_chrdev모듈의 초기화 함수에서 이 함수를 호출해야 합니다. 예를 들어:

#include <linux/module.h>
#include <linux/fs.h>

static int my_chardev_open(struct inode *inode, struct file *file)
{
    
    
    // Open operation implementation
    return 0;
}

static int my_chardev_release(struct inode *inode, struct file *file)
{
    
    
    // Release operation implementation
    return 0;
}

static struct file_operations my_fops = {
    
    
    .open = my_chardev_open,
    .release = my_chardev_release,
    // Other operation implementations
};

static int __init my_chardev_init(void)
{
    
    
    int major = register_chrdev(0, "my_chardev", &my_fops);
    if (major < 0) {
    
    
        printk(KERN_ALERT "Failed to register char device\n");
        return major;
    }
    printk(KERN_INFO "Registered char device with major number %d\n", major);
    return 0;
}

static void __exit my_chardev_exit(void)
{
    
    
    unregister_chrdev(major, "my_chardev");
    printk(KERN_INFO "Unregistered char device\n");
}

module_init(my_chardev_init);
module_exit(my_chardev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample Character Device Driver");

my_chardev_open(열기 기능)

my_chardev_open이는 장치 열기 작업을 처리하는 데 사용되는 문자 장치 드라이버의 함수입니다. struct file_operations문자 장치를 등록할 때 구조에 구현된 열기 함수에 대한 포인터를 설정해야 합니다 . 장치가 열릴 때 커널이 이 함수를 호출합니다.

이것은 my_chardev_open 함수의 정의입니다:

static int my_chardev_open(struct inode *inode, struct file *file)
{
    
    
    // Open operation implementation
    return 0;
}

이 기능에서는 장치 상태 초기화, 리소스 할당, 장치 열기 횟수 기록 등 장치 열기 작업과 관련된 작업을 수행할 수 있습니다. 이 함수는 두 개의 매개변수를 받습니다:

struct inode *inode: 파일의 인덱스 노드(inode)에 대한 포인터이며 파일에 대한 메타데이터 정보를 포함합니다. 장치가 열리면 커널은 관련 inode를 open 함수에 전달합니다.

  • 구조체 아이노드 *아이노드:

  • inode(인덱스 노드)에는 파일의 권한, 크기, 사용자 등과 같은 파일의 메타데이터 정보가 포함됩니다. 파일 시스템에서 파일을 고유하게 식별합니다. 필드를 사용하여 파일의 권한 정보를 얻을
    수 있고 필드를 사용하여 파일의 사용자 및 그룹 ID를 얻을 수 있습니다.i_modei_uidi_gid

  • i_private필드는 장치와 관련된 개인 데이터를 저장하는 데 사용될 수 있습니다. 드라이버 초기화 중에 이 필드를 설정할 수 있습니다.

struct file *file: 파일을 나타내는 데이터 구조에 대한 포인터입니다. 접근 모드, 파일 위치 등 파일 작업과 관련된 정보가 포함되어 있습니다.

  • 구조체 파일 *파일:

  • 파일 구조에는 파일 위치, 액세스 모드 등 파일 열기와 관련된 정보가 포함됩니다.

  • f_pos 필드는 파일의 현재 위치 오프셋을 나타냅니다.

  • f_flags 필드에는 읽기, 쓰기, 추가 등과 같이 파일을 열 때 사용되는 플래그가 포함됩니다.

  • f_mode 필드에는 파일을 열 때의 액세스 모드가 포함되어 있으며 이는 비트 연산을 통해 확인할 수 있습니다.

  • private_data 필드는 파일 작업과 관련된 비공개 데이터를 저장하는 데 사용할 수 있으며, 파일을 열 때 설정할 수 있습니다.

함수의 반환 값은 정수이며 일반적으로 작업이 성공했는지 여부를 나타내는 데 사용됩니다. 열기 작업이 성공하면 오류가 발생하지 않았음을 나타내는 0을 반환하는 것이 일반적입니다. 오류가 발생하면 다른 오류 코드에 해당하는 음수 값이 반환될 수 있습니다.

my_chardev_open다음은 함수에서 열기 작업을 구현하는 방법을 보여주는 예입니다 .

static int my_chardev_open(struct inode *inode, struct file *file)
{
    
    
    // Perform device-specific tasks during open, if any
    printk(KERN_INFO "Device opened\n");
    
    // Increment the device's open count (if you want to track it)
    try_module_get(THIS_MODULE);
    
    return 0; // Return 0 to indicate success
}

위의 예에서는 printk장치가 열렸음을 나타내는 로그를 출력하는 함수를 사용합니다. 장치가 열린 횟수를 추적하려면 를 try_module_get(THIS_MODULE)사용하여 커널 모듈의 참조 카운트를 늘릴 수 있습니다. 이 방법을 사용하면 장치가 종료될 때 module_put(THIS_MODULE)참조 카운트를 줄일 수 있습니다.

즉, my_chardev_open캐릭터 디바이스가 열렸을 때 일부 동작을 수행할 수 있도록 하는 기능으로, 디바이스의 특성과 필요에 따라 적절한 열림 동작 코드를 작성할 수 있습니다.

static int my_chardev_open(struct inode *inode, struct file *file)
{
    
    
    // 访问 inode 信息
    printk(KERN_INFO "文件权限: %o\n", inode->i_mode & 0777);
    printk(KERN_INFO "文件所有者用户ID: %d\n", inode->i_uid.val);
    printk(KERN_INFO "文件所有者组ID: %d\n", inode->i_gid.val);

    // 访问文件信息
    printk(KERN_INFO "文件位置: %lld\n", file->f_pos);
    printk(KERN_INFO "文件标志: %x\n", file->f_flags);

    // 在文件结构中设置 private_data
    file->private_data = /* 在此处添加你的私有数据 */;

    return 0;
}

쓰기 기능

함수 프로토타입은 pin5_write사용자 공간에서 장치로 데이터를 쓰는 데 사용되는 문자 장치 드라이버의 쓰기 작업 함수와 유사합니다. 이 함수의 매개변수가 무엇을 의미하는지 설명하겠습니다.

file: 열린 파일을 나타내는 구조체 파일 포인터로, 열린 파일과 관련된 정보를 담고 있습니다. 이 매개변수는 기록할 파일을 지정합니다.
buf: 장치에 쓸 데이터가 포함된 사용자 공간 버퍼에 대한 포인터입니다. __user는 이것이 사용자 공간 데이터이므로 커널 공간에서 주의해서 처리해야 함을 나타내는 플래그입니다.
count: 쓸 바이트 수로, 버퍼에 있는 데이터의 길이를 지정합니다.
ppos: 파일의 현재 위치 오프셋을 나타내는 loff_t 유형에 대한 포인터입니다. 쓰기 작업 중에 커널이 이 위치를 업데이트해야 할 수도 있습니다.

함수의 반환 값은 작성된 바이트 수를 나타내는 ssize_t 유형입니다. 오류가 발생하면 다른 오류 코드에 해당하는 음수 값이 반환됩니다.
다음은 작동 방식을 설명하기 위해 pin5_write 함수 예제의 단순화된 구현입니다.

static ssize_t pin5_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
    
    
        printk("pin5_write\n");
        return 0;
}

실제 드라이버 개발에서는 장치의 특성과 요구 사항에 따라 pin5_write 기능을 구현해야 합니다. 예를 들어 필요에 따라 장치에 데이터 쓰기, 파일 오프셋 업데이트 등의 작업을 추가할 수 있습니다. 동시에 커널 공간과 사용자 공간 간의 적절한 데이터 복사 및 검증을 보장하여 보안과 안정성을 보장합니다.

#include <linux/fs.h>
#include <linux/uaccess.h>

// 假设你的设备在打开时已经被初始化为pin5设备

static ssize_t pin5_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    
    
    ssize_t written = 0;

    // 验证用户空间内存并将数据从用户空间复制到内核空间
    if (!access_ok(VERIFY_READ, buf, count))
        return -EFAULT;

    // 在内核中分配一个临时缓冲区
    char *kernel_buf = kmalloc(count, GFP_KERNEL);
    if (!kernel_buf)
        return -ENOMEM;

    // 从用户空间复制数据到内核缓冲区
    if (copy_from_user(kernel_buf, buf, count)) {
    
    
        kfree(kernel_buf);
        return -EFAULT;
    }

    // 在这里执行将数据写入设备的操作,示例中省略

    // 记录写入的字节数
    written = count;

    // 释放临时分配的内存
    kfree(kernel_buf);

    // 更新文件位置偏移
    *ppos += written;

    return written;
}

위 코드에서는 먼저 사용자 공간 메모리에 액세스할 수 있는지 확인하고 access_ok이를 달성하기 위해 함수를 사용합니다. 그런 다음 커널 공간에 임시 버퍼를 할당하고 copy_from_user함수를 사용하여 사용자 공간에서 커널 공간으로 데이터를 복사했습니다.

다음 부분은 커널 버퍼에서 장치로 데이터를 쓰는 실제 작업이어야 합니다. 여기에는 장치의 특성에 따라 구현될 수 있는 장치 레지스터의 작동, 데이터 전송 등이 포함될 수 있습니다.

마지막으로, 쓴 바이트 수를 기록하고 임시로 할당된 메모리를 해제합니다. 또한 쓰기 작업 후에도 올바른 파일 위치를 유지하기 위해 파일 위치 오프셋이 업데이트되었습니다.

실제 드라이버 개발에서는 기기의 특성, 커널 버전, 요구 사항에 따라 적절하게 처리해야 한다는 점에 유의해야 합니다. 데이터의 정확성과 보안, 메모리의 합리적인 관리를 보장하는 것은 매우 중요합니다.

class_create

class_create 함수는 장치 클래스를 생성하는 데 사용되는 Linux 커널의 함수입니다. 장치 클래스는 관련 장치를 그룹화하고 이러한 장치에 대한 공통 속성 및 작업을 제공하는 장치를 구성 및 관리하는 방법입니다. 캐릭터 디바이스, 블록 디바이스 등을 관리하는데 유용합니다.

struct class *class_create(struct module *owner, const char *name);

매개변수 설명:

owner: 이 클래스를 소유하는 커널 모듈을 지정합니다. 일반적으로 THIS_MODULE로 설정할 수 있습니다. 이는 클래스를 생성한 모듈이 현재 모듈임을 의미합니다.
name: 장치 클래스의 이름을 지정합니다. 이 이름은 /sys/class 디렉토리에 표시되며 장치 클래스를 식별하는 데 사용됩니다.
반환 값:

성공하면 새로 생성된 장치 클래스에 대한 포인터를 반환합니다.
실패하면 오류 포인터가 반환됩니다.
함수를 호출한 후 class_create관련 장치를 이 클래스에 등록하면 커널은 이러한 장치에 해당하는 장치 파일을 만들고 /sys/class디렉터리 아래에 해당 디렉터리를 만듭니다.

다음은 class_create 함수를 사용하는 방법을 보여주는 예입니다.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>

static struct class *my_class;

static int __init my_module_init(void)
{
    
    
    my_class = class_create(THIS_MODULE, "my_device_class");
    if (IS_ERR(my_class)) {
    
    
        printk(KERN_ALERT "Failed to create class\n");
        return PTR_ERR(my_class);
    }

    // Create and register devices here

    printk(KERN_INFO "Module initialized\n");
    return 0;
}

static void __exit my_module_exit(void)
{
    
    
    class_destroy(my_class);
    printk(KERN_INFO "Module exited\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample Device Class Module");

위의 예에서 class_create이름이 지정된 장치 클래스는 먼저 함수를 사용하여 생성됩니다 "my_device_class". 그런 다음 모듈의 초기화 함수에서 관련 장치를 생성하고 등록하여 이 클래스에 추가할 수 있습니다. 모듈이 종료되면 class_destroy이 함수를 사용하여 장치 클래스를 삭제합니다.

커널 모듈 개발에는 class_create 및 관련 장치 동작 함수가 사용된다는 점에 유의하세요. 문자 장치 드라이버나 다른 유형의 커널 모듈을 개발하는 경우 이러한 기능을 사용하여 필요에 따라 장치 클래스와 장치를 관리할 수 있습니다.

device_create

device_createfunction은 Linux 커널에서 장치를 생성하는 데 사용되는 함수로, 장치 클래스에 대한 하위 디렉터리를 /sys/class/생성 하고 해당 하위 디렉터리에 장치 파일을 생성합니다. 이 기능은 일반적으로 class_create장치를 특정 장치 클래스와 연결하는 기능과 함께 사용됩니다.

다음은 device_create함수의 프로토타입입니다.

struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);

매개변수 설명:

class: 장치가 속한 장치 클래스를 지정합니다. 이 매개변수는 일반적으로 class_create 함수에 의해 반환된 장치 클래스 포인터입니다.
parent: 해당 장치의 상위 장치를 지정하며, 일반적으로 NULL로 설정 가능하다.
devt: 장치의 장치 번호를 지정합니다.MKDEV(major,minor) 매크로를 사용하여 장치 번호를 생성할 수 있습니다.
drvdata:장치의 개인 데이터 포인터를 지정합니다.
fmt: /sys/class/ 아래에 하위 디렉터리와 장치 파일을 만드는 데 사용되는 장치 이름의 형식을 지정합니다.

반환 값:

성공하면 새로 생성된 장치에 대한 포인터를 반환합니다.
실패하면 오류 포인터가 반환됩니다.
다음은 device_create 함수를 사용하는 방법을 보여주는 예입니다.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>

static struct class *my_class;
static struct device *my_device;

static int __init my_module_init(void)
{
    
    
    my_class = class_create(THIS_MODULE, "my_device_class");
    if (IS_ERR(my_class)) {
    
    
        printk(KERN_ALERT "Failed to create class\n");
        return PTR_ERR(my_class);
    }

    // Create and register devices here

    my_device = device_create(my_class, NULL, MKDEV(0, 0), NULL, "my_device");
    if (IS_ERR(my_device)) {
    
    
        printk(KERN_ALERT "Failed to create device\n");
        class_destroy(my_class);
        return PTR_ERR(my_device);
    }

    printk(KERN_INFO "Module initialized\n");
    return 0;
}

static void __exit my_module_exit(void)
{
    
    
    device_destroy(my_class, MKDEV(0, 0));
    class_destroy(my_class);
    printk(KERN_INFO "Module exited\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample Device Module");

위의 예에서는 먼저 class_create함수를 사용하여 장치 클래스를 만듭니다. 그런 다음 device_create해당 클래스 아래에 이름이 지정된 장치가 function 을 사용하여 생성되었습니다 "my_device". 모듈 종료 시 device_destroy장치를 파괴하는 함수를 사용한 다음 class_destroy장치 클래스를 파괴하는 함수를 사용합니다.

device_create커널 모듈 개발에는 관련 장치 작동 기능이 사용된다는 점에 유의해야 합니다 . 문자 장치 드라이버나 다른 유형의 커널 모듈을 개발하는 경우 이러한 기능을 사용하여 필요에 따라 장치를 생성하고 관리할 수 있습니다.

아이오레맵

Linux 커널 개발에서는 ioremap커널이 이러한 주소에서 하드웨어 레지스터, 주변 메모리 등에 직접 액세스할 수 있도록 물리적 주소를 커널 공간에 매핑하는 데 함수가 사용됩니다. 이는 하드웨어 레지스터에 액세스하려면 일반적으로 특수 I/O 명령어를 사용해야 하고 이러한 명령어는 커널 모드에서만 실행될 수 있기 때문입니다.

ioremap함수의 프로토타입은 다음과 같습니다.

void __iomem *ioremap(resource_size_t phys_addr, size_t size);

매개변수 설명:

phys_addr: 매핑할 물리적 주소입니다.
size: 맵의 크기(바이트)입니다.
반환 값:

성공하면 매핑된 영역에 대한 포인터를 반환합니다.
실패하면 널 포인터가 반환됩니다.
기능을 사용하기 전에 ioremap귀하의 실제 주소가 유효한지, 다른 사람이 사용하지 않는지 확인해야 합니다. 매핑 후에는 일반 메모리에 액세스하는 것처럼 커널의 포인터를 사용하여 매핑된 영역의 내용에 액세스할 수 있습니다.

다음은 ioremap 함수를 사용하여 하드웨어 레지스터에 액세스하는 방법을 보여주는 예입니다.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/io.h>

static void __iomem *hw_regs;

static int __init my_module_init(void)
{
    
    
    // 为物理地址0x12345678映射一个大小为4字节的映射区域
    hw_regs = ioremap(0x12345678, 4);
    if (!hw_regs) {
    
    
        printk(KERN_ALERT "Failed to remap hardware registers\n");
        return -ENOMEM;
    }

    // 使用映射后的指针来访问寄存器
    unsigned int reg_value = readl(hw_regs);
    printk(KERN_INFO "Hardware register value: %u\n", reg_value);

    printk(KERN_INFO "Module initialized\n");
    return 0;
}

static void __exit my_module_exit(void)
{
    
    
    // 取消映射
    iounmap(hw_regs);

    printk(KERN_INFO "Module exited\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample I/O Remapping Module");

ioremap위의 예에서는 먼저 물리적 주소를 커널 공간에 매핑 하는 함수를 사용한 0x12345678다음, 이 readl함수를 사용하여 매핑된 영역에서 하드웨어 레지스터의 값을 읽습니다. 모듈이 종료될 때 iounmap매핑을 해제하려면 함수를 사용하세요.

ioremap 및 관련 기능은 일반적으로 낮은 수준의 하드웨어 프로그래밍에 사용되며 매핑된 영역에 액세스할 때 하드웨어 장치의 사양 및 요구 사항을 따르도록 주의해서 사용해야 합니다.

MODULE_LICENSE(“GPL”);

MODULE_LICENSE(“GPL”);
MODULE_AUTHOR("당신의 이름");
MODULE_DESCRIPTION("샘플 I/O 재매핑 모듈");

MODULE_LICENSE("GPL");:
이 매크로는 커널 모듈의 라이센스 유형을 지정하는 데 사용됩니다. Linux 커널 개발에서는 다양한 라이센스 유형을 따르는 것이 중요합니다. "GPL"은 GPL을 준수하기 위해 파생 코드가 필요한 오픈 소스 소프트웨어 라이센스인 GNU General Public License의 약자입니다. 모듈이 다른 라이센스를 사용하는 경우 여기에서 "LGPL", "MIT" 등과 같은 다른 라이센스 유형을 지정할 수 있습니다.

MODULE_AUTHOR("Your Name");:
이 매크로는 커널 모듈의 작성자 정보를 지정하는 데 사용됩니다. "Your Name"을 실제 이름이나 로고로 바꿔야 합니다.

MODULE_DESCRIPTION("Sample I/O Remapping Module");:
이 매크로는 커널 모듈에 대한 간략한 설명을 제공하는 데 사용됩니다. 여기서는 모듈의 기능, 목적 또는 특징을 설명할 수 있습니다. 모듈 설명은 일반적으로 모듈이 로드될 때 표시됩니다.

이러한 매크로는 커널 모듈에 대한 정보를 기록하고 표시하는 표준화된 방법을 제공하여 개발자가 모듈의 특성과 배경을 이해하는 데 도움을 줍니다. 다른 개발자나 커널 유지관리자가 여러분의 코드를 보면 모듈에 대한 기본 정보를 쉽게 이해할 수 있습니다.

마치다

질문이 있으시면 언제든지 질문해 주시면 함께 발전해 나갈 수 있습니다.

추천

출처blog.csdn.net/qq_52749711/article/details/132431438