zoukankan      html  css  js  c++  java
  • Samsung_tiny4412(驱动笔记09)----alloc_pages,kmalloc,vmalloc,kmem_cache,class

    /***********************************************************************************
     *                    
     *                   alloc_pages,kmalloc,vmalloc,kmem_cache,class
     *
     *   声明:
     *       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-14 阴 深圳 尚观 Var 曾剑锋
     **********************************************************************************/
    
                            \\\\\--*目录*--///////////
                            |  一. alloc_pages接口:        
                            |  二. kmalloc接口:            
                            |  三. vmalloc接口:            
                            |  四. kmem_cache接口:         
                            |  五. dma_alloc_coherent接口: 
                            |  六. 三星pwm中间层驱动:      
                            |  七. class接口:              
                            \\\\\\\\////////////////
    
    
    一. alloc_pages接口:
        1. 常见内存分配标志:
            1. GFP_KERNEL: 内存分配会睡眠阻塞,当没有足够内存分配时,直到有内存分配;
            2. GFP_ATOMIC: 内存分配不会阻塞,没有足够内存分配时返回错误;
        2. 把需要分配的字节数换算成对应的页页框: get_order(1234);
        3. 分配页框(page frame),如果分配多个页,分配的多个页在物理地址上是连续的;
        4. 两种分配2的get_order(1234)次方个页框,分配失败返回NULL: 
            1. struct page *p  = alloc_pages(GFP_KERNEL, get_order(1234));
            2. unsigned long p = __get_free_pages(GFP_KERNEL, get_order(1234));
        5. 获取虚拟地址: void *addr = page_address(page);
        6. 两种释放连续的页框方法: 
            1. __free_pages(page, get_order(1234));
            2. free_pages(p, get_order(1234));
        7. alloc_pages接口实例Demo:
            ...
            struct page *p;
            /*void *virt = NULL;*/
            unsigned long virt;
            int __init test_init(void)
            {
                /**
                 * printk("order = %d
    ", get_order(1234));
                 * printk("order = %d
    ", get_order(5000));
                 */
            
                /**
                 * p = alloc_pages(GFP_KERNEL, get_order(1234));
                 * if(!p)
                 *     return -ENOMEM;
                 *
                 * virt = page_address(p);
                 * printk("virt = %p.
    ", virt);
                 */
    
                virt = __get_free_pages(GFP_KERNEL, get_order(1234));
                if(!virt)
                    return -ENOMEM;
            
                printk("virt = %p.
    ", (void *)virt);
            
                return 0;
            }
            
            void __exit test_exit(void)
            {
                /*__free_pages(p, get_order(1234));*/
                free_pages(virt, get_order(1234));
            }
            ...
        
    二. kmalloc接口:
        1. 一般来说,kmalloc通常用于分配少量内存,保证可移植一般不超过128k,
            在虚拟地址上连续, 在物理地址上也连续
        2. 分配内存: void *p = kmalloc(1234, GFP_KERNEL);
        3. 分配内存,并初始化为0: kzalloc();
        4. 释放由kmalloc分配的内存空间: kfree(p);
        5. kmalloc接口实例:
            ...    
            void *virt = NULL;
            int __init test_init(void)
            {
                /*virt = kmalloc(1234, GFP_KERNEL);*/
                /*virt = kmalloc(0x400000, GFP_KERNEL);*/
                virt = kzalloc(0x400000, GFP_KERNEL);
                if(!virt)
                    return -ENOMEM;
            
                printk("virt = %p.
    ", virt);
            
                return 0;
            }
            
            void __exit test_exit(void)
            {
                kfree(virt);
            }
            ...
    
    三. vmalloc接口:
        1. 一般来说,vmalloc通常用于分配大量内存,在虚拟地址上连续,在物理地址上不一定连续;
        2. 分配内存: void *p = vmalloc(0x900000);
        3. 释放vmalloc释放的空间: vfree(p);
        4. vmalloc接口实例Demo:
            ...    
            void *virt = NULL;
            int __init test_init(void)
            {
                virt = vmalloc(0x800000);
                if(!virt)
                    return -ENOMEM;
            
                printk("virt = %p.
    ", virt);
            
                return 0;
            }
            
            void __exit test_exit(void)
            {
                vfree(virt);
            }
            ...
                
    四. kmem_cache接口:
        1. 使用高速内存池对象: 
            struct kmem_cache *kc = kmem_cache_create("kc", 16, 0, 
                                                      SLAB_HWCACHE_ALIGN, NULL);
        2. 分配内存块:
            void *p = kmem_cache_alloc(kc, GFP_KERNEL);
        3. 释放内存块: kmem_cache_free(kc, p);
        4. 销毁对象: kmem_cache_destroy(kc);
        5. kmem_cache接口实例Demo:
            ...
            struct kmem_cache *kc;
            void *p[5];
            int __init test_init(void)
            {
                int i;
                kc = kmem_cache_create("kc", 16, 0, SLAB_HWCACHE_ALIGN, NULL);
                if(!kc)
                    return -ENOMEM;
            
                for(i = 0; i < 5; i++)
                {
                    p[i] = kmem_cache_alloc(kc, GFP_KERNEL);
                    printk("p[%d] = %p.
    ", i, p[i]);
                }
            
                return 0;
            }
            
            void __exit test_exit(void)
            {
                int i;
            
                for(i = 0; i < 5; i++)
                    kmem_cache_free(kc, p[i]);
                kmem_cache_destroy(kc);
            }
            ...
    
    五. dma_alloc_coherent接口:
        1. 为dma设备分配内存: 
            virt = dma_alloc_coherent(NULL, 512, &phys, GFP_KERNEL);
            返回2个地址:
                1. virt    ---> 虚拟地址
                2. phys    ---> 物理地址
        2. 释放内存: 
            dma_free_coherent(NULL, 512, virt, phys);
            传递参数:
                1. virt    ---> 虚拟地址
                2. phys    ---> 物理地址
        3. dma_alloc_coherent接口实例Demo:
            ...
            dma_addr_t phys;    //物理地址  physical
            void *virt;         //虚拟地址  virtual
            int __init test_init(void)
            {
                int val;
            
                virt = dma_alloc_coherent(NULL, 100, &phys, GFP_KERNEL);
                if(!virt)
                    return -ENOMEM;
            
                printk("phys = %#x
    ", phys);
                printk("virt = %p
    ", virt);
            
                *(int *)virt = 11223344;
            
                /*virt = phys + PAGE_OFFSET - PHYS_OFFSET*/
                val = *(int *)(phys + PAGE_OFFSET - PHYS_OFFSET);
                printk("val = %d
    ", val);
            
                return 0;
            }
            
            void __exit test_exit(void)
            {
                dma_free_coherent(NULL, 100, virt, phys);
            }
            ...
    
    
    六. 三星pwm中间层驱动:
        1. 使pwm驱动工作:
            1. 打开板级文件: vim arch/arm/mach-exynos/mach-tiny4412.c
            2. 注释掉以下内容: 
                /*#ifdef CONFIG_TINY4412_BUZZER*/
                    &s3c_device_timer[0],
                /*#endif*/
        2. 请求pwm定时器:
            struct pwm_device *pwm0 = pwm_request(int pwm_id, const char *label);
            参数说明:
                1. pwm_id: 请求哪个定时器
                2. label : 设置名字
        3. 配置pwm定时器:
            int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
        4. 开启定时器:  int pwm_enable(struct pwm_device *pwm)
        5. 关闭定时器:  void pwm_disable(struct pwm_device *pwm)
        6. 释放pwm资源: pwm_free(struct pwm_device *pwm);
        7. pwm接口实例Demo:
            ...
            #define DEV_NAME    "test"
            #define PWM0        0
            #define NS_IN_HZ (1000000000UL)
            
            #define PWM_IOC_SET_FREQ    1
            #define PWM_IOC_STOP        0
            
            DEFINE_MUTEX(mtx);
            struct pwm_device *pwm_t0;
            int buzzer_gpio = EXYNOS4_GPD0(0);
            
            void pwm_set_freq(int freq)
            {
                unsigned int cnt = NS_IN_HZ / freq;
            
                pwm_config(pwm_t0, cnt / 2, cnt);
                //配置GPIO引脚为定时器输出功能
                s3c_gpio_cfgpin(buzzer_gpio, S3C_GPIO_SFN(2));
                pwm_enable(pwm_t0);
            }
            
            void pwm_stop(void)
            {
                gpio_direction_output(buzzer_gpio, 0);
                pwm_disable(pwm_t0);
            }
            
            static int test_open(struct inode *inode, struct file *file)
            {
                if(!mutex_trylock(&mtx))
                    return -EAGAIN;
            
                return 0;
            }
            
            static int test_close(struct inode *inode, struct file *file)
            {
                mutex_unlock(&mtx);
            
                return 0;
            }
            
            static long test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
            {
                switch(cmd)
                {
                    case PWM_IOC_SET_FREQ:
                        if(arg <= 0)
                            return -EINVAL;
                        pwm_set_freq(arg);
                        break;
                    case PWM_IOC_STOP:
                        pwm_stop();
                        break;
                    default:
                        return -EINVAL;
                }
            
                return 0;
            }
            
            struct file_operations fops = {
                .owner          = THIS_MODULE,
                .open           = test_open,
                .release        = test_close,
                .unlocked_ioctl = test_ioctl,
            };
            
            int major;
            int __init test_init(void)
            {
                int ret;
            
                //查看pwm0对应的引脚是否被占用,防止占用引脚冲突
                ret = gpio_request(buzzer_gpio, "pwm_tout0");
                if(ret)
                    goto err0;
            
                //查看pwm0定时器是否被占用,防止占用定时器冲突
                pwm_t0 = pwm_request(PWM0, DEV_NAME);
                if(IS_ERR(pwm_t0))
                {
                    //出错了,释放前面申请的资源
                    gpio_free(buzzer_gpio);
                    ret = PTR_ERR(pwm_t0);
                    goto err1;
                }
                //引脚功能,个人感觉这里其实没什么用,但是这是一种保险做法,不错
                gpio_direction_output(buzzer_gpio, 0);
                ret = register_chrdev(major, DEV_NAME, &fops);
                if(ret > 0)
                {
                    major = ret;
                    printk("major = %d
    ", major);
                    ret = 0;
                }
                else
                    goto err2;
            
                return ret;
            
            err2:
                pwm_free(pwm_t0);
            err1:
                gpio_free(buzzer_gpio);
            err0:
                return ret;
            }
            
            void __exit test_exit(void)
            {
                unregister_chrdev(major, DEV_NAME);
                pwm_stop();
                pwm_free(pwm_t0);
                gpio_free(buzzer_gpio);
            }
            ...
    
    七. class接口:
        1. 声明类对象: struct class cls;
        2. 两种注册类对象方式:
            1. class_register(&cls);
            2. class_create();
        3. 两种注销类对象的方式:
            1. class_unregister(&cls);
            2. class_destroy();
        4. 声明设备: struct device dev;
        5. 两种注册设备的方式:
            1. device_register();
            2. device_create();
        6. 两种注销设备的方式:
            1. device_unregister();
            2. device_destroy();
        7. class接口实例Demo:
            ...
            static int test_open(struct inode *inode, struct file *file)
            {
                printk("Dev open.
    ");
            
                return 0;
            }
            
            static int test_close(struct inode *inode, struct file *file)
            {
                printk("Dev close.
    ");
            
                return 0;
            }
            
            static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
            {
                printk("Read data.
    ");
            
                return count;
            }
            
            struct file_operations fops = {
                .owner      = THIS_MODULE,
                .open       = test_open,
                .release    = test_close,
                .read       = test_read,
            };
            int major;
            struct class *cls;
            
            int __init test_init(void)
            {
                int ret;
                struct device *dev;
            
                /**
                 *  ret = class_register(&cls);
                 *  if(ret)
                 *  {
                 *      printk("class_register FAILED!
    ");
                 *      return ret;
                 *  }
                 */
                 
                cls = class_create(THIS_MODULE, "up_class");
                if(IS_ERR(cls))
                {
                    printk("class_create FAILED!
    ");
                    ret = PTR_ERR(cls);
                    goto err0;
                }
            
                /** 
                 * ret = device_register(&dev);
                 * if(ret)
                 * {
                 *     printk("device_create FAILED!
    ");
                 *     class_unregister(&cls);
                 * }
                 */
            
                ret = register_chrdev(major, DEV_NAME, &fops);
                if(ret > 0)
                {
                    major = ret;
                    ret = 0;
                } else {
                    printk("register_chrdev FAILED!
    ");
                    goto err1;
                }
            
                dev = device_create(cls, NULL, MKDEV(major, 0),
                                    NULL, "up_dev%d", 0);
                if(IS_ERR(dev))
                {
                    printk("device_create FAILED!
    ");
                    ret = PTR_ERR(dev);
                    goto err2;
                }
            
                return 0;
            
            err2:
                unregister_chrdev(major, DEV_NAME);
            err1:
                class_destroy(cls);
            err0:
                return ret;
            }
            
            void __exit test_exit(void)
            {
                /**
                 * device_unregister(&dev);
                 */
                device_destroy(cls, MKDEV(1111, 2222));
            
                unregister_chrdev(major, DEV_NAME);
            
                /**
                 * class_unregister(&cls);
                 */
                class_destroy(cls);
            }
            ...
  • 相关阅读:
    mysql 定时器
    mysql 存储过程记录
    mysql 常用sql
    mysql 取最后一条数据 分组
    Java8 Stream使用flatMap合并List 交 并 合 差集
    微服务技术栈
    spring boot 拦截 以及Filter和interceptor 、Aspect区别
    在springMVC的controller中获取request,response对象的一个方法
    spring boot 用@CONFIGURATIONPROPERTIES 和 @Configuration两种方法读取配置文件
    SSRS 2016 Forms Authentication
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4338005.html
Copyright © 2011-2022 走看看