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\n", get_order(1234));
             * printk("order = %d\n", get_order(5000));
             */
        
            /**
             * p = alloc_pages(GFP_KERNEL, get_order(1234));
             * if(!p)
             *     return -ENOMEM;
             *
             * virt = page_address(p);
             * printk("virt = %p.\n", virt);
             */

            virt = __get_free_pages(GFP_KERNEL, get_order(1234));
            if(!virt)
                return -ENOMEM;
        
            printk("virt = %p.\n", (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.\n", 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.\n", 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.\n", 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\n", phys);
            printk("virt = %p\n", virt);
        
            *(int *)virt = 11223344;
        
            /*virt = phys + PAGE_OFFSET - PHYS_OFFSET*/
            val = *(int *)(phys + PAGE_OFFSET - PHYS_OFFSET);
            printk("val = %d\n", 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\n", 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.\n");
        
            return 0;
        }
        
        static int test_close(struct inode *inode, struct file *file)
        {
            printk("Dev close.\n");
        
            return 0;
        }
        
        static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
        {
            printk("Read data.\n");
        
            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!\n");
             *      return ret;
             *  }
             */
             
            cls = class_create(THIS_MODULE, "up_class");
            if(IS_ERR(cls))
            {
                printk("class_create FAILED!\n");
                ret = PTR_ERR(cls);
                goto err0;
            }
        
            /** 
             * ret = device_register(&dev);
             * if(ret)
             * {
             *     printk("device_create FAILED!\n");
             *     class_unregister(&cls);
             * }
             */
        
            ret = register_chrdev(major, DEV_NAME, &fops);
            if(ret > 0)
            {
                major = ret;
                ret = 0;
            } else {
                printk("register_chrdev FAILED!\n");
                goto err1;
            }
        
            dev = device_create(cls, NULL, MKDEV(major, 0),
                                NULL, "up_dev%d", 0);
            if(IS_ERR(dev))
            {
                printk("device_create FAILED!\n");
                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);
        }
        ...

 

上一篇:阿里云公网IP主机如何访问无公网IP主机?


下一篇:阿里云价格/报价 - 阿里云服务器最新收费标准