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;
}
}
return0;
}
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) return0;
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;
}
/* 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;
}
return0;
}
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);
}
/* *SendaresetsequenceoverSPIbus(Resetcommand+wait3ms+ *negativepulseonst95hfenablegpio
*/ 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) return0;
/* 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;
}
return0;
}
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;
}
/* 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;
/* *Ifselectprotocolisdoneonwtxreq.doselectprotocol *againwithdefaultvalues
*/ 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;
}
}
return0;
}
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);
/* *downthesemaphoretoindicatetoremovefuncthatan *ISRispending,notethatitwillnotblockhereinanycase. *Iffoundblocked,itisaBUG!
*/
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)
{ return0;
}
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;
}
/* *FirstresetSPItohandlewarmresetofthesystem. *ItwillputtheST95HFdeviceinPowerONstate *whichmakethestateofdeviceidenticaltostate *atthetimeofcoldresetofthesystem.
*/
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.