Linux设备之字符驱动设备(一)
2016-04-26 22:16
531 查看
一、字符驱动设备
字符设备是能够像字节流(类似文件)一样被访问的设备,有字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少要实现open、close、read、write系统调用。
内核中使用cdev结构体来描述字符设备
字符驱动的设备加载可以分为以下几个步骤:
1、分配cdev设备号
如果我们提前知道设备的编号,那么就用register_chrdev_region()
如果我们不知道,那么就用alloc_chrdev_region(),动态分配
2、初始化cdev
cdev_init,与file operation关联
3、注册cdev
cdev_add,添加字符设备
linux内核源码网站:
http://lxr.free-electrons.com/ident
二、示例
代码一:简单字符驱动模块
代码二:Makefile文件
代码三:用户空间程序
字符设备是能够像字节流(类似文件)一样被访问的设备,有字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少要实现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; }
相关文章推荐
- Linux下mplayer源代码安装方法
- linux基础命令(5)——rm命令
- linux系统下批量解压方法汇总
- 关于一些实验班问题的解答以及下个学期之前小组的任务
- Linux chmod命令 修改文件权限被禁止(not permitted)的解决办法
- linux lnmp安装
- Linux每天学习一个命令之find命令三
- Linux的chattr与lsattr命令详解
- Linux进程管理-调度-通信-
- Linux-内存管理-待补充
- linux环境下git 生成 patch 与 打patch
- linux基本命令(4)——mkdir命令
- Linux系统管理常用命令
- linux进程通信:消息队列
- Linux 小笔记
- linux那点事儿(七)----文件系统管理
- centos 局域网时间同步设置
- centos7之lnmp安装
- linux之dd命令
- Linux 中的五种 IO 模型