1.在drivers/char/目录下建立一个first_driver_hello.c文件 文件的内容如下 /************************************* NAME:first_driver_hello.c COPYRIGHT:328977974@qq.com **************************************/ #include <linux/module.h> #include <linux/kernel.h> MODULE_LICENSE("GPL"); static int hello_init(void) { printk("This is the first char driver! "); return 0; } static void hello_exit(void) { printk("GoodBye,good luck! "); } module_init(hello_init); module_exit(hello_exit); 2.在内核源码处添加对first_driver_hello.c驱动的支持 修改drivers/char 目录下的Kconfig文件,添加如下内容 config FIRST_DRIVER_HELLO tristate "FIRST DRIVER HELLO" depends on ARCH_S3C2440 help BY 328977974@qq.com 3.修改drivers/char 目录下的Makefile文件 添加如下内容 obj-$(CONFIG_FIRST_DRIVER_HELLO) += first_drivers_hello.o 4.进入内核根目录下配置内核,输入make menuconfig Device Drivers -> Character devices -> <M> FIRST DRIVERS HELLO 保存后使用命令 make SUBDIR=drivers/char/ modules 5.在drivers/char/ 目录下面会生成一个first_drivers_hello.ko的文件 将它复制到开发板下,用命令insmod first_drivers_hello.ko挂载驱动 就可以看到结果。
二、嵌入式linux驱动开发流程
嵌入式系统中,操作系统是通过各种驱动程序来驾驭硬件设备的。设备驱动程序是操作系统内核和硬件设备之间的接口,它为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,可以像操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,完成以下功能:
◇ 驱动程序的注册和注销。
◇ 设备的打开和释放。
◇ 设备的读写操作。
◇ 设备的控制操作。
◇ 设备的中断和轮询处理。
Linux主要将设备分为三类:字符设备、块设备和网络设备。字符设备是指发送和接收数据以字符的形式进行,没有缓冲区的设备;块设备是指发送和接收数据以整个数据缓冲区的形式进行的设备;网络设备是指网络设备访问的BSD socket 接口。下面以字符设备为例,写出其驱动编写框架:
一、 编写驱动程序初始化函数
驱动程序的初始化在函数xxx_init()中完成,包括对硬件初始化、中断函数、向内核注册驱动程序等。
首先理解硬件结构,搞清楚其功能,接口寄存器以及CPU怎么访问控制这些寄存器等。
其次向内核注册驱动程序。设备驱动程序可以直接编译进内核,在系统启动的时候初始化,也可以在需要的时候以模块的方式动态加载到内核中去。每个字符设备或是块设备都是通过register_chrdev()函数注册,调用该函数后就可以向系统申请主设备号,操作成功,设备名就会出现在/proc/devices里。
此外,在关闭设备时,需要先解除原先设备的注册,需要有清除函数,在xxx_exit()中通过unregister_chrdev()函数在实现,此后设备就会从/proc/devices里消失。
当驱动程序被编译成模块时,使用insmod加载模块,模块的初始化函数xxx_init()被调用,向内核注册驱动程序;使用rmmod卸载模块,模块的清除函数xxx_exit()被调用。
二、 构造file_operations结构中要用到的各个成员函数
Linux操作系统将所有的设备都看成文件,以操作文件的方式访问设备。应用程序不能直接操作硬件,使用统一的接口函数调用硬件驱动程序,这组接口被成为系统调用。每个系统调用中都有一个与之对应的函数(open、release、read、write、ioctl等),在字符驱动程序中,这些函数集合在一个file_operations类型的数据结构中。以一个键盘驱动程序为例:
struct file_operations Key7279_fops =
{
.open = Key7279_Open,
.ioctl = Key7279_Ioctl,
.release = Key7279_Close,
.read = Key7279_Read,
};
1、 设备的打开和释放
打开设备是由open()函数来完成,在大部分设备驱动中open完成如下工作:
◇ 递增计数器
◇ 检查特定设备的特殊情况
◇ 初始化设备
◇ 识别次设备号
释放设备由release()函数来完成。当一个进程释放设备时,其它进程还能继续使用该设备,只是该进程暂时停止对该设备的的使用,而当一个进程关闭设备时,其它进程必须重新打开此设备才能使用。Release完成如下工作:
◇ 递减计数
◇ 在最后一次释放设备操作时关闭设备
2、 设备的读写操作
读写设备的主要任务就是把内核空间的数据复制到用户空间,或者是从用户空间复制到内核空间,也就是将内核空间缓冲区里的数据复制到用户空间的缓冲区中或者相反。字符设备使用各自的read()函数和write()函数来进行数据读写。
3、 设备的控制操作
大部分设备除了读写能力,还可进行超出简单的数据传输之外的操作,所以设备驱动也必须具备进行各种硬件控制操作的能力. 这些操作常常通过 ioctl 方法来支持。与读写操作不同,ioctl()的用法与具体设备密切相关。以键盘Key7279_Ioctl为例:
static int Key7279_Ioctl(struct inode *inode,struct file *file,unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case Key7279_GETKEY:
return key7279_getkey();
default:
printk("Unkown Keyboard Command ID.
");
}
return 0;
}
cmd的取值及含义都与具体的设备有关,除了ioctl(),设备驱动程序还可能有其他控制函数,比如llseek()等。
当应用程序使用open、release等函数打开某个设备时,设备驱动程序的file_operations结构中的相应成员就会被调用。
三、设备的中断和轮询处理
对于不支持中断的设备,读写时需要轮询设备状态,以及是否需要继续进行数据传输。例如,打印机。如果设备支持中断,则可按照中断方式进行。
模块在使用中断前要先请求一个中断通道(或者 IRQ中断请求),并在使用后释放它。通过request_irq()函数来注册中断,free_irq()函数来释放。
四、驱动程序的测试
对驱动程序的调试可以通过打印的方式来进行,就是通过在驱动程序中添加printk()打印函数,来跟踪驱动程序的执行过程,以此来判断问题。
以上是我根据自己的学习总结的,可能写的比较简单,对于比较复杂的驱动函数,会添加更多的函数,但是大体的框架就是这样了。