您的位置:首页 > 其它

Samsung_tiny4412(驱动笔记09)----alloc_pages,kmalloc,vmalloc,kmem_cache,class

2015-03-14 18:32 405 查看
/***********************************************************************************
*
*                   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);
}
...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: