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");