使用的内核版本为3.4.39,开发工具为Ubuntu的vim编辑器。
一、设备类
1.1 简介
操作系统为方便描述设备,提高代码的复用性,linux为用户封装好了一些描述设备的类,用户在编写关于设备的代码时可以使用。
1.2 主要结构体和方法
#include <linux/device.h>
// 描述设备类的结构体
struct class {
const char *name;
struct module *owner;
struct class_attribute *class_attrs;
struct device_attribute *dev_attrs;
struct bin_attribute *dev_bin_attrs;
struct kobject *dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(struct device *dev, umode_t *mode);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct kobj_ns_type_operations *ns_type;
const void *(*namespace)(struct device *dev);
const struct dev_pm_ops *pm;
struct subsys_private *p;
};
// 创建设备类
extern struct class * __must_check __class_create(struct module *owner,
const char *name,
struct lock_class_key *key);
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
// 销毁设备类
extern void class_destroy(struct class *cls);
// 创建设备文件
struct device *device_create(struct class *cls, struct device *parent,
dev_t devt, void *drvdata,
const char *fmt, ...);
// 销毁设备文件
extern void device_destroy(struct class *cls, dev_t devt);
1.3 代码思路
在/sys/class目录下可以查看操作系统的设备类,在初始化的函数中使用class_create函数可以创建一个设备类,创建成功后可以在该目录查看。在/dev可以查看操作系统下的设备文件,在初始化函数中使用device_create函数可以自动创建一个设备文件,创建成功后可以在该目录下查看,使用这个接口就可以避免每次插入驱动都需要重新创建设备文件了。最后在出口函数中销毁创建的类和设备文件。
1.4 示例代码
Makefile
KERNEL_DIR = /opt/wkspace/kernel-build/kernel-3.4.39/
obj-m += myclass.o
all:
make modules M=`pwd` -C $(KERNEL_DIR) CROSS_COMPILE=/usr/local/arm/arm-eabi-4.8/bin/arm-eabi-
clean:
make modules clean M=`pwd` -C $(KERNEL_DIR) CROSS_COMPILE=/usr/local/arm/arm-eabi-4.8/bin/arm-eabi-
rm -rf app
myclass.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
struct class *cls = NULL;
struct device *dev = NULL;
int major = 100,minor = 0;
static int __init mydev_init(void){
printk(KERN_INFO "mydev init!\n");
cls = class_create(THIS_MODULE,"myclass");
if(cls == NULL){
printk(KERN_INFO "create class failed!\n");
return -1;
}
dev = device_create(cls,NULL,MKDEV(major,minor),NULL,"mydev");
if(dev == NULL){
printk(KERN_INFO "create device failed!\n");
return -1;
}
return 0;
}
static void __exit mydev_exit(void){
printk(KERN_INFO "platform dev exit!\n");
device_destroy(cls,MKDEV(major,minor));
class_destroy(cls);
return ;
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");
二、杂交设备类misc
2.1 简介
misc是操作系统供用户使用的一个描述设备的通用的类,操作系统中也有专用设备类,在没有对应的专用设备类时,我们可以选择misc杂交设备类。杂交设备类只提供一些简单的平台操作和处理机制,涉及到复杂的电路和操作的时候可能不适合。
2.2 杂交设备类和方法
#include <linux/miscdevice.h>
// 杂交设备类描述结构体
struct miscdevice {
int minor; // 次设备号
const char *name; // 设备名称
const struct file_operations *fops; // 驱动方法
struct list_head list; // 链表结构
struct device *parent; // 继承基类
struct device *this_device;
const char *nodename;
umode_t mode;
};
// 杂交设备类注册
extern int misc_register(struct miscdevice * misc);
// 杂交设备类注销
extern int misc_deregister(struct miscdevice *misc);
// 描述设备操作的结构体
struct file_operations { struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
// 应用使用read函数时调用该指针指向的函数
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
// 应用使用write函数时调用该指针指向的函数
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 *); // 应用使用open函数时调用该指针指向的函数
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *); // 应用使用close函数时调用该指针指向的函数
int (*fsync) (struct file *, loff_t, loff_t, 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 (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
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 *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, struct pipe_inode_info *, size_t, unsigned int); };
2.3 代码思路
创建一个miscdevice结构体,填写次设备号等设备信息,这里主设备号已经规定是10,所以只需要填写次设备号,misc设备在操作系统中已经被创建,所以规定了主设备号。然后再初始化函数中注册misc设备,在出口函数中注销misc设备。驱动插入后会执行初始化函数,之后可以在/dev看到一个主设备号为10,此设备号为66的test设备。
2.4 示例代码
Makefile
KERNEL_DIR = /opt/wkspace/kernel-build/kernel-3.4.39/
obj-m += misc.o
all:
make modules M=`pwd` -C $(KERNEL_DIR) CROSS_COMPILE=/usr/local/arm/arm-eabi-4.8/bin/arm-eabi-
clean:
make modules clean M=`pwd` -C $(KERNEL_DIR) CROSS_COMPILE=/usr/local/arm/arm-eabi-4.8/bin/arm-eabi-
rm -rf app
~
misc.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
int misc_open(struct inode *mnode,struct file *mfile){
return 0;
}
int misc_release(struct inode *mnode,struct file *mfile){
return 0;
}
struct file_operations misc_fop = {
.open = misc_open,
.release = misc_release,
};
struct miscdevice miscdev = {
.minor = 66,
.name = "test",
.fops = &misc_fop,
};
static int __init mydev_init(void){
printk(KERN_INFO "mydev init!\n");
misc_register(&miscdev);
return 0;
}
static void __exit mydev_exit(void){
printk(KERN_INFO "platform dev exit!\n");
misc_deregister(&miscdev);
return ;
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");