【1】复习
1.驱动的种类
字符设备驱动:按照字节流来访的,并且顺序访问。
90%设备都对应字符设备驱动。
帧缓存设备驱动:LCD
块设备驱动:按照block来访问,访问的单位512字节
它可以顺序访问,也可以无序访问。
网卡设备驱动:和网络通讯相关的设备
2.内核模块
入口
static int __init demo_init(void)
{
//在安装驱动的时候执行
//资源分配
return 0;
}
module_init(demo_init);
出口
static void __exit demo_exit(void)
{
//在卸载的时候执行
//资源释放
}
module_exit(demo_exit);
许可证
MODULE_LICENSE("GPL");
3.编译
内部编译:在内核源码树中进行编译
Kconfig .config Makefile
外部编译:在内核源码树外进行编译
Makefile:
KERNELDIR:=/lib/modules/$(shell uname -r)/build/
#KERNELDIR:=/home/linux/kernel/kernel-3.4.39/
PWD:=$(shell pwd)
modules:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=demo.o
静态编译:
将驱动编译到uImage中,它是内部模块
动态编译:
编译生成外部模块xxx.ko,在运行的时候必须依赖uImage
4.打印
printk(打印级别 "想打印的内容");
printk("想打印的内容"); //使用默认打印级别
cat /proc/sys/kernel/printk
4 4 1 7
su root
echo 4 3 1 7 > /proc/sys/kernel/printk
dmesg 主动查看内核的打印信息
sudo dmesg -C
sudo dmesg -c
5.模块传参
module_param(变量名,变量类型,权限);
MODULE_PARM_DESC(变量名,"描述字段");
6.作业:
1.通过命令行传参的形式传递字符类型
//只能传递整数,不能传递字符
2.通过命令行传参的形式传递字符串
//在命令行中不允许传递空格
3.通过命令行传参的形式传递数组
module_param_array(name, type, nump, perm)
功能:接收整型的数组
参数:
@name :数组名
@type :数组的类型
@nump :命令行传递的参数的个数
@perm :权限
sudo insmod demo.ko light=50 tt=65
p="www.hqyj.com" ww=11,22,33
【2】内核模块中导出符号表
导出符号表可以让一个内核模块,调用另外一个
内核模块中的函数,它可以让linux更简约,防止
代码冗余的现象。还可以让驱动工程师写否写复杂
驱动的时候更简答。
EXPORT_SYMBOL_GPL(sym)
功能:将函数或者变量的符号表导出
参数:
@sym:函数名或者变量名
编译:
1.先编译提供者模块,编译完之后会产生一个
Module.symvers,这个文件中记录的就是函
数的名字及地址
2.在编译调用者前需要将Module.symvers拷贝到
调用者目录下,然后执行make,如果不拷贝会提示
add undefined
安装:
先安装提供者,在安装调用者
卸载:
先卸载调用者,在卸载提供者
【3】字符设备驱动
用户空间:
open read write close
/dev/myled(c字符设备文件)
-----------------|----------------------------
内核空间: |
字符设备驱动
------------ struct file_operations{
| led_open | int (*open)();
| led_write| int (*read)();
mycdev.ko | led_read | int (*write)();
| led_close| int (*close)()
------------ };
---------------|------------------------------
硬件层: LED
vi -t register_chrdev
int register_chrdev(unsigned int major,
const char *name,
const struct file_operations *fops)
功能:注册一个字符设备驱动
参数:
@major :主设备号
major>0 系统认为这个major就是主设备号
major=0 系统自动分配一个主设备号
设备号(32位)=主设备号(高12)+次设备号(低20)
主设备号:它是哪一类设备 LED UART BEEP WDT
次设备号:同类中的第几个设备
@name :给你的字符设备驱动起的名字
通过 cat /proc/devices 可以查看
Character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
5 ttyprintk
6 lp
7 vcs
| |
主设备号 名字
@fops:操作方法结构体
vi -t file_operations
struct file_operations {
ssize_t (*read) (struct file *,
char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *,
const char __user *, size_t, loff_t *);
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
}
返回值:
major>0 成功返回0,失败返回错误码(负数)
major=0 成功返回主设备号,失败返回错误码
vi -t unregister_chrdev
void unregister_chrdev(unsigned int major, const char *name)
功能:注销一个字符设备驱动
参数:
@major:主设备号
@name : 名字
返回值:无
指定驱动文件的tags就是内核顶层的tags
:set tags=/home/linux/kernel/kernel-3.4.39/tags
【4】驱动的测试流程
1.安装驱动
sudo insmod mycdev.ko
2.查看字符设备驱动是否创建成功
cat /proc/devices
250 mycdev
| |
主设备号 名字
3.创建设备文件(mknod)
sudo mknod 路径(任意)/设备文件的名字 c/b 主设备号 次设备号
sudo mknod /dev/mycdev c 250 0
4.编写应用程序
在应用程序中调用open read write close函数
5.执行应用程序
sudo ./a.out
或者
sudo chmod 777 /dev/mycdev
./a.out
6.查看
dmesg
如果看到驱动中的open read write close都打印了
说明,应用程序调用驱动就成功了。
练习:
在给大家10分钟的时间练习字符设备驱动
【5】用户空间和内核空间数据传递
在内核中有asm/uaccess.h或者asm-generic/uaccess.h
里面写的都是同样类型的函数,如果包含头文件的时候
写上其中一种,可能在当前的内核版本中编译能通过,
换一个内核版本编译就通过不了了,把前面的asm换成
linux/uaccess.h,这是一个链接文件,它总能链接到正确
的头文件的位置。
#include <linux/uaccess.h>
unsigned long copy_from_user(void *to,
const void __user *from, unsigned long n)
功能:将数据从用户空间拷贝到内核空间
参数:
@to :内核空间的首地址
@from:用户空间的首地址
@n :拷贝的字节的个数
返回值:
成功返回0,失败返回未拷贝的字节的个数
内核中错误码问题:
vi -t EIO;
unsigned long copy_to_user(void __user *to,
const void *from, unsigned long n)
功能:将数据从内核空间拷贝到用户空间
参数:
@to :用户空间的首地址
@from:内核空间的首地址
@n :拷贝的字节的个数
返回值:
成功返回0,失败返回未拷贝的字节的个数
【6】驱动和硬件的交互过程
由于驱动运行在3-4G的虚拟地址中,LED灯的
控制寄存器它是物理地址。所以如果想在内核空间
操作物理地址,需要将物理地址映射成虚拟地址。
void *ioremap(phys_addr_t offset, unsigned long size)
功能:将物理地址映射成虚拟地址
参数:
@offset:物理地址
@size :映射的长度(字节)
返回值:成功返回虚拟地址,失败返回NULL
void iounmap(void *addr)
功能:取消映射
参数:
@addr:虚拟地址
返回值:无
【7】source insight创建索引的过程
1.解压内核
将系统移植课上的内核的源码在windows上解压
(不能和ubuntu通过共享目录共享这份内核代码)
2.创建索引的工程
project->new project->填写工程的名字,选择内核源码
的路径->ok ->ok-->选中内核源码目录-->add all-->
会看到有3万多个文件--->close
3.创建索引
project->sychronize files->把能选的对号都选上->ok