1:静态映射方法的特点:
内核移植时以代码的形式硬编码,如果要更改必须改源代码后重新编译内核在内核启动时建立静态映射表,到内核关机时销毁,中间一直有效对于移植好的内核,你用不用他都在那里
2:动态映射方法的特点:
驱动程序根据需要随时动态的建立映射、使用、销毁映射映射是短期临时的
3:如何选择虚拟地址映射方法
(1)2种映射并不排他,可以同时使用
(2)静态映射类似于C语言中全局变量,动态方式类似于C语言中malloc堆内存
(3)静态映射的好处是执行效率高,坏处是始终占用虚拟地址空间;动态映射的 好处是按需使用虚拟地址空间,坏处是每次使用前后都需要代码去建立映射&销毁映射(还得学会使用那些内核函数的使用)
4:静态映射表
不同版本的内核,其静态映射表的文件名以及文件路径不一定相同,但是一般都在arch/arm/xxx/map_xx.h文件中
(1)s5pv210的主映射表位于:arch/arm/plat-s5p/include/plat/map-s5p.h。
CPU在安排寄存器地址时不是随意乱序分布的,而是按照模块去区分的。每一个模块内部的很多个寄存器的地址是连续的。所以内核在定义寄存器地址时都是先找到基地址,然后再用基地址+偏移量来寻找具体的一个寄存器。这个文件夹下面的虚拟地址基地址是不全的,原因是三星在移植的时候只是移植了自己需要的部分,将来我们需要添加其他基地址的时候直接添加就行了。map-s5p.h中定义的就是要用到的几个模块的寄存器基地址(虚拟地址)。
(2)虚拟地址基地址定义在:arch/arm/plat-samsung/include/plat/map-base.h
#define S3C_ADDR_BASE (0xFD000000) // 三星移植时确定的静态映射表的基地址,表中的所有虚拟地址都是以这个地址+偏移量来指定的。
(3)GPIO相关的主映射表位于:arch/arm/machs5pv210/include/mach/regs-gpio.h
表中是GPIO的各个端口的基地址的定义
(4)GPIO的具体寄存器定义位于:arch/arm/mach-s5pv210/include/mach/gpio-bank.h
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GPJ0CON S5PV210_GPJ0CON
#define GPJ0DAT S5PV210_GPJ0DAT
//#define MYMAJOR 200
#define MYCNT 1
#define MYNAME "testchar"
char kbuf[100];
static dev_t mydev;
static struct cdev test_cdev;
static struct class *test_class;
static int test_chrdev_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "test_chrdev_open...\n");
/* 配置IO为输出 */
writel((1 << 12) | (1 << 16) | (1 << 20), GPJ0CON);
writel((1 << 3) | (1 << 4) | (1 << 5), GPJ0DAT);
return 0;
}
static int test_chrdev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "test_chrdev_release...\n");
/* 关闭所有LED */
writel((1 << 3) | (1 << 4) | (1 << 5), GPJ0DAT);
return 0;
}
ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
{
printk(KERN_INFO "test_chrdev_read...\n");
return 0;
}
static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
{
int ret = -1;
printk(KERN_INFO "test_chrdev_write...\n");
/* 清空内存 */
memset(kbuf, 0, sizeof(kbuf));
/* 将数据从用户空间拷贝到内核空间 */
ret = copy_from_user(kbuf, ubuf, count);
if (ret)
{
printk(KERN_ERR "copy_from_user fail\n");
return -EINVAL;
}
/* 判断LED打开还是关闭 */
if (kbuf[0] == '1')
writel((0 << 3) | (0 << 4) | (0 << 5), GPJ0DAT);
else if (kbuf[0] == '0')
writel((1 << 3) | (1 << 4) | (1 << 5), GPJ0DAT);
return 0;
}
static const struct file_operations test_fops = {
.owner = THIS_MODULE,
.open = test_chrdev_open,
.release = test_chrdev_release,
.write = test_chrdev_write,
.read = test_chrdev_read,
};
static int __init chrdev_init(void)
{
int retval;
printk(KERN_INFO "chrdev_init...\n");
/* 分配主次设备号 */
/*
mydev = MKDEV(MYMAJOR, 0);
retval = register_chrdev_region(mydev, MYCNT, MYNAME);
if (retval) {
printk(KERN_ERR "Unable to register minors for %s\n", MYNAME);
return -EINVAL;
}
*/
retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME);
if (retval < 0)
{
printk(KERN_ERR "Unable to alloc minors for %s\n", MYNAME);
goto flag1;
}
printk(KERN_INFO "alloc_chrdev_region success\n");
printk(KERN_INFO "major = %d, minor = %d.\n", MAJOR(mydev), MINOR(mydev));
/* 注册字符设备驱动 */
cdev_init(&test_cdev, &test_fops);
retval = cdev_add(&test_cdev, mydev, MYCNT);
if (retval) {
printk(KERN_ERR "Unable to cdev_add\n");
goto flag2;
}
printk(KERN_INFO "cdev_add success\n");
/* 创建设备类 */
test_class = class_create(THIS_MODULE, "test_class");
if (IS_ERR(test_class)) {
printk(KERN_ERR "Unable to class_create\n");
goto flag3;
}
/* 创建设备节点 */
device_create(test_class, NULL, mydev, NULL, "test");
return 0;
flag3:
cdev_del(&test_cdev);
flag2:
unregister_chrdev_region(mydev, MYCNT);
flag1:
return -EINVAL;
}
static void __exit chrdev_exit(void)
{
printk(KERN_INFO "chrdev_exit...\n");
/* 销毁设备类节点 */
device_destroy(test_class, mydev);
class_destroy(test_class);
/* 注销字符设备驱动 */
cdev_del(&test_cdev);
/* 注销申请的主次设备号 */
unregister_chrdev_region(mydev, MYCNT);
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("lsm"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息
5:动态映射操作LED
5.1:如何建立动态映射
(1)request_mem_region:向内核申请(报告)需要映射的内存资源。
(2)ioremap:真正用来实现映射,传给他物理地址他返回给你一个虚拟地址
返回值:成功返回一个虚拟地址(或者是一段虚拟地址的首地址,具体取决于我们需要映射的字节长度),失败返回0
5.2:如何销毁动态映射
(1)iounmap:解除映射
(2)release_mem_region:释放映射
#include
#include
#include
#include
#include
#include
#include
#define GPJ0CON_PA 0xe0200240
#define GPJ0DAT_PA 0xe0200244
unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;
//#define MYMAJOR 200
#define MYCNT 1
#define MYNAME "testchar"
char kbuf[100];
static dev_t mydev;
static struct cdev test_cdev;
static struct class *test_class;
static int test_chrdev_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "test_chrdev_open...\n");
/* 配置IO为输出 */
writel((1 << 12) | (1 << 16) | (1 << 20), pGPJ0CON);
writel((1 << 3) | (1 << 4) | (1 << 5), pGPJ0DAT);
return 0;
}
static int test_chrdev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "test_chrdev_release...\n");
/* 关闭所有LED */
writel((1 << 3) | (1 << 4) | (1 << 5), pGPJ0DAT);
return 0;
}
ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
{
printk(KERN_INFO "test_chrdev_read...\n");
return 0;
}
static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
{
int ret = -1;
printk(KERN_INFO "test_chrdev_write...\n");
/* 清空内存 */
memset(kbuf, 0, sizeof(kbuf));
/* 将数据从用户空间拷贝到内核空间 */
ret = copy_from_user(kbuf, ubuf, count);
if (ret)
{
printk(KERN_ERR "copy_from_user fail\n");
return -EINVAL;
}
/* 判断LED打开还是关闭 */
if (kbuf[0] == '1')
writel((0 << 3) | (0 << 4) | (0 << 5), pGPJ0DAT);
else if (kbuf[0] == '0')
writel((1 << 3) | (1 << 4) | (1 << 5), pGPJ0DAT);
return 0;
}
static const struct file_operations test_fops = {
.owner = THIS_MODULE,
.open = test_chrdev_open,
.release = test_chrdev_release,
.write = test_chrdev_write,
.read = test_chrdev_read,
};
static int __init chrdev_init(void)
{
int retval;
printk(KERN_INFO "chrdev_init...\n");
/* 分配主次设备号 */
/*
mydev = MKDEV(MYMAJOR, 0);
retval = register_chrdev_region(mydev, MYCNT, MYNAME);
if (retval) {
printk(KERN_ERR "Unable to register minors for %s\n", MYNAME);
return -EINVAL;
}
*/
retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME);
if (retval < 0)
{
printk(KERN_ERR "Unable to alloc minors for %s\n", MYNAME);
goto flag1;
}
printk(KERN_INFO "alloc_chrdev_region success\n");
printk(KERN_INFO "major = %d, minor = %d.\n", MAJOR(mydev), MINOR(mydev));
/* 注册字符设备驱动 */
cdev_init(&test_cdev, &test_fops);
retval = cdev_add(&test_cdev, mydev, MYCNT);
if (retval) {
printk(KERN_ERR "Unable to cdev_add\n");
goto flag2;
}
printk(KERN_INFO "cdev_add success\n");
/* 创建设备类 */
test_class = class_create(THIS_MODULE, "test_class");
if (IS_ERR(test_class)) {
printk(KERN_ERR "Unable to class_create\n");
goto flag3;
}
/* 创建设备节点 */
device_create(test_class, NULL, mydev, NULL, "test");
/* 申请内存 */
if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))
goto err4;
if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0DAT"))
goto err5;
/* 内存映射 */
pGPJ0CON = ioremap(GPJ0CON_PA, 4);
pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);
return 0;
err5:
release_mem_region(GPJ0CON_PA, 4);
err4:
device_destroy(test_class, mydev);
class_destroy(test_class);
flag3:
cdev_del(&test_cdev);
flag2:
unregister_chrdev_region(mydev, MYCNT);
flag1:
return -EINVAL;
}
static void __exit chrdev_exit(void)
{
printk(KERN_INFO "chrdev_exit...\n");
iounmap(pGPJ0CON);
iounmap(pGPJ0DAT);
release_mem_region(GPJ0CON_PA, 4);
release_mem_region(GPJ0DAT_PA, 4);
/* 销毁设备类节点 */
device_destroy(test_class, mydev);
class_destroy(test_class);
/* 注销字符设备驱动 */
cdev_del(&test_cdev);
/* 注销申请的主次设备号 */
unregister_chrdev_region(mydev, MYCNT);
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("lsm"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息