/***********************************************************************************
*
* 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的更多相关文章

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

    /*********************************************************************************** * * linux 3.5,U ...

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

    /**************************************************************************** * * list_head,proc fil ...

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

    /*********************************************************************************** * * Makefile,op ...

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

    /*********************************************************************************** * * volatile,co ...

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

    /*********************************************************************************** * * 字符设备驱动基本操作及 ...

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

    /*********************************************************************************** * * mdev,bus,de ...

  7. Samsung_tiny4412(驱动笔记07)----spinlock,semaphore,atomic,mutex,completion,interrupt

    /*********************************************************************************** * * spinlock,se ...

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

    /**************************************************************************** * * ASM with C,MMU,Exc ...

  9. Samsung_tiny4412(驱动笔记08)----jiffies,timer,kthread,workqueue,tasklet

    /*********************************************************************************** * * jiffies,tim ...

随机推荐

  1. Codeforces 847E - Packmen

    847E - Packmen 思路:二分时间. 代码: #include<bits/stdc++.h> using namespace std; #define ll long long ...

  2. 滑动窗口解决Substring Search Problem

    2018-07-18 11:19:19 一.Minimum Window Substring 问题描述: 问题求解: public String minWindow(String s, String ...

  3. 关于安装php时 --with-mysql命令参数问题

    如果是rpm安装mysql则直接写成--with-mysql 如果是编译安装mysql则写成--with-mysql=mysql安装路劲 如果你还没有安装Mysql数据库,可以暂时不编译

  4. English trip -- VC(情景课) 7 B Clothing 服装

    xu言: 不要使用中式的思维去思考西方的语义!!!切记切记 words a tie   领带 a blouse  女士衬衣 a sweater  毛衣 a skirt  短裙 a jacket   夹 ...

  5. English trip -- VC(情景课)3 B Bamily members

    xu言: 今天,好困啊 -__-. . zZ 早点睡吧...适当的休息,才能更好的学习 Vocabulary focus  husband wife uncle aunt brother sister ...

  6. LeetCode--121--卖卖股票的最佳时机

    问题描述: 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润. 注意你不能在买入股票前卖出 ...

  7. Confluence 6 配置 LDAP 连接池

    当 LDAP 连接池被启用后,LDAP 目录服务器将会维护一个连接池同时当必要的时候指派他们.当一个连接关闭后,这个连接将会放回到连接池中供以后进行使用.这种设置将会有效的提高系统性能. 希望配置 L ...

  8. 我的Java学习笔记-语法

    Java的语法与C#的语法基本都一样,毕竟都是面向对象编程语言.下面记录下Java独有的和我在C#中学习不熟的语法知识 一.Java是解释型语言 二.Java修饰符 1. 访问控制修饰符 defaul ...

  9. ssh The authenticity of host '10.11.26.2 (10.11.26.2)' can't be established

    The authenticity of host '10.11.26.2 (10.11.26.2)' can't be established. ECDSA key fingerprint is SH ...

  10. 『科学计算』科学绘图库matplotlib练习

    思想:万物皆对象 作业 第一题: import numpy as np import matplotlib.pyplot as plt x = [1, 2, 3, 1] y = [1, 3, 0, 1 ...