NFC bcm2079x驱动学习 .
Bcm2079x型号NFC开发版,与主机有5根pin脚,分别是两根IIC通信线、中断脚、使能脚、唤醒脚。
驱动需要完成IIC注册,中断的初始化,pin脚初始化。
2 驱动初始化
2.1 初始化流程图
2.2 代码分析
staticconst unsigned short normal_i2c[] = {0x77,I2C_CLIENT_END}; staticstruct i2c_driver bcm2079x_driver = { .class = I2C_CLASS_HWMON, .id_table = bcm2079x_id, .probe = bcm2079x_probe, .remove = bcm2079x_remove, .driver = { .owner = THIS_MODULE, .name = "bcm2079x-i2c", }, .address_list = normal_i2c, }; |
完成probe、remove方法的映射,已经IIC地址的映射,bcm2079x的IIC地址是0x77。
内核加载驱动模块的时候,调用bcm2079x_dev_init()
#definePIO_BASE_ADDRESS (0x01c20800) //映射的物理地址 #definePIO_RANGE_SIZE (0x400) //映射的长度 static int__init bcm2079x_dev_init(void) { int ret = 0; gpio_addr = ioremap(PIO_BASE_ADDRESS,PIO_RANGE_SIZE);// 将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问 bcm2079x_driver.detect = bcm2079x_detect; return i2c_add_driver(&bcm2079x_driver);//注册 exit_ioremap_failed: return ret; } |
调用ioremap()将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问,PIO_BASE_ADDRESS是要映射的物理地址,PIO_RANGE_SIZE是要映射的长度。
映射detect方法,在模块加载时,进行硬件检测,判断硬件是否存在,满足条件之后,才会注册i2c设备相关信息,创建i2c-client,并注册i2c driver,执行probe操作;看看该方法吧:
intbcm2079x_detect(struct i2c_client *client, struct i2c_board_info*info) { struct i2c_adapter *adapter =client->adapter; if(twi_id ==adapter->nr)//硬件存在 { strlcpy(info->type, BCM2079X_NAME,I2C_NAME_SIZE); info->platform_data =&bcm2079x_pdata; return 0; }else{ } } |
满足条件之后,就调用bcm2079x_driver.probe()方法了,映射到bcm2079x_probe()中,在这里面完成初始化工作。代码如下:
static intbcm2079x_probe(struct i2c_client *client, const struct i2c_device_id*id) { struct bcm2079x_platform_data *platform_data; struct bcm2079x_dev *bcm2079x_dev; platform_data =client->dev.platform_data; …… platform_data->irq_gpio =gpio_request_ex("bcm2079x_para","bcm2079x_int_port");//获取中断引脚的句柄,是在我们平台上特有的,山寨的做法….. …… //中断初始化,又是一个不标准的山寨做法…. printk(" INTERRUPT CONFIG\n"); reg_num = ext_int_num%8; reg_addr = ext_int_num/8; reg_val = readl(gpio_addr +int_cfg_addr[reg_addr]);//读 reg_val &= (~(7 <<(reg_num * 4))); reg_val |= (int_mode << (reg_num *4)); writel(reg_val,gpio_addr+int_cfg_addr[reg_addr]); bcm2079x_clear_penirq(); reg_val = readl(gpio_addr+PIO_INT_CTRL_OFFSET); reg_val |= (1 <<ext_int_num); writel(reg_val,gpio_addr+PIO_INT_CTRL_OFFSET);//写 //end // ret = gpio_request(platform_data->irq_gpio,"nfc_int");这代码备注是掉了,这是标准的? platform_data->en_gpio =gpio_request_ex("bcm2079x_para", "bcm2079x_en_port");//获取使能引脚的句柄,继续山寨 // ret = gpio_request(platform_data->en_gpio,"nfc_ven"); platform_data->wake_gpio =gpio_request_ex("bcm2079x_para","bcm2079x_wakeup");//获取唤醒引脚的句柄,山寨 // ret = gpio_request(platform_data->wake_gpio,"nfc_firm"); //山寨!!!!使唤醒和使能引脚作为输出口 gpio_write_one_pin_value(platform_data->en_gpio,0, "bcm2079x_en_port"); gpio_write_one_pin_value(platform_data->wake_gpio,0, "bcm2079x_wakeup"); // gpio_set_value(platform_data->en_gpio,0); // gpio_set_value(platform_data->wake_gpio,0); bcm2079x_dev = kzalloc(sizeof(*bcm2079x_dev),GFP_KERNEL);//分配内存 bcm2079x_dev->wake_gpio =platform_data->wake_gpio;//初始化工作 bcm2079x_dev->irq_gpio =platform_data->irq_gpio; bcm2079x_dev->en_gpio =platform_data->en_gpio; bcm2079x_dev->client = client; //初始化等待队列 init_waitqueue_head(&bcm2079x_dev->read_wq); mutex_init(&bcm2079x_dev->read_mutex); spin_lock_init(&bcm2079x_dev->irq_enabled_lock); //注册驱动 bcm2079x_dev->bcm2079x_device.minor =MISC_DYNAMIC_MINOR; bcm2079x_dev->bcm2079x_device.name ="bcm2079x"; bcm2079x_dev->bcm2079x_device.fops =&bcm2079x_dev_fops; ret = misc_register(&bcm2079x_dev->bcm2079x_device); //申请中断,中断号是28 client->irq = 28; ret = request_irq(client->irq,bcm2079x_dev_irq_handler, IRQF_TRIGGER_RISING|IRQF_NO_SUSPEND|IRQF_SHARED,client->name, bcm2079x_dev); bcm2079x_disable_irq(bcm2079x_dev);//先不对中断使能 i2c_set_clientdata(client, bcm2079x_dev); return 0; } |
在我们的平台上,pgio和中断的使用都不标准,非常苦恼,这就是山寨的公司!!
为bcm2079x_dev数据结构分配内存空间,并初始化一些变量,其定义如下:
structbcm2079x_dev { wait_queue_head_t read_wq;//等待队里 struct mutex read_mutex; struct i2c_client *client;// struct miscdevice bcm2079x_device;//注册的micc设备 unsigned int wake_gpio;//唤醒引脚的句柄 unsigned int en_gpio;//使能引脚句柄 unsigned int irq_gpio;//中断引脚句柄 bool irq_enabled;//中断是否使能 spinlock_t irq_enabled_lock; unsigned int error_write; unsigned int error_read; unsigned int count_read; unsigned int count_irq; }; |
3 用户空间调用
3.1 流程图
3.2代码分析
3.2.1 打开设备
用户空间通过系统调用open,打开驱动对应的设备节点,那么,将调用到驱动程序中的bcm2079x_dev_open()方法,代码如下:
static intbcm2079x_dev_open(struct inode *inode, struct file*filp) { int ret = 0; //获取在probe()方法中分配内存空间的bcm2079x_dev的指针 struct bcm2079x_dev *bcm2079x_dev =container_of(filp->private_data, structbcm2079x_dev, bcm2079x_device); filp->private_data = bcm2079x_dev; bcm2079x_init_stat(bcm2079x_dev); bcm2079x_enable_irq(bcm2079x_dev);//中断使能 return ret; } |
在open方法中,主要是完成两件事情,第一是获取在probe()方法中分配内存空间的bcm2079x_dev的指针,并赋值给filp->private_data,第二是对中断的使能。
staticvoid bcm2079x_enable_irq(struct bcm2079x_dev*bcm2079x_dev) { unsigned long flags; spin_lock_irqsave(&bcm2079x_dev->irq_enabled_lock,flags); if (!bcm2079x_dev->irq_enabled) { bcm2079x_dev->irq_enabled = true; enable_irq(bcm2079x_dev->client->irq);//对28号中断使能 } spin_unlock_irqrestore(&bcm2079x_dev->irq_enabled_lock,flags); } |
3.2.2 ioctl使能唤醒
staticlong bcm2079x_dev_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct bcm2079x_dev *bcm2079x_dev =filp->private_data;//在打开设备的时候对其赋值了 switch (cmd) { case BCMNFC_POWER_CTL://使能 gpio_write_one_pin_value(bcm2079x_dev->en_gpio,arg, "bcm2079x_en_port"); //gpio_set_value(bcm2079x_dev->en_gpio,arg); break; case BCMNFC_WAKE_CTL://唤醒 gpio_write_one_pin_value(bcm2079x_dev->wake_gpio,arg, "bcm2079x_wakeup"); //gpio_set_value(bcm2079x_dev->wake_gpio,arg); break; } return 0; } |
gpio_write_one_pin_value()方法又看到了(山寨…),调用它往IO口写高低电平,上面代码分别是给使能和唤醒口写高电平,完成使能唤醒。
3.2.3 等待中断
staticunsigned int bcm2079x_dev_poll(struct file *filp, poll_table*wait) { struct bcm2079x_dev *bcm2079x_dev =filp->private_data; unsigned int mask = 0; unsigned long flags; poll_wait(filp,&bcm2079x_dev->read_wq,wait);//等待中断唤醒 spin_lock_irqsave(&bcm2079x_dev->irq_enabled_lock,flags); if (bcm2079x_dev->count_irq >0) { bcm2079x_dev->count_irq--; mask |= POLLIN | POLLRDNORM; } spin_unlock_irqrestore(&bcm2079x_dev->irq_enabled_lock,flags); return mask; } |
void poll_wait(struct file *filp,wait_queue_head_t *queue, poll_table *wait); |
它的作用就是把当前进程添加到wait参数指定的等待列表(poll_table)中。等待唤醒。
3.2.4 响应中断,唤醒等待线程
我们在申请中断的时候,注册了一个响应中断的方法,申请中断的代码如下:
request_irq(client->irq, bcm2079x_dev_irq_handler, IRQF_TRIGGER_RISING|IRQF_NO_SUSPEND|IRQF_SHARED,client->name, bcm2079x_dev); |
int request_irq(unsigned int irq, void (*handler)(int irq,void dev_id,), |
bcm2079x_dev_irq_handler方法为向系统登记的中断处理子程序,其代码如下:
staticirqreturn_t bcm2079x_dev_irq_handler(int irq, void*dev_id) { struct bcm2079x_dev *bcm2079x_dev = dev_id; unsigned long flags; int reg_val; int ret = -1; reg_val = readl(gpio_addr + PIO_INT_STAT_OFFSET); if(reg_val&(1<<(BCM2079X_IRQ_NO))){ bcm2079x_clear_penirq(); spin_lock_irqsave(&bcm2079x_dev->irq_enabled_lock,flags); bcm2079x_dev->count_irq++; spin_unlock_irqrestore(&bcm2079x_dev->irq_enabled_lock,flags); wake_up(&bcm2079x_dev->read_wq);//唤醒等待线程 return IRQ_HANDLED; } return ret; } |
3.2.5 读数据
用户空间调用read()方法,读取设备节点的数据,最终调用到驱动程序的bcm2079x_dev_read()方法,代码如下:
staticssize_t bcm2079x_dev_read(struct file *filp, char __user*buf, size_t count, loff_t *offset) { struct bcm2079x_dev *bcm2079x_dev =filp->private_data; unsigned char tmp[MAX_BUFFER_SIZE]; int total, len, ret; total = 0; len = 0; bcm2079x_dev->count_read++; if (count > MAX_BUFFER_SIZE) count = MAX_BUFFER_SIZE; mutex_lock(&bcm2079x_dev->read_mutex); ret = i2c_master_recv(bcm2079x_dev->client, tmp,4);//通过IIC读取数据 if (ret == 4) { total = ret; switch(tmp[0]) { case PACKET_TYPE_NCI: len = tmp[PACKET_HEADER_SIZE_NCI-1]; break; case PACKET_TYPE_HCIEV: len = tmp[PACKET_HEADER_SIZE_HCI-1]; if (len == 0) total--; else len--; break; default: len =0; break; } if (len > 0 && (len+ total) <= count) { ret = i2c_master_recv(bcm2079x_dev->client,tmp+total, len); if (ret == len) total += len; } } mutex_unlock(&bcm2079x_dev->read_mutex); if (total > count || copy_to_user(buf, tmp, total)){//拷贝数据到用户空间 bcm2079x_dev->error_read++; } return total; } |
container_of:http://www.cnblogs.com/sdphome/archive/2011/09/14/2176624.html
poll_wart():http://blog.chinaunix.net/uid-26851094-id-3175940.html
NFC bcm2079x驱动学习 .相关推荐
- mtk android平台学习,MTK平台的驱动学习——(阶段1规划篇)
受老罗的影响,由于本人还是菜鸟,不能像老罗一样重头开始研究整个系统,决定从就近的工作开始,从android MTK 的驱动-->中间层-->应用层,一步一步研究. 一边看书,一边搜集网上的 ...
- coverity代码检测工具介绍_FOREPOST:一种使用反馈驱动学习软件测试的性能检测工具...
FOREPOST:一种使用反馈驱动学习软件测试的性能检测工具 摘要 性能测试的一个目标是找出某些特定情况,在这些情况下对于某些输入值组合,应用程序意外地展示出更糟糕的特性.性能测试的一个基本问题是如何 ...
- 转载:mongoDB java驱动学习笔记
http://www.blogjava.net/watchzerg/archive/2012/09/22/388346.html mongoDB java驱动学习笔记 指定新mongo实例: Mong ...
- android+5.q,MSM8909+Android5.1.1电池管理(2)--qpnp-linear-charger.txt驱动学习概要
MSM8909+Android5.1.1电池管理(2)--qpnp-linear-charger.txt驱动学习概要 参考文件 \kernel\Documentation\power\qpnp-lin ...
- Linux内核学习-字符设备驱动学习(二)
在Linux内核学习-字符设备驱动学习(一)中编写字符设备驱动的一种方法,但是需要手动创建设备节点. 有没有能够自动的创建设备节点的呢? 有!使用class_create()和device_creat ...
- 驱动学习(十)poll机制
驱动学习(十)poll机制 文章目录 驱动学习(十)poll机制 1. io多路复用思想: 2. 驱动如何实现poll机制呢? 3.测试 1. io多路复用思想: 1 构建一张文件描述符集合表 fd_ ...
- 驱动学习(九)字符设备的非阻塞操作
驱动学习(九)字符设备的非阻塞操作 文章目录 驱动学习(九)字符设备的非阻塞操作 1. 什么是非阻塞操作? 2. 操作 3. 测试 1. 什么是非阻塞操作? 是指在不能进行设备操作时,并不挂起或休眠该 ...
- linux v4l2系统详解,Linux摄像头驱动学习之:(一)V4L2_框架分析
这段时间开始搞安卓camera底层驱动了,把以前的的Linux视频驱动回顾一下,本篇主要概述一下vfl2(video for linux 2). 一. V4L2框架: video for linux ...
- Linux SPI驱动学习——调用SPI读写函数
Linux SPI驱动学习--调用SPI读写函数 博客说明 开发环境 1. 基于W25Q16型FLASH来看SPI的读写函数 附录 博客说明 撰写日期 2019.10.25 完稿日期 未完稿 最近维护 ...
- windbg调试驱动学习总结
简单驱动编写与windbg调试 http://trustsec.blog.51cto.com/305338/64694/ 一.驱动编写 随着对windows系统的深入研究,越来越多的内核方面的知识被挖 ...
最新文章
- 云服务器ECS挖矿木马病毒处理和解决方案
- 数据预处理--样本选择、交叉验证
- 大数据在医疗保健中的真正愿景
- 都快 2022 年了,这些 Github 使用技巧你都会了吗?
- EXT--表单AJax提交后台,返回前端数据格式的转换
- LGOJP1941 飞扬的小鸟
- MySQL 游标(CURSOR)
- android逆向工程dex2jar使用
- 2019年研究生数学建模竞赛优秀论文汇总
- php怎么在表格里插图片大小,如何批量插入图片到Word文档表格中并自动排版调整尺寸...
- 制作u盘winpe启动盘_U启大师U盘启动盘制作教程(装机版)
- 《唯有时间能证明伟大:极客之王特斯拉传》读后感
- 一个海量在线用户即时通讯系统(IM)的完整设计
- 懒出天际--语音鼠标,解放双手,靠嘴使唤鼠标。SAPI语音识别,WINAPI鼠标消息
- UOS系统下安装软件打不开的解决方法
- 同步110序列检测电路
- 【一篇文章告诉你网格策略从理论到实盘的所有内容(python实现)】
- DANN:Domain-Adversarial Training of Neural Networks
- 蒸汽流量计算软件_NHR-5600系列流量计算仪控制仪使用说明 总结
- 【babylonjs】环形屏幕3d展示
热门文章
- Node图片识别文字
- 为基于GTK/CLUTTER的库增加GJS支持
- ant 中日历组件中英文混杂解决办法
- HTTP协议:无状态协议
- 如何查找共享计算机的用户名和密码错误,访问共享文件夹提示“未知的用户名或密码错误...
- oracle isnull使用索引,isnull()用法总结
- 切割视频——将视频截取python实现
- 【算法】O(n2)时间复杂度和O(nlogn)排序算法的简要分析
- qq邮箱绑定重庆大学邮箱服务器,电子邮箱常见问题
- visio付款流程图_visio画程序流程图(转)