【demo】platform-based misc driver

【demo】platform-based misc driver


platform is a virtual bus in Linux, called platform bus, which contains two objects, platform_device and platform_driver, which are used to separate the driver from the device and realize the idea of ​​kernel separation.

A mature character device driver often combines the platform with the character registration program, here demonstrates the use of the platform-based misc driver.

1 platform

The matching priority of the platform bus is: 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
    },
};

in

  • of_match_table; mainly used to match with device tree
  • id_table; used to match with struct platform_device, which is a matching method before the birth of the device tree
  • driver.name; used to match with struct platform_device, only a single match, multi-device use id_table

The following is a multi-device demo of the platform

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

The way of misc registering character devices is much simpler than using alloc_chrdev_region directly, at least it can save a few lines of code.

The driver managed by misc+platform uses the device number to associate the relationship between struct inode and struct miscdevice. The operation function of the device number is as follows.

#include <linux/kdev_t.h>

  • (dev_t)–>major device number, minor device number
    • MAJOR(dev_t dev);
    • MINOR(dev_t dev);
  • Major device number, minor device number –>(dev_t)
    • MKDEV(int major,int minor);

Association structure

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: **Be sure to ensure that struct platform_devicethe median idvalue struct miscdevicecorresponds to the array subscript.

Compared with the platform program above, only need to change 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");

The printout is as follows:

[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

Guess you like

Origin blog.csdn.net/tianzong2019/article/details/124379284