- 联系
- 管理
- 订阅
-
<div class="blogStats"> <!--done-->
随笔- 57
文章- 0
评论- 5
阅读-
22万</div><!--end: blogStats --> </div><!--end: navigator 博客导航栏 --> <div id="post_detail">
</h1> <div class="clear"></div> <div class="postBody"> <div id="cnblogs_post_body" class="blogpost-body blogpost-body-html">
1:驱动开发环境
要进行linux驱动开发我们首先要有linux内核的源码树,并且这个linux内核的源码树要和开发板中的内核源码树要一直;
比如说我们开发板中用的是linux kernel内核版本为2.6.35.7,在我们ubuntu虚拟机上必须要有同样版本的源码树,
我们再编译好驱动的的时候,使用modinfo XXX命令会打印出一个版本号,这个版本号是与使用的源码树版本有关,如果开发板中源码树中版本与
modinfo的版本信息不一致使无法安装驱动的;
我们开发板必须设置好nfs挂载;这些在根文件系统一章有详细的介绍;
2:开发驱动常用的几个命令
lsmod :list moduel 把我们机器上所有的驱动打印出来,
insmod:安装驱动
rmmod:删除驱动
modinfo:打印驱动信息
3:写linux驱动文件和裸机程序有很大的不同,虽然都是操作硬件设备,但是由于写裸机程序的时候是我们直接写代码操作硬件设备,这只有一个层次;
而我们写驱动程序首先要让linux内核通过一定的接口对接,并且要在linux内核注册,应用程序还要通过内核跟应用程序的接口相关api来对接;
4:驱动的编译模式是固定的,以后编译驱动的就是就按照这个模式来套即可,下面我们来分下一下驱动的编译规则:
#ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个 #KERN_VER = $(shell uname -r) #KERN_DIR = /lib/modules/$(KERN_VER)/build
开发板的linux内核的源码树目录
KERN_DIR = /root/driver/kernel
obj-m += module_test.o
all:
make -C $(KERN_DIR) M=pwd
modulescp:
cp *.ko /root/porting_x210/rootfs/rootfs/driver_test.PHONY: clean
clean:
make -C $(KERN_DIR) M=pwd
modules cleanmake -C $(KERN_DIR) M=`PWD` modules
这句话代码的作用就是到 KERN_DIR这个文件夹中 make modules
把当前目录赋值给M,M作为参数传到主目录的Makefile中,实际上是主目录的makefile中有目标modules,下面有一定的规则来编译驱动;
#KERN_VER = $(shell uname -r)
#KERN_DIR = /lib/modules/$(KERN_VER)/build
我们在ubuntu中编译内核的时候用这两句代码,因为在ubuntu中为我们保留了一份linux内核的源码树,我们编译的时候直接调用那个源码树的主Makefile以及一些头文件、内核函数等;
了解规则以后,我们设置好KERN_DIR、obj-m这两个变量以后直接make就可以了;
经过编译会得到下面一些文件:
下面我们可以使用
lsmod命令来看一下我们ubuntu机器现有的一些驱动
可以看到有很多的驱动,
下面我们使用
insmod XXX命令来安装驱动,在使用lsmod命令看一下实验现象
可以看到我们刚才安装的驱动放在了第一个位置;
使用modinfo来打印一下驱动信息
modinfo xxx.ko
这里注意vermagic 这个的1.8.0-41是你用的linux内核源码树的版本号,只有这个编译的版本号与运行的linux内核版本一致的时候,驱动程序才会被安装
注意license:GPL linux内核开元项目的许可证一般都是GPL这里尽量设置为GPL,否则有些情况下会出现错误;
下面使用
rmmod xxx删除驱动;
-------------------------------------------------------------------------------------
5:下面我们分析一下驱动。C文件
#include <linux/module.h> // module_init module_exit #include <linux/init.h> // __init __exit
// 模块安装函数
static int __init chrdev_init(void)
{
printk(KERN_INFO “chrdev_init helloworld init\n”);
//printk("<7>" “chrdev_init helloworld init\n”);
//printk("<7> chrdev_init helloworld init\n");<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
// 模块下载函数
static void __exit chrdev_exit(void)
{
printk(KERN_INFO “chrdev_exit helloworld exit\n”);
}module_init(chrdev_init);
module_exit(chrdev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE(“GPL”); // 描述模块的许可证
MODULE_AUTHOR(“aston”); // 描述模块的作者
MODULE_DESCRIPTION(“module test”); // 描述模块的介绍信息
MODULE_ALIAS(“alias xxx”); // 描述模块的别名信息module_init宏的作用就是把insmod与module_init(XXX)中的XXX绑定起来,insmod命令实际上是执行的chrdev_init这个函数;
我们写的这个chrdev_init函数只有一条打印信息;
因为内核的printk函数是设置打印级别的,我们可以是用dmesg命令来查看打印信息
如上面图,可见我们insmod的时候打印了 chrdev_init helloworld init这条信息,这里正是chrdev_init 这个函数中打印的信息;
因为这里用到了很多内核函数、宏等,所以要包含这些函数、宏的头文件
我们可以建立内核的man手册来查询这些函数;
http://blog.sina.com.cn/s/blog_6642cd020101gtin.html
下载的版本为linux-2.6.35.7建立内核man手册
现在就可以man printk来查找linux内核函数了,但是这里注意到,好像还是没有相关的头文件包含
可以参考,常用的内核函数的头文件包含,
http://blog.youkuaiyun.com/guowenyan001/article/details/43342301
最后办法就是把内核文件在SI中建立连接来查找;
下面来看一下__init
#define __init __section(.init.text) __cold notrace
用来定义段属性的,把Chrdev_init函数定义为.init.text段,这个段在启动内核以后会内核会自动释放掉,以节省内存空间;
采用2.6.35.7源码树编译在开发板上运行;
KERN_DIR = /usr/bhc/kernel/linux-2.6.35.7/
把KERN_DIR目录变量更改为我们安装的内核源码树目录即可
make
注意:一定要把源码树目录中主Makefile中ARCH、cross_compile变量的值更改了;
开发板使用在zImage 也要是用这个内核来编译的zImage
在开发板中同样使用
lsmod
insmod
rmmod
----------------------------------------------------------------------------------------------------------
6:应用层是如何调用驱动的
在应用层进行应用编程的时候我们对底层设备的操作包括:open close write read 等操作;
如fd = open("/dev/mouse", O_RDWR);
这些操作都是通过内核给定的api接口来实现的,这些接口是通过file_operations这个结构体来实现的
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); };
这个结构体中包含了对硬件的所有操作;
结构体中大多都是函数指针,这些函数指针指向真正的设备的操作函数;
写好硬件真正的读写函数以后,在建立一个struct file_operations类型的结构体,把相应的操作对应真正的读写函数初始化以后,
我们还需要把这个结构体向内核初始化,告诉内核,让内核知道我们建立了一个驱动,
我们用register_chrdev这个函数来向内核注册:
static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);}
static inline void unregister_chrdev(unsigned int major, const char *name)
{
__unregister_chrdev(major, 0, 256, name);
}这个函数来注销驱动
注册的时候需要一个注册号major、name、以及建立好的struct file_operations结构体;
内核中有一个结构体数组,这个数组中一共255个元素,我们写好的驱动注册的时候需要一个major主设备号,这个主设备号对应数组的下标,注册的时候设备命令以及file_operations的指针就放入了这个结构体数组对应的major下标的那个元素中;
注册以后,内核知道有这个设备了,应用程序才可以通过内核来调用api来操作这个设备;
应用是如何调用驱动呢?
应用调用驱动是通过驱动设备文件来调用驱动的,我们首先要用mknod /dev/xxx c 主设备号 次设备号 命令来创建驱动设备文件,
这样的话应用程序就可以通/dev/xxx这个设备驱动文件,获取对应的主设备号,内核在通过这个主设备号找到设备名称和file_operations这个结构体;
可以看一下上面这个图:
应用程序:通过/dev/xxx设备文件来找到这个主设备号,在通过系统api(open、close、read、write),执行对设备的操作;
而在内核中linux内核通过主设备号找到file_operation这个结构图,在通过这个结构体找到真正的操作设备的函数;
所以我们写驱动程序在应用层做的事情就是:
使用mknod命令来建立设备驱动文件;
找到设备文件,调用api操作设备即可;
在linux内核中要做的事情有:
写好真正的设备操作函数,建立file_operation结构体,用register_chrdev函数来向内核注册;
下面我们对每一个步骤做详细的操作分析:
真正操作硬件设备的函数(以led为例)
#include <linux/module.h> // module_init module_exit #include <linux/init.h> // __init __exit #include <linux/fs.h>
#define MYMAJOR 200
#define MYNAME “LED_DEVICE”//int (*open) (struct inode *, struct file *);
//open函数的格式是上面的格式:
static int led_dev_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO “led_dev_open open\n”);
}
//release函数的原型是:int (*release) (struct inode *, struct file *);
static int led_dev_close(struct inode *inode, struct file *file)
{
printk(KERN_INFO “led_dev_close close\n”);
}
static const struct file_operations led_dev_fops{
.opne = led_dev_open,
.release = led_dev_close,
}
static int __init leddev_init(void)
{
int ret = -1;
printk(KERN_INFO “leddev_init”);
ret = register_chrdev(MYMAJOR, MYNAME, &led_dev_fops);
if(ret) {
printk(KERN_ERR “led devices rigister failed”);
retunt -EINVAL;
}printk(KERN_INFO “led regist sucess”);
return 0;
}
static int __exit leddev_exit(void)
{
printfk(KERN_INFO “led device exit”);
unregister_chrdev(MYMAJOR, NAME);
}
module_init(leddev_init);
module_exit(leddev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("bhc"); // 描述模块的作者
MODULE_DESCRIPTION("led test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息这样我们就可以去编译以后在开发板中使用insmod rmmod等命令来安装删除驱动了;
在这里补充一问题,安装好驱动以后,主设备号可以在/proc/devices文件中查看,但是由于不同的设备主设备号占用的不一样,有时候需要系统来自动分配
主设备号,这个如何实现呢:
我们可以在register_chrdev函数的major变量传参0进去,因为这个函数的返回值为主设备号,所以我们定义一个全局变量来接受这个值即可
static int mymajor;
//注册的时候
mymajor = register_chrdev(0, MYNAME, &ded_dev_fops);
//释放的时候
unregister_chrdev(mymajor, MYNAME);
这样即可;
-----------------------------------------------------------------------------------------------------------------
下面介绍api的write read函数:
read函数的函数原型是下面:
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
write函数的函数原型:
static ssize_t ab3550_bank_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
write函数的函数原型是这样的
这里注意下:应用层面的内存buf与内核中的buf数据不能直接交换的,不能用memcpy函数复制;
要使用
copy_form_user
copy_to_user两个函数;
static inline long copy_to_user(void __user *to, const void *from, unsigned long n);
static inline long copy_from_user(void *to, const void __user * from, unsigned long n)
两个函数的原型为上:
app程序
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>
#define FILE “/dev/led_device”
char ubuf[100];
int main(void)
{
int fd = -1;
int ret = -1;</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">打开led设备</span> fd =<span style="color: rgba(0, 0, 0, 1)"> open(FILE, O_RDWR); </span><span style="color: rgba(0, 0, 255, 1)">if</span>(fd < <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">) { printf(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/dev/led_device open failed\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> -<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">; } printf(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/dev/led_device open success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">写led设备</span> ret = write(fd, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led_blink_bbb</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 128, 1)">13</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">if</span>(ret < <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">) { printf(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">write error\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); } printf(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">write success...\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">读led设备</span> ret = read(fd, ubuf, <span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">); printf(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">read is %s\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, ubuf); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">关闭led设备</span>
close(fd);
}
驱动程序相关代码
#include <linux/module.h> // module_init module_exit #include <linux/init.h> // __init __exit #include <linux/fs.h> #include <asm/uaccess.h>
#define MYMAJOR 200
#define MYNAME “LED_DEVICE”static char kbuf[100];
static int mymojor;static int led_dev_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO “led_dev open\n”);</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
static int led_dev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO “led_dev close\n”);</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
ssize_t led_dev_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
int ret = -1;ret </span>= copy_to_user(buf, kbuf, <span style="color: rgba(0, 0, 255, 1)">sizeof</span><span style="color: rgba(0, 0, 0, 1)">(kbuf)); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(ret) { printk(KERN_ERR </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kernel led read error\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); } printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led device read success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
static ssize_t led_dev_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{
int ret = -1;ret </span>=<span style="color: rgba(0, 0, 0, 1)"> copy_from_user(kbuf, user_buf, count); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(ret) { printk(KERN_ERR </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kernel led write error\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> -<span style="color: rgba(0, 0, 0, 1)">EINVAL; } printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led device write success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
static const struct file_operations led_dev_fops = {
.open = led_dev_open,
.write = led_dev_write,
.read = led_dev_read,
.release = led_dev_release,
.owner = THIS_MODULE,
};// 模块安装函数
static int __init leddev_init(void)
{printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led_device init\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">printk("<7>" "chrdev_init helloworld init\n"); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">printk("<7> chrdev_init helloworld init\n"); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">在这里进行注册驱动,因为安装驱动实际上执行的就是这个函数;</span> mymojor = register_chrdev(<span style="color: rgba(128, 0, 128, 1)">0</span>, MYNAME, &<span style="color: rgba(0, 0, 0, 1)">led_dev_fops); </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 0, 1)">mymojor) { printk(KERN_ERR </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led_device failed\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> -<span style="color: rgba(0, 0, 0, 1)">EINVAL; } printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">leddev_dev regist success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
// 模块下载函数
static void __exit leddev_exit(void)
{
printk(KERN_INFO “leddev_dev exit\n”);</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">注销led设备驱动</span>
unregister_chrdev(mymojor, MYNAME);
printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">leddev_dev unregist success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
module_init(leddev_init);
module_exit(leddev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE(“GPL”); // 描述模块的许可证
MODULE_AUTHOR(“bhc”); // 描述模块的作者
MODULE_DESCRIPTION(“led test”); // 描述模块的介绍信息
MODULE_ALIAS(“alias xxx”); // 描述模块的别名信息编译以后在开发板上测试。。。。。
-----------------------------------------------------------------------------------------------------
下面我们的低层驱动开始真正的操作硬件了:
在操作硬件的时候,我们会用到硬件的香瓜寄存器,因为我们之前在逻辑程序中使用的物理地址来直接写的,而我们在开发板上移植好内核以后
我们的内核程序就是运行在虚拟地址上了,所以我们要看一下我们的linux内核中虚拟地址跟物理地址是如何映射的,三星在移植的内核的时候,把硬件相关
的寄存器,建立了三个映射表文件,让我们来查找这些物理地址对应的虚拟地址:
首先来看一下静态虚拟地址映射:
分别为
arch/arm/plat-samsung/plat/map-base.h
看一下这个文件中的内容:
#define S3C_VA_IRQ S3C_ADDR(0x00000000) /* irq controller(s) */ #define S3C_VA_SYS S3C_ADDR(0x00100000) /* system control */ #define S3C_VA_MEM S3C_ADDR(0x00200000) /* memory control */ #define S3C_VA_TIMER S3C_ADDR(0x00300000) /* timer block */ #define S3C_VA_WATCHDOG S3C_ADDR(0x00400000) /* watchdog */ #define S3C_VA_OTG S3C_ADDR(0x00E00000) /* OTG */ #define S3C_VA_OTGSFR S3C_ADDR(0x00F00000) /* OTG PHY */ #define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */
因为cpu的相关模块的寄存器地址都是分块的,如终端相关寄存器,被分配在一起,如mem内存相关寄存器,map-base.h中把各个模块先关的虚拟基地址罗列了出来;
arch/arm/plat-s5p/include/plat/map-s5p.h
#define S5P_VA_CHIPID S3C_ADDR(0x00700000) #define S5P_VA_GPIO S3C_ADDR(0x00500000) #define S5P_VA_SYSTIMER S3C_ADDR(0x01200000) #define S5P_VA_SROMC S3C_ADDR(0x01100000) #define S5P_VA_AUDSS S3C_ADDR(0X01600000)
#define S5P_VA_UART0 (S3C_VA_UART + 0x0)
#define S5P_VA_UART1 (S3C_VA_UART + 0x400)
#define S5P_VA_UART2 (S3C_VA_UART + 0x800)
#define S5P_VA_UART3 (S3C_VA_UART + 0xC00)#define S3C_UART_OFFSET (0x400)
#define VA_VIC(x) (S3C_VA_IRQ + ((x) * 0x10000))
#define VA_VIC0 VA_VIC(0)
#define VA_VIC1 VA_VIC(1)
#define VA_VIC2 VA_VIC(2)
#define VA_VIC3 VA_VIC(3)这个文件中三星工程师把要用得到的模块的基地址又细化了,如GPIO UART0-3 SROM VIC中断,如我们要添加新的模块的话,可以在这个文件中,定义相关模块寄存器
的基地址;
arch/arm/mach-s5pv210/include/mach/regs-gpio.h
arch/arm/mach-s5pv210/include/mach/gpio-bank.h
可以看到在gpio-bank.h文件中,定义了每个寄存器的虚拟地址;我们在操作相关寄存器的时候直接用这里定义好的宏就可以;
如何遇到一个新的开发板如何找虚拟内存映射表呢,一般都是在arch/arm/plat或者 arch/arm/mach 等目录,一般是文件名都是map-文件;
下面开始我们真正的应用程序通过驱动来控制led硬件;
驱动模块
#include <linux/module.h> // module_init module_exit #include <linux/init.h> // __init __exit #include <linux/fs.h> #include <asm/uaccess.h> #include <plat/map-base.h> #include <plat/map-s5p.h> #include <mach/regs-gpio.h> #include <mach/gpio-bank.h>
#include <linux/string.h>
#define MYMAJOR 200
#define MYNAME “LED_DEVICE”#define GPJ0CON S5PV210_GPJ0CON
#define GPJ0DAT S5PV210_GPJ0DAT#define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)static char kbuf[100];
static int mymojor;static int led_dev_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO “led_dev open\n”);</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
static int led_dev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO “led_dev close\n”);</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
ssize_t led_dev_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
int ret = -1;ret </span>= copy_to_user(buf, kbuf, <span style="color: rgba(0, 0, 255, 1)">sizeof</span><span style="color: rgba(0, 0, 0, 1)">(kbuf)); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(ret) { printk(KERN_ERR </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kernel led read error\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); } printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led device read success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
static ssize_t led_dev_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{
int ret = -1;</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">首先把kbuf清零</span> memset(kbuf, <span style="color: rgba(128, 0, 128, 1)">0</span>, <span style="color: rgba(0, 0, 255, 1)">sizeof</span><span style="color: rgba(0, 0, 0, 1)">(kbuf)); ret </span>=<span style="color: rgba(0, 0, 0, 1)"> copy_from_user(kbuf, user_buf, count); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(ret) { printk(KERN_ERR </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kernel led write error\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> -<span style="color: rgba(0, 0, 0, 1)">EINVAL; } printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led device write success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">if</span> (kbuf[<span style="color: rgba(128, 0, 128, 1)">0</span>] == <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">1</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">) { rGPJ0CON </span>= <span style="color: rgba(128, 0, 128, 1)">0x11111111</span><span style="color: rgba(0, 0, 0, 1)">; rGPJ0DAT </span>= ((<span style="color: rgba(128, 0, 128, 1)">0</span><<<span style="color: rgba(128, 0, 128, 1)">3</span>) | (<span style="color: rgba(128, 0, 128, 1)">0</span><<<span style="color: rgba(128, 0, 128, 1)">4</span>) | (<span style="color: rgba(128, 0, 128, 1)">0</span><<<span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">)); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (kbuf[<span style="color: rgba(128, 0, 128, 1)">0</span>] == <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">0</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">) { rGPJ0DAT </span>= ((<span style="color: rgba(128, 0, 128, 1)">1</span><<<span style="color: rgba(128, 0, 128, 1)">3</span>) | (<span style="color: rgba(128, 0, 128, 1)">1</span><<<span style="color: rgba(128, 0, 128, 1)">4</span>) | (<span style="color: rgba(128, 0, 128, 1)">1</span><<<span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">)); } </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
static const struct file_operations led_dev_fops = {
.open = led_dev_open,
.write = led_dev_write,
.read = led_dev_read,
.release = led_dev_release,
.owner = THIS_MODULE,
};// 模块安装函数
static int __init leddev_init(void)
{printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led_device init\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">printk("<7>" "chrdev_init helloworld init\n"); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">printk("<7> chrdev_init helloworld init\n"); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">在这里进行注册驱动,因为安装驱动实际上执行的就是这个函数;</span> mymojor = register_chrdev(<span style="color: rgba(128, 0, 128, 1)">0</span>, MYNAME, &<span style="color: rgba(0, 0, 0, 1)">led_dev_fops); </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 0, 1)">mymojor) { printk(KERN_ERR </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led_device failed\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> -<span style="color: rgba(0, 0, 0, 1)">EINVAL; } printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">leddev_dev regist success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
// 模块下载函数
static void __exit leddev_exit(void)
{
printk(KERN_INFO “leddev_dev exit\n”);</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">注销led设备驱动</span>
unregister_chrdev(mymojor, MYNAME);
printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">leddev_dev unregist success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
module_init(leddev_init);
module_exit(leddev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE(“GPL”); // 描述模块的许可证
MODULE_AUTHOR(“bhc”); // 描述模块的作者
MODULE_DESCRIPTION(“led test”); // 描述模块的介绍信息
MODULE_ALIAS(“alias xxx”); // 描述模块的别名信息app文件
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h>
#define FILE “/dev/led_device”
char ubuf[100];
int main(void)
{
int fd = -1;
int ret = -1;</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">打开led设备</span> fd =<span style="color: rgba(0, 0, 0, 1)"> open(FILE, O_RDWR); </span><span style="color: rgba(0, 0, 255, 1)">if</span>(fd < <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">) { printf(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/dev/led_device open failed\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> -<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">; } printf(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">please input on | off | quit.\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">) { memset(ubuf, </span><span style="color: rgba(128, 0, 128, 1)">0</span>, <span style="color: rgba(0, 0, 255, 1)">sizeof</span><span style="color: rgba(0, 0, 0, 1)">(ubuf)); scanf(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">%s</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, ubuf); </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!strcmp(ubuf, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">on</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)) { write(fd , </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">1</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!strcmp(ubuf, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">off</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)) { write(fd, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">0</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!strcmp(ubuf, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">quit</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)) { </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">; } } </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">读led设备 </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">ret = read(fd, ubuf, 100); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">printf("read is %s\n", ubuf); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">关闭led设备</span>
close(fd);
}
使用动态虚拟地址
动态虚拟地址:当我们要使用这个寄存器的物理地址的时候,不用事先建立好的页表,而是给物理地址动态的分配一个虚拟地址,操作的时候直接使用这个动态分配的虚拟地址
操作物理地址即可,使用完以后取消映射即可;
使用动态虚拟地址映射首先:
1:建立映射
使用request_mem_region向内核申请虚拟地址空间;
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0)
request_mem_region实际上是一个宏,真正调用的是 __request_region这个函数;
request_mem_region宏需要三个参数:start:启示的物理地址,n长度,name
申请成功则返回0;
ioremap
#define ioremap(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE)
也是一个宏,调用的是内核函数__arm_ioremap
这个宏需要两个参数起始物理地址以及 长度;
2:使用完以后我们首先要消除映射
取消映射iounmap宏
#define iounmap(cookie) __iounmap(cookie)
接受一个参数,起始物理地址;
然后在消除分配的虚拟地址
使用release_mem_region
#define release_mem_region(start,n) __release_region(&iomem_resource, (start), (n))
这个宏只需要两个参数即可一个是起始物理地址,一个长度;
下面看具体代码:我们只修改驱动代码,应用层代码不进行修改了。。。
#include <linux/module.h> // module_init module_exit #include <linux/init.h> // __init __exit #include <linux/fs.h> #include <asm/uaccess.h> #include <plat/map-base.h> #include <plat/map-s5p.h> #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #include <linux/ioport.h> #include <linux/string.h> #include <asm/io.h>
#define MYMAJOR 200
#define MYNAME “LED_DEVICE”#define GPJ0_PA_base 0xE0200240
#define GPJ0CON_PA_OFFSET 0x0unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;static char kbuf[100];
static int mymojor;static int led_dev_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO “led_dev open\n”);</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
static int led_dev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO “led_dev close\n”);</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
ssize_t led_dev_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
int ret = -1;ret </span>= copy_to_user(buf, kbuf, <span style="color: rgba(0, 0, 255, 1)">sizeof</span><span style="color: rgba(0, 0, 0, 1)">(kbuf)); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(ret) { printk(KERN_ERR </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kernel led read error\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); } printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led device read success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
static ssize_t led_dev_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{
int ret = -1;</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">首先把kbuf清零</span> memset(kbuf, <span style="color: rgba(128, 0, 128, 1)">0</span>, <span style="color: rgba(0, 0, 255, 1)">sizeof</span><span style="color: rgba(0, 0, 0, 1)">(kbuf)); ret </span>=<span style="color: rgba(0, 0, 0, 1)"> copy_from_user(kbuf, user_buf, count); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(ret) { printk(KERN_ERR </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kernel led write error\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> -<span style="color: rgba(0, 0, 0, 1)">EINVAL; } printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led device write success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">if</span> (kbuf[<span style="color: rgba(128, 0, 128, 1)">0</span>] == <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">1</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">) { </span>*pGPJ0CON = <span style="color: rgba(128, 0, 128, 1)">0x11111111</span><span style="color: rgba(0, 0, 0, 1)">; </span>*(pGPJ0CON + <span style="color: rgba(128, 0, 128, 1)">1</span>) = ((<span style="color: rgba(128, 0, 128, 1)">0</span><<<span style="color: rgba(128, 0, 128, 1)">3</span>) | (<span style="color: rgba(128, 0, 128, 1)">0</span><<<span style="color: rgba(128, 0, 128, 1)">4</span>) | (<span style="color: rgba(128, 0, 128, 1)">0</span><<<span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">)); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (kbuf[<span style="color: rgba(128, 0, 128, 1)">0</span>] == <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">0</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">) { </span>*(pGPJ0CON + <span style="color: rgba(128, 0, 128, 1)">1</span>) = ((<span style="color: rgba(128, 0, 128, 1)">1</span><<<span style="color: rgba(128, 0, 128, 1)">3</span>) | (<span style="color: rgba(128, 0, 128, 1)">1</span><<<span style="color: rgba(128, 0, 128, 1)">4</span>) | (<span style="color: rgba(128, 0, 128, 1)">1</span><<<span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">)); } </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
static const struct file_operations led_dev_fops = {
.open = led_dev_open,
.write = led_dev_write,
.read = led_dev_read,
.release = led_dev_release,
.owner = THIS_MODULE,
};// 模块安装函数
static int __init leddev_init(void)
{printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led_device init\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">printk("<7>" "chrdev_init helloworld init\n"); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">printk("<7> chrdev_init helloworld init\n"); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">在这里进行注册驱动,因为安装驱动实际上执行的就是这个函数;</span> mymojor = register_chrdev(<span style="color: rgba(128, 0, 128, 1)">0</span>, MYNAME, &<span style="color: rgba(0, 0, 0, 1)">led_dev_fops); </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 0, 1)">mymojor) { printk(KERN_ERR </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">led_device failed\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> -<span style="color: rgba(0, 0, 0, 1)">EINVAL; } printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">leddev_dev regist success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!request_mem_region(GPJ0_PA_base + GPJ0CON_PA_OFFSET, <span style="color: rgba(128, 0, 128, 1)">8</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">GPJ0PABAST</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)) { </span><span style="color: rgba(0, 0, 255, 1)">return</span> -<span style="color: rgba(0, 0, 0, 1)">EINVAL; } pGPJ0CON </span>= ioremap(GPJ0_PA_base + GPJ0CON_PA_OFFSET, <span style="color: rgba(128, 0, 128, 1)">4</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
// 模块下载函数
static void __exit leddev_exit(void)
{
printk(KERN_INFO “leddev_dev exit\n”);</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">注销led设备驱动</span>
unregister_chrdev(mymojor, MYNAME);
iounmap(GPJ0_PA_base </span>+<span style="color: rgba(0, 0, 0, 1)"> GPJ0CON_PA_OFFSET); release_mem_region(GPJ0_PA_base </span>+ GPJ0CON_PA_OFFSET, <span style="color: rgba(128, 0, 128, 1)">8</span><span style="color: rgba(0, 0, 0, 1)">); printk(KERN_INFO </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">leddev_dev unregist success\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
module_init(leddev_init);
module_exit(leddev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE(“GPL”); // 描述模块的许可证
MODULE_AUTHOR(“bhc”); // 描述模块的作者
MODULE_DESCRIPTION(“led test”); // 描述模块的介绍信息
MODULE_ALIAS(“alias xxx”); // 描述模块的别名信息分类: 驱动<div id="blog_post_info">
20<div class="clear"></div> <div id="post_next_prev"> <a href="https://www.cnblogs.com/biaohc/p/6551383.html" class="p_n_p_prefix">« </a> 上一篇: <a href="https://www.cnblogs.com/biaohc/p/6551383.html" title="发布于 2017-03-16 23:11">根文件系统——busybox</a> <br> <a href="https://www.cnblogs.com/biaohc/p/6597122.html" class="p_n_p_prefix">» </a> 下一篇: <a href="https://www.cnblogs.com/biaohc/p/6597122.html" title="发布于 2017-03-26 21:02">linux驱动开发(二)</a>
· 使用 .NET 6 开发 TodoList 应用(30)——实现 Docker 打包和部署
· [WPF] 用 Effect 实现线条光影效果
· 品味 Spring Cache 设计之美
· 深入浅出 CSS 动画
· 记一次 .NET 某消防物联网 后台服务 内存泄漏分析
LoadPostCategoriesTags(cb_blogId, cb_entryId); LoadPostInfoBlock(cb_blogId, cb_entryId, cb_blogApp, cb_blogUserGuid);
GetPrevNextPost(cb_entryId, cb_blogId, cb_entryCreatedDate, cb_postType);
loadOptUnderPost();
GetHistoryToday(cb_blogId, cb_blogApp, cb_entryCreatedDate);
</div><!--end: forFlow --> </div><!--end: mainContent 主体内容容器--> <div id="sideBar"> <div id="sideBarMain"> <div id="sidebar_news" class="newsItem"><!--done-->
公告
<div id="profile_block"> 昵称: <a href="https://home.cnblogs.com/u/biaohc/"> biaohc </a> <br> 园龄: <a href="https://home.cnblogs.com/u/biaohc/" title="入园时间:2016-10-28"> 5年2个月 </a> <br> 粉丝: <a href="https://home.cnblogs.com/u/biaohc/followers/"> 50 </a> <br> 关注: <a href="https://home.cnblogs.com/u/biaohc/followees/"> 0 </a> <div id="p_b_follow">
+加关注
< 2022年1月 > 日 一 二 三 四 五 六 26 27 28 29 30 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 <div id="leftcontentcontainer"> <div id="blog-sidecolumn"><!-- 搜索 -->
- 我的随笔
- 我的评论
- 我的参与
- 最新评论
- 我的标签
<li><a id="itemListLink" onclick="this.blur();WarpClass('itemListLink', 'itemListLin_con');return false;" href="#">更多链接</a></li>
随笔分类
</h3> <ul> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> </ul> </div> <div class="catListPostArchive"> <h3 class="catListTitle">
随笔档案
</h3> <ul> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> <li data-category-list-item-visible="true" style="display: block">
</li> </ul> </div>
实例:
【更正】7za x filename.7z- 2. Re:(转)Linux下解压:tar、rar、7z命令
- a Add files to archive b Benchmark d Delete files from archive e Extract files from archive (without...
- 3. Re:S5PV210 uboot中的 MMU代码分析
- 朱的教程吗 uboot部分第几节?
- 4. Re:uboot移植——使用三星移植好的uboot开始移植
- 楼主,用的是210,为什么保留110
- 5. Re:s5pv210 uboot——移植DM9000网卡驱动
- 前辈能留个交流方式吗? QQ?wechat? 谢谢前辈~
</div><!--end: sideBarMain --> </div><!--end: sideBar 侧边栏容器 --> <div class="clear"></div> </div>
LinxuGPIO驱动
最新推荐文章于 2025-03-16 23:10:21 发布
【推荐】华为开发者专区,与开发者一起构建万物互联的智能世界
【推荐】参与华为 HarmonyOS 开发者创新大赛,一起创造无限可能