开始从头学起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#
当然了,我们也可以自己写一个测试的应用程序来测试我们的驱动。

结束。

=========================================================

mail & MSN :zhangjie201412@live.com

=========================================================

Linux 设备驱动 ==== 字符驱动相关推荐

  1. [设备驱动] 最简单的内核设备驱动--字符驱动

    [设备驱动] 最简单的内核设备驱动--字符驱动  概要: x86平台上(linux-2.6.34.14;Linux debian 3.2.0-3-686-pae)编写一个256字节的字符驱动程序.在/ ...

  2. Linux 驱动程序之字符驱动

    Linux 驱动程序之字符驱动 系统调用.内核.驱动程序的关系 主要驱动类型: -> 字符设备 ( c) -> 块设备 (b) -> 网络设备 (ifconfig) 字符设备和块设备 ...

  3. 示例:Linux设备属性节点驱动,以及cat, echo操作

    在写Linux字符驱动的时候,经常涉及到一些驱动需要在/sys目录或子目录下创建,一个属性节点,以便与,不用查看驱动的版本信息,时间等等一些属性信息,以判断驱动程序加载的是否有误. 示例代码: // ...

  4. linux 设备树i2驱动,TX2i设备树SPI驱动

    默认/dev下是没有spi设备的@H_301_1@ JetPack版本@H_301_1@ JetPack-L4T-3.2.1-linux-x64_b23.run@H_301_1@ 下载Kernel@H ...

  5. linux设备模型 字符设备,Linux 字符设备驱动模型之框架解说

    一.软件操作硬件设备模型 在进行嵌入式开发的过程中,在常做的事情就是驱动配置硬件设 备,然后根据功能需求使用硬件设备,实现功能的逻辑.如下图为其 相互之间的关系. 如上图所示: 驱动程序:主要作为操作 ...

  6. mcp2515 linux 设备树,mcp2515驱动的实现

    1.在配置Linux 编译选项时,开启相应的SPI 选项,如下所示 -> Device Drivers -> SPI support SPI support *** SPI Master ...

  7. Linux设备驱动——第三章字符驱动

    当对幸福的憧憬过于急切,那痛苦就在人的心灵深处升起.--加缪 本章的目的是编写一个完整的字符设备驱动.我们开发一个字符驱动是因为这一类适合大部分简单的硬件设备.字符驱动也比块驱动易于理解.本章的最终目 ...

  8. Linux字符驱动开发学习总结

    linux驱动编写(虚拟字符设备编写) 昨天我们说了一些简单模块编写方法,但是终归没有涉及到设备的编写内容,今天我们就可以了解一下相关方面的内容,并且用一个实例来说明在linux上面设备是如何编写的. ...

  9. linux Pci字符驱动基本加载流程

    今天有朋友问我linux系统Pci字符驱动加载流程,简单整理了一下,顺便做个记录. 首先说下需要包含的头文件: 一个完整的字符驱动一般包含下面这些头文件: #include <linux/typ ...

  10. 嵌入式Linux设备驱动面试题汇总

    大家平时在写驱动的时候,驱动相关的知识都会用到,但真到面试的时候,很难快速流畅的回答面试提出的问题,特意从网上收集整理网友遇到的问题 驱动大概的分为三部分:基础部分,同步相关,还有中断部分.中断,同步 ...

最新文章

  1. HTML超文本描述语言,HTML超文本标记语言的介绍
  2. Sqoop(四)增量导入、全量导入、减量导入
  3. P4 类、对象、类成员简介
  4. django 如何写model
  5. 2021-08-13servlet 原理及注意事项
  6. 个人简历小程序(附源码)
  7. 【pandas教程】索引操作
  8. linux 中文ssid 显示乱码,把中文SSID变成乱码!一切正常了?_网络设备-中关村在线...
  9. golang解决数据库中null值的问题
  10. 微信验证服务器地址有效性
  11. 磁性开关的种类和工作原理
  12. 18天精读掌握《费曼物理学讲义卷一》 第15天 2019/7/2
  13. apicloud图片缓存的使用和查看清除缓存
  14. 小程序+音视频1:live-pusher
  15. 如何开发一个直播类 APP 项目开发原理
  16. MATLAB极坐标与xy坐标互相转换_不改变数据形状_极坐标变量v_p(theta,r)的平面图
  17. 【TOOLS】python3利用SMTP进行邮件Email自主发送
  18. 小米wifi开发:初始配置wifi模组
  19. C语言位操作中指定的某一位数置0、置1、取反
  20. ArcSDE和Geodatabase10.1抢先版谍照介绍(1)

热门文章

  1. Java学习笔记之设计模式(2)工厂模式
  2. stm32中如何避免等待_地坪漆施工中如何避免常见的小问题
  3. 阿里 java ide_纯JAVA版JAVA IDE环境(源码)
  4. Angr安装与使用之使用篇(七)
  5. 贴一篇以前写的产品推广旧文
  6. Java Runtime 详解
  7. linux安装vnc
  8. hdu 5101 n集合选2个不同集合数使和大于k
  9. Eclipse探秘-第一章-Eclipse启动(1)
  10. 预产期在线计算机,预产期计算器