Linux Kernel Driver 分离模型示例

类型定义

// hello_def.h
#ifndef __HELLO_DEF_H__
#define	__HELLO_DEF_H__

#define HELLO_OPEN      (0X10001001U)
#define HELLO_CLOSE     (0X10001002U)
#define HELLO_INFO      (0X10001003U)

// 声明描述 hello 硬件相关的数据结构(自定义硬件信息)
struct hello_resource
{
    int phys_addr;    // 寄存器的物理地址
    int size;         // 寄存器的内存大小
    int pin;          // 管脚编号
};

#endif // __HELLO_DEF_H__

硬件部分

// hello_dev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "hello_def.h"

// 定义hello的硬件信息
// 此为对应的硬件的固有信息
static struct hello_resource hello_info = {
    .phys_addr = 0xE0200000,
    .size = 8,
    .pin = 3
};

static void hello_release(struct device *dev)
{
    printk("=[frocheng]=[%s]=[%s]=\n", __FILE__, __func__);
}

// 定义初始化硬件节点
static struct platform_device hello_dev = {
    .name = "hello",    // 用于匹配
    .id = -1,
    .dev = {
        .platform_data = &hello_info,   // 装载自定义的硬件信息
        .release = hello_release        // 避免内核警告
    }
};

static int __init hello_dev_init(void)
{
    printk("=[frocheng]=[%s]=[%s]=\n", __FILE__, __func__);
    // 注册硬件节点到dev链表,内核进行遍历和匹配
    platform_device_register(&hello_dev);
    return 0;
}

static void __exit hello_dev_exit(void)
{
    // 删除节点
    platform_device_unregister(&hello_dev);
    printk("=[frocheng]=[%s]=[%s]=\n", __FILE__, __func__);
}

module_init(hello_dev_init);
module_exit(hello_dev_exit);
MODULE_LICENSE("GPL");

软件部分

// hello_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include "hello_def.h"

static struct hello_resource hello_info;

static long hello_unlocked_ioctl(struct file * f, unsigned int cmd, unsigned long arg)
{
    switch (cmd)
    {
        case HELLO_OPEN:
        {
            printk("===[frocheng]===[%s]===[%s]===[open]===\n",__FILE__, __func__);
        }
        break;
        case HELLO_CLOSE:
        {
            printk("===[frocheng]===[%s]===[%s]===[close]===\n",__FILE__, __func__);
        }
        break;
        case HELLO_INFO:
        {
            // info 命令,我们将我们的固有的硬件信息,返回给用户空间
            printk("===[frocheng]===[%s]===[%s]===[info]===\n",__FILE__, __func__);
            copy_to_user((void*)arg, &hello_info, sizeof(hello_info));
        }
        break;
        default:
        {
            printk("===[frocheng]===[%s]===[%s]===[Unkown Command]===\n",__FILE__, __func__);
        }
        return -1;
    }
    return 0;
}

// 定义初始化hello的硬件操作对象
static struct file_operations hello_fops =
{
    .owner = THIS_MODULE,
    .unlocked_ioctl = hello_unlocked_ioctl
};

static const char name[] = "hello";

// 定义初始化混杂设备对象
static struct miscdevice hello_misc =
{
    .minor = MISC_DYNAMIC_MINOR,
    .name = name,
    .fops = &hello_fops
};

// pdev指向匹配成功的硬件节点(hello_dev.c的hello_dev)
static int hello_probe(struct platform_device *pdev)
{
    struct hello_resource *pdata;
    printk("=[frocheng]=[%s]=[%s]=\n", __FILE__, __func__);
	pdata = pdev->dev.platform_data;
    // 获取 硬件信息,进行处理,此处进行打印展示处理
    printk("=[frocheng]=[%s]=[%s]=[0X%X]=[%d]=[%d]=\n",
        __FILE__, __func__, pdata->phys_addr, pdata->size, pdata->pin);
    hello_info.phys_addr = pdata->phys_addr;
    hello_info.pin = pdata->pin;
    hello_info.size = pdata->size;
    
    misc_register(&hello_misc); // 注册
    return 0; 
}

// pdev指向匹配成功的硬件节点(hello_dev.c的hello_dev)
static int hello_remove(struct platform_device *pdev)
{
    misc_deregister(&hello_misc); // 卸载
    printk("=[frocheng]=[%s]=[%s]=\n", __FILE__, __func__);
    return 0; 
}

// 定义初始化软件节点
static struct platform_driver hello_drv = {
    .driver = {
        .name = "hello"     // 用于匹配
    },
    .probe = hello_probe,   // 匹配成功调用
    .remove = hello_remove  // 卸载软件或者硬件调用
};

static int __init hello_drv_init(void)
{
    printk("=[frocheng]=[%s]=[%s]=\n", __FILE__, __func__);
    // 注册硬件节点到drv链表,内核进行遍历和匹配
    platform_driver_register(&hello_drv);
    return 0;
}

static void __exit hello_drv_exit(void)
{
    // 删除节点
    platform_driver_unregister(&hello_drv);
    printk("=[frocheng]=[%s]=[%s]=\n", __FILE__, __func__);
}

module_init(hello_drv_init);
module_exit(hello_drv_exit);
MODULE_LICENSE("GPL");

测试代码

// test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#define HELLO_OPEN  (0X10001001UL)
#define HELLO_CLOSE (0X10001002UL)
#define HELLO_INFO  (0X10001003UL)

struct dev_info
{
    int pysy;
    int size;
    int pin;
};

int main()
{
    int fd = open("/dev/hello", O_RDWR);
    if (fd < 0)
    {
        perror("open");
        return 0;
    }
    do
    {
        int ret = 0;
        ret = ioctl(fd, HELLO_OPEN);
        if (ret != 0)
        {
            perror("ioctl");
            break;
        }
        printf("open success!\n");
        sleep(1);
        ret = ioctl(fd, HELLO_CLOSE);
        if (ret != 0)
        {
            perror("ioctl");
            break;
        }
        printf("close success!\n");
        sleep(1);
        struct dev_info info;
        ret = ioctl(fd, HELLO_INFO, &info);
        if (ret != 0)
        {
            perror("ioctl");
            break;
        }
        printf("info success, 0X%X, %d, %d!\n",info.pysy, info.size, info.pin);

        ret = ioctl(fd, 234);
        if (ret != 0)
        {
            perror("ioctl");
            printf("Other command error!\n");
            break;
        }
    } while(0);
    close(fd);
    return 0;
}

编译

测试程序

(不要跟驱动代码放到一个目录下)

gcc -g tets.c -o ioctl_test

驱动

Makefile

KERNDIR:= /lib/modules/`uname -r`/build/

PWD:= $(shell pwd)

obj-m+= hello_dev.o
obj-m+= hello_drv.o

all:
	make -C $(KERNDIR) M=$(PWD) modules

clean:
	make -C $(KERNDIR) M=$(PWD) clean

测试

1. sudo insmod hello_dev.ko

2. sudo insmod hello_drv.o

3. sudo ./test

4. sudo rmmod hello_drv

5. sudo rmmod hello_dev

结果

上面两个红色框中,其实是我这把 ioctrl_test应用程序运行了两次。如果运行一次,红色框中几条日志就只会打印一遍。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值