• Samsung_tiny4412(驱动笔记06)----list_head,proc file system,GPIO,ioremap


    /****************************************************************************
     *
     *                  list_head,proc file system,GPIO,ioremap
     *
     *   声明:
     *       1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会
     *         不对齐,从而影响阅读.
     *       2. 本文中有些源代码没有全部帖出来,主要是因为篇幅太大的原因;
     *       3. 基于2中的原因,本文借鉴了python中的缩进代码风格进行代码的体现:
     *           1. 有些代码中的"..."代表省略了不影响阅读的代码;
     *           2. 如下代码缩进代表在一个函数内部的代码,至于在什么函数里,不影响阅读:
     *               ... //省略代码
     *               struct test_s {
     *               };
     *               ... //省略代码
     *
     *                   //进入临界区之前加锁     }
     *                   spin_lock(&p->lock);     | 
     *                                            |   |
     *                   /* 有效代码 */           |-->|采用缩进,代表在一个函数内
     *                                            |   |的代码
     *                   //出临界区之后解锁       |
     *                   spin_unlock(&p->lock);   }
     *
     *               ... //省略代码                                
     *               int __init test_init(void)
     *               {   
     *                   ... //省略代码
     *               }  
     *               ... //省略代码
     *
     *                                          2015-3-11 阴 深圳 尚观 Var 曾剑锋
     ****************************************************************************/
    
                        \\\\\--*目录*--//////////
                        |  一. list_head常用接口:     
                        |  二. proc文件系统相关操作:  
                        |  三. gpio_request相关操作:  
                        |  四. ioremap相关操作:       
                        |  五. LED驱动写法:           
                        |  六. 测试LED驱动:           
                        \\\\\\\\///////////////
    
    
    一. list_head常用接口:
        1. 定义内核链表头,并初始化: 
            LIST_HEAD(test_head);
        2. 两种链表添加方式: 
            1. 头插: list_add();
            2. 尾插: list_add_tail();
        3. 两种特殊的for each循环遍历,内部使用了typeof查询类型,再加上container_of: 
            1. list_for_each_entry();
            2. list_for_each_entry_reverse();
        4. 普通的for each循环遍历: 
            list_for_each();
        5. 内核链表Demo:
            ... // "..."代表省略一些不影响分析代码的代码
            struct test_list {
                int data;
                struct list_head entry; //内核链表
            };
            
            //定义一个内核链表头,并初始化
            LIST_HEAD(test_head);
            
            struct test_list item[NUM];
            int __init test_init(void)
            {
                int i;
                struct test_list *tail;
                struct list_head *p;    
            
                for(i = 0; i < NUM; i++)
                {
                    item[i].data = i;
                    //头插方式
                    /*list_add(&item[i].entry, &test_head);*/
                    //尾插方式
                    list_add_tal(&item[i].entry, &test_head);
                }
            
                //遍历链表,这两个宏里面都使用了typeof来配合container_of获取结构体指针
                /*list_for_each_entry(tail, &test_head, entry)*/
                list_for_each_entry_reverse(tail, &test_head, entry)
                {
                    printk("%d ", tail->data);
                }
                printk("
    ");
            
                //没有像上面那样得到结构体指针,需要自己使用container_of获取结构体指针
                list_for_each(p, &test_head)
                {
                    tail = container_of(p, struct test_list, entry);
                    printk("%d ", tail->data);
                }
                printk("
    ");
            
                return 0;
            }
            ...
    
    二. proc文件系统相关操作:  //这里的资料不足,需要另外参考网络资料
        1. 头文件:
            #include <linux/proc_fs.h>
            #include <linux/seq_file.h>
        2. 创建文件:      proc_create();
        3. 创建目录:      proc_mkdir();
        4. 删除文件,目录: remove_proc_entry();
        5. 访问方法,此处的资料不够,需要另外查相关资料:
            1. seq_printf();
            2. single_open();
            3. single_release();
            4. seq_read();
            5. seq_lseek();
        6. proc文件系统访问Demo:
            #include <linux/module.h>
            #include <linux/proc_fs.h>
            #include <linux/seq_file.h>
            
            static int num = 11223344;
            
            static int my_show(struct seq_file *file, void *data)
            {
                return seq_printf(file, "num is %d
    ", num);
            }
            
            static int test_open(struct inode *inode, struct file *file)
            {
                return single_open(file, my_show, NULL);
            }
            
            struct file_operations fops = {
                .owner      = THIS_MODULE,
                .open       = test_open,
                .release    = single_release,
                .read       = seq_read,
                .llseek     = seq_lseek,
            };
            
            struct proc_dir_entry *pde;
            int __init test_init(void)
            {
                //在proc文件系统上创建空目录
                pde = proc_mkdir("test_dir", NULL);
                if(!pde)
                    return -EFAULT;
            
                //在proc文件系统指定目录上创建文件
                /**
                 * 1. "proc_test" : 创建的文件名;
                 * 2. 0644        : 八进制,表示创建的文件权限;
                 * 3. pde         : 前面创建文件夹的指针;
                 * 4. &fops       : 文件操作指针;
                 */
                proc_create("proc_test", 0644, pde, &fops);
            
                return 0;
            }
            
            void __exit test_exit(void)
            {
                //先删除proc文件系统上的目录
                remove_proc_entry("proc_test", pde);
                //删除proc文件系统上proc_test目录的文件
                remove_proc_entry("test_dir", NULL);
            }
            
            module_init(test_init);
            module_exit(test_exit);
            
            MODULE_LICENSE("GPL");
    
    三. gpio_request相关操作:
        1. 头文件: #include <linux/gpio.h>
        2. 获取GPIO在系统中的编号: EXYNOS4X12_GPXn(nr),比如:获取GPM4的0号引脚: 
            unsigned int gpio = EXYNOS4X12_GPM4(0);
        3. 向系统申请引脚编号为gpio的引脚,并命名为"name",成功返回0,主要是为了查看引脚是否被占用: 
            int gpio_request(gpio, "name"),
        4. 将系统编号为gpio的引脚设置为输出管脚,并把管脚的值设置为data_value:
            int gpio_direction_output(gpio, data_value);
        5. 将系统编号为gpio的引脚设置为输入管脚: 
            int gpio_direction_input(gpio);
        6. 三星提供的专门设置管脚的接口: 
            int s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(config_value));
        7. 将系统编号为gpio的引脚值设为data_value,主要是针对输出管脚:
            void gpio_set_value(gpio, data_value);
        8. 获取系统编号为gpio的管脚值: 
            int gpio_get_value(gpio);
        9. 释放系统编号为gpio的管脚:   
            void gpio_free(gpio);
        10. gpio_request操作Demo大致操作:
            ...  // "..."代表省略部分代码,但不影响阅读
            struct test_s {
                struct file_operations fops;
                int major;
                unsigned int led_tbl[LEDNO];
            };
            typedef struct test_s test_t;
            struct test_s test = {
                .fops = {
                    .owner      = THIS_MODULE,
                    .open       = test_open,
                    .release    = test_close,
                    .unlocked_ioctl = test_ioctl,
                },
                .major = 0,
                .led_tbl = {
                    [0] = EXYNOS4X12_GPM4(0), //申请系统对芯片引脚的gpio编号
                    [1] = EXYNOS4X12_GPM4(1),
                    [2] = EXYNOS4X12_GPM4(2),
                    [3] = EXYNOS4X12_GPM4(3),
                },
            };
            
            ...
            
                for(i = 0; i < ARRAY_SIZE(p->led_tbl); i++)
                {
                    ret = gpio_request(p->led_tbl[i], "led"); //主要是为了查看引脚是否被占用
                    if(ret)
                    {
                        for(--i; i >= 0; i--)  //释放之前成功申请的管脚
                        {
                            gpio_free(p->led_tbl[i]); 
                        }
                        return ret;
                    }
                }
                
                //把io口配置成输出功能,输出高电平
                for(i = 0; i < ARRAY_SIZE(p->led_tbl); i++)
                    gpio_direction_output(p->led_tbl[i], LED_OFF); //设置输出方向和初始化值
            
            ...
            
            /* 通过ioctl来控制灯的两灭 */
            static long test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
            {
                int i;
                test_t *p = file->private_data;
            
                if(arg < 0 || arg > 4)
                    return -EINVAL;
            
                switch(cmd)
                {
                    case LED_ON:
                        if(!arg)
                        {
                            for(i = 0; i < ARRAY_SIZE(p->led_tbl); i++)
                                gpio_set_value(p->led_tbl[i], LED_ON);
                        }
                        else
                            gpio_set_value(p->led_tbl[arg - 1], LED_ON);
                        break;
                    case LED_OFF:
                        if(!arg)
                        {
                            for(i = 0; i < ARRAY_SIZE(p->led_tbl); i++)
                                gpio_set_value(p->led_tbl[i], LED_OFF);
                        }
                        else
                            gpio_set_value(p->led_tbl[arg - 1], LED_OFF);
                        break;
                    default:
                        return -EINVAL;
                }
            
                return 0;
            }
            ...
    
    四. ioremap相关操作:
        1. 在内核中下面两个缩写代表的意义:
            1. pa = physical addr  物理地址
            2. va = virtual addr   虚拟地址
        2. 建立映射关系,给出实际引脚控制寄存器的物理地址,以及控制寄存器的大小size(占用字节数):
            void *va = ioremap(pa, size);
        3. 访问外设地址:
            1. 向32位寄存器中读取,写入数据:
                data = ioread32(addr);
                iowrite32(data, addr);
            2. 向16位寄存器中读取,写入数据:
                data = ioread16(addr);
                iowrite16(data, addr);
            3. 向8位寄存器中读取,写入数据:
                data = ioread8(addr);
                iowrite8(data, addr);
        4. 取消映射关系:
            iounmap(va);
    
    五. LED驱动写法: 
    
        #include <linux/module.h>
        #include <linux/fs.h>
        #include <linux/gpio.h>
        #include <linux/uaccess.h>
        #include <linux/io.h>
        
        #define rGPM4CON    0x110002E0
        #define SIZE        SZ_8 //因为GPM4CON和GPM4DAT都是32位寄存器,共8字节
        
        #define GPM4CON     0  //GPM4CON相对rGPM4CON地址的偏移量
        #define GPM4DAT     4  //GPM4CON相对rGPM4CON地址的偏移量
        
        #define LED_ON      0
        #define LED_OFF     1
        
        #define DEV_NAME    "test"
        #define LEDNO       4
        
        struct test_s {
            struct file_operations fops;
            int major;
            void __iomem *reg;
        };
        typedef struct test_s test_t;
        
        static int test_open(struct inode *inode, struct file *file)
        {
            unsigned long val;
            test_t *p;
            p = container_of(file->f_op, test_t, fops);
        
            file->private_data = p;
        
            //映射外设地址,GPM4CON和GPM4DAT都是32位寄存器,共8字节
            p->reg = ioremap(rGPM4CON, SIZE);
            if(!p->reg)  //映射出错
                return -ENOMEM;
        
            //配置IO口为输出功能
            val = ioread32(p->reg + GPM4CON);
            val &= ~0xffff;
            val |= 0x1111;
            iowrite32(val, p->reg + GPM4CON);
        
            return 0;
        }
        
        static int test_close(struct inode *inode, struct file *file)
        {
            test_t *p = file->private_data;
        
            iounmap(p->reg); //取消io映射
        
            return 0;
        }
        
        static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
        {
            int ret, i;
            unsigned long val;
            char kbuf[LEDNO];
            test_t *p = file->private_data;
        
            if(count <= 0 || count > LEDNO)
                return -EINVAL;
        
            if(count > LEDNO - *pos)   //防止获取数据时超出数组下标范围
                count = LEDNO - *pos;
        
            val = ioread32(p->reg + GPM4DAT);
        
            for(i = 0; i < count; i++)
            {
                if(test_bit(i, &val))   //测试val中对应的位是否是1
                    kbuf[i] = LED_OFF;
                else
                    kbuf[i] = LED_ON;
            }
        
            ret = copy_to_user(buf, kbuf + *pos, count);
            if(ret)
                return -EFAULT;
        
            *pos += count;
        
            return count;
        }
        
        static ssize_t test_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
        {
            int ret, i;
            unsigned long val;
            char kbuf[LEDNO];
            test_t *p = file->private_data;
        
            if(count <= 0 || count > LEDNO)
                return -EINVAL;
        
            if(count > LEDNO - *pos)   //和read中的原因一致
                count = LEDNO - *pos;
        
            ret = copy_from_user(kbuf, buf, count);
            if(ret)
                return -EFAULT;
        
            val = ioread32(p->reg + GPM4DAT);
            for(i = 0; i < count; i++)
            {
                if(kbuf[i] == LED_ON)
                    val &= ~(1 << (i + *pos));
                else
                    val |= (1 << (i + *pos));
            }
            iowrite32(val, p->reg + GPM4DAT);
        
            *pos += count;
        
            return count;
        }
        
        static loff_t test_llseek(struct file *file, loff_t offset, int whence)
        {
            loff_t pos = file->f_pos;
        
            switch(whence)
            {
                case SEEK_SET:
                    pos = offset;
                    break;
                case SEEK_CUR:
                    pos += offset;
                    break;
                case SEEK_END:
                    pos = LEDNO + offset;
                    break;
                default:
                    return -EINVAL; //参数非法
            }
        
            file->f_pos = pos;
        
            return pos;
        }
        
        struct test_s test = {
            .fops = {
                .owner      = THIS_MODULE,
                .open       = test_open,
                .release    = test_close,
                .read       = test_read,
                .write      = test_write,
                .llseek     = test_llseek,
            },
            .major = 0,
        };
        
        int __init test_init(void)
        {
            int ret;
        
            ret = register_chrdev(test.major,
                    DEV_NAME, &test.fops);
            if(ret > 0)
            {
                test.major = ret;
                printk("major = %d
    ", test.major);
                ret = 0;
            }
        
            return ret;
        }
        
        
        void __exit test_exit(void)
        {
            unregister_chrdev(test.major, DEV_NAME);
        }
        
        module_init(test_init);
        module_exit(test_exit);
        
        MODULE_LICENSE("GPL");
    
    六. 测试LED驱动:
    
        #include <stdio.h>
        #include <string.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <fcntl.h>
        #include <sys/ioctl.h>
        
        #include "led.h"
        
        int main(int argc, char **argv)
        {
            int fd, ret, i, j;
            char buf[4];
        
            fd = open(argv[1], O_RDWR);
            if(-1 == fd)
            {
                perror("open");
                exit(1);
            }
        
            while(1)
            {
                lseek(fd, 0, SEEK_SET);
                memset(buf, LED_OFF, sizeof(buf));
                write(fd, buf, sizeof(buf));
                usleep(300 * 1000);
        
                lseek(fd, 0, SEEK_SET);
                for(i = 0; i < 4; i++)
                {
                    buf[0] = LED_ON;
                    write(fd, buf, 1);
        
                    lseek(fd, 0, SEEK_SET);
                    ret = read(fd, buf, sizeof(buf));
                    if(-1 == ret)
                        perror("read");
        
                    for(j = 0; j < sizeof(buf); j++)
                    {
                        if(LED_ON == buf[j])
                            printf("led%d is on
    ", j + 1);
                        else
                            printf("led%d is off
    ", j + 1);
                    }
        
                    usleep(300 * 1000);
                    lseek(fd, i + 1, SEEK_SET);
                }
            }
        
            close(fd);
            return 0;
        }
  • 相关阅读:
    Js new一个函数和直接调用函数的区别
    js 获取两个数组的交集,并集,补集,差集
    新版CHROME跨域问题:COOKIE之SAMESITE属性 PS:火狐浏览器SESSIOINID每次请求一致,但在谷歌浏览器每次的请求就不一样
    vue中handsontable 使用
    vue 单独页面定时器 离开页面销毁定时器
    vscode 之 power model 插件 超级炫酷的打字效果动画
    同步和异步的执行顺序
    为什么typeof null 的结果为 object
    悬浮显示input中所有的内容及css处理文字过长时显示为多余部分省略
    第十六次作业
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4330931.html
Copyright © 2020-2023  润新知