diff options
Diffstat (limited to 'drivers/nfc/pn544')
| -rw-r--r-- | drivers/nfc/pn544/Kconfig | 34 | ||||
| -rw-r--r-- | drivers/nfc/pn544/Makefile | 7 | ||||
| -rw-r--r-- | drivers/nfc/pn544/i2c.c | 764 | ||||
| -rw-r--r-- | drivers/nfc/pn544/mei.c | 109 | ||||
| -rw-r--r-- | drivers/nfc/pn544/pn544.c | 294 | ||||
| -rw-r--r-- | drivers/nfc/pn544/pn544.h | 12 |
6 files changed, 1047 insertions, 173 deletions
diff --git a/drivers/nfc/pn544/Kconfig b/drivers/nfc/pn544/Kconfig new file mode 100644 index 00000000000..ccf06f5f6eb --- /dev/null +++ b/drivers/nfc/pn544/Kconfig @@ -0,0 +1,34 @@ +config NFC_PN544 + tristate "NXP PN544 NFC driver" + depends on NFC_HCI + select CRC_CCITT + default n + ---help--- + NXP PN544 core driver. + This is a driver based on the HCI NFC kernel layers and + will thus not work with NXP libnfc library. + + To compile this driver as a module, choose m here. The module will + be called pn544. + Say N if unsure. + +config NFC_PN544_I2C + tristate "NFC PN544 i2c support" + depends on NFC_PN544 && I2C && NFC_SHDLC + ---help--- + This module adds support for the NXP pn544 i2c interface. + Select this if your platform is using the i2c bus. + + If you choose to build a module, it'll be called pn544_i2c. + Say N if unsure. + +config NFC_PN544_MEI + tristate "NFC PN544 MEI support" + depends on NFC_PN544 && NFC_MEI_PHY + ---help--- + This module adds support for the mei interface of adapters using + NXP pn544 chipsets. Select this if your pn544 chipset + is handled by Intel's Management Engine Interface on your platform. + + If you choose to build a module, it'll be called pn544_mei. + Say N if unsure. diff --git a/drivers/nfc/pn544/Makefile b/drivers/nfc/pn544/Makefile index 725733881eb..29fb5a17403 100644 --- a/drivers/nfc/pn544/Makefile +++ b/drivers/nfc/pn544/Makefile @@ -2,6 +2,9 @@ # Makefile for PN544 HCI based NFC driver # -obj-$(CONFIG_PN544_HCI_NFC) += pn544_i2c.o +pn544_i2c-objs = i2c.o +pn544_mei-objs = mei.o -pn544_i2c-y := pn544.o i2c.o +obj-$(CONFIG_NFC_PN544) += pn544.o +obj-$(CONFIG_NFC_PN544_I2C) += pn544_i2c.o +obj-$(CONFIG_NFC_PN544_MEI) += pn544_mei.o diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index 2a9c8d93d2e..440291ab726 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -13,23 +13,28 @@ * 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. + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/crc-ccitt.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/delay.h> - +#include <linux/nfc.h> +#include <linux/firmware.h> +#include <linux/unaligned/access_ok.h> #include <linux/platform_data/pn544.h> #include <net/nfc/hci.h> #include <net/nfc/llc.h> +#include <net/nfc/nfc.h> #include "pn544.h" @@ -55,6 +60,93 @@ MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table); #define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c" +/* + * Exposed through the 4 most significant bytes + * from the HCI SW_VERSION first byte, a.k.a. + * SW RomLib. + */ +#define PN544_HW_VARIANT_C2 0xa +#define PN544_HW_VARIANT_C3 0xb + +#define PN544_FW_CMD_RESET 0x01 +#define PN544_FW_CMD_WRITE 0x08 +#define PN544_FW_CMD_CHECK 0x06 +#define PN544_FW_CMD_SECURE_WRITE 0x0C +#define PN544_FW_CMD_SECURE_CHUNK_WRITE 0x0D + +struct pn544_i2c_fw_frame_write { + u8 cmd; + u16 be_length; + u8 be_dest_addr[3]; + u16 be_datalen; + u8 data[]; +} __packed; + +struct pn544_i2c_fw_frame_check { + u8 cmd; + u16 be_length; + u8 be_start_addr[3]; + u16 be_datalen; + u16 be_crc; +} __packed; + +struct pn544_i2c_fw_frame_response { + u8 status; + u16 be_length; +} __packed; + +struct pn544_i2c_fw_blob { + u32 be_size; + u32 be_destaddr; + u8 data[]; +}; + +struct pn544_i2c_fw_secure_frame { + u8 cmd; + u16 be_datalen; + u8 data[]; +} __packed; + +struct pn544_i2c_fw_secure_blob { + u64 header; + u8 data[]; +}; + +#define PN544_FW_CMD_RESULT_TIMEOUT 0x01 +#define PN544_FW_CMD_RESULT_BAD_CRC 0x02 +#define PN544_FW_CMD_RESULT_ACCESS_DENIED 0x08 +#define PN544_FW_CMD_RESULT_PROTOCOL_ERROR 0x0B +#define PN544_FW_CMD_RESULT_INVALID_PARAMETER 0x11 +#define PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND 0x13 +#define PN544_FW_CMD_RESULT_INVALID_LENGTH 0x18 +#define PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR 0x19 +#define PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR 0x1D +#define PN544_FW_CMD_RESULT_MEMORY_ERROR 0x20 +#define PN544_FW_CMD_RESULT_CHUNK_OK 0x21 +#define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74 +#define PN544_FW_CMD_RESULT_COMMAND_REJECTED 0xE0 +#define PN544_FW_CMD_RESULT_CHUNK_ERROR 0xE6 + +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) + +#define PN544_FW_WRITE_BUFFER_MAX_LEN 0x9f7 +#define PN544_FW_I2C_MAX_PAYLOAD PN544_HCI_I2C_LLC_MAX_SIZE +#define PN544_FW_I2C_WRITE_FRAME_HEADER_LEN 8 +#define PN544_FW_I2C_WRITE_DATA_MAX_LEN MIN((PN544_FW_I2C_MAX_PAYLOAD -\ + PN544_FW_I2C_WRITE_FRAME_HEADER_LEN),\ + PN544_FW_WRITE_BUFFER_MAX_LEN) +#define PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN 3 +#define PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN (PN544_FW_I2C_MAX_PAYLOAD -\ + PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN) +#define PN544_FW_SECURE_FRAME_HEADER_LEN 3 +#define PN544_FW_SECURE_BLOB_HEADER_LEN 8 + +#define FW_WORK_STATE_IDLE 1 +#define FW_WORK_STATE_START 2 +#define FW_WORK_STATE_WAIT_WRITE_ANSWER 3 +#define FW_WORK_STATE_WAIT_CHECK_ANSWER 4 +#define FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER 5 + struct pn544_i2c_phy { struct i2c_client *i2c_dev; struct nfc_hci_dev *hdev; @@ -64,7 +156,22 @@ struct pn544_i2c_phy { unsigned int gpio_fw; unsigned int en_polarity; + u8 hw_variant; + + struct work_struct fw_work; + int fw_work_state; + char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1]; + const struct firmware *fw; + u32 fw_blob_dest_addr; + size_t fw_blob_size; + const u8 *fw_blob_data; + size_t fw_written; + size_t fw_size; + + int fw_cmd_result; + int powered; + int run_mode; int hard_fault; /* * < 0 if hardware error occured (e.g. i2c err) @@ -85,8 +192,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) char rset_cmd[] = { 0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5 }; int count = sizeof(rset_cmd); - pr_info(DRIVER_DESC ": %s\n", __func__); - dev_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n"); + nfc_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n"); /* Disable fw download */ gpio_set_value(phy->gpio_fw, 0); @@ -107,7 +213,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) dev_dbg(&phy->i2c_dev->dev, "Sending reset cmd\n"); ret = i2c_master_send(phy->i2c_dev, rset_cmd, count); if (ret == count) { - dev_info(&phy->i2c_dev->dev, + nfc_info(&phy->i2c_dev->dev, "nfc_en polarity : active %s\n", (polarity == 0 ? "low" : "high")); goto out; @@ -115,22 +221,29 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) } } - dev_err(&phy->i2c_dev->dev, + nfc_err(&phy->i2c_dev->dev, "Could not detect nfc_en polarity, fallback to active high\n"); out: gpio_set_value(phy->gpio_en, !phy->en_polarity); } +static void pn544_hci_i2c_enable_mode(struct pn544_i2c_phy *phy, int run_mode) +{ + gpio_set_value(phy->gpio_fw, run_mode == PN544_FW_MODE ? 1 : 0); + gpio_set_value(phy->gpio_en, phy->en_polarity); + usleep_range(10000, 15000); + + phy->run_mode = run_mode; +} + static int pn544_hci_i2c_enable(void *phy_id) { struct pn544_i2c_phy *phy = phy_id; - pr_info(DRIVER_DESC ": %s\n", __func__); + pr_info("%s\n", __func__); - gpio_set_value(phy->gpio_fw, 0); - gpio_set_value(phy->gpio_en, phy->en_polarity); - usleep_range(10000, 15000); + pn544_hci_i2c_enable_mode(phy, PN544_HCI_MODE); phy->powered = 1; @@ -141,8 +254,6 @@ static void pn544_hci_i2c_disable(void *phy_id) { struct pn544_i2c_phy *phy = phy_id; - pr_info(DRIVER_DESC ": %s\n", __func__); - gpio_set_value(phy->gpio_fw, 0); gpio_set_value(phy->gpio_en, !phy->en_polarity); usleep_range(10000, 15000); @@ -225,11 +336,9 @@ static int check_crc(u8 *buf, int buflen) crc = ~crc; if (buf[len - 2] != (crc & 0xff) || buf[len - 1] != (crc >> 8)) { - pr_err(PN544_HCI_I2C_DRIVER_NAME - ": CRC error 0x%x != 0x%x 0x%x\n", + pr_err("CRC error 0x%x != 0x%x 0x%x\n", crc, buf[len - 1], buf[len - 2]); - - pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__); + pr_info("%s: BAD CRC\n", __func__); print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, 16, 2, buf, buflen, false); return -EPERM; @@ -255,13 +364,13 @@ static int pn544_hci_i2c_read(struct pn544_i2c_phy *phy, struct sk_buff **skb) r = i2c_master_recv(client, &len, 1); if (r != 1) { - dev_err(&client->dev, "cannot read len byte\n"); + nfc_err(&client->dev, "cannot read len byte\n"); return -EREMOTEIO; } if ((len < (PN544_HCI_I2C_LLC_MIN_SIZE - 1)) || (len > (PN544_HCI_I2C_LLC_MAX_SIZE - 1))) { - dev_err(&client->dev, "invalid len byte\n"); + nfc_err(&client->dev, "invalid len byte\n"); r = -EBADMSG; goto flush; } @@ -305,6 +414,55 @@ flush: return r; } +static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy) +{ + int r; + struct pn544_i2c_fw_frame_response response; + struct i2c_client *client = phy->i2c_dev; + + r = i2c_master_recv(client, (char *) &response, sizeof(response)); + if (r != sizeof(response)) { + nfc_err(&client->dev, "cannot read fw status\n"); + return -EIO; + } + + usleep_range(3000, 6000); + + switch (response.status) { + case 0: + return 0; + case PN544_FW_CMD_RESULT_CHUNK_OK: + return response.status; + case PN544_FW_CMD_RESULT_TIMEOUT: + return -ETIMEDOUT; + case PN544_FW_CMD_RESULT_BAD_CRC: + return -ENODATA; + case PN544_FW_CMD_RESULT_ACCESS_DENIED: + return -EACCES; + case PN544_FW_CMD_RESULT_PROTOCOL_ERROR: + return -EPROTO; + case PN544_FW_CMD_RESULT_INVALID_PARAMETER: + return -EINVAL; + case PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND: + return -ENOTSUPP; + case PN544_FW_CMD_RESULT_INVALID_LENGTH: + return -EBADMSG; + case PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR: + return -ENOKEY; + case PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR: + return -EINVAL; + case PN544_FW_CMD_RESULT_MEMORY_ERROR: + return -ENOMEM; + case PN544_FW_CMD_RESULT_COMMAND_REJECTED: + return -EACCES; + case PN544_FW_CMD_RESULT_WRITE_FAILED: + case PN544_FW_CMD_RESULT_CHUNK_ERROR: + return -EIO; + default: + return -EIO; + } +} + /* * Reads an shdlc frame from the chip. This is not as straightforward as it * seems. There are cases where we could loose the frame start synchronization. @@ -339,19 +497,23 @@ static irqreturn_t pn544_hci_i2c_irq_thread_fn(int irq, void *phy_id) if (phy->hard_fault != 0) return IRQ_HANDLED; - r = pn544_hci_i2c_read(phy, &skb); - if (r == -EREMOTEIO) { - phy->hard_fault = r; - - nfc_hci_recv_frame(phy->hdev, NULL); + if (phy->run_mode == PN544_FW_MODE) { + phy->fw_cmd_result = pn544_hci_i2c_fw_read_status(phy); + schedule_work(&phy->fw_work); + } else { + r = pn544_hci_i2c_read(phy, &skb); + if (r == -EREMOTEIO) { + phy->hard_fault = r; - return IRQ_HANDLED; - } else if ((r == -ENOMEM) || (r == -EBADMSG)) { - return IRQ_HANDLED; - } + nfc_hci_recv_frame(phy->hdev, NULL); - nfc_hci_recv_frame(phy->hdev, skb); + return IRQ_HANDLED; + } else if ((r == -ENOMEM) || (r == -EBADMSG)) { + return IRQ_HANDLED; + } + nfc_hci_recv_frame(phy->hdev, skb); + } return IRQ_HANDLED; } @@ -361,6 +523,428 @@ static struct nfc_phy_ops i2c_phy_ops = { .disable = pn544_hci_i2c_disable, }; +static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name, + u8 hw_variant) +{ + struct pn544_i2c_phy *phy = phy_id; + + pr_info("Starting Firmware Download (%s)\n", firmware_name); + + strcpy(phy->firmware_name, firmware_name); + + phy->hw_variant = hw_variant; + phy->fw_work_state = FW_WORK_STATE_START; + + schedule_work(&phy->fw_work); + + return 0; +} + +static void pn544_hci_i2c_fw_work_complete(struct pn544_i2c_phy *phy, + int result) +{ + pr_info("Firmware Download Complete, result=%d\n", result); + + pn544_hci_i2c_disable(phy); + + phy->fw_work_state = FW_WORK_STATE_IDLE; + + if (phy->fw) { + release_firmware(phy->fw); + phy->fw = NULL; + } + + nfc_fw_download_done(phy->hdev->ndev, phy->firmware_name, (u32) -result); +} + +static int pn544_hci_i2c_fw_write_cmd(struct i2c_client *client, u32 dest_addr, + const u8 *data, u16 datalen) +{ + u8 frame[PN544_FW_I2C_MAX_PAYLOAD]; + struct pn544_i2c_fw_frame_write *framep; + u16 params_len; + int framelen; + int r; + + if (datalen > PN544_FW_I2C_WRITE_DATA_MAX_LEN) + datalen = PN544_FW_I2C_WRITE_DATA_MAX_LEN; + + framep = (struct pn544_i2c_fw_frame_write *) frame; + + params_len = sizeof(framep->be_dest_addr) + + sizeof(framep->be_datalen) + datalen; + framelen = params_len + sizeof(framep->cmd) + + sizeof(framep->be_length); + + framep->cmd = PN544_FW_CMD_WRITE; + + put_unaligned_be16(params_len, &framep->be_length); + + framep->be_dest_addr[0] = (dest_addr & 0xff0000) >> 16; + framep->be_dest_addr[1] = (dest_addr & 0xff00) >> 8; + framep->be_dest_addr[2] = dest_addr & 0xff; + + put_unaligned_be16(datalen, &framep->be_datalen); + + memcpy(framep->data, data, datalen); + + r = i2c_master_send(client, frame, framelen); + + if (r == framelen) + return datalen; + else if (r < 0) + return r; + else + return -EIO; +} + +static int pn544_hci_i2c_fw_check_cmd(struct i2c_client *client, u32 start_addr, + const u8 *data, u16 datalen) +{ + struct pn544_i2c_fw_frame_check frame; + int r; + u16 crc; + + /* calculate local crc for the data we want to check */ + crc = crc_ccitt(0xffff, data, datalen); + + frame.cmd = PN544_FW_CMD_CHECK; + + put_unaligned_be16(sizeof(frame.be_start_addr) + + sizeof(frame.be_datalen) + sizeof(frame.be_crc), + &frame.be_length); + + /* tell the chip the memory region to which our crc applies */ + frame.be_start_addr[0] = (start_addr & 0xff0000) >> 16; + frame.be_start_addr[1] = (start_addr & 0xff00) >> 8; + frame.be_start_addr[2] = start_addr & 0xff; + + put_unaligned_be16(datalen, &frame.be_datalen); + + /* + * and give our local crc. Chip will calculate its own crc for the + * region and compare with ours. + */ + put_unaligned_be16(crc, &frame.be_crc); + + r = i2c_master_send(client, (const char *) &frame, sizeof(frame)); + + if (r == sizeof(frame)) + return 0; + else if (r < 0) + return r; + else + return -EIO; +} + +static int pn544_hci_i2c_fw_write_chunk(struct pn544_i2c_phy *phy) +{ + int r; + + r = pn544_hci_i2c_fw_write_cmd(phy->i2c_dev, + phy->fw_blob_dest_addr + phy->fw_written, + phy->fw_blob_data + phy->fw_written, + phy->fw_blob_size - phy->fw_written); + if (r < 0) + return r; + + phy->fw_written += r; + phy->fw_work_state = FW_WORK_STATE_WAIT_WRITE_ANSWER; + + return 0; +} + +static int pn544_hci_i2c_fw_secure_write_frame_cmd(struct pn544_i2c_phy *phy, + const u8 *data, u16 datalen) +{ + u8 buf[PN544_FW_I2C_MAX_PAYLOAD]; + struct pn544_i2c_fw_secure_frame *chunk; + int chunklen; + int r; + + if (datalen > PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN) + datalen = PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN; + + chunk = (struct pn544_i2c_fw_secure_frame *) buf; + + chunk->cmd = PN544_FW_CMD_SECURE_CHUNK_WRITE; + + put_unaligned_be16(datalen, &chunk->be_datalen); + + memcpy(chunk->data, data, datalen); + + chunklen = sizeof(chunk->cmd) + sizeof(chunk->be_datalen) + datalen; + + r = i2c_master_send(phy->i2c_dev, buf, chunklen); + + if (r == chunklen) + return datalen; + else if (r < 0) + return r; + else + return -EIO; + +} + +static int pn544_hci_i2c_fw_secure_write_frame(struct pn544_i2c_phy *phy) +{ + struct pn544_i2c_fw_secure_frame *framep; + int r; + + framep = (struct pn544_i2c_fw_secure_frame *) phy->fw_blob_data; + if (phy->fw_written == 0) + phy->fw_blob_size = get_unaligned_be16(&framep->be_datalen) + + PN544_FW_SECURE_FRAME_HEADER_LEN; + + /* Only secure write command can be chunked*/ + if (phy->fw_blob_size > PN544_FW_I2C_MAX_PAYLOAD && + framep->cmd != PN544_FW_CMD_SECURE_WRITE) + return -EINVAL; + + /* The firmware also have other commands, we just send them directly */ + if (phy->fw_blob_size < PN544_FW_I2C_MAX_PAYLOAD) { + r = i2c_master_send(phy->i2c_dev, + (const char *) phy->fw_blob_data, phy->fw_blob_size); + + if (r == phy->fw_blob_size) + goto exit; + else if (r < 0) + return r; + else + return -EIO; + } + + r = pn544_hci_i2c_fw_secure_write_frame_cmd(phy, + phy->fw_blob_data + phy->fw_written, + phy->fw_blob_size - phy->fw_written); + if (r < 0) + return r; + +exit: + phy->fw_written += r; + phy->fw_work_state = FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER; + + /* SW reset command will not trig any response from PN544 */ + if (framep->cmd == PN544_FW_CMD_RESET) { + pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE); + phy->fw_cmd_result = 0; + schedule_work(&phy->fw_work); + } + + return 0; +} + +static void pn544_hci_i2c_fw_work(struct work_struct *work) +{ + struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy, + fw_work); + int r; + struct pn544_i2c_fw_blob *blob; + struct pn544_i2c_fw_secure_blob *secure_blob; + + switch (phy->fw_work_state) { + case FW_WORK_STATE_START: + pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE); + + r = request_firmware(&phy->fw, phy->firmware_name, + &phy->i2c_dev->dev); + if (r < 0) + goto exit_state_start; + + phy->fw_written = 0; + + switch (phy->hw_variant) { + case PN544_HW_VARIANT_C2: + blob = (struct pn544_i2c_fw_blob *) phy->fw->data; + phy->fw_blob_size = get_unaligned_be32(&blob->be_size); + phy->fw_blob_dest_addr = get_unaligned_be32( + &blob->be_destaddr); + phy->fw_blob_data = blob->data; + + r = pn544_hci_i2c_fw_write_chunk(phy); + break; + case PN544_HW_VARIANT_C3: + secure_blob = (struct pn544_i2c_fw_secure_blob *) + phy->fw->data; + phy->fw_blob_data = secure_blob->data; + phy->fw_size = phy->fw->size; + r = pn544_hci_i2c_fw_secure_write_frame(phy); + break; + default: + r = -ENOTSUPP; + break; + } + +exit_state_start: + if (r < 0) + pn544_hci_i2c_fw_work_complete(phy, r); + break; + + case FW_WORK_STATE_WAIT_WRITE_ANSWER: + r = phy->fw_cmd_result; + if (r < 0) + goto exit_state_wait_write_answer; + + if (phy->fw_written == phy->fw_blob_size) { + r = pn544_hci_i2c_fw_check_cmd(phy->i2c_dev, + phy->fw_blob_dest_addr, + phy->fw_blob_data, + phy->fw_blob_size); + if (r < 0) + goto exit_state_wait_write_answer; + phy->fw_work_state = FW_WORK_STATE_WAIT_CHECK_ANSWER; + break; + } + + r = pn544_hci_i2c_fw_write_chunk(phy); + +exit_state_wait_write_answer: + if (r < 0) + pn544_hci_i2c_fw_work_complete(phy, r); + break; + + case FW_WORK_STATE_WAIT_CHECK_ANSWER: + r = phy->fw_cmd_result; + if (r < 0) + goto exit_state_wait_check_answer; + + blob = (struct pn544_i2c_fw_blob *) (phy->fw_blob_data + + phy->fw_blob_size); + phy->fw_blob_size = get_unaligned_be32(&blob->be_size); + if (phy->fw_blob_size != 0) { + phy->fw_blob_dest_addr = + get_unaligned_be32(&blob->be_destaddr); + phy->fw_blob_data = blob->data; + + phy->fw_written = 0; + r = pn544_hci_i2c_fw_write_chunk(phy); + } + +exit_state_wait_check_answer: + if (r < 0 || phy->fw_blob_size == 0) + pn544_hci_i2c_fw_work_complete(phy, r); + break; + + case FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER: + r = phy->fw_cmd_result; + if (r < 0) + goto exit_state_wait_secure_write_answer; + + if (r == PN544_FW_CMD_RESULT_CHUNK_OK) { + r = pn544_hci_i2c_fw_secure_write_frame(phy); + goto exit_state_wait_secure_write_answer; + } + + if (phy->fw_written == phy->fw_blob_size) { + secure_blob = (struct pn544_i2c_fw_secure_blob *) + (phy->fw_blob_data + phy->fw_blob_size); + phy->fw_size -= phy->fw_blob_size + + PN544_FW_SECURE_BLOB_HEADER_LEN; + if (phy->fw_size >= PN544_FW_SECURE_BLOB_HEADER_LEN + + PN544_FW_SECURE_FRAME_HEADER_LEN) { + phy->fw_blob_data = secure_blob->data; + + phy->fw_written = 0; + r = pn544_hci_i2c_fw_secure_write_frame(phy); + } + } + +exit_state_wait_secure_write_answer: + if (r < 0 || phy->fw_size == 0) + pn544_hci_i2c_fw_work_complete(phy, r); + break; + + default: + break; + } +} + +#ifdef CONFIG_OF + +static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) +{ + struct pn544_i2c_phy *phy = i2c_get_clientdata(client); + struct device_node *pp; + int ret; + + pp = client->dev.of_node; + if (!pp) { + ret = -ENODEV; + goto err_dt; + } + + /* Obtention of EN GPIO from device tree */ + ret = of_get_named_gpio(pp, "enable-gpios", 0); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + nfc_err(&client->dev, + "Failed to get EN gpio, error: %d\n", ret); + goto err_dt; + } + phy->gpio_en = ret; + + /* Configuration of EN GPIO */ + ret = gpio_request(phy->gpio_en, "pn544_en"); + if (ret) { + nfc_err(&client->dev, "Fail EN pin\n"); + goto err_dt; + } + ret = gpio_direction_output(phy->gpio_en, 0); + if (ret) { + nfc_err(&client->dev, "Fail EN pin direction\n"); + goto err_gpio_en; + } + + /* Obtention of FW GPIO from device tree */ + ret = of_get_named_gpio(pp, "firmware-gpios", 0); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + nfc_err(&client->dev, + "Failed to get FW gpio, error: %d\n", ret); + goto err_gpio_en; + } + phy->gpio_fw = ret; + + /* Configuration of FW GPIO */ + ret = gpio_request(phy->gpio_fw, "pn544_fw"); + if (ret) { + nfc_err(&client->dev, "Fail FW pin\n"); + goto err_gpio_en; + } + ret = gpio_direction_output(phy->gpio_fw, 0); + if (ret) { + nfc_err(&client->dev, "Fail FW pin direction\n"); + goto err_gpio_fw; + } + + /* IRQ */ + ret = irq_of_parse_and_map(pp, 0); + if (ret < 0) { + nfc_err(&client->dev, + "Unable to get irq, error: %d\n", ret); + goto err_gpio_fw; + } + client->irq = ret; + + return 0; + +err_gpio_fw: + gpio_free(phy->gpio_fw); +err_gpio_en: + gpio_free(phy->gpio_en); +err_dt: + return ret; +} + +#else + +static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) +{ + return -ENODEV; +} + +#endif + static int pn544_hci_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -372,43 +956,55 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, dev_dbg(&client->dev, "IRQ: %d\n", client->irq); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, "Need I2C_FUNC_I2C\n"); + nfc_err(&client->dev, "Need I2C_FUNC_I2C\n"); return -ENODEV; } - phy = kzalloc(sizeof(struct pn544_i2c_phy), GFP_KERNEL); + phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy), + GFP_KERNEL); if (!phy) { - dev_err(&client->dev, + nfc_err(&client->dev, "Cannot allocate memory for pn544 i2c phy.\n"); - r = -ENOMEM; - goto err_phy_alloc; + return -ENOMEM; } + INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work); + phy->fw_work_state = FW_WORK_STATE_IDLE; + phy->i2c_dev = client; i2c_set_clientdata(client, phy); pdata = client->dev.platform_data; - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - r = -EINVAL; - goto err_pdata; - } - if (pdata->request_resources == NULL) { - dev_err(&client->dev, "request_resources() missing\n"); - r = -EINVAL; - goto err_pdata; - } + /* No platform data, using device tree. */ + if (!pdata && client->dev.of_node) { + r = pn544_hci_i2c_of_request_resources(client); + if (r) { + nfc_err(&client->dev, "No DT data\n"); + return r; + } + /* Using platform data. */ + } else if (pdata) { - r = pdata->request_resources(client); - if (r) { - dev_err(&client->dev, "Cannot get platform resources\n"); - goto err_pdata; - } + if (pdata->request_resources == NULL) { + nfc_err(&client->dev, "request_resources() missing\n"); + return -EINVAL; + } - phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); - phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET); - phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ); + r = pdata->request_resources(client); + if (r) { + nfc_err(&client->dev, + "Cannot get platform resources\n"); + return r; + } + + phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); + phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET); + phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ); + } else { + nfc_err(&client->dev, "No platform data\n"); + return -EINVAL; + } pn544_hci_i2c_platform_init(phy); @@ -416,13 +1012,14 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, IRQF_TRIGGER_RISING | IRQF_ONESHOT, PN544_HCI_I2C_DRIVER_NAME, phy); if (r < 0) { - dev_err(&client->dev, "Unable to register IRQ handler\n"); + nfc_err(&client->dev, "Unable to register IRQ handler\n"); goto err_rti; } r = pn544_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME, PN544_I2C_FRAME_HEADROOM, PN544_I2C_FRAME_TAILROOM, - PN544_HCI_I2C_LLC_MAX_PAYLOAD, &phy->hdev); + PN544_HCI_I2C_LLC_MAX_PAYLOAD, + pn544_hci_i2c_fw_download, &phy->hdev); if (r < 0) goto err_hci; @@ -432,13 +1029,13 @@ err_hci: free_irq(client->irq, phy); err_rti: - if (pdata->free_resources != NULL) + if (!pdata) { + gpio_free(phy->gpio_en); + gpio_free(phy->gpio_fw); + } else if (pdata->free_resources) { pdata->free_resources(); + } -err_pdata: - kfree(phy); - -err_phy_alloc: return r; } @@ -449,52 +1046,47 @@ static int pn544_hci_i2c_remove(struct i2c_client *client) dev_dbg(&client->dev, "%s\n", __func__); + cancel_work_sync(&phy->fw_work); + if (phy->fw_work_state != FW_WORK_STATE_IDLE) + pn544_hci_i2c_fw_work_complete(phy, -ENODEV); + pn544_hci_remove(phy->hdev); if (phy->powered) pn544_hci_i2c_disable(phy); free_irq(client->irq, phy); - if (pdata->free_resources) - pdata->free_resources(); - kfree(phy); + /* No platform data, GPIOs have been requested by this driver */ + if (!pdata) { + gpio_free(phy->gpio_en); + gpio_free(phy->gpio_fw); + /* Using platform data */ + } else if (pdata->free_resources) { + pdata->free_resources(); + } return 0; } +static const struct of_device_id of_pn544_i2c_match[] = { + { .compatible = "nxp,pn544-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_pn544_i2c_match); + static struct i2c_driver pn544_hci_i2c_driver = { .driver = { .name = PN544_HCI_I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_pn544_i2c_match), }, .probe = pn544_hci_i2c_probe, .id_table = pn544_hci_i2c_id_table, .remove = pn544_hci_i2c_remove, }; -static int __init pn544_hci_i2c_init(void) -{ - int r; - - pr_debug(DRIVER_DESC ": %s\n", __func__); - - r = i2c_add_driver(&pn544_hci_i2c_driver); - if (r) { - pr_err(PN544_HCI_I2C_DRIVER_NAME - ": driver registration failed\n"); - return r; - } - - return 0; -} - -static void __exit pn544_hci_i2c_exit(void) -{ - i2c_del_driver(&pn544_hci_i2c_driver); -} - -module_init(pn544_hci_i2c_init); -module_exit(pn544_hci_i2c_exit); +module_i2c_driver(pn544_hci_i2c_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/pn544/mei.c b/drivers/nfc/pn544/mei.c new file mode 100644 index 00000000000..330cd403100 --- /dev/null +++ b/drivers/nfc/pn544/mei.c @@ -0,0 +1,109 @@ +/* + * HCI based Driver for NXP pn544 NFC Chip + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/nfc.h> +#include <net/nfc/hci.h> +#include <net/nfc/llc.h> + +#include "../mei_phy.h" +#include "pn544.h" + +#define PN544_DRIVER_NAME "pn544" + +static int pn544_mei_probe(struct mei_cl_device *device, + const struct mei_cl_device_id *id) +{ + struct nfc_mei_phy *phy; + int r; + + pr_info("Probing NFC pn544\n"); + + phy = nfc_mei_phy_alloc(device); + if (!phy) { + pr_err("Cannot allocate memory for pn544 mei phy.\n"); + return -ENOMEM; + } + + r = pn544_hci_probe(phy, &mei_phy_ops, LLC_NOP_NAME, + MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD, + NULL, &phy->hdev); + if (r < 0) { + nfc_mei_phy_free(phy); + + return r; + } + + return 0; +} + +static int pn544_mei_remove(struct mei_cl_device *device) +{ + struct nfc_mei_phy *phy = mei_cl_get_drvdata(device); + + pr_info("Removing pn544\n"); + + pn544_hci_remove(phy->hdev); + + nfc_mei_phy_free(phy); + + return 0; +} + +static struct mei_cl_device_id pn544_mei_tbl[] = { + { PN544_DRIVER_NAME }, + + /* required last entry */ + { } +}; +MODULE_DEVICE_TABLE(mei, pn544_mei_tbl); + +static struct mei_cl_driver pn544_driver = { + .id_table = pn544_mei_tbl, + .name = PN544_DRIVER_NAME, + + .probe = pn544_mei_probe, + .remove = pn544_mei_remove, +}; + +static int pn544_mei_init(void) +{ + int r; + + pr_debug(DRIVER_DESC ": %s\n", __func__); + + r = mei_cl_driver_register(&pn544_driver); + if (r) { + pr_err(PN544_DRIVER_NAME ": driver registration failed\n"); + return r; + } + + return 0; +} + +static void pn544_mei_exit(void) +{ + mei_cl_driver_unregister(&pn544_driver); +} + +module_init(pn544_mei_init); +module_exit(pn544_mei_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index cc666de3b8e..9c8051d20ce 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -13,13 +13,14 @@ * 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. + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/delay.h> #include <linux/slab.h> +#include <linux/module.h> #include <linux/nfc.h> #include <net/nfc/hci.h> @@ -30,9 +31,6 @@ /* Timing restrictions (ms) */ #define PN544_HCI_RESETVEN_TIME 30 -#define HCI_MODE 0 -#define FW_MODE 1 - enum pn544_state { PN544_ST_COLD, PN544_ST_FW_READY, @@ -43,6 +41,7 @@ enum pn544_state { /* Proprietary commands */ #define PN544_WRITE 0x3f +#define PN544_TEST_SWP 0x21 /* Proprietary gates, events, commands and registers */ @@ -83,14 +82,17 @@ enum pn544_state { #define PN544_PL_NFCT_DEACTIVATED 0x09 #define PN544_SWP_MGMT_GATE 0xA0 +#define PN544_SWP_DEFAULT_MODE 0x01 #define PN544_NFC_WI_MGMT_GATE 0xA1 +#define PN544_NFC_ESE_DEFAULT_MODE 0x01 #define PN544_HCI_EVT_SND_DATA 0x01 #define PN544_HCI_EVT_ACTIVATED 0x02 #define PN544_HCI_EVT_DEACTIVATED 0x03 #define PN544_HCI_EVT_RCV_DATA 0x04 #define PN544_HCI_EVT_CONTINUE_MI 0x05 +#define PN544_HCI_EVT_SWITCH_MODE 0x03 #define PN544_HCI_CMD_ATTREQUEST 0x12 #define PN544_HCI_CMD_CONTINUE_ACTIVATION 0x13 @@ -129,6 +131,8 @@ struct pn544_hci_info { int async_cb_type; data_exchange_cb_t async_cb; void *async_cb_context; + + fw_download_t fw_download; }; static int pn544_hci_open(struct nfc_hci_dev *hdev) @@ -187,53 +191,46 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev) {{0x9e, 0xb4}, 0x00}, - {{0x9e, 0xd9}, 0xff}, - {{0x9e, 0xda}, 0xff}, - {{0x9e, 0xdb}, 0x23}, - {{0x9e, 0xdc}, 0x21}, - {{0x9e, 0xdd}, 0x22}, - {{0x9e, 0xde}, 0x24}, - {{0x9c, 0x01}, 0x08}, {{0x9e, 0xaa}, 0x01}, - {{0x9b, 0xd1}, 0x0d}, - {{0x9b, 0xd2}, 0x24}, - {{0x9b, 0xd3}, 0x0a}, - {{0x9b, 0xd4}, 0x22}, - {{0x9b, 0xd5}, 0x08}, - {{0x9b, 0xd6}, 0x1e}, - {{0x9b, 0xdd}, 0x1c}, + {{0x9b, 0xd1}, 0x17}, + {{0x9b, 0xd2}, 0x58}, + {{0x9b, 0xd3}, 0x10}, + {{0x9b, 0xd4}, 0x47}, + {{0x9b, 0xd5}, 0x0c}, + {{0x9b, 0xd6}, 0x37}, + {{0x9b, 0xdd}, 0x33}, - {{0x9b, 0x84}, 0x13}, - {{0x99, 0x81}, 0x7f}, - {{0x99, 0x31}, 0x70}, + {{0x9b, 0x84}, 0x00}, + {{0x99, 0x81}, 0x79}, + {{0x99, 0x31}, 0x79}, {{0x98, 0x00}, 0x3f}, - {{0x9f, 0x09}, 0x00}, + {{0x9f, 0x09}, 0x02}, {{0x9f, 0x0a}, 0x05}, {{0x9e, 0xd1}, 0xa1}, - {{0x99, 0x23}, 0x00}, - - {{0x9e, 0x74}, 0x80}, + {{0x99, 0x23}, 0x01}, + {{0x9e, 0x74}, 0x00}, + {{0x9e, 0x90}, 0x00}, {{0x9f, 0x28}, 0x10}, - {{0x9f, 0x35}, 0x14}, + {{0x9f, 0x35}, 0x04}, - {{0x9f, 0x36}, 0x60}, + {{0x9f, 0x36}, 0x11}, {{0x9c, 0x31}, 0x00}, - {{0x9c, 0x32}, 0xc8}, + {{0x9c, 0x32}, 0x00}, - {{0x9c, 0x19}, 0x40}, + {{0x9c, 0x19}, 0x0a}, - {{0x9c, 0x1a}, 0x40}, + {{0x9c, 0x1a}, 0x0a}, {{0x9c, 0x0c}, 0x00}, @@ -243,13 +240,13 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev) {{0x9c, 0x13}, 0x00}, - {{0x98, 0xa2}, 0x0e}, + {{0x98, 0xa2}, 0x09}, - {{0x98, 0x93}, 0x40}, + {{0x98, 0x93}, 0x00}, - {{0x98, 0x7d}, 0x02}, + {{0x98, 0x7d}, 0x08}, {{0x98, 0x7e}, 0x00}, - {{0x9f, 0xc8}, 0x01}, + {{0x9f, 0xc8}, 0x00}, }; struct hw_config *p = hw_config; int count = ARRAY_SIZE(hw_config); @@ -394,7 +391,7 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { hdev->gb = nfc_get_local_general_bytes(hdev->ndev, &hdev->gb_len); - pr_debug("generate local bytes %p", hdev->gb); + pr_debug("generate local bytes %p\n", hdev->gb); if (hdev->gb == NULL || hdev->gb_len == 0) { im_protocols &= ~NFC_PROTO_NFC_DEP_MASK; tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK; @@ -550,20 +547,25 @@ static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev, return -EPROTO; } - r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE, - PN544_RF_READER_CMD_ACTIVATE_NEXT, - uid_skb->data, uid_skb->len, NULL); - kfree_skb(uid_skb); - - r = nfc_hci_send_cmd(hdev, + /* Type F NFC-DEP IDm has prefix 0x01FE */ + if ((uid_skb->data[0] == 0x01) && (uid_skb->data[1] == 0xfe)) { + kfree_skb(uid_skb); + r = nfc_hci_send_cmd(hdev, PN544_RF_READER_NFCIP1_INITIATOR_GATE, PN544_HCI_CMD_CONTINUE_ACTIVATION, NULL, 0, NULL); - if (r < 0) - return r; - - target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE; - target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; + if (r < 0) + return r; + + target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; + target->hci_reader_gate = + PN544_RF_READER_NFCIP1_INITIATOR_GATE; + } else { + r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE, + PN544_RF_READER_CMD_ACTIVATE_NEXT, + uid_skb->data, uid_skb->len, NULL); + kfree_skb(uid_skb); + } } else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) { /* * TODO: maybe other ISO 14443 require some kind of continue @@ -675,17 +677,23 @@ static int pn544_hci_im_transceive(struct nfc_hci_dev *hdev, static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb) { + int r; + /* Set default false for multiple information chaining */ *skb_push(skb, 1) = 0; - return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, - PN544_HCI_EVT_SND_DATA, skb->data, skb->len); + r = nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_HCI_EVT_SND_DATA, skb->data, skb->len); + + kfree_skb(skb); + + return r; } static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, struct nfc_target *target) { - pr_debug("supported protocol %d", target->supported_protocols); + pr_debug("supported protocol %d\b", target->supported_protocols); if (target->supported_protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_ISO14443_B_MASK)) { return nfc_hci_send_cmd(hdev, target->hci_reader_gate, @@ -699,12 +707,9 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, PN544_RF_READER_CMD_ACTIVATE_NEXT, target->nfcid1, target->nfcid1_len, NULL); - } else if (target->supported_protocols & NFC_PROTO_JEWEL_MASK) { - return nfc_hci_send_cmd(hdev, target->hci_reader_gate, - PN544_JEWEL_RAW_CMD, NULL, 0, NULL); - } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) { - return nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE, - PN544_FELICA_RAW, NULL, 0, NULL); + } else if (target->supported_protocols & (NFC_PROTO_JEWEL_MASK | + NFC_PROTO_FELICA_MASK)) { + return -EOPNOTSUPP; } else if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) { return nfc_hci_send_cmd(hdev, target->hci_reader_gate, PN544_HCI_CMD_ATTREQUEST, @@ -714,35 +719,40 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, return 0; } -static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, - u8 event, struct sk_buff *skb) +/* + * Returns: + * <= 0: driver handled the event, skb consumed + * 1: driver does not handle the event, please do standard processing + */ +static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, + struct sk_buff *skb) { struct sk_buff *rgb_skb = NULL; - int r = 0; + int r; - pr_debug("hci event %d", event); + pr_debug("hci event %d\n", event); switch (event) { case PN544_HCI_EVT_ACTIVATED: - if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) - nfc_hci_target_discovered(hdev, gate); - else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { + if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) { + r = nfc_hci_target_discovered(hdev, gate); + } else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ, - &rgb_skb); - + &rgb_skb); if (r < 0) goto exit; - nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, - NFC_COMM_PASSIVE, rgb_skb->data, - rgb_skb->len); + r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, + NFC_COMM_PASSIVE, rgb_skb->data, + rgb_skb->len); kfree_skb(rgb_skb); + } else { + r = -EINVAL; } - break; case PN544_HCI_EVT_DEACTIVATED: - nfc_hci_send_event(hdev, gate, - NFC_HCI_EVT_END_OPERATION, NULL, 0); + r = nfc_hci_send_event(hdev, gate, NFC_HCI_EVT_END_OPERATION, + NULL, 0); break; case PN544_HCI_EVT_RCV_DATA: if (skb->len < 2) { @@ -751,21 +761,134 @@ static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, } if (skb->data[0] != 0) { - pr_debug("data0 %d", skb->data[0]); + pr_debug("data0 %d\n", skb->data[0]); r = -EPROTO; goto exit; } skb_pull(skb, 2); - nfc_tm_data_received(hdev->ndev, skb); - - return; + return nfc_tm_data_received(hdev->ndev, skb); default: - break; + return 1; } exit: kfree_skb(skb); + + return r; +} + +static int pn544_hci_fw_download(struct nfc_hci_dev *hdev, + const char *firmware_name) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + + if (info->fw_download == NULL) + return -ENOTSUPP; + + return info->fw_download(info->phy_id, firmware_name, hdev->sw_romlib); +} + +static int pn544_hci_discover_se(struct nfc_hci_dev *hdev) +{ + u32 se_idx = 0; + u8 ese_mode = 0x01; /* Default mode */ + struct sk_buff *res_skb; + int r; + + r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, PN544_TEST_SWP, + NULL, 0, &res_skb); + + if (r == 0) { + if (res_skb->len == 2 && res_skb->data[0] == 0x00) + nfc_add_se(hdev->ndev, se_idx++, NFC_SE_UICC); + + kfree_skb(res_skb); + } + + r = nfc_hci_send_event(hdev, PN544_NFC_WI_MGMT_GATE, + PN544_HCI_EVT_SWITCH_MODE, + &ese_mode, 1); + if (r == 0) + nfc_add_se(hdev->ndev, se_idx++, NFC_SE_EMBEDDED); + + return !se_idx; +} + +#define PN544_SE_MODE_OFF 0x00 +#define PN544_SE_MODE_ON 0x01 +static int pn544_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx) +{ + struct nfc_se *se; + u8 enable = PN544_SE_MODE_ON; + static struct uicc_gatelist { + u8 head; + u8 adr[2]; + u8 value; + } uicc_gatelist[] = { + {0x00, {0x9e, 0xd9}, 0x23}, + {0x00, {0x9e, 0xda}, 0x21}, + {0x00, {0x9e, 0xdb}, 0x22}, + {0x00, {0x9e, 0xdc}, 0x24}, + }; + struct uicc_gatelist *p = uicc_gatelist; + int count = ARRAY_SIZE(uicc_gatelist); + struct sk_buff *res_skb; + int r; + + se = nfc_find_se(hdev->ndev, se_idx); + + switch (se->type) { + case NFC_SE_UICC: + while (count--) { + r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, + PN544_WRITE, (u8 *)p, 4, &res_skb); + if (r < 0) + return r; + + if (res_skb->len != 1) { + kfree_skb(res_skb); + return -EPROTO; + } + + if (res_skb->data[0] != p->value) { + kfree_skb(res_skb); + return -EIO; + } + + kfree_skb(res_skb); + + p++; + } + + return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE, + PN544_SWP_DEFAULT_MODE, &enable, 1); + case NFC_SE_EMBEDDED: + return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE, + PN544_NFC_ESE_DEFAULT_MODE, &enable, 1); + + default: + return -EINVAL; + } +} + +static int pn544_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx) +{ + struct nfc_se *se; + u8 disable = PN544_SE_MODE_OFF; + + se = nfc_find_se(hdev->ndev, se_idx); + + switch (se->type) { + case NFC_SE_UICC: + return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE, + PN544_SWP_DEFAULT_MODE, &disable, 1); + case NFC_SE_EMBEDDED: + return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE, + PN544_NFC_ESE_DEFAULT_MODE, &disable, 1); + default: + return -EINVAL; + } } static struct nfc_hci_ops pn544_hci_ops = { @@ -782,11 +905,15 @@ static struct nfc_hci_ops pn544_hci_ops = { .tm_send = pn544_hci_tm_send, .check_presence = pn544_hci_check_presence, .event_received = pn544_hci_event_received, + .fw_download = pn544_hci_fw_download, + .discover_se = pn544_hci_discover_se, + .enable_se = pn544_hci_enable_se, + .disable_se = pn544_hci_disable_se, }; int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, int phy_headroom, int phy_tailroom, int phy_payload, - struct nfc_hci_dev **hdev) + fw_download_t fw_download, struct nfc_hci_dev **hdev) { struct pn544_hci_info *info; u32 protocols; @@ -795,13 +922,13 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL); if (!info) { - pr_err("Cannot allocate memory for pn544_hci_info.\n"); r = -ENOMEM; goto err_info_alloc; } info->phy_ops = phy_ops; info->phy_id = phy_id; + info->fw_download = fw_download; info->state = PN544_ST_COLD; mutex_init(&info->info_lock); @@ -822,12 +949,12 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_NFC_DEP_MASK; - info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, + info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0, protocols, llc_name, phy_headroom + PN544_CMDS_HEADROOM, phy_tailroom, phy_payload); if (!info->hdev) { - pr_err("Cannot allocate nfc hdev.\n"); + pr_err("Cannot allocate nfc hdev\n"); r = -ENOMEM; goto err_alloc_hdev; } @@ -851,6 +978,7 @@ err_alloc_hdev: err_info_alloc: return r; } +EXPORT_SYMBOL(pn544_hci_probe); void pn544_hci_remove(struct nfc_hci_dev *hdev) { @@ -860,3 +988,7 @@ void pn544_hci_remove(struct nfc_hci_dev *hdev) nfc_hci_free_device(hdev); kfree(info); } +EXPORT_SYMBOL(pn544_hci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/pn544/pn544.h b/drivers/nfc/pn544/pn544.h index f47c6454914..2aa9233e808 100644 --- a/drivers/nfc/pn544/pn544.h +++ b/drivers/nfc/pn544/pn544.h @@ -12,9 +12,7 @@ * 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. + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef __LOCAL_PN544_H_ @@ -24,9 +22,15 @@ #define DRIVER_DESC "HCI NFC driver for PN544" +#define PN544_HCI_MODE 0 +#define PN544_FW_MODE 1 + +typedef int (*fw_download_t)(void *context, const char *firmware_name, + u8 hw_variant); + int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, int phy_headroom, int phy_tailroom, int phy_payload, - struct nfc_hci_dev **hdev); + fw_download_t fw_download, struct nfc_hci_dev **hdev); void pn544_hci_remove(struct nfc_hci_dev *hdev); #endif /* __LOCAL_PN544_H_ */ |
