您的位置:首页 > 运维架构 > Linux

Linux设备之字符驱动设备(一)

2016-04-26 22:16 531 查看
一、字符驱动设备

字符设备是能够像字节流(类似文件)一样被访问的设备,有字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少要实现open、close、read、write系统调用。

内核中使用cdev结构体来描述字符设备

struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};


字符驱动的设备加载可以分为以下几个步骤:

1、分配cdev设备号

如果我们提前知道设备的编号,那么就用register_chrdev_region()

如果我们不知道,那么就用alloc_chrdev_region(),动态分配

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
{
struct char_device_struct *cd;
cd = __register_chrdev_region(0, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
*dev = MKDEV(cd->major, cd->baseminor);
return 0;
}


2、初始化cdev

cdev_init,与file operation关联

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}


3、注册cdev

cdev_add,添加字符设备

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
int error;
p->dev = dev;
p->count = count;
error = kobj_map(cdev_map, dev, count, NULL,
exact_match, exact_lock, p);
if (error)
return error;
kobject_get(p->kobj.parent);
return 0;
}


linux内核源码网站:

http://lxr.free-electrons.com/ident

二、示例

代码一:简单字符驱动模块

#include "linux/init.h"
#include "linux/module.h"
#include <linux/sched.h>  //struct task_struct *current
#include <linux/fs.h>     //file_operation, file, inode
#include <linux/types.h>  //设备号相关,eg MAJOR, MINOR, MKDEV
#include <linux/cdev.h>   //字符设备相关操作,init,add,del
#include <linux/slab.h>   //kmalloc, kfree
#include <asm/uaccess.h>  //copy_to_user, copy_from_user

dev_t dev; //device number
int chardev_minor = 5; //字符设备的次设备号
int chardev_count = 1; //字符设备的数量
struct cdev *char_dev = NULL; //字符驱动设备
int chardev_read( struct file *filp, char *buf, size_t count, loff_t *ppos )
{
if (filp->private_data==NULL)
printk(KERN_EMERG "in read: private data in file struct is NULL\n");
else {
printk(KERN_EMERG "in read: private data in file struct is not NULL\n");
/*将内核空间中filp->private_data指向的数据拷贝到用户空间buf */
copy_to_user(buf, filp->private_data, count);
}

kfree(filp->private_data);
filp->private_data = NULL;
return 0;
}

int chardev_write( struct file *filp, const char *buf, size_t count, loff_t *offp )
{
if (filp->private_data==NULL) {
printk("in write: private data in file struct is NULL\n");
filp->private_data = (void*)kmalloc(100, GFP_KERNEL);
}
else
printk("in write: private data in file struct is not NULL\n");

/*将用户空间buf中的数据拷贝到内核空间filp->private_data */
copy_from_user(filp->private_data, buf, count);

return 0;
}

struct file_operations file_ops = {
.owner =    THIS_MODULE,
.write = chardev_write,
.read = chardev_read,
};

static int __init chardev_init(void)
{
int ret = 0;
/*动态分配设备号*/
ret = alloc_chrdev_region(&dev, chardev_minor, chardev_count, "chardev");
if (ret) {
printk("alloc chrdev region fail!\n");
return -EINVAL;
} else {
printk("char device major device number %d\n", MAJOR(dev));
printk("char device minor device number %d\n", MINOR(dev));
}

/*为申请的字符设备分配内存*/
char_dev = cdev_alloc();

/*关联file_operation*/
char_dev->ops = &file_ops;

/*建立cdev和file operation之间的连接*/
cdev_init(char_dev, &file_ops);
printk("cdev init success!\n");

/*在系统中注册cdev*/
ret = cdev_add(char_dev, dev, 1);
if (ret) {
printk("cdev add failed!\n");
goto fail;
}

return 0;
fail:
unregister_chrdev_region(dev, chardev_count);
return -EINVAL;
}

static int __exit chardev_exit(void)
{
/*释放设备号*/
unregister_chrdev_region(dev, chardev_count);
/*在系统中删除字符设备*/
cdev_del(char_dev);
printk("delete cdev success!\n");
return 0;
}

module_init(chardev_init);
module_exit(chardev_exit);
MODULE_LICENSE("GPL");


代码二:Makefile文件

obj-m := char_dev.o
KERNEL_DIR := /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

all:

make -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules

clean:

rm *.o *.ko *.mod.c

.PHONY:clean


代码三:用户空间程序

int main()
{
int fp, ret;
unsigned char ch[]="hello world,my char device!";
unsigned char *buf1 = (unsigned char *)malloc(sizeof(ch)+1);
unsigned char *buf2 = (unsigned char *)malloc(sizeof(ch)+1);
//init buff
memset(buf1, 0, sizeof(ch)+1);
memset(buf2, 0, sizeof(ch)+1);
strcpy(buf1, ch);//ch to buf1
//打开设备驱动,前提是用mknode把字符设备建立为/dev/char
fp = open("/dev/char_new_dev", O_RDONLY);
printf("fp is %d\n", fp);

ret = write(fp, buf1, sizeof(ch));//write buf1 to fp
printf("write return : %d\n", ret);

ret = read(fp, buf2, sizeof(ch));//read fp to buf2

printf("read return : %d\n", ret);
printf("read data:%s\n", buf2);
close(fp);
return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: