1.最近研究了下字符驱动,现在将过程记录下来。
2.首先应该搞明白你要再那个内核下编写驱动,2.4x or 2.6x ???我现在是2.4编写驱动,然后了解结构file_operations,这个结构在linux/fs.h中定义。
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, 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);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, 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 *);
};
上面的结构来自于内核2.4.2,这个结构的作用是把你的设备执行相应操作的函数指针对应起来,比如你要执行read操作,你就应该有一个read函数;
后面会看到例子里面;
然后是给file_operations结构赋值,这里有两种不同的写法分别在2.4与2.6内核里;
2.4里面比较老的方法如下:
struct file_operations fops = {
read: device_read,
write: device_write,
open: device_open,
release: device_release
};
2.6里面的赋值如下:
struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
如果你没有初始化结构成员的值,那么它会被gcc设置为NULL。
3.接下来我们需要注册一个设备;
字符设备是被当作为一个设备文件来操作的,这个设备文件在/dev目录下;主设备号告诉驱动应该和那个设备文件关联,副设备号是为了告诉驱动应该去操作
哪个具体的设备文件,当这个驱动与多个设备文件关联的时候;
int register_chrdev(unsigned int major, const char *name,
struct file_operations *fops);
使用上面的函数注册一个设备,注册一个设备到系统,我们不需要传递副设备号给系统;
major主设备号是你请求的设备号,name 是你的设备名称,这个设备名称会出现在/proc/devices里面, fops为相应的file_operations结构指针;
如果major为0,系统会动态分配一个主设备号给我们,如果动态生成,我们还需要将设备文件与主设备号关联;
这里提供三种方法进行配对:
a.动态分配后,由驱动打印出主设备号,然后我们通过mknod手动创建设备文件;
b.动态分配后,通过自动脚本读取/proc/devices里面的设备号建立设备文件;
c.动态分配后,直接在驱动里面调用mknod命令创建设备文件;
4.卸载设备;
当你已经完成了字符设备的所有操作函数与注册设备后,你需要知道如何卸载一个设备;
在卸载设备前我们来认识下几个宏:这些宏定义在linux/modules.h里面
MOD_INC_USE_COUNT: 递增使用计数器
MOD_DEC_USE_COUNT: 递减使用计数器
MOD_IN_USE: 显示使用计数器
unregister_chrdev(Major, DEVICE_NAME);此函数用来卸载一个设备;
5.下面我们来看下例子代码
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#include
#include
#define BUFFERSIZE 200
#define DEVICE_MAJOR 250
#define DEVICE_NAME "mycdev"
#define SUCCESS 0
static int device_major = DEVICE_MAJOR;
static char msg[BUFFERSIZE];
static char *msg_Ptr;
static int Device_Open = 0;
static int init_module(void);
static void cleanup_module(void);
static int my_cdev_open(struct inode *, struct file *);
static int my_cdev_release(struct inode *, struct file *);
static size_t my_cdev_read(struct file *, char *, size_t, loff_t *);
static size_t my_cdev_write(struct file *, const char *, size_t, loff_t *);
static loff_t my_cdev_llseek(struct file *filp, loff_t offset, int orig);
static const struct file_operations my_cdev_fops=
{
.owner = THIS_MODULE,
.open = my_cdev_open,
.release = my_cdev_release,
.read = my_cdev_read,
.write = my_cdev_write,
.llseek = my_cdev_llseek,
};
static int my_cdev_open(struct inode *node, struct file *filp)
{
static int counter = 0;
if(Device_Open) return -EBUSY;
Device_Open++;
sprintf(msg, "I already told you %d times Hello world!\n", counter++);
msg_Ptr = msg;
MOD_INC_USE_COUNT;
return 0;
}
static int my_cdev_release(struct inode* node, struct file* filp)
{
Device_Open--;
MOD_DEC_USE_COUNT;
return 0;
}
static size_t my_cdev_read(struct file *filp, char *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
if(*msg_Ptr == 0)
return 0;
if(p >= BUFFERSIZE)
return count ? -ENXIO : 0;
if(copy_to_user(buf, (void*)(msg_Ptr+p), count))
{
ret = -EFAULT;
}else{
*ppos += count;
ret = count;
}
return ret;
}
static size_t my_cdev_write(struct file *filp, const char *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
if(p >= BUFFERSIZE)
return count ? -ENXIO : 0;
if(count > BUFFERSIZE - p)
count = BUFFERSIZE -p;
if(copy_from_user(msg_Ptr, buf, count))
{
ret = -EFAULT;
}else{
*ppos += count;
ret = count;
}
return ret;
}
static loff_t my_cdev_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
switch(orig)
{
case 0:
if(offset < 0)
{
ret = -EINVAL;
break;
}
if(offset > BUFFERSIZE)
{
ret = -EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/*
static void my_cdev_setup(struct my_cdev *dev, int index)
{
int err;
dev_t devno = MKDEV(DEVICE_MAJOR, index);
cdev_init(&dev->cdev, &my_cdev_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &my_cdev_fops;
err = cdev_add(&dev->cdev, devno, 1);
if(err)
printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}
*/
static int init_module(void)
{
device_major = register_chrdev(0, DEVICE_NAME, &my_cdev_fops);
if(device_major < 0)
{
printk("Register the character device failed with %d\n", device_major);
return device_major;
}
printk("<1>I was assigned major number %d. To talk to\n", device_major);
printk("<1>the driver, create a dev file with\n");
printk("'mknod /dev/mycdev c %d 0'.\n", device_major);
printk("<1>Try various minor numbers. Try to cat and echo to\n");
printk("the device file.\n");
printk("<1>Remove the device file and module when done.\n");
return 0;
}
static void cleanup_module(void)
{
int ret = unregister_chrdev(device_major, DEVICE_NAME);
if(ret < 0)
printk("Error in unregister_chrdev: %d\n", ret);
}
MODULE_AUTHOR("Robert Luo");
MODULE_LICENSE("Dual BSD/GPL");
然后根据提示建立一个设备文件mknod /dev/mycdev c 254 0
最后我们写一段测试代码来看看驱动是否可以用了;
#include
#include
#include
#include
#define BUFFERSIZE 200
int main(void)
{
int fp =0;
char str[BUFFERSIZE];
fp = open("/dev/mycdev", O_RDWR);
if(!fp)
{
printf("Open device failed.\n");
return -1;
}
write(fp, "Hello, my devices", strlen("Hello, my devices"));
lseek(fp, 0, 0 );
read(fp, str, BUFFERSIZE);
printf("Read content: %s\n", str);
close(fp);
}
Read content: Hello, my devicesu 0 times Hello world!
Read content: Hello, my devicesu 1 times Hello world!
Read content: Hello, my devicesu 2 times Hello world!