mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-28 23:23:55 +08:00
This is the first NFC pull request for the 3.12 release.
With this one we have: - A few pn533 improvements and minor fixes. Testing our pn533 driver against Google's NCI stack triggered a few issues that we fixed now. We also added Tx fragmentation support to this driver. - More NFC secure element handling. We added a GET_SE netlink command for getting all the discovered secure elements, and we defined 2 additional secure element netlink event (transaction and connectivity). We also fixed a couple of typos and copy-paste bugs from the secure element handling code. - Firmware download support for the pn544 driver. This chipset can enter a special mode where it's waiting for firmware blobs to replace the already flashed one. We now support that mode. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJSCr8sAAoJEIqAPN1PVmxKPRwP/3hMmPn7xTRHh+snJW/aLl+P nKpLg+kLcT8QedPzB5W/N1jmI9KCdEEtw5WAzwaWW55hpc5rBpw3/aF0/WJfkZqv b4SSJ42R6XKctN+6LRFeFZyBzhgObFZqEoNsXx1C22wKCJJ4rt9HT3XFlsMihn7q 723VIWpoKc3IzUn6GAEGJNm54519aiLwVisSWsSVF1L8uRP3MBuPb2jD1/QaY19n x8J3Cj/SIXBAz1F0+J81Un1Qpi+QegGCIsQZhE7xk4s2PwmbI6dd5ic9ew5P1OlN kb79OZAG69eyI4Z3yi4ozk51gWL6gRAkhhYQIPehYa03wh4tP14dfGM+reYLUOMt 7PiWMDPtmWNfylFiLdFYbWOee6uDGtI4+AsHMMqLSjvICTtjJFIsQ4ZLnHZv/xuw E8n0vZJaEGY2g6z1LNjflFO7Ajv9oMuM+oYxPRxJsAL9olID+zlNTdEefr5gt8oL xhLvzCZjgrGCzHv+HdpNSGYL6jLjeJ5rosETw3VbC99JXBJ/YEA1ycF4hRLjPUrS 88IEODyIIGCKjf7hUoF91XRlfSVKefiDWaXKmYW6JeXnvxYdaZhILwz+VwzLbFyp dlRn5dbovkMglmiTo17awySQukw0M6OdbsisGPhDB6qvSpn2ceDlBht020hh9rTv HnEL351h1Ll1QWnZkQPQ =G1Os -----END PGP SIGNATURE----- Merge tag 'nfc-next-3.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/nfc-next Samuel Ortiz <sameo@linux.intel.com> says: "This is the first NFC pull request for the 3.12 release. With this one we have: - A few pn533 improvements and minor fixes. Testing our pn533 driver against Google's NCI stack triggered a few issues that we fixed now. We also added Tx fragmentation support to this driver. - More NFC secure element handling. We added a GET_SE netlink command for getting all the discovered secure elements, and we defined 2 additional secure element netlink event (transaction and connectivity). We also fixed a couple of typos and copy-paste bugs from the secure element handling code. - Firmware download support for the pn544 driver. This chipset can enter a special mode where it's waiting for firmware blobs to replace the already flashed one. We now support that mode." Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
commit
74ea1f4524
@ -5791,7 +5791,7 @@ M: Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
|
|||||||
M: Samuel Ortiz <sameo@linux.intel.com>
|
M: Samuel Ortiz <sameo@linux.intel.com>
|
||||||
L: linux-wireless@vger.kernel.org
|
L: linux-wireless@vger.kernel.org
|
||||||
L: linux-nfc@lists.01.org (moderated for non-subscribers)
|
L: linux-nfc@lists.01.org (moderated for non-subscribers)
|
||||||
S: Maintained
|
S: Supported
|
||||||
F: net/nfc/
|
F: net/nfc/
|
||||||
F: include/net/nfc/
|
F: include/net/nfc/
|
||||||
F: include/uapi/linux/nfc.h
|
F: include/uapi/linux/nfc.h
|
||||||
|
@ -60,7 +60,7 @@ struct nfcsim {
|
|||||||
static struct nfcsim *dev0;
|
static struct nfcsim *dev0;
|
||||||
static struct nfcsim *dev1;
|
static struct nfcsim *dev1;
|
||||||
|
|
||||||
struct workqueue_struct *wq;
|
static struct workqueue_struct *wq;
|
||||||
|
|
||||||
static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown)
|
static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown)
|
||||||
{
|
{
|
||||||
@ -481,7 +481,7 @@ static void nfcsim_free_device(struct nfcsim *dev)
|
|||||||
kfree(dev);
|
kfree(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __init nfcsim_init(void)
|
static int __init nfcsim_init(void)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@ -522,7 +522,7 @@ exit:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __exit nfcsim_exit(void)
|
static void __exit nfcsim_exit(void)
|
||||||
{
|
{
|
||||||
nfcsim_cleanup_dev(dev0, 1);
|
nfcsim_cleanup_dev(dev0, 1);
|
||||||
nfcsim_cleanup_dev(dev1, 1);
|
nfcsim_cleanup_dev(dev1, 1);
|
||||||
|
@ -83,12 +83,20 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
|
|||||||
|
|
||||||
/* How much time we spend listening for initiators */
|
/* How much time we spend listening for initiators */
|
||||||
#define PN533_LISTEN_TIME 2
|
#define PN533_LISTEN_TIME 2
|
||||||
|
/* Delay between each poll frame (ms) */
|
||||||
|
#define PN533_POLL_INTERVAL 10
|
||||||
|
|
||||||
/* Standard pn533 frame definitions */
|
/* Standard pn533 frame definitions (standard and extended)*/
|
||||||
#define PN533_STD_FRAME_HEADER_LEN (sizeof(struct pn533_std_frame) \
|
#define PN533_STD_FRAME_HEADER_LEN (sizeof(struct pn533_std_frame) \
|
||||||
+ 2) /* data[0] TFI, data[1] CC */
|
+ 2) /* data[0] TFI, data[1] CC */
|
||||||
#define PN533_STD_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/
|
#define PN533_STD_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/
|
||||||
|
|
||||||
|
#define PN533_EXT_FRAME_HEADER_LEN (sizeof(struct pn533_ext_frame) \
|
||||||
|
+ 2) /* data[0] TFI, data[1] CC */
|
||||||
|
|
||||||
|
#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
|
||||||
|
#define PN533_CMD_DATAFRAME_MAXLEN 240 /* max data length (send) */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Max extended frame payload len, excluding TFI and CC
|
* Max extended frame payload len, excluding TFI and CC
|
||||||
* which are already in PN533_FRAME_HEADER_LEN.
|
* which are already in PN533_FRAME_HEADER_LEN.
|
||||||
@ -99,6 +107,10 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
|
|||||||
Postamble (1) */
|
Postamble (1) */
|
||||||
#define PN533_STD_FRAME_CHECKSUM(f) (f->data[f->datalen])
|
#define PN533_STD_FRAME_CHECKSUM(f) (f->data[f->datalen])
|
||||||
#define PN533_STD_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1])
|
#define PN533_STD_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1])
|
||||||
|
/* Half start code (3), LEN (4) should be 0xffff for extended frame */
|
||||||
|
#define PN533_STD_IS_EXTENDED(hdr) ((hdr)->datalen == 0xFF \
|
||||||
|
&& (hdr)->datalen_checksum == 0xFF)
|
||||||
|
#define PN533_EXT_FRAME_CHECKSUM(f) (f->data[be16_to_cpu(f->datalen)])
|
||||||
|
|
||||||
/* start of frame */
|
/* start of frame */
|
||||||
#define PN533_STD_FRAME_SOF 0x00FF
|
#define PN533_STD_FRAME_SOF 0x00FF
|
||||||
@ -124,7 +136,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
|
|||||||
#define PN533_ACR122_RDR_TO_PC_ESCAPE 0x83
|
#define PN533_ACR122_RDR_TO_PC_ESCAPE 0x83
|
||||||
|
|
||||||
/* PN533 Commands */
|
/* PN533 Commands */
|
||||||
#define PN533_STD_FRAME_CMD(f) (f->data[1])
|
#define PN533_FRAME_CMD(f) (f->data[1])
|
||||||
|
|
||||||
#define PN533_CMD_GET_FIRMWARE_VERSION 0x02
|
#define PN533_CMD_GET_FIRMWARE_VERSION 0x02
|
||||||
#define PN533_CMD_RF_CONFIGURATION 0x32
|
#define PN533_CMD_RF_CONFIGURATION 0x32
|
||||||
@ -168,8 +180,9 @@ struct pn533_fw_version {
|
|||||||
#define PN533_CFGITEM_MAX_RETRIES 0x05
|
#define PN533_CFGITEM_MAX_RETRIES 0x05
|
||||||
#define PN533_CFGITEM_PASORI 0x82
|
#define PN533_CFGITEM_PASORI 0x82
|
||||||
|
|
||||||
#define PN533_CFGITEM_RF_FIELD_ON 0x1
|
#define PN533_CFGITEM_RF_FIELD_AUTO_RFCA 0x2
|
||||||
#define PN533_CFGITEM_RF_FIELD_OFF 0x0
|
#define PN533_CFGITEM_RF_FIELD_ON 0x1
|
||||||
|
#define PN533_CFGITEM_RF_FIELD_OFF 0x0
|
||||||
|
|
||||||
#define PN533_CONFIG_TIMING_102 0xb
|
#define PN533_CONFIG_TIMING_102 0xb
|
||||||
#define PN533_CONFIG_TIMING_204 0xc
|
#define PN533_CONFIG_TIMING_204 0xc
|
||||||
@ -257,7 +270,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
|
|||||||
.initiator_data.felica = {
|
.initiator_data.felica = {
|
||||||
.opcode = PN533_FELICA_OPC_SENSF_REQ,
|
.opcode = PN533_FELICA_OPC_SENSF_REQ,
|
||||||
.sc = PN533_FELICA_SENSF_SC_ALL,
|
.sc = PN533_FELICA_SENSF_SC_ALL,
|
||||||
.rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
|
.rc = PN533_FELICA_SENSF_RC_SYSTEM_CODE,
|
||||||
.tsn = 0x03,
|
.tsn = 0x03,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -270,7 +283,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
|
|||||||
.initiator_data.felica = {
|
.initiator_data.felica = {
|
||||||
.opcode = PN533_FELICA_OPC_SENSF_REQ,
|
.opcode = PN533_FELICA_OPC_SENSF_REQ,
|
||||||
.sc = PN533_FELICA_SENSF_SC_ALL,
|
.sc = PN533_FELICA_SENSF_SC_ALL,
|
||||||
.rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
|
.rc = PN533_FELICA_SENSF_RC_SYSTEM_CODE,
|
||||||
.tsn = 0x03,
|
.tsn = 0x03,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -352,13 +365,16 @@ struct pn533 {
|
|||||||
struct urb *in_urb;
|
struct urb *in_urb;
|
||||||
|
|
||||||
struct sk_buff_head resp_q;
|
struct sk_buff_head resp_q;
|
||||||
|
struct sk_buff_head fragment_skb;
|
||||||
|
|
||||||
struct workqueue_struct *wq;
|
struct workqueue_struct *wq;
|
||||||
struct work_struct cmd_work;
|
struct work_struct cmd_work;
|
||||||
struct work_struct cmd_complete_work;
|
struct work_struct cmd_complete_work;
|
||||||
struct work_struct poll_work;
|
struct delayed_work poll_work;
|
||||||
struct work_struct mi_work;
|
struct work_struct mi_rx_work;
|
||||||
|
struct work_struct mi_tx_work;
|
||||||
struct work_struct tg_work;
|
struct work_struct tg_work;
|
||||||
|
struct work_struct rf_work;
|
||||||
|
|
||||||
struct list_head cmd_queue;
|
struct list_head cmd_queue;
|
||||||
struct pn533_cmd *cmd;
|
struct pn533_cmd *cmd;
|
||||||
@ -366,6 +382,7 @@ struct pn533 {
|
|||||||
struct mutex cmd_lock; /* protects cmd queue */
|
struct mutex cmd_lock; /* protects cmd queue */
|
||||||
|
|
||||||
void *cmd_complete_mi_arg;
|
void *cmd_complete_mi_arg;
|
||||||
|
void *cmd_complete_dep_arg;
|
||||||
|
|
||||||
struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
|
struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
|
||||||
u8 poll_mod_count;
|
u8 poll_mod_count;
|
||||||
@ -404,6 +421,15 @@ struct pn533_std_frame {
|
|||||||
u8 data[];
|
u8 data[];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct pn533_ext_frame { /* Extended Information frame */
|
||||||
|
u8 preamble;
|
||||||
|
__be16 start_frame;
|
||||||
|
__be16 eif_flag; /* fixed to 0xFFFF */
|
||||||
|
__be16 datalen;
|
||||||
|
u8 datalen_checksum;
|
||||||
|
u8 data[];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
struct pn533_frame_ops {
|
struct pn533_frame_ops {
|
||||||
void (*tx_frame_init)(void *frame, u8 cmd_code);
|
void (*tx_frame_init)(void *frame, u8 cmd_code);
|
||||||
void (*tx_frame_finish)(void *frame);
|
void (*tx_frame_finish)(void *frame);
|
||||||
@ -411,7 +437,7 @@ struct pn533_frame_ops {
|
|||||||
int tx_header_len;
|
int tx_header_len;
|
||||||
int tx_tail_len;
|
int tx_tail_len;
|
||||||
|
|
||||||
bool (*rx_is_frame_valid)(void *frame);
|
bool (*rx_is_frame_valid)(void *frame, struct pn533 *dev);
|
||||||
int (*rx_frame_size)(void *frame);
|
int (*rx_frame_size)(void *frame);
|
||||||
int rx_header_len;
|
int rx_header_len;
|
||||||
int rx_tail_len;
|
int rx_tail_len;
|
||||||
@ -486,7 +512,7 @@ static void pn533_acr122_tx_update_payload_len(void *_frame, int len)
|
|||||||
frame->datalen += len;
|
frame->datalen += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool pn533_acr122_is_rx_frame_valid(void *_frame)
|
static bool pn533_acr122_is_rx_frame_valid(void *_frame, struct pn533 *dev)
|
||||||
{
|
{
|
||||||
struct pn533_acr122_rx_frame *frame = _frame;
|
struct pn533_acr122_rx_frame *frame = _frame;
|
||||||
|
|
||||||
@ -511,7 +537,7 @@ static u8 pn533_acr122_get_cmd_code(void *frame)
|
|||||||
{
|
{
|
||||||
struct pn533_acr122_rx_frame *f = frame;
|
struct pn533_acr122_rx_frame *f = frame;
|
||||||
|
|
||||||
return PN533_STD_FRAME_CMD(f);
|
return PN533_FRAME_CMD(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pn533_frame_ops pn533_acr122_frame_ops = {
|
static struct pn533_frame_ops pn533_acr122_frame_ops = {
|
||||||
@ -530,6 +556,12 @@ static struct pn533_frame_ops pn533_acr122_frame_ops = {
|
|||||||
.get_cmd_code = pn533_acr122_get_cmd_code,
|
.get_cmd_code = pn533_acr122_get_cmd_code,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* The rule: value(high byte) + value(low byte) + checksum = 0 */
|
||||||
|
static inline u8 pn533_ext_checksum(u16 value)
|
||||||
|
{
|
||||||
|
return ~(u8)(((value & 0xFF00) >> 8) + (u8)(value & 0xFF)) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* The rule: value + checksum = 0 */
|
/* The rule: value + checksum = 0 */
|
||||||
static inline u8 pn533_std_checksum(u8 value)
|
static inline u8 pn533_std_checksum(u8 value)
|
||||||
{
|
{
|
||||||
@ -555,7 +587,7 @@ static void pn533_std_tx_frame_init(void *_frame, u8 cmd_code)
|
|||||||
frame->preamble = 0;
|
frame->preamble = 0;
|
||||||
frame->start_frame = cpu_to_be16(PN533_STD_FRAME_SOF);
|
frame->start_frame = cpu_to_be16(PN533_STD_FRAME_SOF);
|
||||||
PN533_STD_FRAME_IDENTIFIER(frame) = PN533_STD_FRAME_DIR_OUT;
|
PN533_STD_FRAME_IDENTIFIER(frame) = PN533_STD_FRAME_DIR_OUT;
|
||||||
PN533_STD_FRAME_CMD(frame) = cmd_code;
|
PN533_FRAME_CMD(frame) = cmd_code;
|
||||||
frame->datalen = 2;
|
frame->datalen = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,21 +610,41 @@ static void pn533_std_tx_update_payload_len(void *_frame, int len)
|
|||||||
frame->datalen += len;
|
frame->datalen += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool pn533_std_rx_frame_is_valid(void *_frame)
|
static bool pn533_std_rx_frame_is_valid(void *_frame, struct pn533 *dev)
|
||||||
{
|
{
|
||||||
u8 checksum;
|
u8 checksum;
|
||||||
struct pn533_std_frame *frame = _frame;
|
struct pn533_std_frame *stdf = _frame;
|
||||||
|
|
||||||
if (frame->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF))
|
if (stdf->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
checksum = pn533_std_checksum(frame->datalen);
|
if (likely(!PN533_STD_IS_EXTENDED(stdf))) {
|
||||||
if (checksum != frame->datalen_checksum)
|
/* Standard frame code */
|
||||||
return false;
|
dev->ops->rx_header_len = PN533_STD_FRAME_HEADER_LEN;
|
||||||
|
|
||||||
checksum = pn533_std_data_checksum(frame->data, frame->datalen);
|
checksum = pn533_std_checksum(stdf->datalen);
|
||||||
if (checksum != PN533_STD_FRAME_CHECKSUM(frame))
|
if (checksum != stdf->datalen_checksum)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
checksum = pn533_std_data_checksum(stdf->data, stdf->datalen);
|
||||||
|
if (checksum != PN533_STD_FRAME_CHECKSUM(stdf))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
/* Extended */
|
||||||
|
struct pn533_ext_frame *eif = _frame;
|
||||||
|
|
||||||
|
dev->ops->rx_header_len = PN533_EXT_FRAME_HEADER_LEN;
|
||||||
|
|
||||||
|
checksum = pn533_ext_checksum(be16_to_cpu(eif->datalen));
|
||||||
|
if (checksum != eif->datalen_checksum)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* check data checksum */
|
||||||
|
checksum = pn533_std_data_checksum(eif->data,
|
||||||
|
be16_to_cpu(eif->datalen));
|
||||||
|
if (checksum != PN533_EXT_FRAME_CHECKSUM(eif))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -612,6 +664,14 @@ static inline int pn533_std_rx_frame_size(void *frame)
|
|||||||
{
|
{
|
||||||
struct pn533_std_frame *f = frame;
|
struct pn533_std_frame *f = frame;
|
||||||
|
|
||||||
|
/* check for Extended Information frame */
|
||||||
|
if (PN533_STD_IS_EXTENDED(f)) {
|
||||||
|
struct pn533_ext_frame *eif = frame;
|
||||||
|
|
||||||
|
return sizeof(struct pn533_ext_frame)
|
||||||
|
+ be16_to_cpu(eif->datalen) + PN533_STD_FRAME_TAIL_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
return sizeof(struct pn533_std_frame) + f->datalen +
|
return sizeof(struct pn533_std_frame) + f->datalen +
|
||||||
PN533_STD_FRAME_TAIL_LEN;
|
PN533_STD_FRAME_TAIL_LEN;
|
||||||
}
|
}
|
||||||
@ -619,8 +679,12 @@ static inline int pn533_std_rx_frame_size(void *frame)
|
|||||||
static u8 pn533_std_get_cmd_code(void *frame)
|
static u8 pn533_std_get_cmd_code(void *frame)
|
||||||
{
|
{
|
||||||
struct pn533_std_frame *f = frame;
|
struct pn533_std_frame *f = frame;
|
||||||
|
struct pn533_ext_frame *eif = frame;
|
||||||
|
|
||||||
return PN533_STD_FRAME_CMD(f);
|
if (PN533_STD_IS_EXTENDED(f))
|
||||||
|
return PN533_FRAME_CMD(eif);
|
||||||
|
else
|
||||||
|
return PN533_FRAME_CMD(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pn533_frame_ops pn533_std_frame_ops = {
|
static struct pn533_frame_ops pn533_std_frame_ops = {
|
||||||
@ -675,7 +739,7 @@ static void pn533_recv_response(struct urb *urb)
|
|||||||
print_hex_dump_debug("PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame,
|
print_hex_dump_debug("PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame,
|
||||||
dev->ops->rx_frame_size(in_frame), false);
|
dev->ops->rx_frame_size(in_frame), false);
|
||||||
|
|
||||||
if (!dev->ops->rx_is_frame_valid(in_frame)) {
|
if (!dev->ops->rx_is_frame_valid(in_frame, dev)) {
|
||||||
nfc_dev_err(&dev->interface->dev, "Received an invalid frame");
|
nfc_dev_err(&dev->interface->dev, "Received an invalid frame");
|
||||||
cmd->status = -EIO;
|
cmd->status = -EIO;
|
||||||
goto sched_wq;
|
goto sched_wq;
|
||||||
@ -1657,7 +1721,56 @@ static void pn533_listen_mode_timer(unsigned long data)
|
|||||||
|
|
||||||
pn533_poll_next_mod(dev);
|
pn533_poll_next_mod(dev);
|
||||||
|
|
||||||
queue_work(dev->wq, &dev->poll_work);
|
queue_delayed_work(dev->wq, &dev->poll_work,
|
||||||
|
msecs_to_jiffies(PN533_POLL_INTERVAL));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pn533_rf_complete(struct pn533 *dev, void *arg,
|
||||||
|
struct sk_buff *resp)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
|
||||||
|
|
||||||
|
if (IS_ERR(resp)) {
|
||||||
|
rc = PTR_ERR(resp);
|
||||||
|
|
||||||
|
nfc_dev_err(&dev->interface->dev, "%s RF setting error %d",
|
||||||
|
__func__, rc);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_delayed_work(dev->wq, &dev->poll_work,
|
||||||
|
msecs_to_jiffies(PN533_POLL_INTERVAL));
|
||||||
|
|
||||||
|
dev_kfree_skb(resp);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pn533_wq_rf(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct pn533 *dev = container_of(work, struct pn533, rf_work);
|
||||||
|
struct sk_buff *skb;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
|
||||||
|
|
||||||
|
skb = pn533_alloc_skb(dev, 2);
|
||||||
|
if (!skb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
*skb_put(skb, 1) = PN533_CFGITEM_RF_FIELD;
|
||||||
|
*skb_put(skb, 1) = PN533_CFGITEM_RF_FIELD_AUTO_RFCA;
|
||||||
|
|
||||||
|
rc = pn533_send_cmd_async(dev, PN533_CMD_RF_CONFIGURATION, skb,
|
||||||
|
pn533_rf_complete, NULL);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_kfree_skb(skb);
|
||||||
|
nfc_dev_err(&dev->interface->dev, "RF setting error %d", rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pn533_poll_complete(struct pn533 *dev, void *arg,
|
static int pn533_poll_complete(struct pn533 *dev, void *arg,
|
||||||
@ -1705,7 +1818,8 @@ static int pn533_poll_complete(struct pn533 *dev, void *arg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pn533_poll_next_mod(dev);
|
pn533_poll_next_mod(dev);
|
||||||
queue_work(dev->wq, &dev->poll_work);
|
/* Not target found, turn radio off */
|
||||||
|
queue_work(dev->wq, &dev->rf_work);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
dev_kfree_skb(resp);
|
dev_kfree_skb(resp);
|
||||||
@ -1770,7 +1884,7 @@ static int pn533_send_poll_frame(struct pn533 *dev)
|
|||||||
|
|
||||||
static void pn533_wq_poll(struct work_struct *work)
|
static void pn533_wq_poll(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct pn533 *dev = container_of(work, struct pn533, poll_work);
|
struct pn533 *dev = container_of(work, struct pn533, poll_work.work);
|
||||||
struct pn533_poll_modulations *cur_mod;
|
struct pn533_poll_modulations *cur_mod;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@ -1799,6 +1913,7 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
|
|||||||
u32 im_protocols, u32 tm_protocols)
|
u32 im_protocols, u32 tm_protocols)
|
||||||
{
|
{
|
||||||
struct pn533 *dev = nfc_get_drvdata(nfc_dev);
|
struct pn533 *dev = nfc_get_drvdata(nfc_dev);
|
||||||
|
u8 rand_mod;
|
||||||
|
|
||||||
nfc_dev_dbg(&dev->interface->dev,
|
nfc_dev_dbg(&dev->interface->dev,
|
||||||
"%s: im protocols 0x%x tm protocols 0x%x",
|
"%s: im protocols 0x%x tm protocols 0x%x",
|
||||||
@ -1822,11 +1937,15 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
|
|||||||
tm_protocols = 0;
|
tm_protocols = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->poll_mod_curr = 0;
|
|
||||||
pn533_poll_create_mod_list(dev, im_protocols, tm_protocols);
|
pn533_poll_create_mod_list(dev, im_protocols, tm_protocols);
|
||||||
dev->poll_protocols = im_protocols;
|
dev->poll_protocols = im_protocols;
|
||||||
dev->listen_protocols = tm_protocols;
|
dev->listen_protocols = tm_protocols;
|
||||||
|
|
||||||
|
/* Do not always start polling from the same modulation */
|
||||||
|
get_random_bytes(&rand_mod, sizeof(rand_mod));
|
||||||
|
rand_mod %= dev->poll_mod_count;
|
||||||
|
dev->poll_mod_curr = rand_mod;
|
||||||
|
|
||||||
return pn533_send_poll_frame(dev);
|
return pn533_send_poll_frame(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1845,6 +1964,7 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pn533_abort_cmd(dev, GFP_KERNEL);
|
pn533_abort_cmd(dev, GFP_KERNEL);
|
||||||
|
flush_delayed_work(&dev->poll_work);
|
||||||
pn533_poll_reset_mod_list(dev);
|
pn533_poll_reset_mod_list(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2037,28 +2157,15 @@ error:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pn533_mod_to_baud(struct pn533 *dev)
|
static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf);
|
||||||
{
|
|
||||||
switch (dev->poll_mod_curr) {
|
|
||||||
case PN533_POLL_MOD_106KBPS_A:
|
|
||||||
return 0;
|
|
||||||
case PN533_POLL_MOD_212KBPS_FELICA:
|
|
||||||
return 1;
|
|
||||||
case PN533_POLL_MOD_424KBPS_FELICA:
|
|
||||||
return 2;
|
|
||||||
default:
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define PASSIVE_DATA_LEN 5
|
#define PASSIVE_DATA_LEN 5
|
||||||
static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
|
static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
|
||||||
u8 comm_mode, u8 *gb, size_t gb_len)
|
u8 comm_mode, u8 *gb, size_t gb_len)
|
||||||
{
|
{
|
||||||
struct pn533 *dev = nfc_get_drvdata(nfc_dev);
|
struct pn533 *dev = nfc_get_drvdata(nfc_dev);
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int rc, baud, skb_len;
|
int rc, skb_len;
|
||||||
u8 *next, *arg;
|
u8 *next, *arg, nfcid3[NFC_NFCID3_MAXSIZE];
|
||||||
|
|
||||||
u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
|
u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
|
||||||
|
|
||||||
@ -2076,41 +2183,39 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
|
|||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
baud = pn533_mod_to_baud(dev);
|
|
||||||
if (baud < 0) {
|
|
||||||
nfc_dev_err(&dev->interface->dev,
|
|
||||||
"Invalid curr modulation %d", dev->poll_mod_curr);
|
|
||||||
return baud;
|
|
||||||
}
|
|
||||||
|
|
||||||
skb_len = 3 + gb_len; /* ActPass + BR + Next */
|
skb_len = 3 + gb_len; /* ActPass + BR + Next */
|
||||||
if (comm_mode == NFC_COMM_PASSIVE)
|
skb_len += PASSIVE_DATA_LEN;
|
||||||
skb_len += PASSIVE_DATA_LEN;
|
|
||||||
|
|
||||||
if (target && target->nfcid2_len)
|
/* NFCID3 */
|
||||||
skb_len += NFC_NFCID3_MAXSIZE;
|
skb_len += NFC_NFCID3_MAXSIZE;
|
||||||
|
if (target && !target->nfcid2_len) {
|
||||||
|
nfcid3[0] = 0x1;
|
||||||
|
nfcid3[1] = 0xfe;
|
||||||
|
get_random_bytes(nfcid3 + 2, 6);
|
||||||
|
}
|
||||||
|
|
||||||
skb = pn533_alloc_skb(dev, skb_len);
|
skb = pn533_alloc_skb(dev, skb_len);
|
||||||
if (!skb)
|
if (!skb)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
*skb_put(skb, 1) = !comm_mode; /* ActPass */
|
*skb_put(skb, 1) = !comm_mode; /* ActPass */
|
||||||
*skb_put(skb, 1) = baud; /* Baud rate */
|
*skb_put(skb, 1) = 0x02; /* 424 kbps */
|
||||||
|
|
||||||
next = skb_put(skb, 1); /* Next */
|
next = skb_put(skb, 1); /* Next */
|
||||||
*next = 0;
|
*next = 0;
|
||||||
|
|
||||||
if (comm_mode == NFC_COMM_PASSIVE && baud > 0) {
|
/* Copy passive data */
|
||||||
memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data,
|
memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data, PASSIVE_DATA_LEN);
|
||||||
PASSIVE_DATA_LEN);
|
*next |= 1;
|
||||||
*next |= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target && target->nfcid2_len) {
|
/* Copy NFCID3 (which is NFCID2 from SENSF_RES) */
|
||||||
|
if (target && target->nfcid2_len)
|
||||||
memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), target->nfcid2,
|
memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), target->nfcid2,
|
||||||
target->nfcid2_len);
|
target->nfcid2_len);
|
||||||
*next |= 2;
|
else
|
||||||
}
|
memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), nfcid3,
|
||||||
|
NFC_NFCID3_MAXSIZE);
|
||||||
|
*next |= 2;
|
||||||
|
|
||||||
if (gb != NULL && gb_len > 0) {
|
if (gb != NULL && gb_len > 0) {
|
||||||
memcpy(skb_put(skb, gb_len), gb, gb_len);
|
memcpy(skb_put(skb, gb_len), gb, gb_len);
|
||||||
@ -2127,6 +2232,8 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
|
|||||||
|
|
||||||
*arg = !comm_mode;
|
*arg = !comm_mode;
|
||||||
|
|
||||||
|
pn533_rf_field(dev->nfc_dev, 0);
|
||||||
|
|
||||||
rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb,
|
rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb,
|
||||||
pn533_in_dep_link_up_complete, arg);
|
pn533_in_dep_link_up_complete, arg);
|
||||||
|
|
||||||
@ -2232,7 +2339,15 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
|
|||||||
|
|
||||||
if (mi) {
|
if (mi) {
|
||||||
dev->cmd_complete_mi_arg = arg;
|
dev->cmd_complete_mi_arg = arg;
|
||||||
queue_work(dev->wq, &dev->mi_work);
|
queue_work(dev->wq, &dev->mi_rx_work);
|
||||||
|
return -EINPROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare for the next round */
|
||||||
|
if (skb_queue_len(&dev->fragment_skb) > 0) {
|
||||||
|
dev->cmd_complete_dep_arg = arg;
|
||||||
|
queue_work(dev->wq, &dev->mi_tx_work);
|
||||||
|
|
||||||
return -EINPROGRESS;
|
return -EINPROGRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2253,6 +2368,50 @@ _error:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Split the Tx skb into small chunks */
|
||||||
|
static int pn533_fill_fragment_skbs(struct pn533 *dev, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct sk_buff *frag;
|
||||||
|
int frag_size;
|
||||||
|
|
||||||
|
do {
|
||||||
|
/* Remaining size */
|
||||||
|
if (skb->len > PN533_CMD_DATAFRAME_MAXLEN)
|
||||||
|
frag_size = PN533_CMD_DATAFRAME_MAXLEN;
|
||||||
|
else
|
||||||
|
frag_size = skb->len;
|
||||||
|
|
||||||
|
/* Allocate and reserve */
|
||||||
|
frag = pn533_alloc_skb(dev, frag_size);
|
||||||
|
if (!frag) {
|
||||||
|
skb_queue_purge(&dev->fragment_skb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reserve the TG/MI byte */
|
||||||
|
skb_reserve(frag, 1);
|
||||||
|
|
||||||
|
/* MI + TG */
|
||||||
|
if (frag_size == PN533_CMD_DATAFRAME_MAXLEN)
|
||||||
|
*skb_push(frag, sizeof(u8)) = (PN533_CMD_MI_MASK | 1);
|
||||||
|
else
|
||||||
|
*skb_push(frag, sizeof(u8)) = 1; /* TG */
|
||||||
|
|
||||||
|
memcpy(skb_put(frag, frag_size), skb->data, frag_size);
|
||||||
|
|
||||||
|
/* Reduce the size of incoming buffer */
|
||||||
|
skb_pull(skb, frag_size);
|
||||||
|
|
||||||
|
/* Add this to skb_queue */
|
||||||
|
skb_queue_tail(&dev->fragment_skb, frag);
|
||||||
|
|
||||||
|
} while (skb->len > 0);
|
||||||
|
|
||||||
|
dev_kfree_skb(skb);
|
||||||
|
|
||||||
|
return skb_queue_len(&dev->fragment_skb);
|
||||||
|
}
|
||||||
|
|
||||||
static int pn533_transceive(struct nfc_dev *nfc_dev,
|
static int pn533_transceive(struct nfc_dev *nfc_dev,
|
||||||
struct nfc_target *target, struct sk_buff *skb,
|
struct nfc_target *target, struct sk_buff *skb,
|
||||||
data_exchange_cb_t cb, void *cb_context)
|
data_exchange_cb_t cb, void *cb_context)
|
||||||
@ -2263,15 +2422,6 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
|
|||||||
|
|
||||||
nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
|
nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
|
||||||
|
|
||||||
if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
|
|
||||||
/* TODO: Implement support to multi-part data exchange */
|
|
||||||
nfc_dev_err(&dev->interface->dev,
|
|
||||||
"Data length greater than the max allowed: %d",
|
|
||||||
PN533_CMD_DATAEXCH_DATA_MAXLEN);
|
|
||||||
rc = -ENOSYS;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dev->tgt_active_prot) {
|
if (!dev->tgt_active_prot) {
|
||||||
nfc_dev_err(&dev->interface->dev,
|
nfc_dev_err(&dev->interface->dev,
|
||||||
"Can't exchange data if there is no active target");
|
"Can't exchange data if there is no active target");
|
||||||
@ -2299,7 +2449,20 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
*skb_push(skb, sizeof(u8)) = 1; /*TG*/
|
/* jumbo frame ? */
|
||||||
|
if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
|
||||||
|
rc = pn533_fill_fragment_skbs(dev, skb);
|
||||||
|
if (rc <= 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
skb = skb_dequeue(&dev->fragment_skb);
|
||||||
|
if (!skb) {
|
||||||
|
rc = -EIO;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*skb_push(skb, sizeof(u8)) = 1; /* TG */
|
||||||
|
}
|
||||||
|
|
||||||
rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE,
|
rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE,
|
||||||
skb, pn533_data_exchange_complete,
|
skb, pn533_data_exchange_complete,
|
||||||
@ -2370,7 +2533,7 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
|
|||||||
|
|
||||||
static void pn533_wq_mi_recv(struct work_struct *work)
|
static void pn533_wq_mi_recv(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct pn533 *dev = container_of(work, struct pn533, mi_work);
|
struct pn533 *dev = container_of(work, struct pn533, mi_rx_work);
|
||||||
|
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int rc;
|
int rc;
|
||||||
@ -2418,6 +2581,61 @@ error:
|
|||||||
queue_work(dev->wq, &dev->cmd_work);
|
queue_work(dev->wq, &dev->cmd_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pn533_wq_mi_send(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct pn533 *dev = container_of(work, struct pn533, mi_tx_work);
|
||||||
|
struct sk_buff *skb;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
|
||||||
|
|
||||||
|
/* Grab the first skb in the queue */
|
||||||
|
skb = skb_dequeue(&dev->fragment_skb);
|
||||||
|
|
||||||
|
if (skb == NULL) { /* No more data */
|
||||||
|
/* Reset the queue for future use */
|
||||||
|
skb_queue_head_init(&dev->fragment_skb);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (dev->device_type) {
|
||||||
|
case PN533_DEVICE_PASORI:
|
||||||
|
if (dev->tgt_active_prot != NFC_PROTO_FELICA) {
|
||||||
|
rc = -EIO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = pn533_send_cmd_direct_async(dev, PN533_CMD_IN_COMM_THRU,
|
||||||
|
skb,
|
||||||
|
pn533_data_exchange_complete,
|
||||||
|
dev->cmd_complete_dep_arg);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Still some fragments? */
|
||||||
|
rc = pn533_send_cmd_direct_async(dev,PN533_CMD_IN_DATA_EXCHANGE,
|
||||||
|
skb,
|
||||||
|
pn533_data_exchange_complete,
|
||||||
|
dev->cmd_complete_dep_arg);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == 0) /* success */
|
||||||
|
return;
|
||||||
|
|
||||||
|
nfc_dev_err(&dev->interface->dev,
|
||||||
|
"Error %d when trying to perform data_exchange", rc);
|
||||||
|
|
||||||
|
dev_kfree_skb(skb);
|
||||||
|
kfree(dev->cmd_complete_dep_arg);
|
||||||
|
|
||||||
|
error:
|
||||||
|
pn533_send_ack(dev, GFP_KERNEL);
|
||||||
|
queue_work(dev->wq, &dev->cmd_work);
|
||||||
|
}
|
||||||
|
|
||||||
static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
|
static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
|
||||||
u8 cfgdata_len)
|
u8 cfgdata_len)
|
||||||
{
|
{
|
||||||
@ -2562,6 +2780,8 @@ static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf)
|
|||||||
u8 rf_field = !!rf;
|
u8 rf_field = !!rf;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
rf_field |= PN533_CFGITEM_RF_FIELD_AUTO_RFCA;
|
||||||
|
|
||||||
rc = pn533_set_configuration(dev, PN533_CFGITEM_RF_FIELD,
|
rc = pn533_set_configuration(dev, PN533_CFGITEM_RF_FIELD,
|
||||||
(u8 *)&rf_field, 1);
|
(u8 *)&rf_field, 1);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
@ -2605,17 +2825,6 @@ static int pn533_setup(struct pn533 *dev)
|
|||||||
|
|
||||||
switch (dev->device_type) {
|
switch (dev->device_type) {
|
||||||
case PN533_DEVICE_STD:
|
case PN533_DEVICE_STD:
|
||||||
max_retries.mx_rty_atr = PN533_CONFIG_MAX_RETRIES_ENDLESS;
|
|
||||||
max_retries.mx_rty_psl = 2;
|
|
||||||
max_retries.mx_rty_passive_act =
|
|
||||||
PN533_CONFIG_MAX_RETRIES_NO_RETRY;
|
|
||||||
|
|
||||||
timing.rfu = PN533_CONFIG_TIMING_102;
|
|
||||||
timing.atr_res_timeout = PN533_CONFIG_TIMING_204;
|
|
||||||
timing.dep_timeout = PN533_CONFIG_TIMING_409;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PN533_DEVICE_PASORI:
|
case PN533_DEVICE_PASORI:
|
||||||
case PN533_DEVICE_ACR122U:
|
case PN533_DEVICE_ACR122U:
|
||||||
max_retries.mx_rty_atr = 0x2;
|
max_retries.mx_rty_atr = 0x2;
|
||||||
@ -2729,9 +2938,11 @@ static int pn533_probe(struct usb_interface *interface,
|
|||||||
|
|
||||||
INIT_WORK(&dev->cmd_work, pn533_wq_cmd);
|
INIT_WORK(&dev->cmd_work, pn533_wq_cmd);
|
||||||
INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete);
|
INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete);
|
||||||
INIT_WORK(&dev->mi_work, pn533_wq_mi_recv);
|
INIT_WORK(&dev->mi_rx_work, pn533_wq_mi_recv);
|
||||||
|
INIT_WORK(&dev->mi_tx_work, pn533_wq_mi_send);
|
||||||
INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
|
INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
|
||||||
INIT_WORK(&dev->poll_work, pn533_wq_poll);
|
INIT_DELAYED_WORK(&dev->poll_work, pn533_wq_poll);
|
||||||
|
INIT_WORK(&dev->rf_work, pn533_wq_rf);
|
||||||
dev->wq = alloc_ordered_workqueue("pn533", 0);
|
dev->wq = alloc_ordered_workqueue("pn533", 0);
|
||||||
if (dev->wq == NULL)
|
if (dev->wq == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
@ -2741,6 +2952,7 @@ static int pn533_probe(struct usb_interface *interface,
|
|||||||
dev->listen_timer.function = pn533_listen_mode_timer;
|
dev->listen_timer.function = pn533_listen_mode_timer;
|
||||||
|
|
||||||
skb_queue_head_init(&dev->resp_q);
|
skb_queue_head_init(&dev->resp_q);
|
||||||
|
skb_queue_head_init(&dev->fragment_skb);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&dev->cmd_queue);
|
INIT_LIST_HEAD(&dev->cmd_queue);
|
||||||
|
|
||||||
@ -2842,6 +3054,7 @@ static void pn533_disconnect(struct usb_interface *interface)
|
|||||||
usb_kill_urb(dev->in_urb);
|
usb_kill_urb(dev->in_urb);
|
||||||
usb_kill_urb(dev->out_urb);
|
usb_kill_urb(dev->out_urb);
|
||||||
|
|
||||||
|
flush_delayed_work(&dev->poll_work);
|
||||||
destroy_workqueue(dev->wq);
|
destroy_workqueue(dev->wq);
|
||||||
|
|
||||||
skb_queue_purge(&dev->resp_q);
|
skb_queue_purge(&dev->resp_q);
|
||||||
|
@ -25,11 +25,14 @@
|
|||||||
#include <linux/miscdevice.h>
|
#include <linux/miscdevice.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/delay.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 <linux/platform_data/pn544.h>
|
||||||
|
|
||||||
#include <net/nfc/hci.h>
|
#include <net/nfc/hci.h>
|
||||||
#include <net/nfc/llc.h>
|
#include <net/nfc/llc.h>
|
||||||
|
#include <net/nfc/nfc.h>
|
||||||
|
|
||||||
#include "pn544.h"
|
#include "pn544.h"
|
||||||
|
|
||||||
@ -55,6 +58,58 @@ MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table);
|
|||||||
|
|
||||||
#define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c"
|
#define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c"
|
||||||
|
|
||||||
|
#define PN544_FW_CMD_WRITE 0x08
|
||||||
|
#define PN544_FW_CMD_CHECK 0x06
|
||||||
|
|
||||||
|
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[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#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_INVALID_LENGTH 0x18
|
||||||
|
#define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74
|
||||||
|
|
||||||
|
#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 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
|
||||||
|
|
||||||
struct pn544_i2c_phy {
|
struct pn544_i2c_phy {
|
||||||
struct i2c_client *i2c_dev;
|
struct i2c_client *i2c_dev;
|
||||||
struct nfc_hci_dev *hdev;
|
struct nfc_hci_dev *hdev;
|
||||||
@ -64,7 +119,18 @@ struct pn544_i2c_phy {
|
|||||||
unsigned int gpio_fw;
|
unsigned int gpio_fw;
|
||||||
unsigned int en_polarity;
|
unsigned int en_polarity;
|
||||||
|
|
||||||
|
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;
|
||||||
|
int fw_cmd_result;
|
||||||
|
|
||||||
int powered;
|
int powered;
|
||||||
|
int run_mode;
|
||||||
|
|
||||||
int hard_fault; /*
|
int hard_fault; /*
|
||||||
* < 0 if hardware error occured (e.g. i2c err)
|
* < 0 if hardware error occured (e.g. i2c err)
|
||||||
@ -122,15 +188,22 @@ out:
|
|||||||
gpio_set_value(phy->gpio_en, !phy->en_polarity);
|
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)
|
static int pn544_hci_i2c_enable(void *phy_id)
|
||||||
{
|
{
|
||||||
struct pn544_i2c_phy *phy = phy_id;
|
struct pn544_i2c_phy *phy = phy_id;
|
||||||
|
|
||||||
pr_info(DRIVER_DESC ": %s\n", __func__);
|
pr_info(DRIVER_DESC ": %s\n", __func__);
|
||||||
|
|
||||||
gpio_set_value(phy->gpio_fw, 0);
|
pn544_hci_i2c_enable_mode(phy, PN544_HCI_MODE);
|
||||||
gpio_set_value(phy->gpio_en, phy->en_polarity);
|
|
||||||
usleep_range(10000, 15000);
|
|
||||||
|
|
||||||
phy->powered = 1;
|
phy->powered = 1;
|
||||||
|
|
||||||
@ -305,6 +378,42 @@ flush:
|
|||||||
return r;
|
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)) {
|
||||||
|
dev_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_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_INVALID_LENGTH:
|
||||||
|
return -EBADMSG;
|
||||||
|
case PN544_FW_CMD_RESULT_WRITE_FAILED:
|
||||||
|
return -EIO;
|
||||||
|
default:
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reads an shdlc frame from the chip. This is not as straightforward as it
|
* 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.
|
* seems. There are cases where we could loose the frame start synchronization.
|
||||||
@ -339,19 +448,23 @@ static irqreturn_t pn544_hci_i2c_irq_thread_fn(int irq, void *phy_id)
|
|||||||
if (phy->hard_fault != 0)
|
if (phy->hard_fault != 0)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
r = pn544_hci_i2c_read(phy, &skb);
|
if (phy->run_mode == PN544_FW_MODE) {
|
||||||
if (r == -EREMOTEIO) {
|
phy->fw_cmd_result = pn544_hci_i2c_fw_read_status(phy);
|
||||||
phy->hard_fault = r;
|
schedule_work(&phy->fw_work);
|
||||||
|
} else {
|
||||||
|
r = pn544_hci_i2c_read(phy, &skb);
|
||||||
|
if (r == -EREMOTEIO) {
|
||||||
|
phy->hard_fault = r;
|
||||||
|
|
||||||
nfc_hci_recv_frame(phy->hdev, NULL);
|
nfc_hci_recv_frame(phy->hdev, NULL);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
} else if ((r == -ENOMEM) || (r == -EBADMSG)) {
|
} else if ((r == -ENOMEM) || (r == -EBADMSG)) {
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
nfc_hci_recv_frame(phy->hdev, skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
nfc_hci_recv_frame(phy->hdev, skb);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,6 +474,215 @@ static struct nfc_phy_ops i2c_phy_ops = {
|
|||||||
.disable = pn544_hci_i2c_disable,
|
.disable = pn544_hci_i2c_disable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name)
|
||||||
|
{
|
||||||
|
struct pn544_i2c_phy *phy = phy_id;
|
||||||
|
|
||||||
|
pr_info(DRIVER_DESC ": Starting Firmware Download (%s)\n",
|
||||||
|
firmware_name);
|
||||||
|
|
||||||
|
strcpy(phy->firmware_name, firmware_name);
|
||||||
|
|
||||||
|
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(DRIVER_DESC ": 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 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
phy->fw_written = 0;
|
||||||
|
r = pn544_hci_i2c_fw_write_chunk(phy);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int pn544_hci_i2c_probe(struct i2c_client *client,
|
static int pn544_hci_i2c_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
@ -384,6 +706,9 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work);
|
||||||
|
phy->fw_work_state = FW_WORK_STATE_IDLE;
|
||||||
|
|
||||||
phy->i2c_dev = client;
|
phy->i2c_dev = client;
|
||||||
i2c_set_clientdata(client, phy);
|
i2c_set_clientdata(client, phy);
|
||||||
|
|
||||||
@ -420,7 +745,8 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
r = pn544_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
|
r = pn544_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
|
||||||
PN544_I2C_FRAME_HEADROOM, PN544_I2C_FRAME_TAILROOM,
|
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)
|
if (r < 0)
|
||||||
goto err_hci;
|
goto err_hci;
|
||||||
|
|
||||||
@ -443,6 +769,10 @@ static int pn544_hci_i2c_remove(struct i2c_client *client)
|
|||||||
|
|
||||||
dev_dbg(&client->dev, "%s\n", __func__);
|
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);
|
pn544_hci_remove(phy->hdev);
|
||||||
|
|
||||||
if (phy->powered)
|
if (phy->powered)
|
||||||
|
@ -45,7 +45,7 @@ static int pn544_mei_probe(struct mei_cl_device *device,
|
|||||||
|
|
||||||
r = pn544_hci_probe(phy, &mei_phy_ops, LLC_NOP_NAME,
|
r = pn544_hci_probe(phy, &mei_phy_ops, LLC_NOP_NAME,
|
||||||
MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD,
|
MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD,
|
||||||
&phy->hdev);
|
NULL, &phy->hdev);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
nfc_mei_phy_free(phy);
|
nfc_mei_phy_free(phy);
|
||||||
|
|
||||||
|
@ -31,9 +31,6 @@
|
|||||||
/* Timing restrictions (ms) */
|
/* Timing restrictions (ms) */
|
||||||
#define PN544_HCI_RESETVEN_TIME 30
|
#define PN544_HCI_RESETVEN_TIME 30
|
||||||
|
|
||||||
#define HCI_MODE 0
|
|
||||||
#define FW_MODE 1
|
|
||||||
|
|
||||||
enum pn544_state {
|
enum pn544_state {
|
||||||
PN544_ST_COLD,
|
PN544_ST_COLD,
|
||||||
PN544_ST_FW_READY,
|
PN544_ST_FW_READY,
|
||||||
@ -130,6 +127,8 @@ struct pn544_hci_info {
|
|||||||
int async_cb_type;
|
int async_cb_type;
|
||||||
data_exchange_cb_t async_cb;
|
data_exchange_cb_t async_cb;
|
||||||
void *async_cb_context;
|
void *async_cb_context;
|
||||||
|
|
||||||
|
fw_download_t fw_download;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int pn544_hci_open(struct nfc_hci_dev *hdev)
|
static int pn544_hci_open(struct nfc_hci_dev *hdev)
|
||||||
@ -782,6 +781,17 @@ exit:
|
|||||||
return r;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
static struct nfc_hci_ops pn544_hci_ops = {
|
static struct nfc_hci_ops pn544_hci_ops = {
|
||||||
.open = pn544_hci_open,
|
.open = pn544_hci_open,
|
||||||
.close = pn544_hci_close,
|
.close = pn544_hci_close,
|
||||||
@ -796,11 +806,12 @@ static struct nfc_hci_ops pn544_hci_ops = {
|
|||||||
.tm_send = pn544_hci_tm_send,
|
.tm_send = pn544_hci_tm_send,
|
||||||
.check_presence = pn544_hci_check_presence,
|
.check_presence = pn544_hci_check_presence,
|
||||||
.event_received = pn544_hci_event_received,
|
.event_received = pn544_hci_event_received,
|
||||||
|
.fw_download = pn544_hci_fw_download,
|
||||||
};
|
};
|
||||||
|
|
||||||
int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
|
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,
|
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;
|
struct pn544_hci_info *info;
|
||||||
u32 protocols;
|
u32 protocols;
|
||||||
@ -816,6 +827,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
|
|||||||
|
|
||||||
info->phy_ops = phy_ops;
|
info->phy_ops = phy_ops;
|
||||||
info->phy_id = phy_id;
|
info->phy_id = phy_id;
|
||||||
|
info->fw_download = fw_download;
|
||||||
info->state = PN544_ST_COLD;
|
info->state = PN544_ST_COLD;
|
||||||
mutex_init(&info->info_lock);
|
mutex_init(&info->info_lock);
|
||||||
|
|
||||||
|
@ -24,9 +24,14 @@
|
|||||||
|
|
||||||
#define DRIVER_DESC "HCI NFC driver for PN544"
|
#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);
|
||||||
|
|
||||||
int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
|
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,
|
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);
|
void pn544_hci_remove(struct nfc_hci_dev *hdev);
|
||||||
|
|
||||||
#endif /* __LOCAL_PN544_H_ */
|
#endif /* __LOCAL_PN544_H_ */
|
||||||
|
@ -224,6 +224,9 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev,
|
|||||||
u8 *gt, u8 gt_len);
|
u8 *gt, u8 gt_len);
|
||||||
u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len);
|
u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len);
|
||||||
|
|
||||||
|
int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
|
||||||
|
u32 result);
|
||||||
|
|
||||||
int nfc_targets_found(struct nfc_dev *dev,
|
int nfc_targets_found(struct nfc_dev *dev,
|
||||||
struct nfc_target *targets, int ntargets);
|
struct nfc_target *targets, int ntargets);
|
||||||
int nfc_target_lost(struct nfc_dev *dev, u32 target_idx);
|
int nfc_target_lost(struct nfc_dev *dev, u32 target_idx);
|
||||||
|
@ -71,6 +71,20 @@
|
|||||||
* @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element.
|
* @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element.
|
||||||
* @NFC_CMD_FW_DOWNLOAD: Request to Load/flash firmware, or event to inform
|
* @NFC_CMD_FW_DOWNLOAD: Request to Load/flash firmware, or event to inform
|
||||||
* that some firmware was loaded
|
* that some firmware was loaded
|
||||||
|
* @NFC_EVENT_SE_ADDED: Event emitted when a new secure element is discovered.
|
||||||
|
* This typically will be sent whenever a new NFC controller with either
|
||||||
|
* an embedded SE or an UICC one connected to it through SWP.
|
||||||
|
* @NFC_EVENT_SE_REMOVED: Event emitted when a secure element is removed from
|
||||||
|
* the system, as a consequence of e.g. an NFC controller being unplugged.
|
||||||
|
* @NFC_EVENT_SE_CONNECTIVITY: This event is emitted whenever a secure element
|
||||||
|
* is requesting connectivity access. For example a UICC SE may need to
|
||||||
|
* talk with a sleeping modem and will notify this need by sending this
|
||||||
|
* event. It is then up to userspace to decide if it will wake the modem
|
||||||
|
* up or not.
|
||||||
|
* @NFC_EVENT_SE_TRANSACTION: This event is sent when an application running on
|
||||||
|
* a specific SE notifies us about the end of a transaction. The parameter
|
||||||
|
* for this event is the application ID (AID).
|
||||||
|
* @NFC_CMD_GET_SE: Dump all discovered secure elements from an NFC controller.
|
||||||
*/
|
*/
|
||||||
enum nfc_commands {
|
enum nfc_commands {
|
||||||
NFC_CMD_UNSPEC,
|
NFC_CMD_UNSPEC,
|
||||||
@ -97,6 +111,9 @@ enum nfc_commands {
|
|||||||
NFC_CMD_FW_DOWNLOAD,
|
NFC_CMD_FW_DOWNLOAD,
|
||||||
NFC_EVENT_SE_ADDED,
|
NFC_EVENT_SE_ADDED,
|
||||||
NFC_EVENT_SE_REMOVED,
|
NFC_EVENT_SE_REMOVED,
|
||||||
|
NFC_EVENT_SE_CONNECTIVITY,
|
||||||
|
NFC_EVENT_SE_TRANSACTION,
|
||||||
|
NFC_CMD_GET_SE,
|
||||||
/* private: internal use only */
|
/* private: internal use only */
|
||||||
__NFC_CMD_AFTER_LAST
|
__NFC_CMD_AFTER_LAST
|
||||||
};
|
};
|
||||||
@ -129,6 +146,7 @@ enum nfc_commands {
|
|||||||
* @NFC_ATTR_FIRMWARE_NAME: Free format firmware version
|
* @NFC_ATTR_FIRMWARE_NAME: Free format firmware version
|
||||||
* @NFC_ATTR_SE_INDEX: Secure element index
|
* @NFC_ATTR_SE_INDEX: Secure element index
|
||||||
* @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED)
|
* @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED)
|
||||||
|
* @NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS: Firmware download operation status
|
||||||
*/
|
*/
|
||||||
enum nfc_attrs {
|
enum nfc_attrs {
|
||||||
NFC_ATTR_UNSPEC,
|
NFC_ATTR_UNSPEC,
|
||||||
@ -154,6 +172,8 @@ enum nfc_attrs {
|
|||||||
NFC_ATTR_FIRMWARE_NAME,
|
NFC_ATTR_FIRMWARE_NAME,
|
||||||
NFC_ATTR_SE_INDEX,
|
NFC_ATTR_SE_INDEX,
|
||||||
NFC_ATTR_SE_TYPE,
|
NFC_ATTR_SE_TYPE,
|
||||||
|
NFC_ATTR_SE_AID,
|
||||||
|
NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS,
|
||||||
/* private: internal use only */
|
/* private: internal use only */
|
||||||
__NFC_ATTR_AFTER_LAST
|
__NFC_ATTR_AFTER_LAST
|
||||||
};
|
};
|
||||||
|
@ -77,11 +77,19 @@ error:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name)
|
/**
|
||||||
|
* nfc_fw_download_done - inform that a firmware download was completed
|
||||||
|
*
|
||||||
|
* @dev: The nfc device to which firmware was downloaded
|
||||||
|
* @firmware_name: The firmware filename
|
||||||
|
* @result: The positive value of a standard errno value
|
||||||
|
*/
|
||||||
|
int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
|
||||||
|
u32 result)
|
||||||
{
|
{
|
||||||
dev->fw_download_in_progress = false;
|
dev->fw_download_in_progress = false;
|
||||||
|
|
||||||
return nfc_genl_fw_download_done(dev, firmware_name);
|
return nfc_genl_fw_download_done(dev, firmware_name, result);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(nfc_fw_download_done);
|
EXPORT_SYMBOL(nfc_fw_download_done);
|
||||||
|
|
||||||
@ -129,7 +137,7 @@ int nfc_dev_up(struct nfc_dev *dev)
|
|||||||
/* We have to enable the device before discovering SEs */
|
/* We have to enable the device before discovering SEs */
|
||||||
if (dev->ops->discover_se) {
|
if (dev->ops->discover_se) {
|
||||||
rc = dev->ops->discover_se(dev);
|
rc = dev->ops->discover_se(dev);
|
||||||
if (!rc)
|
if (rc)
|
||||||
pr_warn("SE discovery failed\n");
|
pr_warn("SE discovery failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,12 +583,14 @@ int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (se->type == NFC_SE_ENABLED) {
|
if (se->state == NFC_SE_ENABLED) {
|
||||||
rc = -EALREADY;
|
rc = -EALREADY;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = dev->ops->enable_se(dev, se_idx);
|
rc = dev->ops->enable_se(dev, se_idx);
|
||||||
|
if (rc >= 0)
|
||||||
|
se->state = NFC_SE_ENABLED;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
device_unlock(&dev->dev);
|
device_unlock(&dev->dev);
|
||||||
@ -618,12 +628,14 @@ int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (se->type == NFC_SE_DISABLED) {
|
if (se->state == NFC_SE_DISABLED) {
|
||||||
rc = -EALREADY;
|
rc = -EALREADY;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = dev->ops->disable_se(dev, se_idx);
|
rc = dev->ops->disable_se(dev, se_idx);
|
||||||
|
if (rc >= 0)
|
||||||
|
se->state = NFC_SE_DISABLED;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
device_unlock(&dev->dev);
|
device_unlock(&dev->dev);
|
||||||
|
@ -717,7 +717,7 @@ static int hci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
|
|||||||
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
|
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
|
||||||
|
|
||||||
if (hdev->ops->disable_se)
|
if (hdev->ops->disable_se)
|
||||||
return hdev->ops->enable_se(hdev, se_idx);
|
return hdev->ops->disable_se(hdev, se_idx);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1114,7 +1114,8 @@ static int nfc_genl_fw_download(struct sk_buff *skb, struct genl_info *info)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name)
|
int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
|
||||||
|
u32 result)
|
||||||
{
|
{
|
||||||
struct sk_buff *msg;
|
struct sk_buff *msg;
|
||||||
void *hdr;
|
void *hdr;
|
||||||
@ -1129,6 +1130,7 @@ int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name)
|
|||||||
goto free_msg;
|
goto free_msg;
|
||||||
|
|
||||||
if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) ||
|
if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) ||
|
||||||
|
nla_put_u32(msg, NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS, result) ||
|
||||||
nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
|
nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
||||||
@ -1191,6 +1193,91 @@ static int nfc_genl_disable_se(struct sk_buff *skb, struct genl_info *info)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nfc_genl_send_se(struct sk_buff *msg, struct nfc_dev *dev,
|
||||||
|
u32 portid, u32 seq,
|
||||||
|
struct netlink_callback *cb,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
void *hdr;
|
||||||
|
struct nfc_se *se, *n;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(se, n, &dev->secure_elements, list) {
|
||||||
|
hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, flags,
|
||||||
|
NFC_CMD_GET_SE);
|
||||||
|
if (!hdr)
|
||||||
|
goto nla_put_failure;
|
||||||
|
|
||||||
|
if (cb)
|
||||||
|
genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
|
||||||
|
|
||||||
|
if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
|
||||||
|
nla_put_u32(msg, NFC_ATTR_SE_INDEX, se->idx) ||
|
||||||
|
nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type))
|
||||||
|
goto nla_put_failure;
|
||||||
|
|
||||||
|
if (genlmsg_end(msg, hdr) < 0)
|
||||||
|
goto nla_put_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nla_put_failure:
|
||||||
|
genlmsg_cancel(msg, hdr);
|
||||||
|
return -EMSGSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nfc_genl_dump_ses(struct sk_buff *skb,
|
||||||
|
struct netlink_callback *cb)
|
||||||
|
{
|
||||||
|
struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
|
||||||
|
struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
|
||||||
|
bool first_call = false;
|
||||||
|
|
||||||
|
if (!iter) {
|
||||||
|
first_call = true;
|
||||||
|
iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL);
|
||||||
|
if (!iter)
|
||||||
|
return -ENOMEM;
|
||||||
|
cb->args[0] = (long) iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&nfc_devlist_mutex);
|
||||||
|
|
||||||
|
cb->seq = nfc_devlist_generation;
|
||||||
|
|
||||||
|
if (first_call) {
|
||||||
|
nfc_device_iter_init(iter);
|
||||||
|
dev = nfc_device_iter_next(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (dev) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = nfc_genl_send_se(skb, dev, NETLINK_CB(cb->skb).portid,
|
||||||
|
cb->nlh->nlmsg_seq, cb, NLM_F_MULTI);
|
||||||
|
if (rc < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
dev = nfc_device_iter_next(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&nfc_devlist_mutex);
|
||||||
|
|
||||||
|
cb->args[1] = (long) dev;
|
||||||
|
|
||||||
|
return skb->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nfc_genl_dump_ses_done(struct netlink_callback *cb)
|
||||||
|
{
|
||||||
|
struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
|
||||||
|
|
||||||
|
nfc_device_iter_exit(iter);
|
||||||
|
kfree(iter);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct genl_ops nfc_genl_ops[] = {
|
static struct genl_ops nfc_genl_ops[] = {
|
||||||
{
|
{
|
||||||
.cmd = NFC_CMD_GET_DEVICE,
|
.cmd = NFC_CMD_GET_DEVICE,
|
||||||
@ -1265,6 +1352,12 @@ static struct genl_ops nfc_genl_ops[] = {
|
|||||||
.doit = nfc_genl_disable_se,
|
.doit = nfc_genl_disable_se,
|
||||||
.policy = nfc_genl_policy,
|
.policy = nfc_genl_policy,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.cmd = NFC_CMD_GET_SE,
|
||||||
|
.dumpit = nfc_genl_dump_ses,
|
||||||
|
.done = nfc_genl_dump_ses_done,
|
||||||
|
.policy = nfc_genl_policy,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -124,9 +124,8 @@ static inline void nfc_device_iter_exit(struct class_dev_iter *iter)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name);
|
int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name);
|
||||||
int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name);
|
int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
|
||||||
|
u32 result);
|
||||||
int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name);
|
|
||||||
|
|
||||||
int nfc_dev_up(struct nfc_dev *dev);
|
int nfc_dev_up(struct nfc_dev *dev);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user