summaryrefslogtreecommitdiffstats
path: root/net/nfc/nci/core.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-03-20 21:04:47 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-03-20 21:04:47 -0700
commit3b59bf081622b6446db77ad06c93fe23677bc533 (patch)
tree3f4bb5a27c90cc86994a1f6d3c53fbf9208003cb /net/nfc/nci/core.c
parente45836fafe157df137a837093037f741ad8f4c90 (diff)
parentbbdb32cb5b73597386913d052165423b9d736145 (diff)
downloadlinux-3b59bf081622b6446db77ad06c93fe23677bc533.tar.bz2
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking merge from David Miller: "1) Move ixgbe driver over to purely page based buffering on receive. From Alexander Duyck. 2) Add receive packet steering support to e1000e, from Bruce Allan. 3) Convert TCP MD5 support over to RCU, from Eric Dumazet. 4) Reduce cpu usage in handling out-of-order TCP packets on modern systems, also from Eric Dumazet. 5) Support the IP{,V6}_UNICAST_IF socket options, making the wine folks happy, from Erich Hoover. 6) Support VLAN trunking from guests in hyperv driver, from Haiyang Zhang. 7) Support byte-queue-limtis in r8169, from Igor Maravic. 8) Outline code intended for IP_RECVTOS in IP_PKTOPTIONS existed but was never properly implemented, Jiri Benc fixed that. 9) 64-bit statistics support in r8169 and 8139too, from Junchang Wang. 10) Support kernel side dump filtering by ctmark in netfilter ctnetlink, from Pablo Neira Ayuso. 11) Support byte-queue-limits in gianfar driver, from Paul Gortmaker. 12) Add new peek socket options to assist with socket migration, from Pavel Emelyanov. 13) Add sch_plug packet scheduler whose queue is controlled by userland daemons using explicit freeze and release commands. From Shriram Rajagopalan. 14) Fix FCOE checksum offload handling on transmit, from Yi Zou." * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1846 commits) Fix pppol2tp getsockname() Remove printk from rds_sendmsg ipv6: fix incorrent ipv6 ipsec packet fragment cpsw: Hook up default ndo_change_mtu. net: qmi_wwan: fix build error due to cdc-wdm dependecy netdev: driver: ethernet: Add TI CPSW driver netdev: driver: ethernet: add cpsw address lookup engine support phy: add am79c874 PHY support mlx4_core: fix race on comm channel bonding: send igmp report for its master fs_enet: Add MPC5125 FEC support and PHY interface selection net: bpf_jit: fix BPF_S_LDX_B_MSH compilation net: update the usage of CHECKSUM_UNNECESSARY fcoe: use CHECKSUM_UNNECESSARY instead of CHECKSUM_PARTIAL on tx net: do not do gso for CHECKSUM_UNNECESSARY in netif_needs_gso ixgbe: Fix issues with SR-IOV loopback when flow control is disabled net/hyperv: Fix the code handling tx busy ixgbe: fix namespace issues when FCoE/DCB is not enabled rtlwifi: Remove unused ETH_ADDR_LEN defines igbvf: Use ETH_ALEN ... Fix up fairly trivial conflicts in drivers/isdn/gigaset/interface.c and drivers/net/usb/{Kconfig,qmi_wwan.c} as per David.
Diffstat (limited to 'net/nfc/nci/core.c')
-rw-r--r--net/nfc/nci/core.c209
1 files changed, 151 insertions, 58 deletions
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 7650139a1a05..9ec065bb9ee1 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -66,9 +66,8 @@ static void nci_req_cancel(struct nci_dev *ndev, int err)
/* Execute request and wait for completion. */
static int __nci_request(struct nci_dev *ndev,
- void (*req)(struct nci_dev *ndev, unsigned long opt),
- unsigned long opt,
- __u32 timeout)
+ void (*req)(struct nci_dev *ndev, unsigned long opt),
+ unsigned long opt, __u32 timeout)
{
int rc = 0;
long completion_rc;
@@ -77,9 +76,9 @@ static int __nci_request(struct nci_dev *ndev,
init_completion(&ndev->req_completion);
req(ndev, opt);
- completion_rc = wait_for_completion_interruptible_timeout(
- &ndev->req_completion,
- timeout);
+ completion_rc =
+ wait_for_completion_interruptible_timeout(&ndev->req_completion,
+ timeout);
pr_debug("wait_for_completion return %ld\n", completion_rc);
@@ -110,8 +109,9 @@ static int __nci_request(struct nci_dev *ndev,
}
static inline int nci_request(struct nci_dev *ndev,
- void (*req)(struct nci_dev *ndev, unsigned long opt),
- unsigned long opt, __u32 timeout)
+ void (*req)(struct nci_dev *ndev,
+ unsigned long opt),
+ unsigned long opt, __u32 timeout)
{
int rc;
@@ -152,14 +152,14 @@ static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt)
/* by default mapping is set to NCI_RF_INTERFACE_FRAME */
for (i = 0; i < ndev->num_supported_rf_interfaces; i++) {
if (ndev->supported_rf_interfaces[i] ==
- NCI_RF_INTERFACE_ISO_DEP) {
+ NCI_RF_INTERFACE_ISO_DEP) {
cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP;
cfg[*num].mode = NCI_DISC_MAP_MODE_POLL |
NCI_DISC_MAP_MODE_LISTEN;
cfg[*num].rf_interface = NCI_RF_INTERFACE_ISO_DEP;
(*num)++;
} else if (ndev->supported_rf_interfaces[i] ==
- NCI_RF_INTERFACE_NFC_DEP) {
+ NCI_RF_INTERFACE_NFC_DEP) {
cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP;
cfg[*num].mode = NCI_DISC_MAP_MODE_POLL |
NCI_DISC_MAP_MODE_LISTEN;
@@ -172,8 +172,7 @@ static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt)
}
nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD,
- (1 + ((*num)*sizeof(struct disc_map_config))),
- &cmd);
+ (1 + ((*num) * sizeof(struct disc_map_config))), &cmd);
}
static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
@@ -184,36 +183,68 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
cmd.num_disc_configs = 0;
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_JEWEL_MASK
- || protocols & NFC_PROTO_MIFARE_MASK
- || protocols & NFC_PROTO_ISO14443_MASK
- || protocols & NFC_PROTO_NFC_DEP_MASK)) {
+ (protocols & NFC_PROTO_JEWEL_MASK
+ || protocols & NFC_PROTO_MIFARE_MASK
+ || protocols & NFC_PROTO_ISO14443_MASK
+ || protocols & NFC_PROTO_NFC_DEP_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
- NCI_NFC_A_PASSIVE_POLL_MODE;
+ NCI_NFC_A_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
cmd.num_disc_configs++;
}
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_ISO14443_MASK)) {
+ (protocols & NFC_PROTO_ISO14443_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
- NCI_NFC_B_PASSIVE_POLL_MODE;
+ NCI_NFC_B_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
cmd.num_disc_configs++;
}
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_FELICA_MASK
- || protocols & NFC_PROTO_NFC_DEP_MASK)) {
+ (protocols & NFC_PROTO_FELICA_MASK
+ || protocols & NFC_PROTO_NFC_DEP_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
- NCI_NFC_F_PASSIVE_POLL_MODE;
+ NCI_NFC_F_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
cmd.num_disc_configs++;
}
nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD,
- (1 + (cmd.num_disc_configs*sizeof(struct disc_config))),
- &cmd);
+ (1 + (cmd.num_disc_configs * sizeof(struct disc_config))),
+ &cmd);
+}
+
+struct nci_rf_discover_select_param {
+ __u8 rf_discovery_id;
+ __u8 rf_protocol;
+};
+
+static void nci_rf_discover_select_req(struct nci_dev *ndev, unsigned long opt)
+{
+ struct nci_rf_discover_select_param *param =
+ (struct nci_rf_discover_select_param *)opt;
+ struct nci_rf_discover_select_cmd cmd;
+
+ cmd.rf_discovery_id = param->rf_discovery_id;
+ cmd.rf_protocol = param->rf_protocol;
+
+ switch (cmd.rf_protocol) {
+ case NCI_RF_PROTOCOL_ISO_DEP:
+ cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP;
+ break;
+
+ case NCI_RF_PROTOCOL_NFC_DEP:
+ cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP;
+ break;
+
+ default:
+ cmd.rf_interface = NCI_RF_INTERFACE_FRAME;
+ break;
+ }
+
+ nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD,
+ sizeof(struct nci_rf_discover_select_cmd), &cmd);
}
static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt)
@@ -223,8 +254,7 @@ static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt)
cmd.type = NCI_DEACTIVATE_TYPE_IDLE_MODE;
nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD,
- sizeof(struct nci_rf_deactivate_cmd),
- &cmd);
+ sizeof(struct nci_rf_deactivate_cmd), &cmd);
}
static int nci_open_device(struct nci_dev *ndev)
@@ -248,22 +278,24 @@ static int nci_open_device(struct nci_dev *ndev)
set_bit(NCI_INIT, &ndev->flags);
rc = __nci_request(ndev, nci_reset_req, 0,
- msecs_to_jiffies(NCI_RESET_TIMEOUT));
+ msecs_to_jiffies(NCI_RESET_TIMEOUT));
if (!rc) {
rc = __nci_request(ndev, nci_init_req, 0,
- msecs_to_jiffies(NCI_INIT_TIMEOUT));
+ msecs_to_jiffies(NCI_INIT_TIMEOUT));
}
if (!rc) {
rc = __nci_request(ndev, nci_init_complete_req, 0,
- msecs_to_jiffies(NCI_INIT_TIMEOUT));
+ msecs_to_jiffies(NCI_INIT_TIMEOUT));
}
clear_bit(NCI_INIT, &ndev->flags);
if (!rc) {
set_bit(NCI_UP, &ndev->flags);
+ nci_clear_target_list(ndev);
+ atomic_set(&ndev->state, NCI_IDLE);
} else {
/* Init failed, cleanup */
skb_queue_purge(&ndev->cmd_q);
@@ -286,6 +318,7 @@ static int nci_close_device(struct nci_dev *ndev)
if (!test_and_clear_bit(NCI_UP, &ndev->flags)) {
del_timer_sync(&ndev->cmd_timer);
+ del_timer_sync(&ndev->data_timer);
mutex_unlock(&ndev->req_lock);
return 0;
}
@@ -304,7 +337,7 @@ static int nci_close_device(struct nci_dev *ndev)
set_bit(NCI_INIT, &ndev->flags);
__nci_request(ndev, nci_reset_req, 0,
- msecs_to_jiffies(NCI_RESET_TIMEOUT));
+ msecs_to_jiffies(NCI_RESET_TIMEOUT));
clear_bit(NCI_INIT, &ndev->flags);
/* Flush cmd wq */
@@ -331,6 +364,15 @@ static void nci_cmd_timer(unsigned long arg)
queue_work(ndev->cmd_wq, &ndev->cmd_work);
}
+/* NCI data exchange timer function */
+static void nci_data_timer(unsigned long arg)
+{
+ struct nci_dev *ndev = (void *) arg;
+
+ set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
+ queue_work(ndev->rx_wq, &ndev->rx_work);
+}
+
static int nci_dev_up(struct nfc_dev *nfc_dev)
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
@@ -350,7 +392,8 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols)
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
int rc;
- if (test_bit(NCI_DISCOVERY, &ndev->flags)) {
+ if ((atomic_read(&ndev->state) == NCI_DISCOVERY) ||
+ (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) {
pr_err("unable to start poll, since poll is already active\n");
return -EBUSY;
}
@@ -360,17 +403,18 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols)
return -EBUSY;
}
- if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) {
- pr_debug("target is active, implicitly deactivate...\n");
+ if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) ||
+ (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) {
+ pr_debug("target active or w4 select, implicitly deactivate\n");
rc = nci_request(ndev, nci_rf_deactivate_req, 0,
- msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
+ msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
if (rc)
return -EBUSY;
}
rc = nci_request(ndev, nci_rf_discover_req, protocols,
- msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
+ msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
if (!rc)
ndev->poll_prots = protocols;
@@ -382,23 +426,29 @@ static void nci_stop_poll(struct nfc_dev *nfc_dev)
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
- if (!test_bit(NCI_DISCOVERY, &ndev->flags)) {
+ if ((atomic_read(&ndev->state) != NCI_DISCOVERY) &&
+ (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) {
pr_err("unable to stop poll, since poll is not active\n");
return;
}
nci_request(ndev, nci_rf_deactivate_req, 0,
- msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
+ msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
}
static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx,
- __u32 protocol)
+ __u32 protocol)
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ struct nci_rf_discover_select_param param;
+ struct nfc_target *target = NULL;
+ int i;
+ int rc = 0;
pr_debug("target_idx %d, protocol 0x%x\n", target_idx, protocol);
- if (!test_bit(NCI_POLL_ACTIVE, &ndev->flags)) {
+ if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) &&
+ (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) {
pr_err("there is no available target to activate\n");
return -EINVAL;
}
@@ -408,16 +458,47 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx,
return -EBUSY;
}
- if (!(ndev->target_available_prots & (1 << protocol))) {
+ for (i = 0; i < ndev->n_targets; i++) {
+ if (ndev->targets[i].idx == target_idx) {
+ target = &ndev->targets[i];
+ break;
+ }
+ }
+
+ if (!target) {
+ pr_err("unable to find the selected target\n");
+ return -EINVAL;
+ }
+
+ if (!(target->supported_protocols & (1 << protocol))) {
pr_err("target does not support the requested protocol 0x%x\n",
protocol);
return -EINVAL;
}
- ndev->target_active_prot = protocol;
- ndev->target_available_prots = 0;
+ if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) {
+ param.rf_discovery_id = target->idx;
- return 0;
+ if (protocol == NFC_PROTO_JEWEL)
+ param.rf_protocol = NCI_RF_PROTOCOL_T1T;
+ else if (protocol == NFC_PROTO_MIFARE)
+ param.rf_protocol = NCI_RF_PROTOCOL_T2T;
+ else if (protocol == NFC_PROTO_FELICA)
+ param.rf_protocol = NCI_RF_PROTOCOL_T3T;
+ else if (protocol == NFC_PROTO_ISO14443)
+ param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP;
+ else
+ param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP;
+
+ rc = nci_request(ndev, nci_rf_discover_select_req,
+ (unsigned long)&param,
+ msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT));
+ }
+
+ if (!rc)
+ ndev->target_active_prot = protocol;
+
+ return rc;
}
static void nci_deactivate_target(struct nfc_dev *nfc_dev, __u32 target_idx)
@@ -433,16 +514,15 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, __u32 target_idx)
ndev->target_active_prot = 0;
- if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) {
+ if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) {
nci_request(ndev, nci_rf_deactivate_req, 0,
- msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
+ msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
}
}
static int nci_data_exchange(struct nfc_dev *nfc_dev, __u32 target_idx,
- struct sk_buff *skb,
- data_exchange_cb_t cb,
- void *cb_context)
+ struct sk_buff *skb,
+ data_exchange_cb_t cb, void *cb_context)
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
int rc;
@@ -487,9 +567,8 @@ static struct nfc_ops nci_nfc_ops = {
* @supported_protocols: NFC protocols supported by the device
*/
struct nci_dev *nci_allocate_device(struct nci_ops *ops,
- __u32 supported_protocols,
- int tx_headroom,
- int tx_tailroom)
+ __u32 supported_protocols,
+ int tx_headroom, int tx_tailroom)
{
struct nci_dev *ndev;
@@ -510,9 +589,9 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,
ndev->tx_tailroom = tx_tailroom;
ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
- supported_protocols,
- tx_headroom + NCI_DATA_HDR_SIZE,
- tx_tailroom);
+ supported_protocols,
+ tx_headroom + NCI_DATA_HDR_SIZE,
+ tx_tailroom);
if (!ndev->nfc_dev)
goto free_exit;
@@ -584,7 +663,9 @@ int nci_register_device(struct nci_dev *ndev)
skb_queue_head_init(&ndev->tx_q);
setup_timer(&ndev->cmd_timer, nci_cmd_timer,
- (unsigned long) ndev);
+ (unsigned long) ndev);
+ setup_timer(&ndev->data_timer, nci_data_timer,
+ (unsigned long) ndev);
mutex_init(&ndev->req_lock);
@@ -633,7 +714,7 @@ int nci_recv_frame(struct sk_buff *skb)
pr_debug("len %d\n", skb->len);
if (!ndev || (!test_bit(NCI_UP, &ndev->flags)
- && !test_bit(NCI_INIT, &ndev->flags))) {
+ && !test_bit(NCI_INIT, &ndev->flags))) {
kfree_skb(skb);
return -ENXIO;
}
@@ -713,7 +794,7 @@ static void nci_tx_work(struct work_struct *work)
/* Check if data flow control is used */
if (atomic_read(&ndev->credits_cnt) !=
- NCI_DATA_FLOW_CONTROL_NOT_USED)
+ NCI_DATA_FLOW_CONTROL_NOT_USED)
atomic_dec(&ndev->credits_cnt);
pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n",
@@ -722,6 +803,9 @@ static void nci_tx_work(struct work_struct *work)
nci_plen(skb->data));
nci_send_frame(skb);
+
+ mod_timer(&ndev->data_timer,
+ jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT));
}
}
@@ -753,6 +837,15 @@ static void nci_rx_work(struct work_struct *work)
break;
}
}
+
+ /* check if a data exchange timout has occurred */
+ if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) {
+ /* complete the data exchange transaction, if exists */
+ if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
+ nci_data_exchange_complete(ndev, NULL, -ETIMEDOUT);
+
+ clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
+ }
}
/* ----- NCI TX CMD worker thread ----- */
@@ -781,6 +874,6 @@ static void nci_cmd_work(struct work_struct *work)
nci_send_frame(skb);
mod_timer(&ndev->cmd_timer,
- jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT));
+ jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT));
}
}