(一)新的申请设备号的方法
1》静态申请设备号
int register_chrdev_region(dev_t from, unsigned count, const char *name)
//参数1:要申请的设备号-----包含主设备号和次设备号
//参数2:设备的个数
//参数3: 设备号的描述,字符串,自定义
//返回值:成功---0;失败---错误码
2》动态申请设备号
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
//参数1:保存设备号变量的地址
//参数2:次设备号的起始值
//参数3: 设备的个数
//参数4: 设备号的描述,字符串,自定义
//返回值:成功---0;失败---错误码
//如果使用上面方式申请设备号,必须自己创建cdev对象,并且初始化对象,将对象注册到系统中
struct cdev *cdev ; //首先创建一个cdev的对象
drv_led->cdev=cdev_alloc(); //给这个cdev的对象分配空间
cdev_init(drv_led->cdev,&fops); //初始化这个对象
cdev_add(drv_led->cdev,drv_led->devno,1);//将cdev的对象注册到系统中
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops; //设备操作接口
struct list_head list;
dev_t dev; //设备号
unsigned int count;
};
void cdev_del(struct cdev *p) //删除cdev对象
二,应用程序调用底层驱动的过程分析
了解几个重要的结构体
1,当应用进程每次打开一个文件时,就会创建一个结构体:struct file -----//动态产生的
struct file {
const struct file_operations *f_op; //设备操作接口
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;
/* needed for tty driver, and maybe others */
void *private_data; //私有数据
};
2,当你创建一个文件时,系统会在磁盘上或者内存上创建一个结构体:struct inode
struct inode {
/* RCU path lookup touches following: */
umode_t i_mode; //文件的类型和属性
uid_t i_uid;
gid_t i_gid;
const struct inode_operations *i_op;
struct super_block *i_sb;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
unsigned int i_flags;
unsigned long i_state;
#ifdef CONFIG_SECURITY
void *i_security;
#endif
struct mutex i_mutex;
unsigned long dirtied_when; /* jiffies of first dirtying */
dev_t i_rdev; //设备号
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ //设备操作接口
void *i_private; /* fs or device private pointer */ //私有数据
};
3,设备操作接口
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 *);
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 **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
};
4,封装了设备号和设备操作接口的结构体
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops; //设备操作接口
struct list_head list;
dev_t dev; //设备号
unsigned int count;
};
三,硬件初始化----中断申请
1》申请中断
static inline int __must_check request_irq
(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
//参数1:中断通道号
IRQ_EINT(x) 获取中断通道号 //比如获取外部中断0的中断通道号 IRQ_EINT(0)//返回值就是中断通道号
//参数2:中断处理函数的函数名--led_irq_handler
//参数3:中断触发方式
#define IRQF_TRIGGER_NONE 0x00000000 //内部中断触发
#define IRQF_TRIGGER_RISING 0x00000001 //上升沿触发
#define IRQF_TRIGGER_FALLING 0x00000002//下降沿触发
#define IRQF_TRIGGER_HIGH 0x00000004 //高电平触发
#define IRQF_TRIGGER_LOW 0x00000008 //低电平触发
//参数4:字符串,描述中断
//参数5:传给中断处理函数的参数
//返回值:成功---0;失败---错误码
typedef irqreturn_t led_irq_handler(int, void *) //中断处理函数 参数1:中断通道号
{
}
//中断服务函数
irqreturn_t drv_keyirq_handler(int irq, void *dev)
{
printk("------------%s--------------\n",__FUNCTION__);
printk("IRQ_HANDLED: %d\n",*((unsigned int *)dev));
return IRQ_HANDLED;
}
// 4,硬件初始化 ------中断申请
drv_key->irq=IRQ_EINT(0); //获取中断通道号
drv_key->key_code=0x01; //给传递给中断处理函数的参数赋值
//进行中断申请
ret=request_irq(drv_key->irq, drv_keyirq_handler, IRQF_TRIGGER_FALLING,"drv_key1",&drv_key->key_code);
if(ret<0){
printk("key1_request_irq is error\n");
goto device_err;
}
//释放中断
free_irq(drv_key->irq,&drv_key->key_code);
参数1:中断通道号
参数2:传给中断服务函数的参数
作业:实现key0----key4打印不同键值
四,阻塞IO和非阻塞IO在内核驱动中的实现
//实现的功能:按键按下后,将按键值和中断通道号,上传到应用层,并且打印
//要实现此功能,就要实现read()函数------因为read函数时能够将内核空间数据传递到应用
//由于read函数时阻塞函数,所以要实现阻塞------没有数据,等待数据,有数据上传到应用层
//实现阻塞需要以下几步
1》阻塞IO的实现
在应用空间中,很多函数默认时阻塞的,read(),accept(),connect(),recv()等等
在驱动中如何实现阻塞IO:
1> 定义一个等待队列头,并且初始化这个等待队列头---------------//在硬件初始化的位置初始化等待队列头
wait_queue_head_t Wq;
init_waitqueue_head(wait_queue_head_t *q)
2>根据条件使当前的进程进入阻塞状态--------//在设备的操作接口中:read()中实现
wait_event_interruptible(wait_queue_head_t q, condition)
//参数1:-----等待队列头
//参数2:-----条件变量,condition=0-----休眠 condition不为0 -----不休眠
3>当有资源可以用时,要唤醒进程-------//在中断处理函数中-----按键按下
wake_up_interruptible(wait_queue_head_t *q)
ssize_t drv_key_read (struct file *filp, char __user *buf, size_t size, loff_t *flags)
{
int ret;
printk("------------%s--------------\n",__FUNCTION__);
wait_event_interruptible(drv_key->wq,drv_key->have_data);
ret=copy_to_user(buf, &drv_key->key_infos,size);
if(ret!=0){
printk("drv_key copy_to_user is error\n ");
return ret;
}
//清除数据包,并且将是否有资源清零
drv_key->have_data=0;
memset(&drv_key->key_infos,0,sizeof(drv_key->key_infos));
return size;
}
2》非阻塞IO的实现
阻塞IO 非阻塞IO
fd=open("/dev/drv_key",O_RDWR); fd=open("/dev/drv_key",O_RDWR);
read(fd,......) read(fd,.....)
如果有数据-----读取数据,返回读到数据 如果有数据-----读取数据,返回读到数据
如果没有数据-----使进程阻塞 如果没有数据----立即返回
如果应用层程序open("/dev/drv_key",O_RDWR|O_NONBLOCK),代表此应用程序调用的函数,实现非阻塞
//应用层,告诉内核阻塞或不阻塞(|O_NONBLOCK),内核实现阻塞或不阻塞
//如果没有数据且应用空间传过来了O_NONBLOCK
if(!drv_key->have_data&&filp->f_flags&O_NONBLOCK){
return -EAGAIN;
}