NXP NFC kernel 分析

初始化与卸载

从 module_init 和 module_exit 开始读

static int __init pn544_dev_init(void)
{
    pr_info("Loading pn544 driver\n");
    return i2c_add_driver(&pn544_driver);
}
module_init(pn544_dev_init);

static void __exit pn544_dev_exit(void)
{
    pr_info("Unloading pn544 driver\n");
    i2c_del_driver(&pn544_driver);
}
module_exit(pn544_dev_exit);

通过 i2c_add_driver 和 i2c_del_driver 来注册与卸载 nfc driver
pn544_driver:

static struct i2c_driver pn544_driver = {
    .id_table   = pn544_id,
    .probe      = pn544_probe,
    .remove     = pn544_remove,
    .driver     = {
        .owner  = THIS_MODULE,
        .name   = PN544_NAME,
        .of_match_table = msm_match_table,
    },
};

其中 id_table为

static const struct i2c_device_id pn544_id[] = {
    { PN544_NAME, 0 },
    { }
};

由于 Linux3.0 后采用了 DTS 机制,使用 Device Tree 后,驱动需要与.dts中描述的设备结点进行匹配,从而引发驱动的probe()函数执行。对于platform_driver而言,需要添加一个OF匹配表 of_match_table,在这里即 msm_match_table[]。

static struct of_device_id msm_match_table[] = {
    {.compatible = "nxp,nfc-pn544"},
    {}
};
MODULE_DEVICE_TABLE(of, msm_match_table);

probe 函数

probe函数如下,只保留了关键代码。

static int pn544_probe(struct i2c_client *client,
        const struct i2c_device_id *id)
{   
    int r = 0;
    int ret;
    //struct pn544_i2c_platform_data *platform_data_from_board;
    struct pn544_dev *pn544_dev;

    printk("pn547 enter probe\n");
    //iomux_set(GPIO3_A3);   //FIRM
    //iomux_set(GPIO3_A6);   //VEN
    //iomux_set(GPIO3_A7);   //IRQ
    //platform_data_from_board = client->dev.platform_data;

    //if (platform_data_from_board == NULL) {
    //  printk("%s : nfc probe fail\n", __func__);
    //  return  -ENODEV;
    //}

    printk("nfc probe step01 is ok\n");
    //判定适配器能力,这里检测适配器具有I2C功能
    if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
        printk("%s : need I2C_FUNC_I2C\n", __func__);
        return  -ENODEV;
    }
    printk("nfc probe step02 is ok\n");

    printk("nfc probe step03 is ok\n");

    //分配内核空间,填充 pn544_dev 结构
    pn544_dev = kzalloc(sizeof(*pn544_dev), GFP_KERNEL);

    //g_pn544_dev指向所分配内核空间的起始位置
    g_pn544_dev = pn544_dev;

    if (pn544_dev == NULL) {
        dev_err(&client->dev,
                "failed to allocate memory for module data\n");
        ret = -ENOMEM;
        goto err_exit;
    }

    printk("nfc probe step04 is ok\n");
    //pn547_dev->irq_gpio = platform_data_from_board->irq_gpio;
    //pn547_dev->ven_gpio  = platform_data_from_board->ven_gpio;
    //pn547_dev->firm_gpio  = platform_data_from_board->firm_gpio;

    //填充 pn544_dev 中的 client
    pn544_dev->client   = client;

    //使用这个函数填充 pdata->xxx_gpio
    //包括 使能管脚 VEN_GPIO、中断管脚IRQ_GPIO、固件下载管脚 FIRM_GPIO
    ret=nfc_parse_dt(&client->dev,pn544_dev);

    pr_info("irq_gpio=%d; ven_gpio=%d; firm_gpio=%d; \n", \
        pn544_dev->irq_gpio,pn544_dev->ven_gpio,pn544_dev->firm_gpio);
    if(ret){
        pr_err("parse node error. pls check the dts file \n");
        ret = -EINVAL;
        //goto err_parser;
    }

    //???禁能时钟功能???
    pn544_dev->clk_run = false;
    //if (0)    
    // pn544_clock_select(pn544_dev);
    #if 0
    //gpio_is_valid 测试gpio管脚是否合法
    if (gpio_is_valid(pn544_dev->clkreq_gpio)) {
        //gpio_request是申请gpio
        //第一个参数是你要申请的管脚,第二个参数是名字,成功返回 0
        r = gpio_request(pn544_dev->clkreq_gpio,"nfc_clkreq_gpio");
        if (r) {
            dev_err(&client->dev, "unable to request gpio [%d]\n",
                    pn544_dev->clkreq_gpio);
            goto err_clkreq_gpio;
        }
        //设置gpio方向为输入,成功返回 0
        r = gpio_direction_input(pn544_dev->clkreq_gpio);
        if (r) {
            dev_err(&client->dev,
                    "unable to set direction for gpio [%d]\n",
                    pn544_dev->clkreq_gpio);
            goto err_clkreq_gpio;
        }
    } else {
        dev_err(&client->dev, "clkreq gpio not provided\n");
        goto err_clk;
    }
    #endif

    //return 0;

    //初始化 互斥锁 和 等待队列
    /* init mutex and queues */
    //初始化等待队列头
    init_waitqueue_head(&pn544_dev->read_wq);
    //初始化互斥锁
    mutex_init(&pn544_dev->read_mutex);
    //初始化自旋锁
    spin_lock_init(&pn544_dev->irq_enabled_lock);
    //填充 pn544_dev 中的 pn544_device 的
    //次级设备号,设备名,设备操作符
    pn544_dev->pn544_device.minor = MISC_DYNAMIC_MINOR;
    pn544_dev->pn544_device.name = PN544_NAME;
    //在此处与 pn544_dev_fops 操作列表进行连接
    //在 pn544_dev_fops 中实现了一些函数,具体在后面分析
    pn544_dev->pn544_device.fops = &pn544_dev_fops;

    //注册混杂设备
    ret = misc_register(&pn544_dev->pn544_device);
    if (ret) {
        printk("%s : misc_register failed\n", __FILE__);
        goto err_misc_register;
    }
    printk("nfc probe step05 is ok\n");

    //申请中断的 GPIO
    if(gpio_request(pn544_dev->irq_gpio,"nfc_int") != 0)
    {
      printk("PN544: gpio_IRQ_request error\n");
      goto err_irq;
    }
    //申请使能的 GPIO
    if(gpio_request(pn544_dev->ven_gpio,"nfc_ven") != 0)
    {
      printk("PN544: gpio_VEN_request error\n");
      goto err_ven;
    }

   //申请固件下载的 GPIO
    if(gpio_request(pn544_dev->firm_gpio,"nfc_firm") != 0)
    {
      printk("PN544: gpio_firm_request error\n");
      goto err_firm;
    }
    //设置GPIO 方向
    gpio_direction_output(pn544_dev->firm_gpio, 0);
    gpio_direction_output(pn544_dev->ven_gpio, 1);
       printk("nfc probe GPIO is ok\n");

    gpio_direction_input(pn544_dev->irq_gpio);
    printk("pn544 client->irq = %d", client->irq); //kingsun, zhudm
    client->irq = gpio_to_irq(pn544_dev->irq_gpio);
    printk("%s : requesting IRQ %d\n", __func__, client->irq);
    pn544_dev->irq_enabled = true;

    ret = request_irq(client->irq, pn544_dev_irq_handler,
              IRQF_TRIGGER_HIGH, client->name, pn544_dev);
#if 1

    if (ret) {
        printk("request_irq failed\n");
        goto err_request_irq_failed;
    }
#endif
    printk("nfc probe step06 is ok\n");

    pn544_disable_irq(pn544_dev);

    //return 0;
    i2c_set_clientdata(client, pn544_dev);

    printk("nfc probe successful\n");


#if defined(PN544_DEBUG)
        ret = device_create_file(&client->dev, &pn544_dev_attr);
        if (ret) {
            //NFC_ERR("sysfs registration failed, error %d \n", ret);
            goto err_request_irq_failed;
        }
#endif

    return 0;

err_request_irq_failed:
    misc_deregister(&pn544_dev->pn544_device);
err_misc_register:
    mutex_destroy(&pn544_dev->read_mutex);
    //kfree(pn547_dev); 
//err_clkreq_gpio:
     //gpio_free(pn544_dev->clkreq_gpio);
//err_clk:
    //pn544_clock_deselect(pn544_dev);
err_exit:
    gpio_free(pn544_dev->firm_gpio);
err_ven:
    gpio_free(pn544_dev->ven_gpio);
err_irq:
    gpio_free(pn544_dev->irq_gpio);
err_firm:
    gpio_free(pn544_dev->firm_gpio);
    kfree(pn544_dev);   
    return ret;
}

remove 函数

static int pn544_remove(struct i2c_client *client)
{
    struct pn544_dev *pn544_dev;

    pn544_dev = i2c_get_clientdata(client);
    //释放中断
    free_irq(client->irq, pn544_dev);
    //卸载字符设备
    misc_deregister(&pn544_dev->pn544_device);
    //kingsun, jerome/zhudm add 
#if defined(PN544_DEBUG)
    device_remove_file(&client->dev, &pn544_dev_attr);
#endif
    //pn544_clock_deselect(pn544_dev);
    //销毁互斥锁
    mutex_destroy(&pn544_dev->read_mutex);
    //释放 GPIO
    gpio_free(pn544_dev->irq_gpio);
    gpio_free(pn544_dev->ven_gpio);
    gpio_free(pn544_dev->firm_gpio);
    //释放内核空间
    kfree(pn544_dev);

    return 0;
}

在 probe 中,有这样一行代码

pn544_dev->pn544_device.fops = &pn544_dev_fops;

此处将 fops 注册到节点

static const struct file_operations pn544_dev_fops = {
    .owner  = THIS_MODULE,
    .llseek = no_llseek,
    .read   = pn544_dev_read,
    .write  = pn544_dev_write,
    .open   = pn544_dev_open,
    .unlocked_ioctl  = pn544_dev_ioctl,
};

fops 中包含了 ioctl 的操作方式,cmd 为 0 关闭 nfc , 1 为开启,2 为固件下载。

static long pn544_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct pn544_dev *pn544_dev = filp->private_data;

    switch (cmd) {
    case PN544_SET_PWR:
        if (arg == 2) {
        /* power on with firmware download (requires hw reset) */
            printk("%s power on with firmware\n", __func__);
            gpio_set_value(pn544_dev->ven_gpio, 1);
            gpio_set_value(pn544_dev->firm_gpio, 1);
            msleep(10);
            gpio_set_value(pn544_dev->ven_gpio, 0);
            msleep(50);
            gpio_set_value(pn544_dev->ven_gpio, 1);
            msleep(10);
        } else if (arg == 1) {
            /* power on */
            printk("%s power on\n", __func__);
            gpio_set_value(pn544_dev->firm_gpio, 0);
            gpio_set_value(pn544_dev->ven_gpio, 1);
            irq_set_irq_wake(pn544_dev->client->irq, 1);
            msleep(10);
        } else  if (arg == 0) {
            /* power off */
            printk("%s power off\n", __func__);
            gpio_set_value(pn544_dev->firm_gpio, 0);
            gpio_set_value(pn544_dev->ven_gpio, 0);
            irq_set_irq_wake(pn544_dev->client->irq, 0);
            msleep(10);
        } else {
            printk("%s bad arg %lu\n", __func__, arg);
            return -EINVAL;
        }
        break;
    default:
        printk("%s bad ioctl %u\n", __func__, cmd);
        return -EINVAL;
    }
    return 0;
}

NFC 的 Kernel 并没有很多的东西,总的来说其实只是提供了一个 ioctl 来给我们上层进行操作。

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
PN532是一款常用的NFC读写芯片,支持ISO14443A/B、FeliCa以及ISO18092标准。在STM32中使用PN532可以实现NFC功能,下面是使用PN532的基本步骤: 1. 硬件连接:将PN532的SDA、SCK、MISO、MOSI、NSS、IRQ、RST引脚分别连接到STM32的对应引脚,同时将PN532的VCC和GND引脚连接到STM32的电源和地。 2. 初始化:使用SPI总线与PN532进行通信,并初始化PN532芯片,可以使用以下代码: ```C void PN532_Init(void) { HAL_GPIO_WritePin(PN532_CS_GPIO_Port, PN532_CS_Pin, GPIO_PIN_RESET); HAL_Delay(100); PN532_Reset(); HAL_Delay(100); PN532_Wakeup(); HAL_Delay(100); } void PN532_Reset(void) { HAL_GPIO_WritePin(PN532_RST_GPIO_Port, PN532_RST_Pin, GPIO_PIN_RESET); HAL_Delay(10); HAL_GPIO_WritePin(PN532_RST_GPIO_Port, PN532_RST_Pin, GPIO_PIN_SET); HAL_Delay(10); } void PN532_Wakeup(void) { uint8_t cmd[] = {0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; HAL_SPI_Transmit(&hspi1, cmd, sizeof(cmd), 100); } ``` 3. 发送命令:PN532可以执行多种命令,如初始化、读写卡片等,可以使用以下代码发送命令: ```C uint8_t PN532_SendCmd(uint8_t *cmd, uint8_t cmdlen, uint8_t *response, uint8_t resplen) { HAL_GPIO_WritePin(PN532_CS_GPIO_Port, PN532_CS_Pin, GPIO_PIN_RESET); HAL_Delay(10); HAL_SPI_Transmit(&hspi1, cmd, cmdlen, 100); HAL_Delay(10); HAL_SPI_Receive(&hspi1, response, resplen, 100); HAL_Delay(10); HAL_GPIO_WritePin(PN532_CS_GPIO_Port, PN532_CS_Pin, GPIO_PIN_SET); return response[0]; } ``` 4. 读写卡片:PN532可以读写ISO14443A/B、FeliCa以及ISO18092标准的卡片,可以使用以下代码读写卡片: ```C uint8_t PN532_ReadCard(uint8_t *cardData, uint8_t *cardLen) { uint8_t cmd[] = {0xD4, 0x4A, 0x01, 0x00}; uint8_t response[32]; uint8_t responseLen = 0; PN532_SendCmd(cmd, sizeof(cmd), response, sizeof(response)); if (response[0] != 0x01) { return 0; } responseLen = response[1] - 1; memcpy(cardData, &response[2], responseLen); *cardLen = responseLen; return 1; } ``` 以上代码仅为参考,具体使用时需要根据实际情况进行修改和完善。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值