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