【demo】基于platform的misc driver
platform是Linux内的一种虚拟总线,称为platform总线,包含platform_device设备和platform_driver驱动两个对象,用于将驱动和设备分开,实现内核分离的思想。
一个成熟的字符设备驱动往往是把platform 跟字符注册程序结合起来,这里演示基于platform的misc driver的使用。
1 platform
platform总线的匹配优先级是:of_match_table > id_table > name
static struct platform_driver xx_driver = {
.probe = xx_probe,
.remove = xx_remove,
.id_table = tbl, ;2
.driver = {
.name = "phoenix", ;3
.of_match_table = NULL, ;1
},
};
其中
- of_match_table ;主要用于和device tree 匹配
- id_table ;用于和 struct platform_device 匹配,也就是设备树诞生之前的一种匹配方式
- driver.name ;用于和 struct platform_device 匹配,只不过是单一匹配,多设备用id_table
下面是platform的多设备demo
dev0.c
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
static struct resource dres[] = {
[0] = {
.name = "dev0",
.start = 0x23010000,
.end = 0x23010000 + 0xffff,
.flags = IORESOURCE_MEM,
},
[1] = {
.name = "dev0",
.start = 19,
.end = 22,
.flags = IORESOURCE_IRQ,
},
};
static void release(struct device *dev)
{
printk("%s %d\n", __func__, __LINE__);
return ;
}
static struct platform_device devc=
{
.name = "phoenix0",
.id = 0,
.dev.release = release,
.num_resources = ARRAY_SIZE(dres),
.resource = dres,
};
static int heo_init(void)
{
printk("%s %d\n", __func__, __LINE__);
return platform_device_register(&devc);
}
static void heo_exit(void)
{
printk("%s %d\n", __func__, __LINE__);
platform_device_unregister(&devc);
return;
}
module_init(heo_init);
module_exit(heo_exit);
MODULE_LICENSE("GPL");
dev1.c
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
static struct resource dres[] = {
[0] = {
.name = "dev1",
.start = 0x23020000,
.end = 0x23020000 + 0xffff,
.flags = IORESOURCE_MEM,
},
[1] = {
.name = "dev1",
.start = 85,
.end = 88,
.flags = IORESOURCE_IRQ,
},
};
static void release(struct device *dev)
{
printk("%s %d\n", __func__, __LINE__);
return ;
}
static struct platform_device devc=
{
.name = "phoenix1",
.id = 1,
.dev.release = release,
.num_resources = ARRAY_SIZE(dres),
.resource = dres,
};
static int heo_init(void)
{
printk("%s %d\n", __func__, __LINE__);
return platform_device_register(&devc);
}
static void heo_exit(void)
{
printk("%s %d\n", __func__, __LINE__);
platform_device_unregister(&devc);
return;
}
module_init(heo_init);
module_exit(heo_exit);
MODULE_LICENSE("GPL");
driver.c
#include <linux/module.h>
#include <linux/platform_device.h>
static int hello_probe(struct platform_device *pdev)
{
printk("%s %d, match ok, %d, %s\n", __func__, __LINE__, pdev->id, pdev->name);
return 0;
}
static int hello_remove(struct platform_device *pdev)
{
printk("%s %d\n", __func__, __LINE__);
return 0;
}
static struct platform_device_id tbl[] = {
{
"phoenix0"},
{
"phoenix1"},
{
},
};
static struct platform_driver hello_driver = {
.probe = hello_probe,
.remove = hello_remove,
.id_table = tbl,
.driver = {
.name = "phoenix",
},
};
static int hello_init(void)
{
printk("%s %d\n", __func__, __LINE__);
return platform_driver_register(&hello_driver);
}
static void hello_exit(void)
{
printk("%s %d\n", __func__, __LINE__);
platform_driver_unregister(&hello_driver);
return;
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
Makefile
ARCH := arm64
ARMCC := aarch64-linux-gnu-
KDIR := ${HOME}/xxx/kernel
ifneq ($(KERNELRELEASE),)
obj-m:=dev0.o dev1.o driver.o
$(info "2nd")
else
PWD:=$(shell pwd)
all:
$(info "1st")
make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(ARMCC) modules
.PHONY:clean
clean:
rm -rf *.ko *.o *.symvers *.mod.c *.mod.o *.order .tmp_versions .*.cmd
endif
2 misc
misc注册字符设备的方式比直接使用alloc_chrdev_region方式简便很多,最起码可以少敲几行代码。
扫描二维码关注公众号,回复:
15218611 查看本文章
misc+platform方式管理的driver,使用设备号关联struct inode 和struct miscdevice之间的关系,设备号的操作函数如下。
#include <linux/kdev_t.h>
- (dev_t)–>主设备号、次设备号
- MAJOR(dev_t dev);
- MINOR(dev_t dev);
- 主设备号、次设备号–>(dev_t)
- MKDEV(int major,int minor);
关联结构体
struct miscdevice {
int minor; //次设备号
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device; //当前设备,是device_create的返回值
};
struct inode {
...
atomic_t i_count;
...
dev_t i_rdev; //该成员表示设备文件的inode结构,它包含了真正的设备编号。
loff_t i_size;
...
const struct inode_operations *i_op;
const struct file_operations *i_fop; /* former->i_op->default_file_ops */
...
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev; //该成员表示字符设备的内核的内部结构。
};
...
void *i_private; /* fs or device private pointer */
};
**Note:**一定要确保struct platform_device
中 id
值跟 struct miscdevice
数组下标呼应。
跟上面platform的程序相比,只需要改下driver.c 即可
driver.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
static int dmisc_open(struct inode *inode, struct file *filp)
{
printk("%s %d, pdev %d %d %d\n", __func__, __LINE__, inode->i_rdev, MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
return 0;
}
static int dmisc_release(struct inode *inode, struct file *filp)
{
printk ("%s %d\n", __func__, __LINE__);
return 0;
}
static ssize_t dmisc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk ("%s %d\n", __func__, __LINE__);
return 0;
}
static ssize_t dmisc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
printk ("%s %d\n", __func__, __LINE__);
return 0;
}
static long dmisc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
printk ("%s %d\n", __func__, __LINE__);
return 0;
}
static struct file_operations drv_ops= {
.owner = THIS_MODULE,
.open = dmisc_open,
.read = dmisc_read,
.write = dmisc_write,
.release = dmisc_release,
.unlocked_ioctl = dmisc_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = dmisc_ioctl,
#endif
};
static struct miscdevice misc[] = {
{
.minor = MISC_DYNAMIC_MINOR,
.name = "phx0",
.fops = &drv_ops,
},
{
.minor = MISC_DYNAMIC_MINOR,
.name = "phx1",
.fops = &drv_ops,
},
};
static int drvheo_probe(struct platform_device *pdev)
{
if (misc_register(&misc[pdev->id]))
return -EFAULT;
printk("%s %d, match ok, pdev %d, %s, misc %s %d %d %d\n", __func__, __LINE__,
pdev->id, pdev->name,
misc[pdev->id].name, MKDEV(MISC_MAJOR, misc[pdev->id].minor), MISC_MAJOR, misc[pdev->id].minor);
return 0;
}
static int drvheo_remove(struct platform_device *pdev)
{
printk("%s %d\n", __func__, __LINE__);
misc_deregister(&misc[pdev->id]);
return 0;
}
static struct platform_device_id tbl[] = {
{
"phoenix0"},
{
"phoenix1"},
{
},
};
static struct platform_driver drvheo_driver = {
.probe = drvheo_probe,
.remove = drvheo_remove,
.id_table = tbl,
.driver = {
.name = "phoenix",
},
};
static int drvheo_init(void)
{
printk("%s %d\n", __func__, __LINE__);
return platform_driver_register(&drvheo_driver);
}
static void drvheo_exit(void)
{
printk("%s %d\n", __func__, __LINE__);
platform_driver_unregister(&drvheo_driver);
return;
}
module_init(drvheo_init);
module_exit(drvheo_exit);
MODULE_LICENSE("GPL");
打印输出如下:
[root@Hobot msplt] # insmod driver.ko
drvheo_init 102
[root@Hobot msplt] # insmod dev0.ko
heo_init 37
drvheo_probe 69, match ok, pdev 0, phoenix0, misc phx0 10485793 10 33
[root@Hobot msplt] # cat /dev/phx0
dmisc_open 15, pdev 10485793 10 33
dmisc_read 25
dmisc_release 20
[root@Hobot msplt] # insmod dev1.ko
heo_init 37
drvheo_probe 69, match ok, pdev 1, phoenix1, misc phx1 10485792 10 32
[root@Hobot msplt] # cat /dev/phx1
dmisc_open 15, pdev 10485792 10 32
dmisc_read 25
dmisc_release 20
–end