/******************************** * The GPIO driver controls the high and low levels of the GPIO interface * Four GPIOs are recognized as four devices * Create four files to control four GPIOs respectively * echo on|off > /dev/driverx * Use the meter to measure the pin voltage and observe the results * For details of this example, please refer to Chapter 3 of LDD3 * Development board: Tiny 4412 * Main control chip: Exynos 4412 * author: zhangn * date: 2016-1-10 ********************************/ #include <linux/module.h> #include <linux/ioport.h> #include <linux/io.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/cdev.h> #include <linux/proc_fs.h> //define the device number #define GPIO_MAJOR 60 //主 #define GPIO_NUM 4 //次 //define the register address #define GP_BASE 0X11000000 //The physical base address of the device, used by the ioremap function #define GP_SIZE 0X1000 //The physical address is mapped to the space size of the kernel //The register is based on the physical base address offset #define GPX3CON 0x0C60 #define GPX3DAT 0x0C64 #define GPX3PUD 0x0c68 //1. Prepare a private structure for each char device struct mygpio { int gpio_num;//device number, there are four devices in this example int gpio_state;//Device status: on/1,off/0 dev_t dev_id;//Device number, the kernel driver must have struct cdev gpio_cdev;//The kernel uses this structure internally to represent character devices, which must be allocated before the kernel calls device operations }; static struct mygpio *gpios[GPIO_NUM]; static void __iomem *vir_base;//If you map multiple times, write it inside the structure //9. Implement the function pointer in file_operations static int gpio_open(struct inode *inode, struct file *filp) { /* Each time the application layer opens a file, the kernel maintains a file structure Also maintains an inode structure for each open file A file has multiple files opened multiple times, but all point to the same inode For the inode structure, we only care about two members here dev_t i_rdev, contains the device number registered to the system during init struct cdev *i_cdev points to a pointer to a cdev structure, a cdev corresponds to a device Here is the pointer to the member gpio_cdev of the private structure mygpio The application layer opens a file, and the kernel creates a file structure In file, the function association of a set of operation files is described through the file_operations structure At the same time, the kernel maintains an inode, the same file is opened and created for the first time, and the count is increased by opening it multiple times. For details, please refer to the relevant information of vfs When the kernel calls the gpio_open function, the device information is passed in through the inode parameter At the same time, the information of the file each time the file is opened is passed in. See Chapter 3 of ldd3 for details here */ struct mygpio *tmp = container_of(inode->i_cdev, struct mygpio, gpio_cdev); filp->private_data = tmp;//This member is mainly used to pass data through file in different functions return 0; } static int gpio_release(struct inode *inode, struct file *filp) { //If there are resources in open that need to be released, release them here //The memory pointed to by tmp in open is allocated and released outside, and there is no need to release it here return 0; } static ssize_t gpio_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct mygpio *dev = filp->private_data; char tmp[10] = {0}; int value; if(copy_from_user(tmp, buf, 3)) return -EFAULT; if(strncmp(tmp, "on", 2) == 0) { value = readl (vir_base + GPX3DAT); value &= ~(1 << (dev->gpio_num+2)); writel (value, vir_base + GPX3DAT); dev->gpio_state = 1; } else if(strncmp(tmp, "off", 3) == 0) { value = readl (vir_base + GPX3DAT); value |= 1 << (dev->gpio_num + 2); writel (value, vir_base + GPX3DAT); dev->gpio_state = 0; } else return -1; return count; } //2. Prepare the file_operations structure static struct file_operations gpio_fops = { .owner = THIS_MODULE, .open = gpio_open, .release = gpio_release, .write = gpio_write, }; static int __init my_init(void) { int i, value; //3. Physical addresses are mapped to kernel virtual addresses vir_base = ioremap(GP_BASE, GP_SIZE); if (! vir_base) { printk("Cannot ioremap\n"); return -EIO; } // The following work is done once per device for(i = 0; i < GPIO_NUM; ++i) { //4. Allocate memory for each device's private structure gpios[i] = kzalloc(sizeof(*gpios[i]), GFP_KERNEL); if(!gpios[i]) { // release memory for the allocated structure for(; i >= 0; --i) kfree(gpios[i]); // release the address map iounmap(vir_base); return -ENOMEM; } //5. Initialize registers for each device value = readl (vir_base + GPX3CON); value |= 0x11111111; writel(value, vir_base + GPX3CON); value = readl (vir_base + GPX3DAT); value |= 1 << (i+2);//Use the 2345th pin of the register writel (value, vir_base + GPX3DAT); value = readl (vir_base + GPX3PUD); value = 0x3; writel (value, vir_base + GPX3PUD); gpios[i]->gpio_num = i; gpios[i]->gpio_state = 0; //6. Assign major and minor device numbers to the device gpios[i]->dev_id = MKDEV(GPIO_MAJOR, i); //7. Initialize the cdev structure and associate it with the file_operations structure cdev_init(&gpios[i]->gpio_cdev, &gpio_fops); gpios[i]->gpio_cdev.owner = THIS_MODULE; gpios[i]->gpio_cdev.ops = &gpio_fops; //8. Add the device number in mygpio to the cdev structure through cdev_add cdev_add(&gpios[i]->gpio_cdev, gpios[i]->dev_id, 1); } return 0; } static void __exit my_exit(void) { int i; iounmap(vir_base); for(i = 0; i < GPIO_NUM; ++i) { if(gpios[i]) { cdev_del(&gpios[i]->gpio_cdev); kfree(gpios[i]); } } } module_init(my_init); module_exit(my_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("ZhangN");
GPIO control of Linux kernel study notes
Guess you like
Origin http://43.154.161.224:23101/article/api/json?id=325385398&siteId=291194637
Recommended
Ranking