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)
* {
* ... //省略代码
* }
* ... //省略代码
*
*
* -- 阴 深圳 尚观 Var 曾剑锋
**********************************************************************************/ \\\\\\\\\\\--*目录*--///////////
| 一. alloc_pages接口:
| 二. kmalloc接口:
| 三. vmalloc接口:
| 四. kmem_cache接口:
| 五. dma_alloc_coherent接口:
| 六. 三星pwm中间层驱动:
| 七. class接口:
\\\\\\\\\\\\\\\\//////////////// 一. alloc_pages接口:
. 常见内存分配标志:
. GFP_KERNEL: 内存分配会睡眠阻塞,当没有足够内存分配时,直到有内存分配;
. GFP_ATOMIC: 内存分配不会阻塞,没有足够内存分配时返回错误;
. 把需要分配的字节数换算成对应的页页框: get_order();
. 分配页框(page frame),如果分配多个页,分配的多个页在物理地址上是连续的;
. 两种分配2的get_order()次方个页框,分配失败返回NULL:
. struct page *p = alloc_pages(GFP_KERNEL, get_order());
. unsigned long p = __get_free_pages(GFP_KERNEL, get_order());
. 获取虚拟地址: void *addr = page_address(page);
. 两种释放连续的页框方法:
. __free_pages(page, get_order());
. free_pages(p, get_order());
. 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());
if(!virt)
return -ENOMEM; printk("virt = %p.\n", (void *)virt); return ;
} void __exit test_exit(void)
{
/*__free_pages(p, get_order(1234));*/
free_pages(virt, get_order());
}
... 二. kmalloc接口:
. 一般来说,kmalloc通常用于分配少量内存,保证可移植一般不超过128k,
在虚拟地址上连续, 在物理地址上也连续
. 分配内存: void *p = kmalloc(, GFP_KERNEL);
. 分配内存,并初始化为0: kzalloc();
. 释放由kmalloc分配的内存空间: kfree(p);
. 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 ;
} void __exit test_exit(void)
{
kfree(virt);
}
... 三. vmalloc接口:
. 一般来说,vmalloc通常用于分配大量内存,在虚拟地址上连续,在物理地址上不一定连续;
. 分配内存: void *p = vmalloc(0x900000);
. 释放vmalloc释放的空间: vfree(p);
. vmalloc接口实例Demo:
...
void *virt = NULL;
int __init test_init(void)
{
virt = vmalloc(0x800000);
if(!virt)
return -ENOMEM; printk("virt = %p.\n", virt); return ;
} void __exit test_exit(void)
{
vfree(virt);
}
... 四. kmem_cache接口:
. 使用高速内存池对象:
struct kmem_cache *kc = kmem_cache_create("kc", , ,
SLAB_HWCACHE_ALIGN, NULL);
. 分配内存块:
void *p = kmem_cache_alloc(kc, GFP_KERNEL);
. 释放内存块: kmem_cache_free(kc, p);
. 销毁对象: kmem_cache_destroy(kc);
. kmem_cache接口实例Demo:
...
struct kmem_cache *kc;
void *p[];
int __init test_init(void)
{
int i;
kc = kmem_cache_create("kc", , , SLAB_HWCACHE_ALIGN, NULL);
if(!kc)
return -ENOMEM; for(i = ; i < ; i++)
{
p[i] = kmem_cache_alloc(kc, GFP_KERNEL);
printk("p[%d] = %p.\n", i, p[i]);
} return ;
} void __exit test_exit(void)
{
int i; for(i = ; i < ; i++)
kmem_cache_free(kc, p[i]);
kmem_cache_destroy(kc);
}
... 五. dma_alloc_coherent接口:
. 为dma设备分配内存:
virt = dma_alloc_coherent(NULL, , &phys, GFP_KERNEL);
返回2个地址:
. virt ---> 虚拟地址
. phys ---> 物理地址
. 释放内存:
dma_free_coherent(NULL, , virt, phys);
传递参数:
. virt ---> 虚拟地址
. phys ---> 物理地址
. dma_alloc_coherent接口实例Demo:
...
dma_addr_t phys; //物理地址 physical
void *virt; //虚拟地址 virtual
int __init test_init(void)
{
int val; virt = dma_alloc_coherent(NULL, , &phys, GFP_KERNEL);
if(!virt)
return -ENOMEM; printk("phys = %#x\n", phys);
printk("virt = %p\n", virt); *(int *)virt = ; /*virt = phys + PAGE_OFFSET - PHYS_OFFSET*/
val = *(int *)(phys + PAGE_OFFSET - PHYS_OFFSET);
printk("val = %d\n", val); return ;
} void __exit test_exit(void)
{
dma_free_coherent(NULL, , virt, phys);
}
... 六. 三星pwm中间层驱动:
. 使pwm驱动工作:
. 打开板级文件: vim arch/arm/mach-exynos/mach-tiny4412.c
. 注释掉以下内容:
/*#ifdef CONFIG_TINY4412_BUZZER*/
&s3c_device_timer[],
/*#endif*/
. 请求pwm定时器:
struct pwm_device *pwm0 = pwm_request(int pwm_id, const char *label);
参数说明:
. pwm_id: 请求哪个定时器
. label : 设置名字
. 配置pwm定时器:
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
. 开启定时器: int pwm_enable(struct pwm_device *pwm)
. 关闭定时器: void pwm_disable(struct pwm_device *pwm)
. 释放pwm资源: pwm_free(struct pwm_device *pwm);
. 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(); void pwm_set_freq(int freq)
{
unsigned int cnt = NS_IN_HZ / freq; pwm_config(pwm_t0, cnt / , cnt);
//配置GPIO引脚为定时器输出功能
s3c_gpio_cfgpin(buzzer_gpio, S3C_GPIO_SFN());
pwm_enable(pwm_t0);
} void pwm_stop(void)
{
gpio_direction_output(buzzer_gpio, );
pwm_disable(pwm_t0);
} static int test_open(struct inode *inode, struct file *file)
{
if(!mutex_trylock(&mtx))
return -EAGAIN; return ;
} static int test_close(struct inode *inode, struct file *file)
{
mutex_unlock(&mtx); return ;
} static long test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case PWM_IOC_SET_FREQ:
if(arg <= )
return -EINVAL;
pwm_set_freq(arg);
break;
case PWM_IOC_STOP:
pwm_stop();
break;
default:
return -EINVAL;
} return ;
} 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, );
ret = register_chrdev(major, DEV_NAME, &fops);
if(ret > )
{
major = ret;
printk("major = %d\n", major);
ret = ;
}
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接口:
. 声明类对象: struct class cls;
. 两种注册类对象方式:
. class_register(&cls);
. class_create();
. 两种注销类对象的方式:
. class_unregister(&cls);
. class_destroy();
. 声明设备: struct device dev;
. 两种注册设备的方式:
. device_register();
. device_create();
. 两种注销设备的方式:
. device_unregister();
. device_destroy();
. class接口实例Demo:
...
static int test_open(struct inode *inode, struct file *file)
{
printk("Dev open.\n"); return ;
} static int test_close(struct inode *inode, struct file *file)
{
printk("Dev close.\n"); return ;
} 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 > )
{
major = ret;
ret = ;
} else {
printk("register_chrdev FAILED!\n");
goto err1;
} dev = device_create(cls, NULL, MKDEV(major, ),
NULL, "up_dev%d", );
if(IS_ERR(dev))
{
printk("device_create FAILED!\n");
ret = PTR_ERR(dev);
goto err2;
} return ; 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(, )); unregister_chrdev(major, DEV_NAME); /**
* class_unregister(&cls);
*/
class_destroy(cls);
}
...
Samsung_tiny4412(驱动笔记09)----alloc_pages,kmalloc,vmalloc,kmem_cache,class的更多相关文章
- Samsung_tiny4412(驱动笔记01)----linux 3.5,U-Boot,Busybox,SD卡启动环境搭建
/*********************************************************************************** * * linux 3.5,U ...
- Samsung_tiny4412(驱动笔记06)----list_head,proc file system,GPIO,ioremap
/**************************************************************************** * * list_head,proc fil ...
- Samsung_tiny4412(驱动笔记05)----Makefile,open,read,write,lseek,poll,ioctl,fasync
/*********************************************************************************** * * Makefile,op ...
- Samsung_tiny4412(驱动笔记04)----volatile,container_of,file_operations,file,inode
/*********************************************************************************** * * volatile,co ...
- Samsung_tiny4412(驱动笔记03)----字符设备驱动基本操作及调用流程
/*********************************************************************************** * * 字符设备驱动基本操作及 ...
- Samsung_tiny4412(驱动笔记10)----mdev,bus,device,driver,platform
/*********************************************************************************** * * mdev,bus,de ...
- Samsung_tiny4412(驱动笔记07)----spinlock,semaphore,atomic,mutex,completion,interrupt
/*********************************************************************************** * * spinlock,se ...
- Samsung_tiny4412(驱动笔记02)----ASM with C,MMU,Exception,GIC
/**************************************************************************** * * ASM with C,MMU,Exc ...
- Samsung_tiny4412(驱动笔记08)----jiffies,timer,kthread,workqueue,tasklet
/*********************************************************************************** * * jiffies,tim ...
随机推荐
- pandas (loc、iloc、ix)的区别
loc:通过行标签索引数据 iloc:通过行号索引行数据 ix:通过行标签或行号索引数据(基于loc和iloc的混合) 使用loc.iloc.ix索引第一行数据: loc: iloc: ix:
- js 几个重要的特性
背景: 语法借鉴 java 函数借鉴 scheme 原型继承借鉴 self 正则表达式借鉴 Perl 1.动态语言 函数的定义和调用 形参与实参不需要一致 形参可由 argu ...
- const 学习笔记
#include<stdlib.h> #include<iostream> using namespace std; int main(){ // const 仅仅起到是否为常 ...
- Android 使用SQLite
SQLite简介 Google为Andriod的较大的数据处理提供了SQLite,他在数据存储.管理.维护等各方面都相当出色,功能也非常的强大.SQLite具备下列特点: 1.轻量级 使用 SQLit ...
- 3-23 Rspec自动化测试(开始练习)
闰年程序 leap_year_spec.rb require_relative './leap_year' describe "Leap Year" do it "201 ...
- 49 DOM(2)
一.value属性: input ,select 标签 ,textarea 标签中有value属性, 获取他们属性值的方法,先获取该元素ele,然后ele.value得到value值. <!DO ...
- qxx项目大文件上传
1. 在做大文件上传的时候,要注意修改文件的配置,php.ini的配置,还有连接时间.这些东西都记不清了,明天需要问一下芳哥,然后遇到问题的时候就能自己解决了. 2. 然后就遇到一个很尴尬的问题:大文 ...
- python-day29--socket
一 .客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 二. 三. 1.互联网协议就相当于计算机界的英语 2.数据传输的过程中包头一定要是固定的长度 四.socket层的位置 so ...
- C++中的构造函数,拷贝构造函数,赋值函数
C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法.下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象 ...
- UVA-10163 Storage Keepers (0-1背包)
题目大意:有n个仓库,m个应聘者,每人对应一个能力值.一个人可以看多个仓库,一间仓库只能被一个人看.如果一个能力为p的人看k间仓库,那么安全系数为p/k,求出最大的最小安全系数,并且求出在此情况下所有 ...