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