zoukankan      html  css  js  c++  java
  • NFC驱动调试

    1.NFC基本概念:

    NFC 又称为近场通信,是一种新兴技术,可以在彼此靠近的情况下进行数据交换,是由非接触式射频识别(RFID)及互连互通技术整合演变而来,通过单一芯片集成感应式读卡器;
    NFC有效通讯距离一般不超过10厘米,其传输速度有106 Kbit/秒、212 Kbit/秒或者424 Kbit/秒三种。

    2.NFC的工作模式:

    • 读卡器模式(Reader / Writer Mode)
    • 仿真卡模式(Card Emulation Mode)
    • 点对点模式(P2P Mode)

    读卡器模式:
    读卡器模式本质上就是通过NFC设备(比如支持NFC的Android手机)从带有NFC芯片的标签,贴纸,明信片,报纸,名片等媒介读取信息,或者将数据写到这些媒介中。贴有NFC贴纸的产品在市面上很常见。

    仿真卡模式:
    仿真卡模式就是将支持NFC的手机或者其他电子设备当成借记卡、信用卡、公交卡、门禁卡等IC卡使用。基本原理就是将相应IC卡中的信息(支付凭证)封装成数据包存储在支持NFC的手机中。在使用时,还需要一个NFC射频器(相当于刷传统IC卡使用的刷卡器)。将手机靠近NFC射频器,手机就会接收到NFC射频器发过来的信号,在通过一些列验证后,将IC卡的相应信息传入NFC射频器,最后这些IC卡数据会传入NFC射频器连接的电脑,并进行相应的处理。

    点对点(P2P)模式:
    该模式与蓝牙、红外线差不多,可以用于不同NFC设备之间进行数据交换,只是NFC的点对点模式有效距离更短(不能超过10厘米),而且传输建立速度要比红外线和蓝牙技术快很多。
    点对点模式的典型应用是两部支持NFC的手机或平板电脑实现数据的点对点传输,例如,下载音乐、交换图片、同步设备地址薄。因此,通过NFC,多个设备如数字相机,PDA,计算机,手机之间,都可以快速链接并交换资料或者服务。

    3.NFC与其他模块的比较

    对比项 NFC 蓝牙 红外
    网络类型 点对点 单点对多点 点对点
    使用距离 ≤0.1m ≤10m ≤1m
    传输速度 106、212、424、868、721、115Kbps 2.1 Mbps ~1.0 Mbps
    建立时间 < 0.1s 6s 0.5s
    安全性 主动-主动/被动 主动-主动 主动-主动
    成本

    4.NFC的物理组成

    读写器(Reader/Interrogator)、标签(Tag/Transponder)、天线(Antenna)
    1.读写器将要发送的信息,编码并加载到高频载波信号上再经天线向外发送。
    2.进入读写器工作区域的电子标签接收到信号,其卡内芯片的有关电路就会进行倍压整流、调制、解密,然后对命令请求、密码、权限进行判断。

    5.NFC手机的几种实现方式

    根据SE(安全模块的Security Element为用用户账号,身份认证等敏感信息提供安全载体,为加强手机支付的安全性)所在位置不同;

    5.1 NFC-SD卡方案

    5.2 NFC-SWP模式

    5.3 NFC的全终端模式

    6.NFC kernel分析

    6.1 从module_init函数开始:

    /*
     * module load/unload record keeping
     */
    static int __init nqx_dev_init(void)
    {
    	return i2c_add_driver(&nqx);
    }
    module_init(nqx_dev_init);
    
    static void __exit nqx_dev_exit(void)
    {
    	unregister_reboot_notifier(&nfcc_notifier);
    	i2c_del_driver(&nqx);
    }
    module_exit(nqx_dev_exit);
    

    通过i2c_add_driver(&nqx)和i2c_del_driver(&nqx)注册相应的i2c设备驱动,通过i2c传输相应数据。

    static struct i2c_driver nqx = {
    	.id_table = nqx_id,
    	.probe = nqx_probe,
    	.remove = nqx_remove,
    	.driver = {
    		.owner = THIS_MODULE,
    		.name = "nq-nci",
    		.of_match_table = msm_match_table,
    		.pm = &nfc_pm_ops,
    	},
    };
    

    通过of_match_table匹配上:

    static struct of_device_id msm_match_table[] = {
    	{.compatible = "qcom,nq-nci"},
    	{}
    };
    

    6.2 probe函数

    probe函数如下:

    static int nqx_probe(struct i2c_client *client,
    			const struct i2c_device_id *id)
    {
    	int r = 0;
    	int irqn = 0;
    	struct nqx_platform_data *platform_data;
    	struct nqx_dev *nqx_dev;
    
    	dev_dbg(&client->dev, "%s: enter
    ", __func__);
    	if (client->dev.of_node) {
    		platform_data = devm_kzalloc(&client->dev,
    			sizeof(struct nqx_platform_data), GFP_KERNEL);
    		if (!platform_data) {
    			r = -ENOMEM;
    			goto err_platform_data;
    		}
    		//解析设备树
    		r = nfc_parse_dt(&client->dev, platform_data);
    		if (r)
    			goto err_free_data;
    	} else
    		platform_data = client->dev.platform_data;
    
    	dev_dbg(&client->dev,
    		"%s, inside nfc-nci flags = %x
    ",
    		__func__, client->flags);
    
    	if (platform_data == NULL) {
    		dev_err(&client->dev, "%s: failed
    ", __func__);
    		r = -ENODEV;
    		goto err_platform_data;
    	}
    	//判断适配器能力,这里检测适配器具有I2C功能
    	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
    		dev_err(&client->dev, "%s: need I2C_FUNC_I2C
    ", __func__);
    		r = -ENODEV;
    		goto err_free_data;
    	}
    	//分配内存空间
    	nqx_dev = kzalloc(sizeof(*nqx_dev), GFP_KERNEL);
    	if (nqx_dev == NULL) {
    		r = -ENOMEM;
    		goto err_free_data;
    	}
    	nqx_dev->client = client;
    	nqx_dev->kbuflen = MAX_BUFFER_SIZE;
    	nqx_dev->kbuf = kzalloc(MAX_BUFFER_SIZE, GFP_KERNEL);
    	if (!nqx_dev->kbuf) {
    		dev_err(&client->dev,
    			"failed to allocate memory for nqx_dev->kbuf
    ");
    		r = -ENOMEM;
    		goto err_free_dev;
    	}
    
    	if (gpio_is_valid(platform_data->en_gpio)) {
    		r = gpio_request(platform_data->en_gpio, "nfc_reset_gpio");
    		if (r) {
    			dev_err(&client->dev,
    			"%s: unable to request nfc reset gpio [%d]
    ",
    				__func__,
    				platform_data->en_gpio);
    			goto err_mem;
    		}
    		r = gpio_direction_output(platform_data->en_gpio, 0);
    		if (r) {
    			dev_err(&client->dev,
    				"%s: unable to set direction for nfc reset gpio [%d]
    ",
    					__func__,
    					platform_data->en_gpio);
    			goto err_en_gpio;
    		}
    	} else {
    		dev_err(&client->dev,
    		"%s: nfc reset gpio not provided
    ", __func__);
    		goto err_mem;
    	}
    
    	if (gpio_is_valid(platform_data->irq_gpio)) {
    		r = gpio_request(platform_data->irq_gpio, "nfc_irq_gpio");
    		if (r) {
    			dev_err(&client->dev, "%s: unable to request nfc irq gpio [%d]
    ",
    				__func__, platform_data->irq_gpio);
    			goto err_en_gpio;
    		}
    		r = gpio_direction_input(platform_data->irq_gpio);
    		if (r) {
    			dev_err(&client->dev,
    			"%s: unable to set direction for nfc irq gpio [%d]
    ",
    				__func__,
    				platform_data->irq_gpio);
    			goto err_irq_gpio;
    		}
    		irqn = gpio_to_irq(platform_data->irq_gpio);
    		if (irqn < 0) {
    			r = irqn;
    			goto err_irq_gpio;
    		}
    		client->irq = irqn;
    	} else {
    		dev_err(&client->dev, "%s: irq gpio not provided
    ", __func__);
    		goto err_en_gpio;
    	}
    	if (gpio_is_valid(platform_data->firm_gpio)) {
    		r = gpio_request(platform_data->firm_gpio,
    			"nfc_firm_gpio");
    		if (r) {
    			dev_err(&client->dev,
    				"%s: unable to request nfc firmware gpio [%d]
    ",
    				__func__, platform_data->firm_gpio);
    			goto err_irq_gpio;
    		}
    		r = gpio_direction_output(platform_data->firm_gpio, 0);
    		if (r) {
    			dev_err(&client->dev,
    			"%s: cannot set direction for nfc firmware gpio [%d]
    ",
    			__func__, platform_data->firm_gpio);
    			goto err_firm_gpio;
    		}
    	} else {
    		dev_err(&client->dev,
    			"%s: firm gpio not provided
    ", __func__);
    		goto err_irq_gpio;
    	}
    	if (gpio_is_valid(platform_data->ese_gpio)) {
    	    //申请中断
    		r = gpio_request(platform_data->ese_gpio,
    				"nfc-ese_pwr");
    		if (r) {
    			nqx_dev->ese_gpio = -EINVAL;
    			dev_err(&client->dev,
    				"%s: unable to request nfc ese gpio [%d]
    ",
    					__func__, platform_data->ese_gpio);
    			/* ese gpio optional so we should continue */
    		} else {
    			nqx_dev->ese_gpio = platform_data->ese_gpio;
    			r = gpio_direction_output(platform_data->ese_gpio, 0);
    			if (r) {
    				/* free ese gpio and set invalid
    				   to avoid further use
    				*/
    				gpio_free(platform_data->ese_gpio);
    				nqx_dev->ese_gpio = -EINVAL;
    				dev_err(&client->dev,
    				"%s: cannot set direction for nfc ese gpio [%d]
    ",
    				__func__, platform_data->ese_gpio);
    				/* ese gpio optional so we should continue */
    			}
    		}
    	} else {
    		nqx_dev->ese_gpio = -EINVAL;
    		dev_err(&client->dev,
    			"%s: ese gpio not provided
    ", __func__);
    		/* ese gpio optional so we should continue */
    	}
    	if (gpio_is_valid(platform_data->clkreq_gpio)) {
    		r = gpio_request(platform_data->clkreq_gpio,
    			"nfc_clkreq_gpio");
    		if (r) {
    			dev_err(&client->dev,
    				"%s: unable to request nfc clkreq gpio [%d]
    ",
    				__func__, platform_data->clkreq_gpio);
    			goto err_ese_gpio;
    		}
    		r = gpio_direction_input(platform_data->clkreq_gpio);
    		if (r) {
    			dev_err(&client->dev,
    			"%s: cannot set direction for nfc clkreq gpio [%d]
    ",
    			__func__, platform_data->clkreq_gpio);
    			goto err_clkreq_gpio;
    		}
    	} else {
    		dev_err(&client->dev,
    			"%s: clkreq gpio not provided
    ", __func__);
    		goto err_ese_gpio;
    	}
    
    	nqx_dev->en_gpio = platform_data->en_gpio;
    	nqx_dev->irq_gpio = platform_data->irq_gpio;
    	nqx_dev->firm_gpio  = platform_data->firm_gpio;
    	nqx_dev->clkreq_gpio = platform_data->clkreq_gpio;
    	nqx_dev->pdata = platform_data;
    
    	/* init mutex and queues */
    	init_waitqueue_head(&nqx_dev->read_wq);
    	mutex_init(&nqx_dev->read_mutex);
    	spin_lock_init(&nqx_dev->irq_enabled_lock);
    
    	nqx_dev->nqx_device.minor = MISC_DYNAMIC_MINOR;
    	nqx_dev->nqx_device.name = "nq-nci";
    	 //在此处与 nfc_dev_fops 操作列表进行连接
    	nqx_dev->nqx_device.fops = &nfc_dev_fops;
        //注册混杂设备驱动
    	r = misc_register(&nqx_dev->nqx_device);
    	if (r) {
    		dev_err(&client->dev, "%s: misc_register failed
    ", __func__);
    		goto err_misc_register;
    	}
    
    	/* NFC_INT IRQ */
    	nqx_dev->irq_enabled = true;
    	r = request_irq(client->irq, nqx_dev_irq_handler,
    			  IRQF_TRIGGER_HIGH, client->name, nqx_dev);
    	if (r) {
    		dev_err(&client->dev, "%s: request_irq failed
    ", __func__);
    		goto err_request_irq_failed;
    	}
    	nqx_disable_irq(nqx_dev);
    
    	/*
    	 * To be efficient we need to test whether nfcc hardware is physically
    	 * present before attempting further hardware initialisation.
    	 *
    	 */
    	r = nfcc_hw_check(client , platform_data->en_gpio);
    	if (r) {
    		/* make sure NFCC is not enabled */
    		gpio_set_value(platform_data->en_gpio, 0);
    		/* We don't think there is hardware switch NFC OFF */
    		goto err_request_hw_check_failed;
    	}
    
    	/* Register reboot notifier here */
    	r = register_reboot_notifier(&nfcc_notifier);
    	if (r) {
    		dev_err(&client->dev,
    			"%s: cannot register reboot notifier(err = %d)
    ",
    			__func__, r);
    		/* nfcc_hw_check function not doing memory
    		   allocation so using same goto target here
    		*/
    		goto err_request_hw_check_failed;
    	}
    
    #ifdef NFC_KERNEL_BU
    	r = nqx_clock_select(nqx_dev);
    	if (r < 0) {
    		dev_err(&client->dev,
    			"%s: nqx_clock_select failed
    ", __func__);
    		goto err_clock_en_failed;
    	}
    	gpio_set_value(platform_data->en_gpio, 1);
    #endif
    	device_init_wakeup(&client->dev, true);
    	device_set_wakeup_capable(&client->dev, true);
    	i2c_set_clientdata(client, nqx_dev);
    	nqx_dev->irq_wake_up = false;
    
    	dev_err(&client->dev,
    	"%s: probing NFCC NQxxx exited successfully
    ",
    		 __func__);
    	return 0;
    
    #ifdef NFC_KERNEL_BU
    err_clock_en_failed:
    	unregister_reboot_notifier(&nfcc_notifier);
    #endif
    err_request_hw_check_failed:
    	free_irq(client->irq, nqx_dev);
    err_request_irq_failed:
    	misc_deregister(&nqx_dev->nqx_device);
    err_misc_register:
    	mutex_destroy(&nqx_dev->read_mutex);
    err_clkreq_gpio:
    	gpio_free(platform_data->clkreq_gpio);
    err_ese_gpio:
    	/* optional gpio, not sure was configured in probe */
    	if (nqx_dev->ese_gpio > 0)
    		gpio_free(platform_data->ese_gpio);
    err_firm_gpio:
    	gpio_free(platform_data->firm_gpio);
    err_irq_gpio:
    	gpio_free(platform_data->irq_gpio);
    err_en_gpio:
    	gpio_free(platform_data->en_gpio);
    err_mem:
    	kfree(nqx_dev->kbuf);
    err_free_dev:
    	kfree(nqx_dev);
    err_free_data:
    	if (client->dev.of_node)
    		devm_kfree(&client->dev, platform_data);
    err_platform_data:
    	dev_err(&client->dev,
    	"%s: probing nqxx failed, check hardware
    ",
    		 __func__);
    	return r;
    }
    
    

    6.3 file_operations

    fops 中包含了 ioctl 的操作方式,cmd 为 1 关闭 nfc , 2 为开启ese功能,3 为获取ese功能。

    static const struct file_operations nfc_dev_fops = {
    	.owner = THIS_MODULE,
    	.llseek = no_llseek,
    	.read  = nfc_read,
    	.write = nfc_write,
    	.open = nfc_open,
    	.unlocked_ioctl = nfc_ioctl,
    #ifdef CONFIG_COMPAT
    	.compat_ioctl = nfc_compat_ioctl
    #endif
    };
    
    static long nfc_ioctl(struct file *pfile, unsigned int cmd,
    			unsigned long arg)
    {
    	int r = 0;
    
    	switch (cmd) {
    	case NFC_SET_PWR:
    		r = nfc_ioctl_power_states(pfile, arg);
    		break;
    	case ESE_SET_PWR:
    		r = nqx_ese_pwr(pfile->private_data, arg);
    		break;
    	case ESE_GET_PWR:
    		r = nqx_ese_pwr(pfile->private_data, 3);
    		break;
    	case SET_RX_BLOCK:
    		break;
    	case SET_EMULATOR_TEST_POINT:
    		break;
    	case NFCC_INITIAL_CORE_RESET_NTF:
    		r = nfc_ioctl_core_reset_ntf(pfile);
    		break;
    	default:
    		r = -ENOIOCTLCMD;
    	}
    	return r;
    }
    

    因为NQ210的eSE功能被阉割,所以,只需要调通I2C即可;上层只需调用相应的ioctl功能;高通的中NQ220有eSE功能就是在trustzone的QSEE环境下运行的;如果有调试到,再分析分析;
    第一次使用markdown功能写博客,挺好用的;

  • 相关阅读:
    php部分---include()与require()的区别、empty()与isset is_null的区别与用法详解
    DataSet 的详细用法(转)
    DataSet 的用法(转)
    大神的博客地址
    c#报表 柱,饼状图
    WebApi 增删改查(2)
    Linq to SQL 的左连,右连,内连(转)
    WebApi 增删改查
    Linq to SQL 的连表查询(转)
    LINQ
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/8716333.html
Copyright © 2011-2022 走看看