Linux 设备驱动 ====> 字符驱动
来源:互联网 发布:淘宝手机改好评怎么改 编辑:程序博客网 时间:2024/06/07 08:55
开始从头学起linux 设备驱动,当然是先从字符驱动看起。
下面仿照着书上的例子,写了一个misc 字符驱动。
- root@jay-LJ:/home/jay/globalmem# tree globalmem/
- globalmem/
- ├── globalmem.c
- └── Makefile
首先咱来看下Makefile, 其实这东西都一个模子,
- KVERS = $(shell uname -r)
- obj-m += globalmem.o
- build: kernel_modules
- kernel_modules:
- make -C /lib/modules/$(KVERS)/build/ M=$(CURDIR) modules
- clean:
- make -C /lib/modules/$(KVERS)/build/ M=$(CURDIR) clean
然后是我们的驱动文件globalmem.c,我们从init函数看起
- int globalmem_init(void)
- {
- int result;
- globalmem_devp = kmalloc(sizeof(struct globalmem_dev) ,GFP_KERNEL);
- if(!globalmem_devp) {
- result = -ENOMEM;
- goto fail_malloc;
- }
- memset(globalmem_devp, 0, sizeof(struct globalmem_dev));
- globalmem_devp->mdev = mdev_struct;
- result = misc_register(&(globalmem_devp->mdev));
- if(result<0)
- return result;
- else
- return 0;
- fail_malloc:
- return result;
- }
首先是给我们的全局指针变量分配内存
- globalmem_devp = kmalloc(sizeof(struct globalmem_dev) ,GFP_KERNEL);
- if(!globalmem_devp) {
- result = -ENOMEM;
- goto fail_malloc;
- }
- memset(globalmem_devp, 0, sizeof(struct globalmem_dev));
看下这个指针的定义
- struct globalmem_dev {
- struct miscdevice mdev;
- unsigned char mem[GLOBALMEM_SIZE];
- };
- struct globalmem_dev *globalmem_devp;
globalmem_dev结构体中内嵌了一个miscdevice结构体,这个结构体就是描述我们的misc字符驱动的结构体,若想注册一个misc 字符设备就必须包含有这样一个结构体,
- struct miscdevice {
- int minor;
- const char *name;
- const struct file_operations *fops;
- struct list_head list;
- struct device *parent;
- struct device *this_device;
- };
这个结构体描述了这个驱动的信息,包含次设备号, 文件操作结构体,驱动名称等。
内存分配结束之后是调用misc_register来注册我们的misc驱动,使用misc字符驱动相对于标准的字符驱动写起来简单。
然后是exit函数,跟init相反:
- void globalmem_exit(void)
- {
- misc_deregister(&(globalmem_devp->mdev));
- if(globalmem_devp)
- kfree(globalmem_devp);
- }
- module_init(globalmem_init);
- module_exit(globalmem_exit);
千万别忘了最后要释放我们分配的内存。
然后使我们的miscdevice和fops结构体
- static const struct file_operations globalmem_fops = {
- .owner = THIS_MODULE,
- .open = globalmem_open,
- .release = globalmem_release,
- .unlocked_ioctl = globalmem_ioctl,
- .read = globalmem_read,
- .write = globalmem_write,
- };
- static struct miscdevice mdev_struct = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "globalmem",
- .fops = &globalmem_fops,
- };
这里主要是这个file_operations接头体的定义,这里主要是把这个结构体中我们想要实现的读,写等方法与对应的回调函数挂钩起来。
然后就是我们的读写函数了
- int globalmem_open(struct inode *inode, struct file *filp)
- {
- printk(KERN_INFO "globalmem open!\n");
- filp->private_data = globalmem_devp;
- return 0;
- }
- int globalmem_release(struct inode *inode ,struct file *filp)
- {
- printk(KERN_INFO "globalmem release!\n");
- return 0;
- }
- static int globalmem_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
- {
- struct globalmem_dev *dev = filp->private_data; //get global data pointer
- switch(cmd) {
- case MEM_CLEAR:
- memset(dev->mem, 0, GLOBALMEM_SIZE);
- printk(KERN_INFO "clear globalmem!\n");\
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- static ssize_t globalmem_read(struct file *filp, char __user *buf,size_t size,
- loff_t *ppos)
- {
- unsigned long p = *ppos;
- unsigned int count = size;
- int ret = 0;
- struct globalmem_dev *dev = filp->private_data; //get global data pointer
- if(p>=GLOBALMEM_SIZE)
- return 0;
- if(count > GLOBALMEM_SIZE-p)
- count = GLOBALMEM_SIZE-p;
- if(copy_to_user(buf, (void *)(dev->mem + p), count))
- ret = -EFAULT;
- else {
- *ppos += count;
- ret = count;
- printk(KERN_INFO "read %u bytes(s) from %lu\n",count,p);
- }
- return ret;
- }
- static ssize_t globalmem_write(struct file *filp, const char __user *buf,
- size_t size, loff_t *ppos)
- {
- unsigned long p = *ppos;
- unsigned int count = size;
- int ret = 0;
- struct globalmem_dev *dev = filp->private_data; //get global data pointer
- if(p >= GLOBALMEM_SIZE)
- return 0;
- if(count > GLOBALMEM_SIZE - p)
- count = GLOBALMEM_SIZE - p;
- if(copy_from_user(dev->mem+p, buf, count)) {
- printk(KERN_INFO "copy from user error!!\n");
- ret = -EFAULT;
- }
- else {
- *ppos += count;
- ret = count;
- printk(KERN_INFO "written %u bytes(s) from %lu\n",count,p);
- }
- return ret;
- }
我这里实现了open release write read四个方法,open方法很简单,把我们的自己定义的全局的结构体指针保存到我们驱动中的私有指针,这样的话,在我们别的方法中就可以直接用这个私有指针来获取我们的全局结构体指针,
- filp->private_data = globalmem_devp;
保存指针,
- struct globalmem_dev *dev = filp->private_data; //get global data p
获取指针。
在我们的read/write中是与用户空间进行数据处理的过程,主要是调用了copy_from_user和copy_to_user这2个函数实现的,实现了从用户态到内核态的数据传递。
在应用层使用read/write来进行系统调用call到我们这边的read/write回调函数。
============================================================
接下来我们来测试一下我们的驱动,
编译
- root@jay-LJ:/home/jay/globalmem/globalmem# make
- make -C /lib/modules/2.6.35-22-generic/build/ M=/home/jay/globalmem/globalmem modules
- make[1]: Entering directory `/usr/src/linux-headers-2.6.35-22-generic'
- CC [M] /home/jay/globalmem/globalmem/globalmem.o
- /home/jay/globalmem/globalmem/globalmem.c:110: warning: initialization from incompatible pointer type
- Building modules, stage 2.
- MODPOST 1 modules
- CC /home/jay/globalmem/globalmem/globalmem.mod.o
- LD [M] /home/jay/globalmem/globalmem/globalmem.ko
- make[1]: Leaving directory `/usr/src/linux-headers-2.6.35-22-generic'
加载
insmod globalmem.ko
查看驱动
- root@jay-LJ:/home/jay/globalmem/globalmem# ls -l /dev/globalmem
- crw------- 1 root root 10, 53 2012-03-27 21:11 /dev/globalmem
进行读写测试
- root@jay-LJ:/dev# chmod 666 globalmem
- root@jay-LJ:/dev# echo "Hello globalmem driver" > globalmem
- root@jay-LJ:/dev# cat globalmem
- Hello globalmem driver
- root@jay-LJ:/dev#
当然了,我们也可以自己写一个测试的应用程序来测试我们的驱动。
结束。
转自http://blog.csdn.net/zhangjie201412
- Linux 设备驱动 ====> 字符驱动
- Linux 设备驱动 ====> 字符驱动
- 【Linux驱动】字符设备驱动
- 【Linux驱动】字符设备驱动
- linux驱动-字符设备驱动
- 【Linux驱动】字符设备驱动
- LINUX--字符设备驱动
- Linux字符设备驱动
- Linux字符设备驱动
- Linux字符设备驱动
- linux字符设备驱动
- linux字符设备驱动
- linux 字符设备驱动
- linux字符设备驱动
- linux 字符设备驱动
- Linux字符设备驱动
- linux字符设备驱动
- Linux字符设备驱动
- ffmpeg 0.10.3 h264_mp4toannexb 异常
- Linux 设备驱动 ====> 并发控制 --- 原子操作
- 硬盘逻辑结构简介
- android 彻底关闭应用程序(back按键)
- 如何看懂ORACLE执行计划
- Linux 设备驱动 ====> 字符驱动
- 设计模式的解析和实现(C++)之五-Singleton模式
- Android开机自启动程序
- JavaScript 性能提升的13个小技巧
- Oracle关联加号(+)
- 黑客专家教你如何设计不易破解的密码
- 设计模式的解析和实现(C++)之六-Adapt模式
- yii Maximum function nesting level of '100' reached, aborting!
- 设计模式的解析和实现(C++)之七-Bridge模式