linux字符驱动之初见
2015-11-18 15:07
417 查看
http://blog.csdn.net/lwj103862095/article/details/17468587
学习驱动也有长达一年多的时间了,受益最深的就是看韦东山老师的视频,如今已经几乎将二期三期的视频全部看完,甚至已经将二期视频看过好几遍,为了再次加深印象,我将韦老师的源码自己全部编写一遍。将所有遇到的问题,记录在此。觉得看了韦老师的视频,再看其他视频都是弱爆了。由于是文章记录,不可能写的非常详细,只摘录关键点,想具体详细的深入,还请去看韦老大的视频吧。
这篇文章是主要是讲解字符驱动的框架,并没有涉及高级字符驱动。
一、字符驱动框架
------------------------------------------------------------------------
APP: open read write
------------------------------------------------------------------------
C 库
------------------------------------------------------------------------
system_open system_read system_write
------------------------------------------------------------------------
KERNEL:
led_open led_read led_wirte
------------------------------------------------------------------------
问:应用程序open如何找到驱动程序的open函数
答:应用程序的open通过C库的open函数,通过系统调用的system_open函数,进而通过swi val指令,进入内核,通过一定的办法来找到驱动程序的open函数。
问:通过什么样的方法来找到驱动程序的open函数
答:通过一个注册函数+设备节点
注册函数如下(旧的注册函数,新的以后再说):
register_chrdev(unsigned int major, const char * name, const struct file_operations * fops)
参数1:主设备号(重要)
参数2:名字(不重要)
参数3:file_operations结构体(重要)
设备节点:
可以手工创建也可以自动创建,这里暂且只说手工创建
mknod /dev/xxx c 252 0
具体什么含义,我就不多说了,看视频吧,很简单。
问:应用程序一般是由main函数开始执行,那么驱动程序一般是先执行什么?
答:通过一个宏,指定驱动程序的入口函数,当装载驱动时就会执行入口函数。
例如:module_init(first_drv_init); //用于修饰入口函数
自然地,驱动程序的出口函数,则是在卸载驱动时就会执行出口函数。
例如:module_exit(first_drv_exit); //用于修饰出口函数
驱动源程序如下:
[cpp] view
plaincopyprint?
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/module.h>
int major;
static int first_drv_open(struct inode * inode, struct file * filp)
{
printk("first_drv_open\n");
return 0;
}
static int first_drv_write(struct file * file, const char __user * buffer, size_t count, loff_t * ppos)
{
printk("first_drv_write\n");
return 0;
}
/* File operations struct for character device */
static const struct file_operations first_drv_fops = {
.owner = THIS_MODULE,
.open = first_drv_open,
.write = first_drv_write,
};
/* 驱动入口函数 */
static int first_drv_init(void)
{
/* 主设备号设置为0表示由系统自动分配主设备号 */
major = register_chrdev(0, "first_drv", &first_drv_fops);
return 0;
}
/* 驱动出口函数 */
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv");
}
module_init(first_drv_init); //用于修饰入口函数
module_exit(first_drv_exit); //用于修饰出口函数
MODULE_AUTHOR("LWJ");
MODULE_DESCRIPTION("Just for Demon");
MODULE_LICENSE("GPL"); //遵循GPL协议
Makefile源码如下:
[cpp] view
plaincopyprint?
ifneq ($(KERNELRELEASE),)
obj-m := first_drv.o
else
KDIR := /home/opt/EmbedSky/linux-2.6.30.4
all:
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif
测试程序如下:
[cpp] view
plaincopyprint?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd;
int val = 1;
fd = open("/dev/xxx",O_RDWR);
if(fd < 0)
{
printf("open error\n");
}
write(fd,&val,4);
return 0;
}
开发板上的测试步骤如下:
[cpp] view
plaincopyprint?
[WJ2440]# insmod first_drv.ko
[WJ2440]# ./first_test
open error
[WJ2440]# cat proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
29 fb
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 tq2440_serial
<span style="color:#ff0000;">252 first_drv</span>
253 usb_endpoint
254 rtc
Block devices:
259 blkext
7 loop
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
[WJ2440]# mknod /dev/xxx c 252 0
[WJ2440]# ls -l /dev/xxx
crw-r--r-- 1 root root 252, 0 Jan 1 20:49 /dev/xxx
[WJ2440]# ./first_test
first_drv_open
first_drv_write
[WJ2440]#
驱动环境的搭建详情请参考:
http://blog.csdn.net/lwj103862095/article/details/17465835
学习驱动也有长达一年多的时间了,受益最深的就是看韦东山老师的视频,如今已经几乎将二期三期的视频全部看完,甚至已经将二期视频看过好几遍,为了再次加深印象,我将韦老师的源码自己全部编写一遍。将所有遇到的问题,记录在此。觉得看了韦老师的视频,再看其他视频都是弱爆了。由于是文章记录,不可能写的非常详细,只摘录关键点,想具体详细的深入,还请去看韦老大的视频吧。
这篇文章是主要是讲解字符驱动的框架,并没有涉及高级字符驱动。
一、字符驱动框架
------------------------------------------------------------------------
APP: open read write
------------------------------------------------------------------------
C 库
------------------------------------------------------------------------
system_open system_read system_write
------------------------------------------------------------------------
KERNEL:
led_open led_read led_wirte
------------------------------------------------------------------------
问:应用程序open如何找到驱动程序的open函数
答:应用程序的open通过C库的open函数,通过系统调用的system_open函数,进而通过swi val指令,进入内核,通过一定的办法来找到驱动程序的open函数。
问:通过什么样的方法来找到驱动程序的open函数
答:通过一个注册函数+设备节点
注册函数如下(旧的注册函数,新的以后再说):
register_chrdev(unsigned int major, const char * name, const struct file_operations * fops)
参数1:主设备号(重要)
参数2:名字(不重要)
参数3:file_operations结构体(重要)
设备节点:
可以手工创建也可以自动创建,这里暂且只说手工创建
mknod /dev/xxx c 252 0
具体什么含义,我就不多说了,看视频吧,很简单。
问:应用程序一般是由main函数开始执行,那么驱动程序一般是先执行什么?
答:通过一个宏,指定驱动程序的入口函数,当装载驱动时就会执行入口函数。
例如:module_init(first_drv_init); //用于修饰入口函数
自然地,驱动程序的出口函数,则是在卸载驱动时就会执行出口函数。
例如:module_exit(first_drv_exit); //用于修饰出口函数
驱动源程序如下:
[cpp] view
plaincopyprint?
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/module.h>
int major;
static int first_drv_open(struct inode * inode, struct file * filp)
{
printk("first_drv_open\n");
return 0;
}
static int first_drv_write(struct file * file, const char __user * buffer, size_t count, loff_t * ppos)
{
printk("first_drv_write\n");
return 0;
}
/* File operations struct for character device */
static const struct file_operations first_drv_fops = {
.owner = THIS_MODULE,
.open = first_drv_open,
.write = first_drv_write,
};
/* 驱动入口函数 */
static int first_drv_init(void)
{
/* 主设备号设置为0表示由系统自动分配主设备号 */
major = register_chrdev(0, "first_drv", &first_drv_fops);
return 0;
}
/* 驱动出口函数 */
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv");
}
module_init(first_drv_init); //用于修饰入口函数
module_exit(first_drv_exit); //用于修饰出口函数
MODULE_AUTHOR("LWJ");
MODULE_DESCRIPTION("Just for Demon");
MODULE_LICENSE("GPL"); //遵循GPL协议
Makefile源码如下:
[cpp] view
plaincopyprint?
ifneq ($(KERNELRELEASE),)
obj-m := first_drv.o
else
KDIR := /home/opt/EmbedSky/linux-2.6.30.4
all:
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif
测试程序如下:
[cpp] view
plaincopyprint?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd;
int val = 1;
fd = open("/dev/xxx",O_RDWR);
if(fd < 0)
{
printf("open error\n");
}
write(fd,&val,4);
return 0;
}
开发板上的测试步骤如下:
[cpp] view
plaincopyprint?
[WJ2440]# insmod first_drv.ko
[WJ2440]# ./first_test
open error
[WJ2440]# cat proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
29 fb
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 tq2440_serial
<span style="color:#ff0000;">252 first_drv</span>
253 usb_endpoint
254 rtc
Block devices:
259 blkext
7 loop
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
[WJ2440]# mknod /dev/xxx c 252 0
[WJ2440]# ls -l /dev/xxx
crw-r--r-- 1 root root 252, 0 Jan 1 20:49 /dev/xxx
[WJ2440]# ./first_test
first_drv_open
first_drv_write
[WJ2440]#
驱动环境的搭建详情请参考:
http://blog.csdn.net/lwj103862095/article/details/17465835
相关文章推荐
- CentOS 中 YUM 安装桌面环境(转)
- 用户不在sudoers文件中解决方法以及sudoer文件修改错误后的恢复方法
- 在 Linux 下安装复古终端
- linux常用命令
- linux下安装RabbitMQ消息机制
- linux内核的 等待队列 使用方法,wait_queue_head_t,进程休眠
- linux 下chkconfig 命令详解
- linux中stdout,stdin,stderr意义
- 将java程序发布到linux服务器
- Linux 下 SD 卡 只读文件系统
- 第二弹Linux基础一mkdir/cp/mv/rm/zip/unzip命令详解
- linux系统数据库定时备份
- 查看linux静态库*.a中的函数和文件内容
- ACE在Linux下编译安装
- Linux下编译和运行c/c++
- centos7通过yum安装mysql,并授权远程连接
- linux INIT_WORK 创建工作队列
- windows C 与 linux C区别?
- centos5安装supervisor 3.1.3
- linux常用的内核参数的设置