cregit-Linux how code gets into the kernel

Release 4.14 drivers/nfc/st95hf/core.c

/*
 * --------------------------------------------------------------------
 * Driver for ST NFC Transceiver ST95HF
 * --------------------------------------------------------------------
 * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. 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/err.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/nfc.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/wait.h>
#include <net/nfc/digital.h>
#include <net/nfc/nfc.h>

#include "spi.h"

/* supported protocols */

#define ST95HF_SUPPORTED_PROT		(NFC_PROTO_ISO14443_MASK | \
                                        NFC_PROTO_ISO14443_B_MASK | \
                                        NFC_PROTO_ISO15693_MASK)
/* driver capabilities */

#define ST95HF_CAPABILITIES		NFC_DIGITAL_DRV_CAPS_IN_CRC

/* Command Send Interface */
/* ST95HF_COMMAND_SEND CMD Ids */

#define ECHO_CMD			0x55

#define WRITE_REGISTER_CMD		0x9

#define PROTOCOL_SELECT_CMD		0x2

#define SEND_RECEIVE_CMD		0x4

/* Select protocol codes */

#define ISO15693_PROTOCOL_CODE		0x1

#define ISO14443A_PROTOCOL_CODE		0x2

#define ISO14443B_PROTOCOL_CODE		0x3

/*
 * head room len is 3
 * 1 byte for control byte
 * 1 byte for cmd
 * 1 byte for size
 */

#define ST95HF_HEADROOM_LEN		3

/*
 * tailroom is 1 for ISO14443A
 * and 0 for ISO14443B/ISO15693,
 * hence the max value 1 should be
 * taken.
 */

#define ST95HF_TAILROOM_LEN		1

/* Command Response interface */

#define MAX_RESPONSE_BUFFER_SIZE	280

#define ECHORESPONSE			0x55

#define ST95HF_ERR_MASK			0xF

#define ST95HF_TIMEOUT_ERROR		0x87

#define ST95HF_NFCA_CRC_ERR_MASK	0x20

#define ST95HF_NFCB_CRC_ERR_MASK	0x01

/* ST95HF transmission flag values */

#define TRFLAG_NFCA_SHORT_FRAME		0x07

#define TRFLAG_NFCA_STD_FRAME		0x08

#define TRFLAG_NFCA_STD_FRAME_CRC	0x28

/* Misc defs */

#define HIGH				1

#define LOW				0

#define ISO14443A_RATS_REQ		0xE0

#define RATS_TB1_PRESENT_MASK		0x20

#define RATS_TA1_PRESENT_MASK		0x10

#define TB1_FWI_MASK			0xF0

#define WTX_REQ_FROM_TAG		0xF2


#define MAX_CMD_LEN			0x7


#define MAX_CMD_PARAMS			4

struct cmd {
	
int cmd_len;
	
unsigned char cmd_id;
	
unsigned char no_cmd_params;
	
unsigned char cmd_params[MAX_CMD_PARAMS];
	
enum req_type req;
};


struct param_list {
	
int param_offset;
	
int new_param_val;
};

/*
 * List of top-level cmds to be used internally by the driver.
 * All these commands are build on top of ST95HF basic commands
 * such as SEND_RECEIVE_CMD, PROTOCOL_SELECT_CMD, etc.
 * These top level cmds are used internally while implementing various ops of
 * digital layer/driver probe or extending the digital framework layer for
 * features that are not yet implemented there, for example, WTX cmd handling.
 */

enum st95hf_cmd_list {
	
CMD_ECHO,
	
CMD_ISO14443A_CONFIG,
	
CMD_ISO14443A_DEMOGAIN,
	
CMD_ISO14443B_DEMOGAIN,
	
CMD_ISO14443A_PROTOCOL_SELECT,
	
CMD_ISO14443B_PROTOCOL_SELECT,
	
CMD_WTX_RESPONSE,
	
CMD_FIELD_OFF,
	
CMD_ISO15693_PROTOCOL_SELECT,
};


static const struct cmd cmd_array[] = {
	[CMD_ECHO] = {
		.cmd_len = 0x2,
		.cmd_id = ECHO_CMD,
		.no_cmd_params = 0,
		.req = SYNC,
        },
	[CMD_ISO14443A_CONFIG] = {
		.cmd_len = 0x7,
		.cmd_id = WRITE_REGISTER_CMD,
		.no_cmd_params = 0x4,
		.cmd_params = {0x3A, 0x00, 0x5A, 0x04},
		.req = SYNC,
        },
	[CMD_ISO14443A_DEMOGAIN] = {
		.cmd_len = 0x7,
		.cmd_id = WRITE_REGISTER_CMD,
		.no_cmd_params = 0x4,
		.cmd_params = {0x68, 0x01, 0x01, 0xDF},
		.req = SYNC,
        },
	[CMD_ISO14443B_DEMOGAIN] = {
		.cmd_len = 0x7,
		.cmd_id = WRITE_REGISTER_CMD,
		.no_cmd_params = 0x4,
		.cmd_params = {0x68, 0x01, 0x01, 0x51},
		.req = SYNC,
        },
	[CMD_ISO14443A_PROTOCOL_SELECT] = {
		.cmd_len = 0x7,
		.cmd_id = PROTOCOL_SELECT_CMD,
		.no_cmd_params = 0x4,
		.cmd_params = {ISO14443A_PROTOCOL_CODE, 0x00, 0x01, 0xA0},
		.req = SYNC,
        },
	[CMD_ISO14443B_PROTOCOL_SELECT] = {
		.cmd_len = 0x7,
		.cmd_id = PROTOCOL_SELECT_CMD,
		.no_cmd_params = 0x4,
		.cmd_params = {ISO14443B_PROTOCOL_CODE, 0x01, 0x03, 0xFF},
		.req = SYNC,
        },
	[CMD_WTX_RESPONSE] = {
		.cmd_len = 0x6,
		.cmd_id = SEND_RECEIVE_CMD,
		.no_cmd_params = 0x3,
		.cmd_params = {0xF2, 0x00, TRFLAG_NFCA_STD_FRAME_CRC},
		.req = ASYNC,
        },
	[CMD_FIELD_OFF] = {
		.cmd_len = 0x5,
		.cmd_id = PROTOCOL_SELECT_CMD,
		.no_cmd_params = 0x2,
		.cmd_params = {0x0, 0x0},
		.req = SYNC,
        },
	[CMD_ISO15693_PROTOCOL_SELECT] = {
		.cmd_len = 0x5,
		.cmd_id = PROTOCOL_SELECT_CMD,
		.no_cmd_params = 0x2,
		.cmd_params = {ISO15693_PROTOCOL_CODE, 0x0D},
		.req = SYNC,
        },
};

/* st95_digital_cmd_complete_arg stores client context */

struct st95_digital_cmd_complete_arg {
	
struct sk_buff *skb_resp;
	
nfc_digital_cmd_complete_t complete_cb;
	
void *cb_usrarg;
	
bool rats;
};

/*
 * structure containing ST95HF driver specific data.
 * @spicontext: structure containing information required
 *      for spi communication between st95hf and host.
 * @ddev: nfc digital device object.
 * @nfcdev: nfc device object.
 * @enable_gpio: gpio used to enable st95hf transceiver.
 * @complete_cb_arg: structure to store various context information
 *      that is passed from nfc requesting thread to the threaded ISR.
 * @st95hf_supply: regulator "consumer" for NFC device.
 * @sendrcv_trflag: last byte of frame send by sendrecv command
 *      of st95hf. This byte contains transmission flag info.
 * @exchange_lock: semaphore used for signaling the st95hf_remove
 *      function that the last outstanding async nfc request is finished.
 * @rm_lock: mutex for ensuring safe access of nfc digital object
 *      from threaded ISR. Usage of this mutex avoids any race between
 *      deletion of the object from st95hf_remove() and its access from
 *      the threaded ISR.
 * @nfcdev_free: flag to have the state of nfc device object.
 *      [alive | died]
 * @current_protocol: current nfc protocol.
 * @current_rf_tech: current rf technology.
 * @fwi: frame waiting index, received in reply of RATS according to
 *      digital protocol.
 */

struct st95hf_context {
	
struct st95hf_spi_context spicontext;
	
struct nfc_digital_dev *ddev;
	
struct nfc_dev *nfcdev;
	
unsigned int enable_gpio;
	
struct st95_digital_cmd_complete_arg complete_cb_arg;
	
struct regulator *st95hf_supply;
	
unsigned char sendrcv_trflag;
	
struct semaphore exchange_lock;
	
struct mutex rm_lock;
	
bool nfcdev_free;
	
u8 current_protocol;
	
u8 current_rf_tech;
	
int fwi;
};

/*
 * st95hf_send_recv_cmd() is for sending commands to ST95HF
 * that are described in the cmd_array[]. It can optionally
 * receive the response if the cmd request is of type
 * SYNC. For that to happen caller must pass true to recv_res.
 * For ASYNC request, recv_res is ignored and the
 * function will never try to receive the response on behalf
 * of the caller.
 */

static int st95hf_send_recv_cmd(struct st95hf_context *st95context, enum st95hf_cmd_list cmd, int no_modif, struct param_list *list_array, bool recv_res) { unsigned char spi_cmd_buffer[MAX_CMD_LEN]; int i, ret; struct device *dev = &st95context->spicontext.spidev->dev; if (cmd_array[cmd].cmd_len > MAX_CMD_LEN) return -EINVAL; if (cmd_array[cmd].no_cmd_params < no_modif) return -EINVAL; if (no_modif && !list_array) return -EINVAL; spi_cmd_buffer[0] = ST95HF_COMMAND_SEND; spi_cmd_buffer[1] = cmd_array[cmd].cmd_id; spi_cmd_buffer[2] = cmd_array[cmd].no_cmd_params; memcpy(&spi_cmd_buffer[3], cmd_array[cmd].cmd_params, spi_cmd_buffer[2]); for (i = 0; i < no_modif; i++) { if (list_array[i].param_offset >= cmd_array[cmd].no_cmd_params) return -EINVAL; spi_cmd_buffer[3 + list_array[i].param_offset] = list_array[i].new_param_val; } ret = st95hf_spi_send(&st95context->spicontext, spi_cmd_buffer, cmd_array[cmd].cmd_len, cmd_array[cmd].req); if (ret) { dev_err(dev, "st95hf_spi_send failed with error %d\n", ret); return ret; } if (cmd_array[cmd].req == SYNC && recv_res) { unsigned char st95hf_response_arr[2]; ret = st95hf_spi_recv_response(&st95context->spicontext, st95hf_response_arr); if (ret < 0) { dev_err(dev, "spi error from st95hf_spi_recv_response(), err = 0x%x\n", ret); return ret; } if (st95hf_response_arr[0]) { dev_err(dev, "st95hf error from st95hf_spi_recv_response(), err = 0x%x\n", st95hf_response_arr[0]); return -EIO; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh325100.00%1100.00%
Total325100.00%1100.00%


static int st95hf_echo_command(struct st95hf_context *st95context) { int result = 0; unsigned char echo_response; result = st95hf_send_recv_cmd(st95context, CMD_ECHO, 0, NULL, false); if (result) return result; /* If control reached here, response can be taken */ result = st95hf_spi_recv_echo_res(&st95context->spicontext, &echo_response); if (result) { dev_err(&st95context->spicontext.spidev->dev, "err: echo response receieve error = 0x%x\n", result); return result; } if (echo_response == ECHORESPONSE) return 0; dev_err(&st95context->spicontext.spidev->dev, "err: echo res is 0x%x\n", echo_response); return -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh110100.00%1100.00%
Total110100.00%1100.00%


static int secondary_configuration_type4a(struct st95hf_context *stcontext) { int result = 0; struct device *dev = &stcontext->nfcdev->dev; /* 14443A config setting after select protocol */ result = st95hf_send_recv_cmd(stcontext, CMD_ISO14443A_CONFIG, 0, NULL, true); if (result) { dev_err(dev, "type a config cmd, err = 0x%x\n", result); return result; } /* 14443A demo gain setting */ result = st95hf_send_recv_cmd(stcontext, CMD_ISO14443A_DEMOGAIN, 0, NULL, true); if (result) dev_err(dev, "type a demogain cmd, err = 0x%x\n", result); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh94100.00%1100.00%
Total94100.00%1100.00%


static int secondary_configuration_type4b(struct st95hf_context *stcontext) { int result = 0; struct device *dev = &stcontext->nfcdev->dev; result = st95hf_send_recv_cmd(stcontext, CMD_ISO14443B_DEMOGAIN, 0, NULL, true); if (result) dev_err(dev, "type b demogain cmd, err = 0x%x\n", result); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh59100.00%1100.00%
Total59100.00%1100.00%


static int st95hf_select_protocol(struct st95hf_context *stcontext, int type) { int result = 0; struct device *dev; dev = &stcontext->nfcdev->dev; switch (type) { case NFC_DIGITAL_RF_TECH_106A: stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106A; result = st95hf_send_recv_cmd(stcontext, CMD_ISO14443A_PROTOCOL_SELECT, 0, NULL, true); if (result) { dev_err(dev, "protocol sel, err = 0x%x\n", result); return result; } /* secondary config. for 14443Type 4A after protocol select */ result = secondary_configuration_type4a(stcontext); if (result) { dev_err(dev, "type a secondary config, err = 0x%x\n", result); return result; } break; case NFC_DIGITAL_RF_TECH_106B: stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106B; result = st95hf_send_recv_cmd(stcontext, CMD_ISO14443B_PROTOCOL_SELECT, 0, NULL, true); if (result) { dev_err(dev, "protocol sel send, err = 0x%x\n", result); return result; } /* * delay of 5-6 ms is required after select protocol * command in case of ISO14443 Type B */ usleep_range(50000, 60000); /* secondary config. for 14443Type 4B after protocol select */ result = secondary_configuration_type4b(stcontext); if (result) { dev_err(dev, "type b secondary config, err = 0x%x\n", result); return result; } break; case NFC_DIGITAL_RF_TECH_ISO15693: stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_ISO15693; result = st95hf_send_recv_cmd(stcontext, CMD_ISO15693_PROTOCOL_SELECT, 0, NULL, true); if (result) { dev_err(dev, "protocol sel send, err = 0x%x\n", result); return result; } break; default: return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh236100.00%1100.00%
Total236100.00%1100.00%


static void st95hf_send_st95enable_negativepulse(struct st95hf_context *st95con) { /* First make irq_in pin high */ gpio_set_value(st95con->enable_gpio, HIGH); /* wait for 1 milisecond */ usleep_range(1000, 2000); /* Make irq_in pin low */ gpio_set_value(st95con->enable_gpio, LOW); /* wait for minimum interrupt pulse to make st95 active */ usleep_range(1000, 2000); /* At end make it high */ gpio_set_value(st95con->enable_gpio, HIGH); }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh57100.00%1100.00%
Total57100.00%1100.00%

/* * Send a reset sequence over SPI bus (Reset command + wait 3ms + * negative pulse on st95hf enable gpio */
static int st95hf_send_spi_reset_sequence(struct st95hf_context *st95context) { int result = 0; unsigned char reset_cmd = ST95HF_COMMAND_RESET; result = st95hf_spi_send(&st95context->spicontext, &reset_cmd, ST95HF_RESET_CMD_LEN, ASYNC); if (result) { dev_err(&st95context->spicontext.spidev->dev, "spi reset sequence cmd error = %d", result); return result; } /* wait for 3 milisecond to complete the controller reset process */ usleep_range(3000, 4000); /* send negative pulse to make st95hf active */ st95hf_send_st95enable_negativepulse(st95context); /* wait for 10 milisecond : HFO setup time */ usleep_range(10000, 20000); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh89100.00%1100.00%
Total89100.00%1100.00%


static int st95hf_por_sequence(struct st95hf_context *st95context) { int nth_attempt = 1; int result; st95hf_send_st95enable_negativepulse(st95context); usleep_range(5000, 6000); do { /* send an ECHO command and checks ST95HF response */ result = st95hf_echo_command(st95context); dev_dbg(&st95context->spicontext.spidev->dev, "response from echo function = 0x%x, attempt = %d\n", result, nth_attempt); if (!result) return 0; /* send an pulse on IRQ in case of the chip is on sleep state */ if (nth_attempt == 2) st95hf_send_st95enable_negativepulse(st95context); else st95hf_send_spi_reset_sequence(st95context); /* delay of 50 milisecond */ usleep_range(50000, 51000); } while (nth_attempt++ < 3); return -ETIMEDOUT; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh106100.00%1100.00%
Total106100.00%1100.00%


static int iso14443_config_fdt(struct st95hf_context *st95context, int wtxm) { int result = 0; struct device *dev = &st95context->spicontext.spidev->dev; struct nfc_digital_dev *nfcddev = st95context->ddev; unsigned char pp_typeb; struct param_list new_params[2]; pp_typeb = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[2]; if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 && st95context->fwi < 4) st95context->fwi = 4; new_params[0].param_offset = 2; if (nfcddev->curr_protocol == NFC_PROTO_ISO14443) new_params[0].new_param_val = st95context->fwi; else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B) new_params[0].new_param_val = pp_typeb; new_params[1].param_offset = 3; new_params[1].new_param_val = wtxm; switch (nfcddev->curr_protocol) { case NFC_PROTO_ISO14443: result = st95hf_send_recv_cmd(st95context, CMD_ISO14443A_PROTOCOL_SELECT, 2, new_params, true); if (result) { dev_err(dev, "WTX type a sel proto, err = 0x%x\n", result); return result; } /* secondary config. for 14443Type 4A after protocol select */ result = secondary_configuration_type4a(st95context); if (result) { dev_err(dev, "WTX type a second. config, err = 0x%x\n", result); return result; } break; case NFC_PROTO_ISO14443_B: result = st95hf_send_recv_cmd(st95context, CMD_ISO14443B_PROTOCOL_SELECT, 2, new_params, true); if (result) { dev_err(dev, "WTX type b sel proto, err = 0x%x\n", result); return result; } /* secondary config. for 14443Type 4B after protocol select */ result = secondary_configuration_type4b(st95context); if (result) { dev_err(dev, "WTX type b second. config, err = 0x%x\n", result); return result; } break; default: return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh291100.00%1100.00%
Total291100.00%1100.00%


static int st95hf_handle_wtx(struct st95hf_context *stcontext, bool new_wtx, int wtx_val) { int result = 0; unsigned char val_mm = 0; struct param_list new_params[1]; struct nfc_digital_dev *nfcddev = stcontext->ddev; struct device *dev = &stcontext->nfcdev->dev; if (new_wtx) { result = iso14443_config_fdt(stcontext, wtx_val & 0x3f); if (result) { dev_err(dev, "Config. setting error on WTX req, err = 0x%x\n", result); return result; } /* Send response of wtx with ASYNC as no response expected */ new_params[0].param_offset = 1; new_params[0].new_param_val = wtx_val; result = st95hf_send_recv_cmd(stcontext, CMD_WTX_RESPONSE, 1, new_params, false); if (result) dev_err(dev, "WTX response send, err = 0x%x\n", result); return result; } /* if no new wtx, cofigure with default values */ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443) val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3]; else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B) val_mm = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[3]; result = iso14443_config_fdt(stcontext, val_mm); if (result) dev_err(dev, "Default config. setting error after WTX processing, err = 0x%x\n", result); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh208100.00%1100.00%
Total208100.00%1100.00%


static int st95hf_error_handling(struct st95hf_context *stcontext, struct sk_buff *skb_resp, int res_len) { int result = 0; unsigned char error_byte; struct device *dev = &stcontext->nfcdev->dev; /* First check ST95HF specific error */ if (skb_resp->data[0] & ST95HF_ERR_MASK) { if (skb_resp->data[0] == ST95HF_TIMEOUT_ERROR) result = -ETIMEDOUT; else result = -EIO; return result; } /* Check for CRC err only if CRC is present in the tag response */ switch (stcontext->current_rf_tech) { case NFC_DIGITAL_RF_TECH_106A: if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC) { error_byte = skb_resp->data[res_len - 3]; if (error_byte & ST95HF_NFCA_CRC_ERR_MASK) { /* CRC error occurred */ dev_err(dev, "CRC error, byte received = 0x%x\n", error_byte); result = -EIO; } } break; case NFC_DIGITAL_RF_TECH_106B: case NFC_DIGITAL_RF_TECH_ISO15693: error_byte = skb_resp->data[res_len - 1]; if (error_byte & ST95HF_NFCB_CRC_ERR_MASK) { /* CRC error occurred */ dev_err(dev, "CRC error, byte received = 0x%x\n", error_byte); result = -EIO; } break; } return result; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh180100.00%1100.00%
Total180100.00%1100.00%


static int st95hf_response_handler(struct st95hf_context *stcontext, struct sk_buff *skb_resp, int res_len) { int result = 0; int skb_len; unsigned char val_mm; struct nfc_digital_dev *nfcddev = stcontext->ddev; struct device *dev = &stcontext->nfcdev->dev; struct st95_digital_cmd_complete_arg *cb_arg; cb_arg = &stcontext->complete_cb_arg; /* Process the response */ skb_put(skb_resp, res_len); /* Remove st95 header */ skb_pull(skb_resp, 2); skb_len = skb_resp->len; /* check if it is case of RATS request reply & FWI is present */ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 && cb_arg->rats && (skb_resp->data[1] & RATS_TB1_PRESENT_MASK)) { if (skb_resp->data[1] & RATS_TA1_PRESENT_MASK) stcontext->fwi = (skb_resp->data[3] & TB1_FWI_MASK) >> 4; else stcontext->fwi = (skb_resp->data[2] & TB1_FWI_MASK) >> 4; val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3]; result = iso14443_config_fdt(stcontext, val_mm); if (result) { dev_err(dev, "error in config_fdt to handle fwi of ATS, error=%d\n", result); return result; } } cb_arg->rats = false; /* Remove CRC bytes only if received frames data has an eod (CRC) */ switch (stcontext->current_rf_tech) { case NFC_DIGITAL_RF_TECH_106A: if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC) skb_trim(skb_resp, (skb_len - 5)); else skb_trim(skb_resp, (skb_len - 3)); break; case NFC_DIGITAL_RF_TECH_106B: case NFC_DIGITAL_RF_TECH_ISO15693: skb_trim(skb_resp, (skb_len - 3)); break; } return result; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh268100.00%1100.00%
Total268100.00%1100.00%


static irqreturn_t st95hf_irq_handler(int irq, void *st95hfcontext) { struct st95hf_context *stcontext = (struct st95hf_context *)st95hfcontext; if (stcontext->spicontext.req_issync) { complete(&stcontext->spicontext.done); stcontext->spicontext.req_issync = false; return IRQ_HANDLED; } return IRQ_WAKE_THREAD; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh59100.00%2100.00%
Total59100.00%2100.00%


static irqreturn_t st95hf_irq_thread_handler(int irq, void *st95hfcontext) { int result = 0; int res_len; static bool wtx; struct device *dev; struct device *spidevice; struct nfc_digital_dev *nfcddev; struct sk_buff *skb_resp; struct st95hf_context *stcontext = (struct st95hf_context *)st95hfcontext; struct st95_digital_cmd_complete_arg *cb_arg; spidevice = &stcontext->spicontext.spidev->dev; /* * check semaphore, if not down() already, then we don't * know in which context the ISR is called and surely it * will be a bug. Note that down() of the semaphore is done * in the corresponding st95hf_in_send_cmd() and then * only this ISR should be called. ISR will up() the * semaphore before leaving. Hence when the ISR is called * the correct behaviour is down_trylock() should always * return 1 (indicating semaphore cant be taken and hence no * change in semaphore count). * If not, then we up() the semaphore and crash on * a BUG() ! */ if (!down_trylock(&stcontext->exchange_lock)) { up(&stcontext->exchange_lock); WARN(1, "unknown context in ST95HF ISR"); return IRQ_NONE; } cb_arg = &stcontext->complete_cb_arg; skb_resp = cb_arg->skb_resp; mutex_lock(&stcontext->rm_lock); res_len = st95hf_spi_recv_response(&stcontext->spicontext, skb_resp->data); if (res_len < 0) { dev_err(spidevice, "TISR spi response err = 0x%x\n", res_len); result = res_len; goto end; } /* if stcontext->nfcdev_free is true, it means remove already ran */ if (stcontext->nfcdev_free) { result = -ENODEV; goto end; } dev = &stcontext->nfcdev->dev; nfcddev = stcontext->ddev; if (skb_resp->data[2] == WTX_REQ_FROM_TAG) { /* Request for new FWT from tag */ result = st95hf_handle_wtx(stcontext, true, skb_resp->data[3]); if (result) goto end; wtx = true; mutex_unlock(&stcontext->rm_lock); return IRQ_HANDLED; } result = st95hf_error_handling(stcontext, skb_resp, res_len); if (result) goto end; result = st95hf_response_handler(stcontext, skb_resp, res_len); if (result) goto end; /* * If select protocol is done on wtx req. do select protocol * again with default values */ if (wtx) { wtx = false; result = st95hf_handle_wtx(stcontext, false, 0); if (result) goto end; } /* call digital layer callback */ cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp); /* up the semaphore before returning */ up(&stcontext->exchange_lock); mutex_unlock(&stcontext->rm_lock); return IRQ_HANDLED; end: kfree_skb(skb_resp); wtx = false; cb_arg->rats = false; skb_resp = ERR_PTR(result); /* call of callback with error */ cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp); /* up the semaphore before returning */ up(&stcontext->exchange_lock); mutex_unlock(&stcontext->rm_lock); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh409100.00%2100.00%
Total409100.00%2100.00%

/* NFC ops functions definition */
static int st95hf_in_configure_hw(struct nfc_digital_dev *ddev, int type, int param) { struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); if (type == NFC_DIGITAL_CONFIG_RF_TECH) return st95hf_select_protocol(stcontext, param); if (type == NFC_DIGITAL_CONFIG_FRAMING) { switch (param) { case NFC_DIGITAL_FRAMING_NFCA_SHORT: stcontext->sendrcv_trflag = TRFLAG_NFCA_SHORT_FRAME; break; case NFC_DIGITAL_FRAMING_NFCA_STANDARD: stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME; break; case NFC_DIGITAL_FRAMING_NFCA_T4T: case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP: case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A: stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME_CRC; break; case NFC_DIGITAL_FRAMING_NFCB: case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY: case NFC_DIGITAL_FRAMING_ISO15693_T5T: break; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh104100.00%1100.00%
Total104100.00%1100.00%


static int rf_off(struct st95hf_context *stcontext) { int rc; struct device *dev; dev = &stcontext->nfcdev->dev; rc = st95hf_send_recv_cmd(stcontext, CMD_FIELD_OFF, 0, NULL, true); if (rc) dev_err(dev, "protocol sel send field off, err = 0x%x\n", rc); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh59100.00%1100.00%
Total59100.00%1100.00%


static int st95hf_in_send_cmd(struct nfc_digital_dev *ddev, struct sk_buff *skb, u16 timeout, nfc_digital_cmd_complete_t cb, void *arg) { struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); int rc; struct sk_buff *skb_resp; int len_data_to_tag = 0; skb_resp = nfc_alloc_recv_skb(MAX_RESPONSE_BUFFER_SIZE, GFP_KERNEL); if (!skb_resp) { rc = -ENOMEM; goto error; } switch (stcontext->current_rf_tech) { case NFC_DIGITAL_RF_TECH_106A: len_data_to_tag = skb->len + 1; skb_put_u8(skb, stcontext->sendrcv_trflag); break; case NFC_DIGITAL_RF_TECH_106B: case NFC_DIGITAL_RF_TECH_ISO15693: len_data_to_tag = skb->len; break; default: rc = -EINVAL; goto free_skb_resp; } skb_push(skb, 3); skb->data[0] = ST95HF_COMMAND_SEND; skb->data[1] = SEND_RECEIVE_CMD; skb->data[2] = len_data_to_tag; stcontext->complete_cb_arg.skb_resp = skb_resp; stcontext->complete_cb_arg.cb_usrarg = arg; stcontext->complete_cb_arg.complete_cb = cb; if ((skb->data[3] == ISO14443A_RATS_REQ) && ddev->curr_protocol == NFC_PROTO_ISO14443) stcontext->complete_cb_arg.rats = true; /* * down the semaphore to indicate to remove func that an * ISR is pending, note that it will not block here in any case. * If found blocked, it is a BUG! */ rc = down_killable(&stcontext->exchange_lock); if (rc) { WARN(1, "Semaphore is not found up in st95hf_in_send_cmd\n"); return rc; } rc = st95hf_spi_send(&stcontext->spicontext, skb->data, skb->len, ASYNC); if (rc) { dev_err(&stcontext->nfcdev->dev, "Error %d trying to perform data_exchange", rc); /* up the semaphore since ISR will never come in this case */ up(&stcontext->exchange_lock); goto free_skb_resp; } kfree_skb(skb); return rc; free_skb_resp: kfree_skb(skb_resp); error: return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh30699.35%150.00%
Johannes Berg20.65%150.00%
Total308100.00%2100.00%

/* p2p will be supported in a later release ! */
static int st95hf_tg_configure_hw(struct nfc_digital_dev *ddev, int type, int param) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh20100.00%1100.00%
Total20100.00%1100.00%


static int st95hf_tg_send_cmd(struct nfc_digital_dev *ddev, struct sk_buff *skb, u16 timeout, nfc_digital_cmd_complete_t cb, void *arg) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh29100.00%1100.00%
Total29100.00%1100.00%


static int st95hf_tg_listen(struct nfc_digital_dev *ddev, u16 timeout, nfc_digital_cmd_complete_t cb, void *arg) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh24100.00%1100.00%
Total24100.00%1100.00%


static int st95hf_tg_get_rf_tech(struct nfc_digital_dev *ddev, u8 *rf_tech) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh18100.00%1100.00%
Total18100.00%1100.00%


static int st95hf_switch_rf(struct nfc_digital_dev *ddev, bool on) { u8 rf_tech; struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); rf_tech = ddev->curr_rf_tech; if (on) /* switch on RF field */ return st95hf_select_protocol(stcontext, rf_tech); /* switch OFF RF field */ return rf_off(stcontext); }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh53100.00%1100.00%
Total53100.00%1100.00%

/* TODO st95hf_abort_cmd */
static void st95hf_abort_cmd(struct nfc_digital_dev *ddev) { }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh10100.00%1100.00%
Total10100.00%1100.00%

static struct nfc_digital_ops st95hf_nfc_digital_ops = { .in_configure_hw = st95hf_in_configure_hw, .in_send_cmd = st95hf_in_send_cmd, .tg_listen = st95hf_tg_listen, .tg_configure_hw = st95hf_tg_configure_hw, .tg_send_cmd = st95hf_tg_send_cmd, .tg_get_rf_tech = st95hf_tg_get_rf_tech, .switch_rf = st95hf_switch_rf, .abort_cmd = st95hf_abort_cmd, }; static const struct spi_device_id st95hf_id[] = { { "st95hf", 0 }, {} }; MODULE_DEVICE_TABLE(spi, st95hf_id);
static int st95hf_probe(struct spi_device *nfc_spi_dev) { int ret; struct st95hf_context *st95context; struct st95hf_spi_context *spicontext; nfc_info(&nfc_spi_dev->dev, "ST95HF driver probe called.\n"); st95context = devm_kzalloc(&nfc_spi_dev->dev, sizeof(struct st95hf_context), GFP_KERNEL); if (!st95context) return -ENOMEM; spicontext = &st95context->spicontext; spicontext->spidev = nfc_spi_dev; st95context->fwi = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[2]; if (device_property_present(&nfc_spi_dev->dev, "st95hfvin")) { st95context->st95hf_supply = devm_regulator_get(&nfc_spi_dev->dev, "st95hfvin"); if (IS_ERR(st95context->st95hf_supply)) { dev_err(&nfc_spi_dev->dev, "failed to acquire regulator\n"); return PTR_ERR(st95context->st95hf_supply); } ret = regulator_enable(st95context->st95hf_supply); if (ret) { dev_err(&nfc_spi_dev->dev, "failed to enable regulator\n"); return ret; } } init_completion(&spicontext->done); mutex_init(&spicontext->spi_lock); /* * Store spicontext in spi device object for using it in * remove function */ dev_set_drvdata(&nfc_spi_dev->dev, spicontext); st95context->enable_gpio = of_get_named_gpio(nfc_spi_dev->dev.of_node, "enable-gpio", 0); if (!gpio_is_valid(st95context->enable_gpio)) { dev_err(&nfc_spi_dev->dev, "No valid enable gpio\n"); ret = st95context->enable_gpio; goto err_disable_regulator; } ret = devm_gpio_request_one(&nfc_spi_dev->dev, st95context->enable_gpio, GPIOF_DIR_OUT | GPIOF_INIT_HIGH, "enable_gpio"); if (ret) goto err_disable_regulator; if (nfc_spi_dev->irq > 0) { if (devm_request_threaded_irq(&nfc_spi_dev->dev, nfc_spi_dev->irq, st95hf_irq_handler, st95hf_irq_thread_handler, IRQF_TRIGGER_FALLING, "st95hf", (void *)st95context) < 0) { dev_err(&nfc_spi_dev->dev, "err: irq request for st95hf is failed\n"); ret = -EINVAL; goto err_disable_regulator; } } else { dev_err(&nfc_spi_dev->dev, "not a valid IRQ associated with ST95HF\n"); ret = -EINVAL; goto err_disable_regulator; } /* * First reset SPI to handle warm reset of the system. * It will put the ST95HF device in Power ON state * which make the state of device identical to state * at the time of cold reset of the system. */ ret = st95hf_send_spi_reset_sequence(st95context); if (ret) { dev_err(&nfc_spi_dev->dev, "err: spi_reset_sequence failed\n"); goto err_disable_regulator; } /* call PowerOnReset sequence of ST95hf to activate it */ ret = st95hf_por_sequence(st95context); if (ret) { dev_err(&nfc_spi_dev->dev, "err: por seq failed for st95hf\n"); goto err_disable_regulator; } /* create NFC dev object and register with NFC Subsystem */ st95context->ddev = nfc_digital_allocate_device(&st95hf_nfc_digital_ops, ST95HF_SUPPORTED_PROT, ST95HF_CAPABILITIES, ST95HF_HEADROOM_LEN, ST95HF_TAILROOM_LEN); if (!st95context->ddev) { ret = -ENOMEM; goto err_disable_regulator; } st95context->nfcdev = st95context->ddev->nfc_dev; nfc_digital_set_parent_dev(st95context->ddev, &nfc_spi_dev->dev); ret = nfc_digital_register_device(st95context->ddev); if (ret) { dev_err(&st95context->nfcdev->dev, "st95hf registration failed\n"); goto err_free_digital_device; } /* store st95context in nfc device object */ nfc_digital_set_drvdata(st95context->ddev, st95context); sema_init(&st95context->exchange_lock, 1); mutex_init(&st95context->rm_lock); return ret; err_free_digital_device: nfc_digital_free_device(st95context->ddev); err_disable_regulator: if (st95context->st95hf_supply) regulator_disable(st95context->st95hf_supply); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh556100.00%2100.00%
Total556100.00%2100.00%


static int st95hf_remove(struct spi_device *nfc_spi_dev) { int result = 0; unsigned char reset_cmd = ST95HF_COMMAND_RESET; struct st95hf_spi_context *spictx = dev_get_drvdata(&nfc_spi_dev->dev); struct st95hf_context *stcontext = container_of(spictx, struct st95hf_context, spicontext); mutex_lock(&stcontext->rm_lock); nfc_digital_unregister_device(stcontext->ddev); nfc_digital_free_device(stcontext->ddev); stcontext->nfcdev_free = true; mutex_unlock(&stcontext->rm_lock); /* if last in_send_cmd's ISR is pending, wait for it to finish */ result = down_killable(&stcontext->exchange_lock); if (result == -EINTR) dev_err(&spictx->spidev->dev, "sleep for semaphore interrupted by signal\n"); /* next reset the ST95HF controller */ result = st95hf_spi_send(&stcontext->spicontext, &reset_cmd, ST95HF_RESET_CMD_LEN, ASYNC); if (result) { dev_err(&spictx->spidev->dev, "ST95HF reset failed in remove() err = %d\n", result); return result; } /* wait for 3 ms to complete the controller reset process */ usleep_range(3000, 4000); /* disable regulator */ if (stcontext->st95hf_supply) regulator_disable(stcontext->st95hf_supply); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh182100.00%1100.00%
Total182100.00%1100.00%

/* Register as SPI protocol driver */ static struct spi_driver st95hf_driver = { .driver = { .name = "st95hf", .owner = THIS_MODULE, }, .id_table = st95hf_id, .probe = st95hf_probe, .remove = st95hf_remove, }; module_spi_driver(st95hf_driver); MODULE_AUTHOR("Shikha Singh <shikha.singh@st.com>"); MODULE_DESCRIPTION("ST NFC Transceiver ST95HF driver"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Shikha Singh464099.96%266.67%
Johannes Berg20.04%133.33%
Total4642100.00%3100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.