platform character device creation

    In an embedded system, if you want to operate on GPIO pins, you need to register a character device and corresponding driver in the kernel. Character devices provide a mapping of IO addresses, so that virtual addresses can be used to read and write registers. The character device driver implements the initialization of the device, associates it with the device file, and provides common interfaces such as open, release, ioctl, read, and write. The final application can indirectly control the device through these file interfaces. The device number is divided into a major device number and a minor device number. The device driver associates with the device file through the major device number, and associates with the serial port through the minor device number. The platform bus will traverse the driver list and device list respectively, and match the driver and device through the name field. Therefore, the creation steps of character devices are divided into:
1. Prepare IO resources, that is, fill in the resource structure
2. Fill in the platform_device device structure
3. Call platform_device_register to register the device
4. Fill in the character device file interface file_operations
5. Register the character device driver to get the main device number
6. Create a device file through the device number
7. Map IO addresses to virtual addresses

Example 1: Buzzer drive
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#define BEEP_ON (0x10007) //Custom command
#define BEEP_OFF        (0x10008)
#define BEEP_DISABLE    (0x10009)
#define BEEP_ENABLE        (0x1000a)
static int disable=0;
static int major;
static struct class *beep_class;
static unsigned long *gpio_con;
static unsigned long *gpio_data;
static int pin;

static struct resource beep_resources[] = {
        [0] = {
            .start = 0xE02000A0, //GPIO register start address
            .end = 0xE02000A8 - 1, //GPIO register end address
            .flags = IORESOURCE_MEM, //Resource type: IO memory
        },
        [1] = {
            .start = 1, //start interrupt number
            .end = 1, //end interrupt number
            .flags = IORESOURCE_IRQ, //Interrupt resource
        },
};
static void beep_release(struct device *dev)
{
    printk("%s\n", __func__);
}
static struct platform_device beep_device = {
    .name            =    "tpad_beep",
    .num_resources    =    ARRAY_SIZE(beep_resources),
    .resource        =    beep_resources,
        .dev        = {
            .release = beep_release,
        }
};
static int beep_open(struct inode *inode, struct file *file)
{
    *gpio_con &= ~(0xf << (pin * 4)); //Configure the IO port register according to the controller
    *gpio_con |= (1 << (pin * 4)); //Configure IO as output
    return 0;
}
static int beep_close(struct inode *inode, struct file *file)
{
    return 0;
}
static int beep_ioctl(struct inode *inode, struct file *file,
                        unsigned int cmd, unsigned long arg)
{
    switch (cmd) {
        case BEEP_ON:
            if(!disable)
               *gpio_data |= (1 << pin); //According to the hardware schematic diagram, set the corresponding pin
            break;
        case BEEP_OFF:
            if(!disable)
                *gpio_data    &= ~(1 << pin);       //清除对应管脚
            break;
        case BEEP_DISABLE:
            disable=1;
            break;
        case BEEP_ENABLE:
            disable=0;
            break;
        default:
            return -1;
    };
    return 0;
}
static struct file_operations beep_fops = {
    .open        =    beep_open,
    .release    =    beep_close,
    .ioctl        =    beep_ioctl,
};
static int beep_probe(struct platform_device *dev)
{
    struct resource *res;
    
    major        =    register_chrdev(0, "beeps", &beep_fops);                //注册字符设备
    beep_class     =     class_create(THIS_MODULE, "beeps");                //创建设备类
    device_create(beep_class, NULL, MKDEV(major, 0), NULL, "beeps");   //创建设备文件
    res = platform_get_resource(dev, IORESOURCE_MEM, 0);                //获取platform_device资源
    gpio_con = ioremap(res->start, res->end - res->start + 1);             //映射物理地址
    gpio_data = gpio_con + 1;
    res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
    pin = res->start;
        return 0;
}
static int beep_remove(struct platform_device *dev)
{
    iounmap(gpio_con);
    device_destroy(beep_class, MKDEV(major, 0));
    class_destroy(beep_class);
    unregister_chrdev(major, "beeps");
    
        return 0;
}
static struct platform_driver   beep_driver    = {
    .probe    =    beep_probe,
    .remove    =    beep_remove,
    .driver    =    {
            .name    =    "tpad_beep",
    },
};
static int beep_init(void)
{
    platform_device_register(&beep_device);        //注册platform_devicce
    platform_driver_register(&beep_driver);        //注册platform_driver
    return 0;
}
static void beep_exit(void)
{
    platform_driver_unregister(&beep_driver);
    platform_device_unregister(&beep_device);
}
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");



相关接口:
platform_device_register()            注册设备
platform_driver_register()            注册设备驱动
register_chrdev()                        注册字符设备
class_create()                                创建设备类
device_create()                              创建设备文件
platform_get_resource()                获取设备资源
ioremap                                        映射物理地址到虚拟地址

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324517797&siteId=291194637