Linux驱动:
角色:应用程序
API
操作系统
驱动
实际硬件
功能:
1.对设备进行初始化和释放
2.把数据从内核传送到硬件和从硬件读取数据
3.检测和处理设备出现的错误
Linux驱动程序类型:
字符设备:由文件系统管理
(通过设备文件访问)
块设备:由文件系统管理
网络设备:由协议栈管理
(通过socket访问)
查看系统设备文件
ls -l /dev
cat /proc/devices
cat /sys/power/state
属性:文件类型 主设备号
次设备号
设备编号:<linux/types.h>
dev_t
32位,12为主设备号,20位为次设备号
实际使用:<linux/kdev_t.h>
dev_t -->
主设备号,次设备号
MAJOR(dev_t dev);
MINOR(dev_t
dev);
主设备号,次设备号-->(dev_t)
MKDEV(int major,int minor)
设备驱动组成:
1.配置初始化,注册设备,检测硬件设备是否正常工作。
2.服务于I/O请求的子程序
3.中断服务子程序
1.系统运行时编译并加载,卸载模块:insmod,rmmod
2.Linux内核源代码中编译成模块,启动后用insmod,rmmod
或者直接在源代码中加载进内核。
输出:内核编程不能使用用户态C库函数中的printf()
只能用printk().为日志机制,记录日志信息或者给出警告提示。(var/log/messages)
日志级别:默认:DEFAULT_MESSAGE_LOGLEVEL
4
<linux/kernel.h>
#define KERN_EMERG
"<0>" /* system is unusable */
#define
KERN_ALERT "<1>" /* action must be
taken immediately */
#define KERN_CRIT
"<2>" /* critical conditions */
#define
KERN_ERR "<3>" /* error
conditions */
#define KERN_WARNING "<4>" /*
warning conditions */
#define KERN_NOTICE "<5>"
/* normal but significant */
#define KERN_INFO
"<6>" /* informational */
#define
KERN_DEBUG "<7>" /* debug-level
messages */
当优先级小于int
console_loglevel变量的时候,消息打印到控制台上。
如果syslogd和klogd守护进程在运行的话,消息只能加进var/log/messages文件
klogd只处理内核信息,syslogd处理其他系统信息,比如应用程序
驱动程序与应用程序的区别:
1.驱动没有main,以模块初始化函数为入口
2.驱动完成初始化后不再运行,等待系统调用
3.驱动不能使用标准C库
*编译驱动到内核模板:
1.而配置Kconfig,打开linux-2.6.32.2/drivers/char/Kconfig,
找到MINI2440_HELLO_MODULE,复制一段下来,修改成:
config
MY_HELLO
tristate "my hello
module"
depends on
MACH_MINI2440
default m if
MACH_MINI2440
help
Mini2440 module
samples
2.在make menuconfig,可以在Device Drivers-> Character devices中看到my
hello module
选择为M
3.打开linux-2.6.32.2/drivers/char/Makefile
在有CONFIG_MINI2440_HELLO_MODULE下增加:
obj-S(CONFIG_MY_HELLO)
+=hello.o
4.回到根目录,执行:make modules
生成:hello.ko
系统运行后编译模板基本步骤:
1.编译
make
即等于:make modules
Makefile中:
先看KERNELRELEASE是在linux内核源码中主Makefile中是否定义,如果定义,则编译生成目标文件
若未定义:定义:主机:KERNELDIR ?= /lib/modules/$(shell uname -r)/build
开发板:KERNELDIR ?=/mnt/hgfs/LinuxShared/linux-2.6.32/linux-2.6.32.2
即KERNELDIR是本Makefile 依赖的linux内核源码路径 ,如是交叉编译时就取开发板上运行的源码路径
make: $(MAKE) -C $(KERNELDIR) M=$(PWD)
modules
到linux源码所在的目录执行主Makefile
并当前路径传给主Makefile,告诉主Makefile执行完后返回到当前目录,执行Makefile
2.安装模块
insmod
hello.ko
modprobe -r filename 加载驱动时会同时加载该模块所依赖的其他模块。
insmod 模块名 参数名=参数值
定义参数:
module_param(参数名、参数类型、参数读写属性)
static char *string_test = “this is a test”;
static num_test = 1000;
module_param (num_test,int,S_IRUGO);
module_param (steing_test,charp,S_ITUGO);
3.显示模块
lsmod |grep
hello
4.删除模块
rmmod hello
5.获取模块信息
modinfo
hello.ko
6.清除
make clean
---字符驱动---
操作步骤:
//设备的探测和初始化
************************************************************
static __init
int myinit(void)
{
//设备探测相关的
//1.设备的常用属性分配 -- 设备名,设备号
//2.设备的注册
//设备初始化
//1.驱动中使用的一系列空间分配
//2.和设备相关的资源的初始化 --
GPIO口的定义(属性,引脚)
return 0;
}
static __exit void
myexit(void)
{
//注销设备
//释放资源
}
//IO接口的实现
//中断的处理
***********************************************************
<linux/fs.h>
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
*, struct dentry *, 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
{
/*
* fu_list becomes invalid after
file_free is called and queued via
* fu_rcuhead for RCU
freeing
*/
union
{
struct
list_head fu_list;
struct rcu_head
fu_rcuhead;
} f_u;
struct
path f_path;
#define f_dentry f_path.dentry
#define
f_vfsmnt f_path.mnt
const struct
file_operations *f_op;
spinlock_t f_lock; /*
f_ep_links, f_flags, no IRQ
*/
atomic_long_t f_count;
unsigned int
f_flags;
fmode_t f_mode;
loff_t f_pos;
struct
fown_struct f_owner;
const struct
cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
#ifdef
CONFIG_SECURITY
void *f_security;
#endif
/*
needed for tty driver, and maybe others
*/
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks
to this file */
struct list_head f_ep_links;
#endif /* #ifdef
CONFIG_EPOLL */
struct address_space *f_mapping;
#ifdef
CONFIG_DEBUG_WRITECOUNT
unsigned long
f_mnt_write_state;
#endif
};
FILE *fp;
<asm/uaccess.h>
unsigned long copy_from_user(void *to, const void *from, unsigned long
n);
unsigned long copy_to_user (void * to, void * from, unsigned long len);
globvar_driver.c编译后生成的ko文件加载后,通过lsmod或者cat
/proc/devices都可以查看到该设备已经在系统中加载,但是在/dev下面有生成设备文件,需要手动创建节点
[[email protected]
driver_2]# mknod /dev/globalvar c 230 0
[[email protected] driver_2]# ls
/dev
---Linux2.6内核字符驱动---
<linux/cdev.h>
struct cdev {
struct kobject
kobj; // 每个 cdev 都是一个
kobject
struct module *owner; //
指向实现驱动的模块
const struct file_operations *ops; //
操纵这个字符设备文件的方法
struct list_head list;
// 与 cdev 对应的字符设备文件的 inode->i_devices 的链表头
dev_t dev;
// 起始设备编号
unsigned int count; // 设备范围号大小
};
定义一个struct cdev类型的对象来表示当前的设备驱动
给cdev对象分配空间
初始化cdev:
cdev_init
分配设备号:regist_chrdev_region或alloc_chrdev_region
设备驱动添加到系统:cdev_add
接口实现
注销: cdev_del
unregister_chrdev_region
linux系统驱动基础学习笔记