Linux驱动测试示例
+ -

LED虚拟平台设备驱动程序

2025-07-24 9 0

platform 将驱动分为 platform_device (设备文件)和 platform_driver(总线驱动文件),他们会通过platform(虚拟总线)来相配对。
platform 驱动本质上还是字符设备驱动,他们只是改变注册方式。

driver.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/io.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/uaccess.h>

static int major;
static struct class *cls;
static struct device *gpio_device;
static unsigned int *led_base; //led 寄存器基地址
//写设备触发的服务函数 file:设备 buf:数据缓存区 count:数据个数单位字节

static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    int val,ret;
    //把 buff 缓存数据拷贝到 val 地址空间
    ret = copy_from_user(&val, buf, count);
    //把 val 的值写进 led_base 寄存器
    //iowrite32(val, led_base);
    printk("led : Write 0x%x to 0x%p.\n", val,led_base);
    return 0;
}

//打开设备函数
static int led_open (struct inode *inode, struct file *filep)
{
    printk("dev is open!");
    return 0;
}

//释放资源
static int led_release(struct inode *inode, struct file *filep)
{
    printk("dev is release!");
    return 0;
}

// ile_operations 结构体
static const struct file_operations led_fops=
{
    .owner = THIS_MODULE,
    .open = led_open,
    .write = led_write,
    .release = led_release,
};

// probe 探测函数,在驱动和设备匹配后执行这个函数
static int led_probe(struct platform_device *pdev)
{
    //struct resource *res;
    printk("match ok!");
    //res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取到资源,资源的定义在 led_dev.c 文件中

    //led_base = ioremap(res->start, res->end - res->start + 1);//获取到 AXI-GPIO 的地址空间,实现内存映射
    printk("led_probe, found led\n");

    major = register_chrdev(0, "myled", &led_fops);//注册设备
    printk("register_chrdev major=0x%x",(int)major);

    cls = class_create("myled");
    printk("cls =%p",cls);

    gpio_device = device_create(cls,NULL,MKDEV(major, 0),NULL,"led");//mknod /dev/hello
    if(IS_ERR(gpio_device))
    {
        class_destroy(cls);
        unregister_chrdev(major,"myled");
        return -EBUSY;
    }
    return 0;
}

static int led_remove(struct platform_device *pdev)
{
    printk("Module exit!\n");
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    unregister_chrdev(major,"myled");//删除/dev/目录下的设备节点
    //iounmap(led_base); //取消物理地址的映射
    return 0;
}
static struct platform_driver led_drv =
{
    .probe = led_probe,
    .remove = led_remove,
    .driver ={
        .name = "myled",
    }
};

static int led_drv_init(void)
{
    printk("led_drv_init");
    return platform_driver_register(&led_drv);
}

static void led_drv_exit(void)
{
    printk("led_drv_exit");
    platform_driver_unregister(&led_drv);
    return;
}


module_init(led_drv_init); //模块初始化接口
module_exit(led_drv_exit); //模块推出接口
MODULE_LICENSE("GPL");

device.c

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

#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_IRQ 0x00000400

static struct resource led_resource[] =
{
    [0] = {
    .start = 0x41200000,
    .end = 0x41200000+0x10000-1,
    .flags = IORESOURCE_MEM,
    }
};

static void run_led_release(struct device *dev)
{
    printk("led_dev_release\n");
    return ;
}

static struct platform_device led_device=
{
    .name = "myled",
    .id = -1,
    .dev.release = run_led_release,
    .num_resources = 0,//ARRAY_SIZE(led_resource),
    .resource = 0//led_resource
};

static int led_dev_init(void)
{
    printk("led_dev_init");
    return platform_device_register(&led_device);
}

static void led_dev_exit(void)
{
    printk("led_dev_exit");
    platform_device_unregister(&led_device);
    return;
}

module_init(led_dev_init); //模块初始化接口
module_exit(led_dev_exit); //模块推出接口
MODULE_LICENSE("GPL");

Makefile

obj-m += device.o driver.o
all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

ledapp.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
    int i;
    int fd;
    int val=1;

    fd = open("/dev/led", O_RDWR); //打开设备
    if (fd <= 0) {
        printf("open /dev/led_dev error!\n");
        return 0;
    }

    //LED 流水灯
    while(1)
    {
        for(i=0;i<4;i++)
        {
            val = (1<<i);
            write(fd,&val,4); //把 val 的值写到 fd 设备中,大小为 4 字节
            printf("val=%d\n",val);
            sleep(1);
        }
    }
    return 0;
}

驱动和应用编译后:

-rw-rw-r-- 1 zzmt zzmt 308712  7月 24 15:12 device.ko
-rw-rw-r-- 1 zzmt zzmt   2921  7月 24 15:08 driver.c
-rw-rw-r-- 1 zzmt zzmt 326824  7月 24 15:12 driver.ko
-rw-rw-r-- 1 zzmt zzmt    526  7月 24 15:14 ledapp.c
-rwxrwxr-x 1 zzmt zzmt  16184  7月 24 15:15 ledapp.exe
-rw-rw-r-- 1 zzmt zzmt    166  7月 24 14:20 Makefile

安装驱动:

zzmt@zzmt-virtual-machine:~/Desktop/platform$ sudo insmod driver.ko
zzmt@zzmt-virtual-machine:~/Desktop/platform$ sudo insmod device.ko
zzmt@zzmt-virtual-machine:~/Desktop/platform$ ls /dev/ | grep led
led

zzmt@zzmt-virtual-machine:~/Desktop/platform$ sudo dmesg | tail -n 10
[17887.818414] led_drv_init
[17895.051485] led_dev_init
[17895.052194] match ok!
[17895.052196] led_probe, found led
[17895.052200] register_chrdev major=0xf0

测试应用:

zzmt@zzmt-virtual-machine:~/Desktop/platform$ sudo ./ledapp.exe 
val=1
val=2
val=4
val=8
^C
zzmt@zzmt-virtual-machine:~/Desktop/platform$ sudo dmesg | tail -n 10
[17895.051485] led_dev_init
[17895.052194] match ok!
[17895.052196] led_probe, found led
[17895.052200] register_chrdev major=0xf0
[17895.052215] cls =000000004396fc91
[17948.439288] dev is open!
[17948.439294] led : Write 0x1 to 0x0000000000000000.
[17949.439917] led : Write 0x2 to 0x0000000000000000.
[17950.440465] led : Write 0x4 to 0x0000000000000000.

备注

在内核5.10.134-18.al8.x86_64

    cls = class_create(THIS_MODULE,"myled");

而在6.8.0-64-generic

    cls = class_create("myled");

0 篇笔记 写笔记

LED虚拟平台设备驱动程序
platform 将驱动分为 platform_device (设备文件)和 platform_driver(总线驱动文件),他们会通过platform(虚拟总线)来相配对。platform 驱动本质上还是字符设备驱动,他们只是改变注册方式。driver.c#include
关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!