aboutsummaryrefslogtreecommitdiff
path: root/net/nfc/hci
diff options
context:
space:
mode:
Diffstat (limited to 'net/nfc/hci')
-rw-r--r--net/nfc/hci/command.c17
-rw-r--r--net/nfc/hci/core.c176
-rw-r--r--net/nfc/hci/hci.h4
-rw-r--r--net/nfc/hci/hcp.c11
-rw-r--r--net/nfc/hci/llc.c8
-rw-r--r--net/nfc/hci/llc.h4
-rw-r--r--net/nfc/hci/llc_nop.c4
-rw-r--r--net/nfc/hci/llc_shdlc.c6
8 files changed, 145 insertions, 85 deletions
diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c
index 7d99410e6c1..677d24bb70f 100644
--- a/net/nfc/hci/command.c
+++ b/net/nfc/hci/command.c
@@ -12,9 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "hci: %s: " fmt, __func__
@@ -28,6 +26,8 @@
#include "hci.h"
+#define MAX_FWI 4949
+
static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
const u8 *param, size_t param_len,
data_exchange_cb_t cb, void *cb_context)
@@ -39,7 +39,7 @@ static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
* for all commands?
*/
return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd,
- param, param_len, cb, cb_context, 3000);
+ param, param_len, cb, cb_context, MAX_FWI);
}
/*
@@ -84,7 +84,7 @@ static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
NFC_HCI_HCP_COMMAND, cmd,
param, param_len,
nfc_hci_execute_cb, &hcp_ew,
- 3000);
+ MAX_FWI);
if (hcp_ew.exec_result < 0)
return hcp_ew.exec_result;
@@ -280,14 +280,19 @@ static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe)
static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev)
{
u8 param[2];
+ size_t param_len = 2;
/* TODO: Find out what the identity reference data is
* and fill param with it. HCI spec 6.1.3.5 */
pr_debug("\n");
+ if (test_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &hdev->quirks))
+ param_len = 0;
+
return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
- NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL);
+ NFC_HCI_ADM_CLEAR_ALL_PIPE, param, param_len,
+ NULL);
}
int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate)
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index 7bea574d593..47403705197 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -12,9 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "hci: %s: " fmt, __func__
@@ -57,6 +55,8 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
int r = 0;
mutex_lock(&hdev->msg_tx_mutex);
+ if (hdev->shutting_down)
+ goto exit;
if (hdev->cmd_pending_msg) {
if (timer_pending(&hdev->cmd_timer) == 0) {
@@ -225,7 +225,7 @@ int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
goto exit;
}
- targets->sens_res = be16_to_cpu(*(u16 *)atqa_skb->data);
+ targets->sens_res = be16_to_cpu(*(__be16 *)atqa_skb->data);
targets->sel_res = sak_skb->data[0];
r = nfc_hci_get_param(hdev, NFC_HCI_RF_READER_A_GATE,
@@ -295,6 +295,12 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
goto exit;
}
+ if (hdev->ops->event_received) {
+ r = hdev->ops->event_received(hdev, gate, event, skb);
+ if (r <= 0)
+ goto exit_noskb;
+ }
+
switch (event) {
case NFC_HCI_EVT_TARGET_DISCOVERED:
if (skb->len < 1) { /* no status data? */
@@ -320,22 +326,17 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
r = nfc_hci_target_discovered(hdev, gate);
break;
default:
- if (hdev->ops->event_received) {
- hdev->ops->event_received(hdev, gate, event, skb);
- return;
- }
-
+ pr_info("Discarded unknown event %x to gate %x\n", event, gate);
+ r = -EINVAL;
break;
}
exit:
kfree_skb(skb);
- if (r) {
- /* TODO: There was an error dispatching the event,
- * how to propagate up to nfc core?
- */
- }
+exit_noskb:
+ if (r)
+ nfc_hci_driver_failure(hdev, r);
}
static void nfc_hci_cmd_timeout(unsigned long data)
@@ -379,34 +380,31 @@ static int hci_dev_session_init(struct nfc_hci_dev *hdev)
if (r < 0)
goto disconnect_all;
- if (skb->len && skb->len == strlen(hdev->init_data.session_id))
- if (memcmp(hdev->init_data.session_id, skb->data,
- skb->len) == 0) {
- /* TODO ELa: restore gate<->pipe table from
- * some TBD location.
- * note: it doesn't seem possible to get the chip
- * currently open gate/pipe table.
- * It is only possible to obtain the supported
- * gate list.
- */
+ if (skb->len && skb->len == strlen(hdev->init_data.session_id) &&
+ (memcmp(hdev->init_data.session_id, skb->data,
+ skb->len) == 0) && hdev->ops->load_session) {
+ /* Restore gate<->pipe table from some proprietary location. */
- /* goto exit
- * For now, always do a full initialization */
- }
+ r = hdev->ops->load_session(hdev);
- r = nfc_hci_disconnect_all_gates(hdev);
- if (r < 0)
- goto exit;
+ if (r < 0)
+ goto disconnect_all;
+ } else {
- r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count,
- hdev->init_data.gates);
- if (r < 0)
- goto disconnect_all;
+ r = nfc_hci_disconnect_all_gates(hdev);
+ if (r < 0)
+ goto exit;
+
+ r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count,
+ hdev->init_data.gates);
+ if (r < 0)
+ goto disconnect_all;
- r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
- NFC_HCI_ADMIN_SESSION_IDENTITY,
- hdev->init_data.session_id,
- strlen(hdev->init_data.session_id));
+ r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
+ NFC_HCI_ADMIN_SESSION_IDENTITY,
+ hdev->init_data.session_id,
+ strlen(hdev->init_data.session_id));
+ }
if (r == 0)
goto exit;
@@ -564,21 +562,21 @@ static int hci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
- if (hdev->ops->dep_link_up)
- return hdev->ops->dep_link_up(hdev, target, comm_mode,
- gb, gb_len);
+ if (!hdev->ops->dep_link_up)
+ return 0;
- return 0;
+ return hdev->ops->dep_link_up(hdev, target, comm_mode,
+ gb, gb_len);
}
static int hci_dep_link_down(struct nfc_dev *nfc_dev)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
- if (hdev->ops->dep_link_down)
- return hdev->ops->dep_link_down(hdev);
+ if (!hdev->ops->dep_link_down)
+ return 0;
- return 0;
+ return hdev->ops->dep_link_down(hdev);
}
static int hci_activate_target(struct nfc_dev *nfc_dev,
@@ -667,10 +665,12 @@ static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
- if (hdev->ops->tm_send)
- return hdev->ops->tm_send(hdev, skb);
- else
+ if (!hdev->ops->tm_send) {
+ kfree_skb(skb);
return -ENOTSUPP;
+ }
+
+ return hdev->ops->tm_send(hdev, skb);
}
static int hci_check_presence(struct nfc_dev *nfc_dev,
@@ -678,8 +678,38 @@ static int hci_check_presence(struct nfc_dev *nfc_dev,
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
- if (hdev->ops->check_presence)
- return hdev->ops->check_presence(hdev, target);
+ if (!hdev->ops->check_presence)
+ return 0;
+
+ return hdev->ops->check_presence(hdev, target);
+}
+
+static int hci_discover_se(struct nfc_dev *nfc_dev)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (hdev->ops->discover_se)
+ return hdev->ops->discover_se(hdev);
+
+ return 0;
+}
+
+static int hci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (hdev->ops->enable_se)
+ return hdev->ops->enable_se(hdev, se_idx);
+
+ return 0;
+}
+
+static int hci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (hdev->ops->disable_se)
+ return hdev->ops->disable_se(hdev, se_idx);
return 0;
}
@@ -771,6 +801,16 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
}
}
+static int hci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (!hdev->ops->fw_download)
+ return -ENOTSUPP;
+
+ return hdev->ops->fw_download(hdev, firmware_name);
+}
+
static struct nfc_ops hci_nfc_ops = {
.dev_up = hci_dev_up,
.dev_down = hci_dev_down,
@@ -783,10 +823,15 @@ static struct nfc_ops hci_nfc_ops = {
.im_transceive = hci_transceive,
.tm_send = hci_tm_send,
.check_presence = hci_check_presence,
+ .fw_download = hci_fw_download,
+ .discover_se = hci_discover_se,
+ .enable_se = hci_enable_se,
+ .disable_se = hci_disable_se,
};
struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
struct nfc_hci_init_data *init_data,
+ unsigned long quirks,
u32 protocols,
const char *llc_name,
int tx_headroom,
@@ -830,6 +875,8 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
+ hdev->quirks = quirks;
+
return hdev;
}
EXPORT_SYMBOL(nfc_hci_allocate_device);
@@ -868,6 +915,28 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
{
struct hci_msg *msg, *n;
+ mutex_lock(&hdev->msg_tx_mutex);
+
+ if (hdev->cmd_pending_msg) {
+ if (hdev->cmd_pending_msg->cb)
+ hdev->cmd_pending_msg->cb(
+ hdev->cmd_pending_msg->cb_context,
+ NULL, -ESHUTDOWN);
+ kfree(hdev->cmd_pending_msg);
+ hdev->cmd_pending_msg = NULL;
+ }
+
+ hdev->shutting_down = true;
+
+ mutex_unlock(&hdev->msg_tx_mutex);
+
+ del_timer_sync(&hdev->cmd_timer);
+ cancel_work_sync(&hdev->msg_tx_work);
+
+ cancel_work_sync(&hdev->msg_rx_work);
+
+ nfc_unregister_device(hdev->ndev);
+
skb_queue_purge(&hdev->rx_hcp_frags);
skb_queue_purge(&hdev->msg_rx_queue);
@@ -876,13 +945,6 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
skb_queue_purge(&msg->msg_frags);
kfree(msg);
}
-
- del_timer_sync(&hdev->cmd_timer);
-
- nfc_unregister_device(hdev->ndev);
-
- cancel_work_sync(&hdev->msg_tx_work);
- cancel_work_sync(&hdev->msg_rx_work);
}
EXPORT_SYMBOL(nfc_hci_unregister_device);
diff --git a/net/nfc/hci/hci.h b/net/nfc/hci/hci.h
index b274d12c18a..c3d2e2c1394 100644
--- a/net/nfc/hci/hci.h
+++ b/net/nfc/hci/hci.h
@@ -12,9 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LOCAL_HCI_H
diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c
index bc308a7ca60..e9de1514656 100644
--- a/net/nfc/hci/hcp.c
+++ b/net/nfc/hci/hcp.c
@@ -12,9 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "hci: %s: " fmt, __func__
@@ -105,6 +103,13 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
}
mutex_lock(&hdev->msg_tx_mutex);
+
+ if (hdev->shutting_down) {
+ err = -ESHUTDOWN;
+ mutex_unlock(&hdev->msg_tx_mutex);
+ goto out_skb_err;
+ }
+
list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
mutex_unlock(&hdev->msg_tx_mutex);
diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c
index fe5e966e5b8..1b90c053185 100644
--- a/net/nfc/hci/llc.c
+++ b/net/nfc/hci/llc.c
@@ -13,23 +13,19 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <net/nfc/llc.h>
#include "llc.h"
-static struct list_head llc_engines;
+static LIST_HEAD(llc_engines);
int nfc_llc_init(void)
{
int r;
- INIT_LIST_HEAD(&llc_engines);
-
r = nfc_llc_nop_register();
if (r)
goto exit;
diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h
index 7be0b7f3ceb..5dad4c57ffb 100644
--- a/net/nfc/hci/llc.h
+++ b/net/nfc/hci/llc.h
@@ -13,9 +13,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LOCAL_LLC_H_
diff --git a/net/nfc/hci/llc_nop.c b/net/nfc/hci/llc_nop.c
index 87b10291b40..d0435d5a197 100644
--- a/net/nfc/hci/llc_nop.c
+++ b/net/nfc/hci/llc_nop.c
@@ -13,9 +13,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/types.h>
diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c
index 27b313befc3..401c7e25527 100644
--- a/net/nfc/hci/llc_shdlc.c
+++ b/net/nfc/hci/llc_shdlc.c
@@ -13,9 +13,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "shdlc: %s: " fmt, __func__
@@ -300,7 +298,7 @@ static void llc_shdlc_rcv_rej(struct llc_shdlc *shdlc, int y_nr)
{
struct sk_buff *skb;
- pr_debug("remote asks retransmition from frame %d\n", y_nr);
+ pr_debug("remote asks retransmission from frame %d\n", y_nr);
if (llc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {
if (shdlc->t2_active) {