1.NFC基本概念: NFC 又稱為近場通信,是一種新興技術,可以在彼此靠近的情況下進行數據交換,是由非接觸式射頻識別(RFID)及互連互通技術整合演變而來,通過單一晶元集成感應式讀卡器; NFC有效通訊距離一般不超過10釐米,其傳輸速度有106 Kbit/秒、212 Kbit/秒或者424 Kbi ...
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\n", __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\n",
__func__, client->flags);
if (platform_data == NULL) {
dev_err(&client->dev, "%s: failed\n", __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\n", __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\n");
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]\n",
__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]\n",
__func__,
platform_data->en_gpio);
goto err_en_gpio;
}
} else {
dev_err(&client->dev,
"%s: nfc reset gpio not provided\n", __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]\n",
__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]\n",
__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\n", __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]\n",
__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]\n",
__func__, platform_data->firm_gpio);
goto err_firm_gpio;
}
} else {
dev_err(&client->dev,
"%s: firm gpio not provided\n", __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]\n",
__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]\n",
__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\n", __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]\n",
__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]\n",
__func__, platform_data->clkreq_gpio);
goto err_clkreq_gpio;
}
} else {
dev_err(&client->dev,
"%s: clkreq gpio not provided\n", __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\n", __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\n", __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)\n",
__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\n", __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\n",
__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\n",
__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功能寫博客,挺好用的;