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);
        }
        ...

 

时间: 2024-10-01 12:19:59

Samsung_tiny4412(驱动笔记09)----alloc_pages,kmalloc,vmalloc,kmem_cache,class的相关文章

Samsung_tiny4412(驱动笔记10)----mdev,bus,device,driver,platform

/*********************************************************************************** * * mdev,bus,device,driver,platform * * 声明: * 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会 * 不对齐,从而影响阅读. * 2. 由于本人水平有限,很难阐述清楚bus device driver platform的关系 * 所以强烈要求您详细参考本次

Samsung_tiny4412(驱动笔记01)----linux 3.5,U-Boot,Busybox,SD卡启动环境搭建

/*********************************************************************************** * * linux 3.5,U-Boot,Busybox,SD卡启动环境搭建 * * 声明: * 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会 * 不对齐,从而影响阅读. * 2. 以下所有的shell命令都是在root权限下运行的; * 3. minicom(U-Boot)指的是用minico

Samsung_tiny4412(驱动笔记03)----字符设备驱动基本操作及调用流程

/*********************************************************************************** * * 字符设备驱动基本操作及调用流程 * * 声明: * 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会 * 不对齐,从而影响阅读. * 2. 以下所有的shell命令都是在root权限下运行的; * * 2015-3-7 阴 深圳 尚观 Sbin 曾剑锋 *******************

Samsung_tiny4412(驱动笔记04)----volatile,container_of,file_operations,file,inode

/*********************************************************************************** * * volatile,container_of,file_operations,file,inode * * 声明: * 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会 * 不对齐,从而影响阅读. * 2. 本文的结构体的注释主要是参考网络上的解释,几乎无任何个人理解,主要是为后续 * 代码编

Samsung_tiny4412(驱动笔记05)----Makefile,open,read,write,lseek,poll,ioctl,fasync

/*********************************************************************************** * * Makefile,open,read,write,lseek,poll,ioctl,fasync * * 声明: * 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会 * 不对齐,从而影响阅读. * * * 2015-3-9 阴 深圳 尚观 Var 曾剑锋 *****************

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

/**************************************************************************** * * list_head,proc file system,GPIO,ioremap * * 声明: * 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会 * 不对齐,从而影响阅读. * 2. 本文中有些源代码没有全部帖出来,主要是因为篇幅太大的原因; * 3. 基于2中的原因,本文借鉴了python中的缩进代

Samsung_tiny4412(驱动笔记02)----ASM with C,MMU,Exception,GIC

/**************************************************************************** * * ASM with C,MMU,Exception,GIC * * 声明: * 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会 * 不对齐,从而影响阅读. * 2. 以下所有的shell命令都是在root权限下运行的; * 3. 文中在需要往文件中写入内容的时候使用了如下2方式: * 1.如果文件不存在,

kmalloc vmalloc kzalloc malloc 和 get_free_page()【转】

转自:http://blog.csdn.net/hbhhww/article/details/7236695 kmalloc vmalloc kzalloc get_free_page()是内核空间申请内存空间函数 malloc是用户空间申请内存函数   一 ,kmalloc() 与 kfree()  和get_free_page的区别 1,用于申请较小的.连续的物理内存:使用的是内存分配器slab一小片.申请的内存位于物理内存的映射区域.其正真的物理地址只相差一个固定的偏移.    可以用这两

有关java连接数据库的驱动笔记

MSSQL SERVER的驱动由三个JAR包组成: msbase.jar mssqlserver.jar msutil.jar ORACLE 的9i以前的驱动是: classes12.jar ORACLE 的10g的驱动是 ojdbc14.jar classes12.jar - for Java 1.2 and 1.3 ojdbc14.jar - for Java 1.4 and 1.5 ojdbc5.jar - for Java 1.5 ojdbc6.jar - for Java 1.6