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,
};
/* * 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_gpiod: 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; struct gpio_desc *enable_gpiod; struct st95_digital_cmd_complete_arg complete_cb_arg; struct regulator *st95hf_supply; unsignedchar 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.
*/ staticint st95hf_send_recv_cmd(struct st95hf_context *st95context, enum st95hf_cmd_list cmd, int no_modif, struct param_list *list_array, bool recv_res)
{ unsignedchar 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;
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) { unsignedchar 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;
}
staticint st95hf_echo_command(struct st95hf_context *st95context)
{ int result = 0; unsignedchar 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 receive 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;
}
staticint 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;
}
staticint 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;
}
staticint 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;
}
staticvoid st95hf_send_st95enable_negativepulse(struct st95hf_context *st95con)
{ /* First make irq_in pin high */
gpiod_set_value(st95con->enable_gpiod, HIGH);
/* wait for 1 milisecond */
usleep_range(1000, 2000);
/* Make irq_in pin low */
gpiod_set_value(st95con->enable_gpiod, LOW);
/* wait for minimum interrupt pulse to make st95 active */
usleep_range(1000, 2000);
/* At end make it high */
gpiod_set_value(st95con->enable_gpiod, HIGH);
}
/* * Send a reset sequence over SPI bus (Reset command + wait 3ms + * negative pulse on st95hf enable gpio
*/ staticint st95hf_send_spi_reset_sequence(struct st95hf_context *st95context)
{ int result = 0; unsignedchar reset_cmd = ST95HF_COMMAND_RESET;
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;
}
staticint 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; unsignedchar pp_typeb; struct param_list new_params[2];
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;
}
staticint st95hf_handle_wtx(struct st95hf_context *stcontext, bool new_wtx, int wtx_val)
{ int result = 0; unsignedchar 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]; elseif (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;
}
staticint st95hf_error_handling(struct st95hf_context *stcontext, struct sk_buff *skb_resp, int res_len)
{ int result = 0; unsignedchar 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;
}
staticint st95hf_response_handler(struct st95hf_context *stcontext, struct sk_buff *skb_resp, int res_len)
{ int result = 0; int skb_len; unsignedchar 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;
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;
}
/* * 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;
}
/* if stcontext->nfcdev_free is true, it means remove already ran */ if (stcontext->nfcdev_free) {
result = -ENODEV; goto end;
}
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;
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;
}
/* NFC ops functions definition */ staticint 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;
}
staticint 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);
/* * 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"); goto free_skb_resp;
}
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); return rc;
}
/* p2p will be supported in a later release ! */ staticint st95hf_tg_configure_hw(struct nfc_digital_dev *ddev, int type, int param)
{ return 0;
}
/* * Store spicontext in spi device object for using it in * remove function
*/
dev_set_drvdata(&nfc_spi_dev->dev, spicontext);
st95context->enable_gpiod = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); if (IS_ERR(st95context->enable_gpiod)) {
ret = PTR_ERR(st95context->enable_gpiod);
dev_err(&nfc_spi_dev->dev, "No valid enable gpio\n"); goto err_disable_regulator;
}
ret = gpiod_set_consumer_name(st95context->enable_gpiod, "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;
}
/* 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);
/* 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);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.