Commit 0329326e authored by Matti J. Aaltonen's avatar Matti J. Aaltonen Committed by Linus Torvalds

NFC: Driver for NXP Semiconductors PN544 NFC chip.

Creates a new "Near Field Communication" subsystem in drivers/nfc.
http://en.wikipedia.org/wiki/Near_Field_Communication is useful ;)

This is a driver for the pn544 NFC device. The driver transfers
ETSI messages between the device and the user space.
Signed-off-by: default avatarMatti J. Aaltonen <matti.j.aaltonen@nokia.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 6164281a
Kernel driver for the NXP Semiconductors PN544 Near Field
Communication chip
Author: Jari Vanhala
Contact: Matti Aaltonen (matti.j.aaltonen at nokia.com)
General
-------
The PN544 is an integrated transmission module for contactless
communication. The driver goes under drives/nfc/ and is compiled as a
module named "pn544". It registers a misc device and creates a device
file named "/dev/pn544".
Host Interfaces: I2C, SPI and HSU, this driver supports currently only I2C.
The Interface
-------------
The driver offers a sysfs interface for a hardware test and an IOCTL
interface for selecting between two operating modes. There are read,
write and poll functions for transferring messages. The two operating
modes are the normal (HCI) mode and the firmware update mode.
PN544 is controlled by sending messages from the userspace to the
chip. The main function of the driver is just to pass those messages
without caring about the message content.
Protocols
---------
In the normal (HCI) mode and in the firmware update mode read and
write functions behave a bit differently because the message formats
or the protocols are different.
In the normal (HCI) mode the protocol used is derived from the ETSI
HCI specification. The firmware is updated using a specific protocol,
which is different from HCI.
HCI messages consist of an eight bit header and the message body. The
header contains the message length. Maximum size for an HCI message is
33. In HCI mode sent messages are tested for a correct
checksum. Firmware update messages have the length in the second (MSB)
and third (LSB) bytes of the message. The maximum FW message length is
1024 bytes.
For the ETSI HCI specification see
http://www.etsi.org/WebSite/Technologies/ProtocolSpecification.aspx
The Hardware Test
-----------------
The idea of the test is that it can performed by reading from the
corresponding sysfs file. The test is implemented in the board file
and it should test that PN544 can be put into the firmware update
mode. If the test is not implemented the sysfs file does not get
created.
Example:
> cat /sys/module/pn544/drivers/i2c\:pn544/3-002b/nfc_test
1
Normal Operation
----------------
PN544 is powered up when the device file is opened, otherwise it's
turned off. Only one instance can use the device at a time.
Userspace applications control PN544 with HCI messages. The hardware
sends an interrupt when data is available for reading. Data is
physically read when the read function is called by a userspace
application. Poll() checks the read interrupt state. Configuration and
self testing are also done from the userspace using read and write.
Example platform data:
static int rx71_pn544_nfc_request_resources(struct i2c_client *client)
{
/* Get and setup the HW resources for the device */
}
static void rx71_pn544_nfc_free_resources(void)
{
/* Release the HW resources */
}
static void rx71_pn544_nfc_enable(int fw)
{
/* Turn the device on */
}
static int rx71_pn544_nfc_test(void)
{
/*
* Put the device into the FW update mode
* and then back to the normal mode.
* Check the behavior and return one on success,
* zero on failure.
*/
}
static void rx71_pn544_nfc_disable(void)
{
/* turn the power off */
}
static struct pn544_nfc_platform_data rx71_nfc_data = {
.request_resources = rx71_pn544_nfc_request_resources,
.free_resources = rx71_pn544_nfc_free_resources,
.enable = rx71_pn544_nfc_enable,
.test = rx71_pn544_nfc_test,
.disable = rx71_pn544_nfc_disable,
};
......@@ -88,6 +88,8 @@ source "drivers/memstick/Kconfig"
source "drivers/leds/Kconfig"
source "drivers/nfc/Kconfig"
source "drivers/accessibility/Kconfig"
source "drivers/infiniband/Kconfig"
......
......@@ -40,7 +40,7 @@ obj-$(CONFIG_FB_INTEL) += video/intelfb/
obj-y += serial/
obj-$(CONFIG_PARPORT) += parport/
obj-y += base/ block/ misc/ mfd/
obj-y += base/ block/ misc/ mfd/ nfc/
obj-$(CONFIG_NUBUS) += nubus/
obj-y += macintosh/
obj-$(CONFIG_IDE) += ide/
......
#
# Near Field Communication (NFC) devices
#
menuconfig NFC_DEVICES
bool "NFC devices"
default n
---help---
You'll have to say Y if your computer contains an NFC device that
you want to use under Linux.
You can say N here if you don't have any Near Field Communication
devices connected to your computer.
if NFC_DEVICES
config PN544_NFC
tristate "PN544 NFC driver"
depends on I2C
select CRC_CCITT
default n
---help---
Say yes if you want PN544 Near Field Communication driver.
This is for i2c connected version. If unsure, say N here.
To compile this driver as a module, choose m here. The module will
be called pn544.
endif # NFC_DEVICES
#
# Makefile for nfc devices
#
obj-$(CONFIG_PN544_NFC) += pn544.o
/*
* Driver for the PN544 NFC chip.
*
* Copyright (C) Nokia Corporation
*
* Author: Jari Vanhala <ext-jari.vanhala@nokia.com>
* Contact: Matti Aaltonen <matti.j.aaltonen@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/completion.h>
#include <linux/crc-ccitt.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/nfc/pn544.h>
#include <linux/poll.h>
#include <linux/regulator/consumer.h>
#include <linux/serial_core.h> /* for TCGETS */
#include <linux/slab.h>
#define DRIVER_CARD "PN544 NFC"
#define DRIVER_DESC "NFC driver for PN544"
static struct i2c_device_id pn544_id_table[] = {
{ PN544_DRIVER_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pn544_id_table);
#define HCI_MODE 0
#define FW_MODE 1
enum pn544_state {
PN544_ST_COLD,
PN544_ST_FW_READY,
PN544_ST_READY,
};
enum pn544_irq {
PN544_NONE,
PN544_INT,
};
struct pn544_info {
struct miscdevice miscdev;
struct i2c_client *i2c_dev;
struct regulator_bulk_data regs[2];
enum pn544_state state;
wait_queue_head_t read_wait;
loff_t read_offset;
enum pn544_irq read_irq;
struct mutex read_mutex; /* Serialize read_irq access */
struct mutex mutex; /* Serialize info struct access */
u8 *buf;
unsigned int buflen;
};
static const char reg_vdd_io[] = "Vdd_IO";
static const char reg_vbat[] = "VBat";
/* sysfs interface */
static ssize_t pn544_test(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pn544_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->i2c_dev;
struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
return snprintf(buf, PAGE_SIZE, "%d\n", pdata->test());
}
static int pn544_enable(struct pn544_info *info, int mode)
{
struct pn544_nfc_platform_data *pdata;
struct i2c_client *client = info->i2c_dev;
int r;
r = regulator_bulk_enable(ARRAY_SIZE(info->regs), info->regs);
if (r < 0)
return r;
pdata = client->dev.platform_data;
info->read_irq = PN544_NONE;
if (pdata->enable)
pdata->enable(mode);
if (mode) {
info->state = PN544_ST_FW_READY;
dev_dbg(&client->dev, "now in FW-mode\n");
} else {
info->state = PN544_ST_READY;
dev_dbg(&client->dev, "now in HCI-mode\n");
}
usleep_range(10000, 15000);
return 0;
}
static void pn544_disable(struct pn544_info *info)
{
struct pn544_nfc_platform_data *pdata;
struct i2c_client *client = info->i2c_dev;
pdata = client->dev.platform_data;
if (pdata->disable)
pdata->disable();
info->state = PN544_ST_COLD;
dev_dbg(&client->dev, "Now in OFF-mode\n");
msleep(PN544_RESETVEN_TIME);
info->read_irq = PN544_NONE;
regulator_bulk_disable(ARRAY_SIZE(info->regs), info->regs);
}
static int check_crc(u8 *buf, int buflen)
{
u8 len;
u16 crc;
len = buf[0] + 1;
if (len < 4 || len != buflen || len > PN544_MSG_MAX_SIZE) {
pr_err(PN544_DRIVER_NAME
": CRC; corrupt packet len %u (%d)\n", len, buflen);
print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
16, 2, buf, buflen, false);
return -EPERM;
}
crc = crc_ccitt(0xffff, buf, len - 2);
crc = ~crc;
if (buf[len-2] != (crc & 0xff) || buf[len-1] != (crc >> 8)) {
pr_err(PN544_DRIVER_NAME ": CRC error 0x%x != 0x%x 0x%x\n",
crc, buf[len-1], buf[len-2]);
print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
16, 2, buf, buflen, false);
return -EPERM;
}
return 0;
}
static int pn544_i2c_write(struct i2c_client *client, u8 *buf, int len)
{
int r;
if (len < 4 || len != (buf[0] + 1)) {
dev_err(&client->dev, "%s: Illegal message length: %d\n",
__func__, len);
return -EINVAL;
}
if (check_crc(buf, len))
return -EINVAL;
usleep_range(3000, 6000);
r = i2c_master_send(client, buf, len);
dev_dbg(&client->dev, "send: %d\n", r);
if (r == -EREMOTEIO) { /* Retry, chip was in standby */
usleep_range(6000, 10000);
r = i2c_master_send(client, buf, len);
dev_dbg(&client->dev, "send2: %d\n", r);
}
if (r != len)
return -EREMOTEIO;
return r;
}
static int pn544_i2c_read(struct i2c_client *client, u8 *buf, int buflen)
{
int r;
u8 len;
/*
* You could read a packet in one go, but then you'd need to read
* max size and rest would be 0xff fill, so we do split reads.
*/
r = i2c_master_recv(client, &len, 1);
dev_dbg(&client->dev, "recv1: %d\n", r);
if (r != 1)
return -EREMOTEIO;
if (len < PN544_LLC_HCI_OVERHEAD)
len = PN544_LLC_HCI_OVERHEAD;
else if (len > (PN544_MSG_MAX_SIZE - 1))
len = PN544_MSG_MAX_SIZE - 1;
if (1 + len > buflen) /* len+(data+crc16) */
return -EMSGSIZE;
buf[0] = len;
r = i2c_master_recv(client, buf + 1, len);
dev_dbg(&client->dev, "recv2: %d\n", r);
if (r != len)
return -EREMOTEIO;
usleep_range(3000, 6000);
return r + 1;
}
static int pn544_fw_write(struct i2c_client *client, u8 *buf, int len)
{
int r;
dev_dbg(&client->dev, "%s\n", __func__);
if (len < PN544_FW_HEADER_SIZE ||
(PN544_FW_HEADER_SIZE + (buf[1] << 8) + buf[2]) != len)
return -EINVAL;
r = i2c_master_send(client, buf, len);
dev_dbg(&client->dev, "fw send: %d\n", r);
if (r == -EREMOTEIO) { /* Retry, chip was in standby */
usleep_range(6000, 10000);
r = i2c_master_send(client, buf, len);
dev_dbg(&client->dev, "fw send2: %d\n", r);
}
if (r != len)
return -EREMOTEIO;
return r;
}
static int pn544_fw_read(struct i2c_client *client, u8 *buf, int buflen)
{
int r, len;
if (buflen < PN544_FW_HEADER_SIZE)
return -EINVAL;
r = i2c_master_recv(client, buf, PN544_FW_HEADER_SIZE);
dev_dbg(&client->dev, "FW recv1: %d\n", r);
if (r < 0)
return r;
if (r < PN544_FW_HEADER_SIZE)
return -EINVAL;
len = (buf[1] << 8) + buf[2];
if (len == 0) /* just header, no additional data */
return r;
if (len > buflen - PN544_FW_HEADER_SIZE)
return -EMSGSIZE;
r = i2c_master_recv(client, buf + PN544_FW_HEADER_SIZE, len);
dev_dbg(&client->dev, "fw recv2: %d\n", r);
if (r != len)
return -EINVAL;
return r + PN544_FW_HEADER_SIZE;
}
static irqreturn_t pn544_irq_thread_fn(int irq, void *dev_id)
{
struct pn544_info *info = dev_id;
struct i2c_client *client = info->i2c_dev;
BUG_ON(!info);
BUG_ON(irq != info->i2c_dev->irq);
dev_dbg(&client->dev, "IRQ\n");
mutex_lock(&info->read_mutex);
info->read_irq = PN544_INT;
mutex_unlock(&info->read_mutex);
wake_up_interruptible(&info->read_wait);
return IRQ_HANDLED;
}
static enum pn544_irq pn544_irq_state(struct pn544_info *info)
{
enum pn544_irq irq;
mutex_lock(&info->read_mutex);
irq = info->read_irq;
mutex_unlock(&info->read_mutex);
/*
* XXX: should we check GPIO-line status directly?
* return pdata->irq_status() ? PN544_INT : PN544_NONE;
*/
return irq;
}
static ssize_t pn544_read(struct file *file, char __user *buf,
size_t count, loff_t *offset)
{
struct pn544_info *info = container_of(file->private_data,
struct pn544_info, miscdev);
struct i2c_client *client = info->i2c_dev;
enum pn544_irq irq;
size_t len;
int r = 0;
dev_dbg(&client->dev, "%s: info: %p, count: %zu\n", __func__,
info, count);
mutex_lock(&info->mutex);
if (info->state == PN544_ST_COLD) {
r = -ENODEV;
goto out;
}
irq = pn544_irq_state(info);
if (irq == PN544_NONE) {
if (file->f_flags & O_NONBLOCK) {
r = -EAGAIN;
goto out;
}
if (wait_event_interruptible(info->read_wait,
(info->read_irq == PN544_INT))) {
r = -ERESTARTSYS;
goto out;
}
}
if (info->state == PN544_ST_FW_READY) {
len = min(count, info->buflen);
mutex_lock(&info->read_mutex);
r = pn544_fw_read(info->i2c_dev, info->buf, len);
info->read_irq = PN544_NONE;
mutex_unlock(&info->read_mutex);
if (r < 0) {
dev_err(&info->i2c_dev->dev, "FW read failed: %d\n", r);
goto out;
}
print_hex_dump(KERN_DEBUG, "FW read: ", DUMP_PREFIX_NONE,
16, 2, info->buf, r, false);
*offset += r;
if (copy_to_user(buf, info->buf, r)) {
r = -EFAULT;
goto out;
}
} else {
len = min(count, info->buflen);
mutex_lock(&info->read_mutex);
r = pn544_i2c_read(info->i2c_dev, info->buf, len);
info->read_irq = PN544_NONE;
mutex_unlock(&info->read_mutex);
if (r < 0) {
dev_err(&info->i2c_dev->dev, "read failed (%d)\n", r);
goto out;
}
print_hex_dump(KERN_DEBUG, "read: ", DUMP_PREFIX_NONE,
16, 2, info->buf, r, false);
*offset += r;
if (copy_to_user(buf, info->buf, r)) {
r = -EFAULT;
goto out;
}
}
out:
mutex_unlock(&info->mutex);
return r;
}
static unsigned int pn544_poll(struct file *file, poll_table *wait)
{
struct pn544_info *info = container_of(file->private_data,
struct pn544_info, miscdev);
struct i2c_client *client = info->i2c_dev;
int r = 0;
dev_dbg(&client->dev, "%s: info: %p\n", __func__, info);
mutex_lock(&info->mutex);
if (info->state == PN544_ST_COLD) {
r = -ENODEV;
goto out;
}
poll_wait(file, &info->read_wait, wait);
if (pn544_irq_state(info) == PN544_INT) {
r = POLLIN | POLLRDNORM;
goto out;
}
out:
mutex_unlock(&info->mutex);
return r;
}
static ssize_t pn544_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct pn544_info *info = container_of(file->private_data,
struct pn544_info, miscdev);
struct i2c_client *client = info->i2c_dev;
ssize_t len;
int r;
dev_dbg(&client->dev, "%s: info: %p, count %zu\n", __func__,
info, count);
mutex_lock(&info->mutex);
if (info->state == PN544_ST_COLD) {
r = -ENODEV;
goto out;
}
/*
* XXX: should we detect rset-writes and clean possible
* read_irq state
*/
if (info->state == PN544_ST_FW_READY) {
size_t fw_len;
if (count < PN544_FW_HEADER_SIZE) {
r = -EINVAL;
goto out;
}
len = min(count, info->buflen);
if (copy_from_user(info->buf, buf, len)) {
r = -EFAULT;
goto out;
}
print_hex_dump(KERN_DEBUG, "FW write: ", DUMP_PREFIX_NONE,
16, 2, info->buf, len, false);
fw_len = PN544_FW_HEADER_SIZE + (info->buf[1] << 8) +
info->buf[2];
if (len > fw_len) /* 1 msg at a time */
len = fw_len;
r = pn544_fw_write(info->i2c_dev, info->buf, len);
} else {
if (count < PN544_LLC_MIN_SIZE) {
r = -EINVAL;
goto out;
}
len = min(count, info->buflen);
if (copy_from_user(info->buf, buf, len)) {
r = -EFAULT;
goto out;
}
print_hex_dump(KERN_DEBUG, "write: ", DUMP_PREFIX_NONE,
16, 2, info->buf, len, false);
if (len > (info->buf[0] + 1)) /* 1 msg at a time */
len = info->buf[0] + 1;
r = pn544_i2c_write(info->i2c_dev, info->buf, len);
}
out:
mutex_unlock(&info->mutex);
return r;
}
static long pn544_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct pn544_info *info = container_of(file->private_data,
struct pn544_info, miscdev);
struct i2c_client *client = info->i2c_dev;
struct pn544_nfc_platform_data *pdata;
unsigned int val;
int r = 0;
dev_dbg(&client->dev, "%s: info: %p, cmd: 0x%x\n", __func__, info, cmd);
mutex_lock(&info->mutex);
if (info->state == PN544_ST_COLD) {
r = -ENODEV;
goto out;
}
pdata = info->i2c_dev->dev.platform_data;
switch (cmd) {
case PN544_GET_FW_MODE:
dev_dbg(&client->dev, "%s: PN544_GET_FW_MODE\n", __func__);
<