arm-linux character device with device tree io operation

The content of the device tree is as follows

 

In addition to the need /* device operation function */

Also need /* device structure */

eg:

struct dtsled_dev{

    dev_t devid; /* device number */

    struct cdev cdev;       /* cdev     */

    struct class *class;        /* 类        */

    struct device *device; /* device */

    int major; /* major device number */

    int minor; /* Minor device number */

    struct device_node *nd; /* device node */

};

The initialization registration function also needs to be changed

The initialization function needs to use the of_find_node_by_path function to find the device tree node:

    /* 1. Get the device node: alphaled */

    dtsled.nd = of_find_node_by_path("/alphaled");

The initialization function needs to use the of_find_property function to find the compatibility property of the device tree

    /* 2. Get the compatible property content */

    proper = of_find_property(dtsled.nd, "compatible", NULL);

/* 3. Get status attribute content */

    ret = of_property_read_string(dtsled.nd, "status", &str);

It is very important to get the contents of the register! ! !

/* 4. Get the reg attribute content */

    ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);

Need to configure IO mapping

IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);

    SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);

    SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);

    GPIO1_DR = of_iomap(dtsled.nd, 3);

    GPIO1_GDIR = of_iomap(dtsled.nd, 4);

of_iomap
void __iomem *of_iomap(struct device_node *node, int index);

The ioremap() of the device memory range is directly performed through the device node of the device tree, and index is the index of the memory segment. If the reg attribute of the device node has multiple sections, you can use the index to indicate which section you want to ioremap. If there is only one section, the index is 0.

After using Device Tree, a large number of device drivers are mapped through of_iomap() instead of traditional ioremap

references: https://blog.csdn.net/alimingh/article/details/111666429

Use MKDEV to get the major and minor device numbers in the registered driver

Version: linux-2.6.24.4

macro:

MKDEV(MAJOR, MINOR);  

Description: Get the position of the device in the device table.

MAJOR major device number

MINOR minor device number

   

The version number description file used by the kernel:

    Instructions are found in devices.txt in the kernel/Documentation directory.

    generally reserved locally

        MAJOR

234-239 UNASSIGNED

240-254 char LOCAL/EXPERIMENTAL USE

240-254 block LOCAL/EXPERIMENTAL USE  

        MINOR

1 to 250 (0 of the minor device number cannot be used)

     

Static device file creation:

    mknod /dev/gpio_led c 240 

Register with the register_chrdev_region function

The register_chrdev_region() function is used to allocate the specified device number range. If the requested device number range spans the major device number, it will divide the numbers in the allocated range into smaller subranges by major device number, and call __register_chrdev_region() on each subrange. If one of the allocations fails, all previous successful allocations will be returned.

After version 2.4, the following functions have been added to the kernel to also register character devices:

It is divided into static registration (registration by specifying the device number), dynamic allocation (registration without specifying the device number), and the range of secondary device numbers with continuous registration, which avoids the disadvantage of wasting resources in register_chrdev()   

2.1: 

/*Specify the device number to statically register a character device*/ 
int register_chrdev_region(dev_t from, unsigned count, const char *name);   

from:  Registered specified starting device number, for example: MKDEV(100, 0), means the starting major device number is 100, and the starting minor device number is 0

count: The number of secondary device numbers that need to be registered consecutively, for example: the initial secondary device number is 0, count=100, which means that the secondary device numbers from 0 to 99 must be bound to the same file_operations operation method structure

*name: character device name

When the return value is less than 0, it means that the registration failed

2.2:

/* Dynamically allocate a character device, register successfully and put the allocated primary and secondary device numbers into *dev*/ 
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

*dev:  A pointer to store the initial device number. When the registration is successful, *dev will be equal to the assigned initial device number. The major and minor device numbers can be extracted through the MAJOR() and MINNOR() functions

baseminor: the base address of the minor device number, that is, the starting minor device number

count: The number of secondary device numbers that need to be registered consecutively, for example: the initial secondary device number (baseminor) is 0, baseminor=2, which means that the device numbers from 0 to 1 must be bound to the same file_operations operation method structure

*name: character device name

When the return value is less than 0, it means that the registration failed

eg:

    /* Register character device driver */

    /* 1. Create device number*/

    if (dtsled.major) { /* defines the device number */

        dtsled.devid = MKDEV(dtsled.major, 0);

        register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);

    } else { /* device number is not defined */

        alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME); /* Apply for device number*/

        dtsled.major = MAJOR(dtsled.devid); /* Get the major device number of the assigned number*/

        dtsled.minor = MINOR(dtsled.devid); /* Get the minor device number of the assigned number*/

    }

    /* 2. Initialize cdev */

The function cdev_init() is used to initialize a statically allocated cdev structure variable. The function cdev_init will automatically initialize the cdev->opsobject, assign the second input parameter of the function to cdev->opsthe object, and will not initialize cdev->ownerthe object. Therefore, after the function cdev_alloc() and the function cdev_init () After the cdev structure variable is processed, it only needs to assign a value to the object in the application program cdev->owner, and this structure variable can be inserted into the Linux kernel system and used as an available character device.

    dtsled.cdev.owner = THIS_MODULE;

    cdev_init(&dtsled.cdev, &dtsled_fops);

#include <linux/cdev.h>

After initializing cdev  , it needs to be added to the system. To do this the cdev_add() function can be called. Pass in the pointer to the cdev structure, the starting device number, and the range of device numbers.

The function first saves the allocated device number and device number into the cdev structure. Then, the cdev structure is recorded in the cdev_map variable of a kobj_map structure.

/* 3. Add a cdev */

    cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);

    /* 4. Create class*/

    dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);

    if (IS_ERR(dtsled.class)) {

        return PTR_ERR(dtsled.class);

    }

    /* 5. Create device*/

    dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);

    if (IS_ERR(dtsled.device)) {

        return PTR_ERR(dtsled.device);

    }

unregister device driver

/*

 * @description : Driver export function

 * @param : None

 * @return : none

 */

static void __exit led_exit(void)

{

    /* unmap */

    iounmap(IMX6U_CCM_CCGR1);

    iounmap(SW_MUX_GPIO1_IO03);

    iounmap(SW_PAD_GPIO1_IO03);

    iounmap(GPIO1_DR);

    iounmap(GPIO1_GDIR);

    /* Unregister the character device driver */

    cdev_del(&dtsled.cdev);/* delete cdev */

    unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /* Unregister the device number*/

    device_destroy(dtsled.class, dtsled.devid);

    class_destroy(dtsled.class);

}

Guess you like

Origin blog.csdn.net/L1153413073/article/details/125486527