From b078b564292ab87cdf4a58de3c2f86d4300a161c Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:06 +0300 Subject: Bluetooth: Add HCI logical link cmds definitions Add a few definitions to hci.h Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 76b2b6bdcf36..4be26abf6dad 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -556,12 +556,46 @@ struct hci_cp_accept_phy_link { __u8 key[HCI_AMP_LINK_KEY_SIZE]; } __packed; -#define HCI_OP_DISCONN_PHY_LINK 0x0437 +#define HCI_OP_DISCONN_PHY_LINK 0x0437 struct hci_cp_disconn_phy_link { __u8 phy_handle; __u8 reason; } __packed; +struct ext_flow_spec { + __u8 id; + __u8 stype; + __le16 msdu; + __le32 sdu_itime; + __le32 acc_lat; + __le32 flush_to; +} __packed; + +#define HCI_OP_CREATE_LOGICAL_LINK 0x0438 +#define HCI_OP_ACCEPT_LOGICAL_LINK 0x0439 +struct hci_cp_create_accept_logical_link { + __u8 phy_handle; + struct ext_flow_spec tx_flow_spec; + struct ext_flow_spec rx_flow_spec; +} __packed; + +#define HCI_OP_DISCONN_LOGICAL_LINK 0x043a +struct hci_cp_disconn_logical_link { + __le16 log_handle; +} __packed; + +#define HCI_OP_LOGICAL_LINK_CANCEL 0x043b +struct hci_cp_logical_link_cancel { + __u8 phy_handle; + __u8 flow_spec_id; +} __packed; + +struct hci_rp_logical_link_cancel { + __u8 status; + __u8 phy_handle; + __u8 flow_spec_id; +} __packed; + #define HCI_OP_SNIFF_MODE 0x0803 struct hci_cp_sniff_mode { __le16 handle; -- cgit v1.2.3 From f97268fccdd4e76462195216fcab621b8d4a6cd1 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:07 +0300 Subject: Bluetooth: A2MP: Create amp_mgr global list Create amp_mgr_list global list which will be used by different hci devices to find amp_mgr. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 10 ++++++++++ net/bluetooth/a2mp.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 6a76e0a0705e..316c1c803676 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -19,12 +19,18 @@ #define A2MP_FEAT_EXT 0x8000 +enum amp_mgr_state { + READ_LOC_AMP_INFO, +}; + struct amp_mgr { + struct list_head list; struct l2cap_conn *l2cap_conn; struct l2cap_chan *a2mp_chan; struct kref kref; __u8 ident; __u8 handle; + enum amp_mgr_state state; unsigned long flags; }; @@ -118,9 +124,13 @@ struct a2mp_physlink_rsp { #define A2MP_STATUS_PHYS_LINK_EXISTS 0x05 #define A2MP_STATUS_SECURITY_VIOLATION 0x06 +extern struct list_head amp_mgr_list; +extern struct mutex amp_mgr_list_lock; + void amp_mgr_get(struct amp_mgr *mgr); int amp_mgr_put(struct amp_mgr *mgr); struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct sk_buff *skb); +struct amp_mgr *amp_mgr_lookup_by_state(u8 state); #endif /* __A2MP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 0760d1fed6f0..3f930608ecfa 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -17,6 +17,10 @@ #include #include +/* Global AMP Manager list */ +LIST_HEAD(amp_mgr_list); +DEFINE_MUTEX(amp_mgr_list_lock); + /* A2MP build & send command helper functions */ static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data) { @@ -516,6 +520,10 @@ static void amp_mgr_destroy(struct kref *kref) BT_DBG("mgr %p", mgr); + mutex_lock(&_mgr_list_lock); + list_del(&mgr->list); + mutex_unlock(&_mgr_list_lock); + kfree(mgr); } @@ -552,6 +560,10 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) kref_init(&mgr->kref); + mutex_lock(&_mgr_list_lock); + list_add(&mgr->list, &_mgr_list); + mutex_unlock(&_mgr_list_lock); + return mgr; } @@ -570,3 +582,20 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, return mgr->a2mp_chan; } + +struct amp_mgr *amp_mgr_lookup_by_state(u8 state) +{ + struct amp_mgr *mgr; + + mutex_lock(&_mgr_list_lock); + list_for_each_entry(mgr, &_mgr_list, list) { + if (mgr->state == state) { + amp_mgr_get(mgr); + mutex_unlock(&_mgr_list_lock); + return mgr; + } + } + mutex_unlock(&_mgr_list_lock); + + return NULL; +} -- cgit v1.2.3 From 8e2a0d92c56ec6955526a8b60838c9b00f70540d Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:08 +0300 Subject: Bluetooth: AMP: Use HCI cmd to Read AMP Info When receiving A2MP Get Info Request execute Read Local AMP Info HCI command to AMP controller with function to be executed upon receiving command complete event. Function will handle A2MP Get Info Response. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 ++ net/bluetooth/a2mp.c | 57 +++++++++++++++++++++++++++++++------------- net/bluetooth/hci_event.c | 6 ++++- 3 files changed, 48 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 316c1c803676..af594685112e 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -132,5 +132,7 @@ int amp_mgr_put(struct amp_mgr *mgr); struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct sk_buff *skb); struct amp_mgr *amp_mgr_lookup_by_state(u8 state); +void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data); +void a2mp_send_getinfo_rsp(struct hci_dev *hdev); #endif /* __A2MP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 3f930608ecfa..0e97b3b42ab1 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -41,8 +41,7 @@ static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data) return cmd; } -static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, - void *data) +void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) { struct l2cap_chan *chan = mgr->a2mp_chan; struct a2mp_cmd *cmd; @@ -185,7 +184,6 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { struct a2mp_info_req *req = (void *) skb->data; - struct a2mp_info_rsp rsp; struct hci_dev *hdev; if (le16_to_cpu(hdr->len) < sizeof(*req)) @@ -193,23 +191,23 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("id %d", req->id); - rsp.id = req->id; - rsp.status = A2MP_STATUS_INVALID_CTRL_ID; - hdev = hci_dev_get(req->id); - if (hdev && hdev->amp_type != HCI_BREDR) { - rsp.status = 0; - rsp.total_bw = cpu_to_le32(hdev->amp_total_bw); - rsp.max_bw = cpu_to_le32(hdev->amp_max_bw); - rsp.min_latency = cpu_to_le32(hdev->amp_min_latency); - rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap); - rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size); + if (!hdev) { + struct a2mp_info_rsp rsp; + + rsp.id = req->id; + rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + + a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), + &rsp); } - if (hdev) - hci_dev_put(hdev); + if (hdev->dev_type != HCI_BREDR) { + mgr->state = READ_LOC_AMP_INFO; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); + } - a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp); + hci_dev_put(hdev); skb_pull(skb, sizeof(*req)); return 0; @@ -599,3 +597,30 @@ struct amp_mgr *amp_mgr_lookup_by_state(u8 state) return NULL; } + +void a2mp_send_getinfo_rsp(struct hci_dev *hdev) +{ + struct amp_mgr *mgr; + struct a2mp_info_rsp rsp; + + mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_INFO); + if (!mgr) + return; + + BT_DBG("%s mgr %p", hdev->name, mgr); + + rsp.id = hdev->id; + rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + + if (hdev->amp_type != HCI_BREDR) { + rsp.status = 0; + rsp.total_bw = cpu_to_le32(hdev->amp_total_bw); + rsp.max_bw = cpu_to_le32(hdev->amp_max_bw); + rsp.min_latency = cpu_to_le32(hdev->amp_min_latency); + rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap); + rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size); + } + + a2mp_send(mgr, A2MP_GETINFO_RSP, mgr->ident, sizeof(rsp), &rsp); + amp_mgr_put(mgr); +} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2022b43c7353..eb45774165f4 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -30,6 +30,7 @@ #include #include #include +#include /* Handle HCI Event packets */ @@ -846,7 +847,7 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) - return; + goto a2mp_rsp; hdev->amp_status = rp->amp_status; hdev->amp_total_bw = __le32_to_cpu(rp->total_bw); @@ -860,6 +861,9 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev, hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to); hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status); + +a2mp_rsp: + a2mp_send_getinfo_rsp(hdev); } static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, -- cgit v1.2.3 From 903e45411099ae8292f5ce637ad0c72f6fef61db Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:09 +0300 Subject: Bluetooth: AMP: Use HCI cmd to Read Loc AMP Assoc When receiving A2MP Get AMP Assoc Request execute Read Local AMP Assoc HCI command to AMP controller. If the AMP Assoc data is larger than it can fit to HCI event only fragment is read. When all fragments are read send A2MP Get AMP Assoc Response. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 ++ include/net/bluetooth/amp.h | 21 +++++++++++++++ include/net/bluetooth/hci.h | 2 ++ include/net/bluetooth/hci_core.h | 8 ++++++ net/bluetooth/Makefile | 2 +- net/bluetooth/a2mp.c | 56 ++++++++++++++++++++++++++++++++++++---- net/bluetooth/amp.c | 45 ++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 41 +++++++++++++++++++++++++++++ 8 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 include/net/bluetooth/amp.h create mode 100644 net/bluetooth/amp.c (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index af594685112e..d5476719fa4c 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -21,6 +21,7 @@ enum amp_mgr_state { READ_LOC_AMP_INFO, + READ_LOC_AMP_ASSOC, }; struct amp_mgr { @@ -134,5 +135,6 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct amp_mgr *amp_mgr_lookup_by_state(u8 state); void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data); void a2mp_send_getinfo_rsp(struct hci_dev *hdev); +void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status); #endif /* __A2MP_H */ diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h new file mode 100644 index 000000000000..e8616751bc81 --- /dev/null +++ b/include/net/bluetooth/amp.h @@ -0,0 +1,21 @@ +/* + Copyright (c) 2011,2012 Intel Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 and + only version 2 as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#ifndef __AMP_H +#define __AMP_H + +void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr); +void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); +void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); + +#endif /* __AMP_H */ diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4be26abf6dad..ccc08e57c107 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -33,6 +33,8 @@ #define HCI_LINK_KEY_SIZE 16 #define HCI_AMP_LINK_KEY_SIZE (2 * HCI_LINK_KEY_SIZE) +#define HCI_MAX_AMP_ASSOC_SIZE 672 + /* HCI dev events */ #define HCI_DEV_REG 1 #define HCI_DEV_UNREG 2 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e7d454609881..b1c7d0607244 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -124,6 +124,12 @@ struct le_scan_params { #define HCI_MAX_SHORT_NAME_LENGTH 10 +struct amp_assoc { + __u16 len; + __u16 offset; + __u8 data[HCI_MAX_AMP_ASSOC_SIZE]; +}; + #define NUM_REASSEMBLY 4 struct hci_dev { struct list_head list; @@ -177,6 +183,8 @@ struct hci_dev { __u32 amp_max_flush_to; __u32 amp_be_flush_to; + struct amp_assoc loc_assoc; + __u8 flow_ctl_mode; unsigned int auto_accept_delay; diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index fa6d94a4602a..dea6a287daca 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -10,4 +10,4 @@ obj-$(CONFIG_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ - a2mp.o + a2mp.o amp.o diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 0e97b3b42ab1..71400612843d 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -16,6 +16,7 @@ #include #include #include +#include /* Global AMP Manager list */ LIST_HEAD(amp_mgr_list); @@ -218,26 +219,37 @@ static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb, { struct a2mp_amp_assoc_req *req = (void *) skb->data; struct hci_dev *hdev; + struct amp_mgr *tmp; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; BT_DBG("id %d", req->id); + /* Make sure that other request is not processed */ + tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC); + hdev = hci_dev_get(req->id); - if (!hdev || hdev->amp_type == HCI_BREDR) { + if (!hdev || hdev->amp_type == HCI_BREDR || tmp) { struct a2mp_amp_assoc_rsp rsp; rsp.id = req->id; - rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + + if (tmp) { + rsp.status = A2MP_STATUS_COLLISION_OCCURED; + amp_mgr_put(tmp); + } else { + rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + } a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp), &rsp); - goto clean; + + goto done; } - /* Placeholder for HCI Read AMP Assoc */ + amp_read_loc_assoc(hdev, mgr); -clean: +done: if (hdev) hci_dev_put(hdev); @@ -624,3 +636,37 @@ void a2mp_send_getinfo_rsp(struct hci_dev *hdev) a2mp_send(mgr, A2MP_GETINFO_RSP, mgr->ident, sizeof(rsp), &rsp); amp_mgr_put(mgr); } + +void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status) +{ + struct amp_mgr *mgr; + struct amp_assoc *loc_assoc = &hdev->loc_assoc; + struct a2mp_amp_assoc_rsp *rsp; + size_t len; + + mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC); + if (!mgr) + return; + + BT_DBG("%s mgr %p", hdev->name, mgr); + + len = sizeof(struct a2mp_amp_assoc_rsp) + loc_assoc->len; + rsp = kzalloc(len, GFP_KERNEL); + if (!rsp) { + amp_mgr_put(mgr); + return; + } + + rsp->id = hdev->id; + + if (status) { + rsp->status = A2MP_STATUS_INVALID_CTRL_ID; + } else { + rsp->status = A2MP_STATUS_SUCCESS; + memcpy(rsp->amp_assoc, loc_assoc->data, loc_assoc->len); + } + + a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, mgr->ident, len, rsp); + amp_mgr_put(mgr); + kfree(rsp); +} diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c new file mode 100644 index 000000000000..2d4e79ee06a5 --- /dev/null +++ b/net/bluetooth/amp.c @@ -0,0 +1,45 @@ +/* + Copyright (c) 2011,2012 Intel Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 and + only version 2 as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include + +void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle) +{ + struct hci_cp_read_local_amp_assoc cp; + struct amp_assoc *loc_assoc = &hdev->loc_assoc; + + BT_DBG("%s handle %d", hdev->name, phy_handle); + + cp.phy_handle = phy_handle; + cp.max_len = cpu_to_le16(hdev->amp_assoc_size); + cp.len_so_far = cpu_to_le16(loc_assoc->offset); + + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); +} + +void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) +{ + struct hci_cp_read_local_amp_assoc cp; + + memset(&hdev->loc_assoc, 0, sizeof(struct amp_assoc)); + memset(&cp, 0, sizeof(cp)); + + cp.max_len = cpu_to_le16(hdev->amp_assoc_size); + + mgr->state = READ_LOC_AMP_ASSOC; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); +} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index eb45774165f4..a4240f7a142e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -31,6 +31,7 @@ #include #include #include +#include /* Handle HCI Event packets */ @@ -866,6 +867,42 @@ a2mp_rsp: a2mp_send_getinfo_rsp(hdev); } +static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_local_amp_assoc *rp = (void *) skb->data; + struct amp_assoc *assoc = &hdev->loc_assoc; + size_t rem_len, frag_len; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + goto a2mp_rsp; + + frag_len = skb->len - sizeof(*rp); + rem_len = __le16_to_cpu(rp->rem_len); + + if (rem_len > frag_len) { + BT_DBG("frag_len %d rem_len %d", frag_len, rem_len); + + memcpy(assoc->data + assoc->offset, rp->frag, frag_len); + assoc->offset += frag_len; + + /* Read other fragments */ + amp_read_loc_assoc_frag(hdev, rp->phy_handle); + + return; + } + + memcpy(assoc->data + assoc->offset, rp->frag, rem_len); + assoc->len = assoc->offset + rem_len; + assoc->offset = 0; + +a2mp_rsp: + /* Send A2MP Rsp when all fragments are received */ + a2mp_send_getampassoc_rsp(hdev, rp->status); +} + static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2318,6 +2355,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_local_amp_info(hdev, skb); break; + case HCI_OP_READ_LOCAL_AMP_ASSOC: + hci_cc_read_local_amp_assoc(hdev, skb); + break; + case HCI_OP_DELETE_STORED_LINK_KEY: hci_cc_delete_stored_link_key(hdev, skb); break; -- cgit v1.2.3 From 3161ae1c72f03b021bc67504c13025626c26d30c Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:11 +0300 Subject: Bluetooth: AMP: Physical link struct and helpers Define physical link structures. Physical links are represented by hci_conn structure. For BR/EDR we use type ACL_LINK and for AMP we use AMP_LINK. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 3 +++ include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 1 + net/bluetooth/amp.c | 29 +++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index e8616751bc81..3414dfdc404c 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -14,6 +14,9 @@ #ifndef __AMP_H #define __AMP_H +struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, + u8 remote_id); + void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr); void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ccc08e57c107..77b6a197a6f6 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -207,6 +207,7 @@ enum { #define ESCO_LINK 0x02 /* Low Energy links do not have defined link type. Use invented one */ #define LE_LINK 0x80 +#define AMP_LINK 0x81 /* LMP features */ #define LMP_3SLOT 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b1c7d0607244..464eae340419 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -318,6 +318,7 @@ struct hci_conn { __u8 remote_cap; __u8 remote_auth; + __u8 remote_id; bool flush_key; unsigned int sent; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 2d4e79ee06a5..50a7b2fb8bf3 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -17,6 +17,35 @@ #include #include +/* Physical Link interface */ +static u8 __next_handle(struct amp_mgr *mgr) +{ + if (++mgr->handle == 0) + mgr->handle = 1; + + return mgr->handle; +} + +struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, + u8 remote_id) +{ + bdaddr_t *dst = mgr->l2cap_conn->dst; + struct hci_conn *hcon; + + hcon = hci_conn_add(hdev, AMP_LINK, dst); + if (!hcon) + return NULL; + + hcon->state = BT_CONNECT; + hcon->out = true; + hcon->attempt++; + hcon->handle = __next_handle(mgr); + hcon->remote_id = remote_id; + hcon->amp_mgr = mgr; + + return hcon; +} + void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle) { struct hci_cp_read_local_amp_assoc cp; -- cgit v1.2.3 From 52c0d6e56b634b195e377192182391d526cdd5e4 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:12 +0300 Subject: Bluetooth: AMP: Remote AMP ctrl definitions Create remote AMP controllers structure. It is used to keep information about discovered remote AMP controllers by A2MP protocol. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 3 ++ include/net/bluetooth/amp.h | 15 +++++++++ net/bluetooth/a2mp.c | 5 +++ net/bluetooth/amp.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index d5476719fa4c..99c7389b9189 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -33,6 +33,9 @@ struct amp_mgr { __u8 handle; enum amp_mgr_state state; unsigned long flags; + + struct list_head amp_ctrls; + struct mutex amp_ctrls_lock; }; struct a2mp_cmd { diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 3414dfdc404c..f57854f0786d 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -14,6 +14,21 @@ #ifndef __AMP_H #define __AMP_H +struct amp_ctrl { + struct list_head list; + struct kref kref; + __u8 id; + __u16 assoc_len_so_far; + __u16 assoc_rem_len; + __u16 assoc_len; + __u8 *assoc; +}; + +int amp_ctrl_put(struct amp_ctrl *ctrl); +struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr); +struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id); +void amp_ctrl_list_flush(struct amp_mgr *mgr); + struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, u8 remote_id); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index f04c44146663..35e188c7a441 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -594,6 +594,7 @@ static void amp_mgr_destroy(struct kref *kref) list_del(&mgr->list); mutex_unlock(&_mgr_list_lock); + amp_ctrl_list_flush(mgr); kfree(mgr); } @@ -630,6 +631,10 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) kref_init(&mgr->kref); + /* Remote AMP ctrl list initialization */ + INIT_LIST_HEAD(&mgr->amp_ctrls); + mutex_init(&mgr->amp_ctrls_lock); + mutex_lock(&_mgr_list_lock); list_add(&mgr->list, &_mgr_list); mutex_unlock(&_mgr_list_lock); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 50a7b2fb8bf3..8ef912c36224 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -17,6 +17,85 @@ #include #include +/* Remote AMP Controllers interface */ +static void amp_ctrl_get(struct amp_ctrl *ctrl) +{ + BT_DBG("ctrl %p orig refcnt %d", ctrl, + atomic_read(&ctrl->kref.refcount)); + + kref_get(&ctrl->kref); +} + +static void amp_ctrl_destroy(struct kref *kref) +{ + struct amp_ctrl *ctrl = container_of(kref, struct amp_ctrl, kref); + + BT_DBG("ctrl %p", ctrl); + + kfree(ctrl->assoc); + kfree(ctrl); +} + +int amp_ctrl_put(struct amp_ctrl *ctrl) +{ + BT_DBG("ctrl %p orig refcnt %d", ctrl, + atomic_read(&ctrl->kref.refcount)); + + return kref_put(&ctrl->kref, &_ctrl_destroy); +} + +struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr) +{ + struct amp_ctrl *ctrl; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return NULL; + + mutex_lock(&mgr->amp_ctrls_lock); + list_add(&ctrl->list, &mgr->amp_ctrls); + mutex_unlock(&mgr->amp_ctrls_lock); + + kref_init(&ctrl->kref); + + BT_DBG("mgr %p ctrl %p", mgr, ctrl); + + return ctrl; +} + +void amp_ctrl_list_flush(struct amp_mgr *mgr) +{ + struct amp_ctrl *ctrl, *n; + + BT_DBG("mgr %p", mgr); + + mutex_lock(&mgr->amp_ctrls_lock); + list_for_each_entry_safe(ctrl, n, &mgr->amp_ctrls, list) { + list_del(&ctrl->list); + amp_ctrl_put(ctrl); + } + mutex_unlock(&mgr->amp_ctrls_lock); +} + +struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id) +{ + struct amp_ctrl *ctrl; + + BT_DBG("mgr %p id %d", mgr, id); + + mutex_lock(&mgr->amp_ctrls_lock); + list_for_each_entry(ctrl, &mgr->amp_ctrls, list) { + if (ctrl->id == id) { + amp_ctrl_get(ctrl); + mutex_unlock(&mgr->amp_ctrls_lock); + return ctrl; + } + } + mutex_unlock(&mgr->amp_ctrls_lock); + + return NULL; +} + /* Physical Link interface */ static u8 __next_handle(struct amp_mgr *mgr) { -- cgit v1.2.3 From 93c3e8f5c9a0e4dc6b6c93108dcf3ec54ab1191a Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:16 +0300 Subject: Bluetooth: Choose connection based on capabilities Choose which L2CAP connection to establish by checking support for HS and remote side supported features. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 ++ include/net/bluetooth/l2cap.h | 1 + net/bluetooth/a2mp.c | 34 +++++++++++++++++++++++++++++----- net/bluetooth/l2cap_core.c | 33 ++++++++++++++++++++++++++++----- 4 files changed, 60 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 99c7389b9189..9fda7c94913f 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -28,6 +28,7 @@ struct amp_mgr { struct list_head list; struct l2cap_conn *l2cap_conn; struct l2cap_chan *a2mp_chan; + struct l2cap_chan *bredr_chan; struct kref kref; __u8 ident; __u8 handle; @@ -137,6 +138,7 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct sk_buff *skb); struct amp_mgr *amp_mgr_lookup_by_state(u8 state); void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data); +void a2mp_discover_amp(struct l2cap_chan *chan); void a2mp_send_getinfo_rsp(struct hci_dev *hdev); void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status); diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7ed8e356425a..aba830fc762f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -767,6 +767,7 @@ int l2cap_chan_check_security(struct l2cap_chan *chan); void l2cap_chan_set_defaults(struct l2cap_chan *chan); int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); +void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void l2cap_chan_del(struct l2cap_chan *chan, int err); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index d0fde05e8b17..93adaad782ed 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -638,7 +638,7 @@ static struct l2cap_ops a2mp_chan_ops = { .ready = l2cap_chan_no_ready, }; -static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) +static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) { struct l2cap_chan *chan; int err; @@ -673,7 +673,10 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) chan->conf_state = 0; - l2cap_chan_add(conn, chan); + if (locked) + __l2cap_chan_add(conn, chan); + else + l2cap_chan_add(conn, chan); chan->remote_mps = chan->omtu; chan->mps = chan->omtu; @@ -712,7 +715,7 @@ int amp_mgr_put(struct amp_mgr *mgr) return kref_put(&mgr->kref, &_mgr_destroy); } -static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) +static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn, bool locked) { struct amp_mgr *mgr; struct l2cap_chan *chan; @@ -725,7 +728,7 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) mgr->l2cap_conn = conn; - chan = a2mp_chan_open(conn); + chan = a2mp_chan_open(conn, locked); if (!chan) { kfree(mgr); return NULL; @@ -754,7 +757,7 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, { struct amp_mgr *mgr; - mgr = amp_mgr_create(conn); + mgr = amp_mgr_create(conn, false); if (!mgr) { BT_ERR("Could not create AMP manager"); return NULL; @@ -842,3 +845,24 @@ void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status) amp_mgr_put(mgr); kfree(rsp); } + +void a2mp_discover_amp(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct amp_mgr *mgr = conn->hcon->amp_mgr; + struct a2mp_discov_req req; + + BT_DBG("chan %p conn %p mgr %p", chan, conn, mgr); + + if (!mgr) { + mgr = amp_mgr_create(conn, true); + if (!mgr) + return; + } + + mgr->bredr_chan = chan; + + req.mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU); + req.ext_feat = 0; + a2mp_send(mgr, A2MP_DISCOVER_REQ, 1, sizeof(req), &req); +} diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 7a59e929febc..781a085ae5bc 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -455,7 +455,7 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) set_bit(FLAG_FORCE_ACTIVE, &chan->flags); } -static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) +void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, __le16_to_cpu(chan->psm), chan->dcid); @@ -946,6 +946,18 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan) return !test_bit(CONF_CONNECT_PEND, &chan->conf_state); } +static bool __amp_capable(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + + if (enable_hs && + chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED && + conn->fixed_chan_mask & L2CAP_FC_A2MP) + return true; + else + return false; +} + static void l2cap_send_conn_req(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -972,6 +984,16 @@ static void l2cap_chan_ready(struct l2cap_chan *chan) chan->ops->ready(chan); } +static void l2cap_start_connection(struct l2cap_chan *chan) +{ + if (__amp_capable(chan)) { + BT_DBG("chan %p AMP capable: discover AMPs", chan); + a2mp_discover_amp(chan); + } else { + l2cap_send_conn_req(chan); + } +} + static void l2cap_do_start(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -986,8 +1008,9 @@ static void l2cap_do_start(struct l2cap_chan *chan) return; if (l2cap_chan_check_security(chan) && - __l2cap_no_conn_pending(chan)) - l2cap_send_conn_req(chan); + __l2cap_no_conn_pending(chan)) { + l2cap_start_connection(chan); + } } else { struct l2cap_info_req req; req.type = __constant_cpu_to_le16(L2CAP_IT_FEAT_MASK); @@ -1082,7 +1105,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) continue; } - l2cap_send_conn_req(chan); + l2cap_start_connection(chan); } else if (chan->state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; @@ -5456,7 +5479,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (chan->state == BT_CONNECT) { if (!status) { - l2cap_send_conn_req(chan); + l2cap_start_connection(chan); } else { __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); } -- cgit v1.2.3 From 5a349186692950b13896abc3fb2f491d023f95a1 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:18 +0300 Subject: Bluetooth: AMP: Add AMP key calculation Function calculates AMP keys using hmac_sha256 helper. Calculated keys are Generic AMP Link Key (gamp) and Dedicated AMP Link Key with keyID "802b" for 802.11 PAL. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 ++ net/bluetooth/Kconfig | 1 + net/bluetooth/amp.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index f57854f0786d..763b4635c304 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -32,6 +32,8 @@ void amp_ctrl_list_flush(struct amp_mgr *mgr); struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, u8 remote_id); +int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type); + void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr); void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 3537d385035e..1c11d0dcd863 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -11,6 +11,7 @@ menuconfig BT select CRYPTO_BLKCIPHER select CRYPTO_AES select CRYPTO_ECB + select CRYPTO_SHA256 help Bluetooth is low-cost, low-power, short-range wireless technology. It was designed as a replacement for cables and other short-range diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index ea4d5ffc1151..67bc2c2f8783 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -161,6 +161,51 @@ static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output) return ret; } +int phylink_gen_key(struct hci_conn *conn, u8 *data, u8 *len, u8 *type) +{ + struct hci_dev *hdev = conn->hdev; + struct link_key *key; + u8 keybuf[HCI_AMP_LINK_KEY_SIZE]; + u8 gamp_key[HCI_AMP_LINK_KEY_SIZE]; + int err; + + if (!hci_conn_check_link_mode(conn)) + return -EACCES; + + BT_DBG("conn %p key_type %d", conn, conn->key_type); + + /* Legacy key */ + if (conn->key_type < 3) { + BT_ERR("Legacy key type %d", conn->key_type); + return -EACCES; + } + + *type = conn->key_type; + *len = HCI_AMP_LINK_KEY_SIZE; + + key = hci_find_link_key(hdev, &conn->dst); + + /* BR/EDR Link Key concatenated together with itself */ + memcpy(&keybuf[0], key->val, HCI_LINK_KEY_SIZE); + memcpy(&keybuf[HCI_LINK_KEY_SIZE], key->val, HCI_LINK_KEY_SIZE); + + /* Derive Generic AMP Link Key (gamp) */ + err = hmac_sha256(keybuf, HCI_AMP_LINK_KEY_SIZE, "gamp", 4, gamp_key); + if (err) { + BT_ERR("Could not derive Generic AMP Key: err %d", err); + return err; + } + + if (conn->key_type == HCI_LK_DEBUG_COMBINATION) { + BT_DBG("Use Generic AMP Key (gamp)"); + memcpy(data, gamp_key, HCI_AMP_LINK_KEY_SIZE); + return err; + } + + /* Derive Dedicated AMP Link Key: "802b" is 802.11 PAL keyID */ + return hmac_sha256(gamp_key, HCI_AMP_LINK_KEY_SIZE, "802b", 4, data); +} + void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle) { struct hci_cp_read_local_amp_assoc cp; -- cgit v1.2.3 From a02226d6ff5098e6b97590cc55aabe7faf0860ed Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:19 +0300 Subject: Bluetooth: AMP: Create Physical Link When receiving A2MP Get AMP Assoc Response execute HCI Create Physical Link to AMP controller. Define function which will run when receiving HCI Command Status. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 ++ net/bluetooth/a2mp.c | 2 ++ net/bluetooth/amp.c | 19 +++++++++++++++++++ net/bluetooth/hci_event.c | 9 +++++++++ 4 files changed, 32 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 763b4635c304..cadb3d032856 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -37,5 +37,7 @@ int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type); void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr); void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); +void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, + struct hci_conn *hcon); #endif /* __AMP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 93adaad782ed..773e8fc41670 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -392,6 +392,8 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id); + amp_create_phylink(hdev, mgr, hcon); + done: hci_dev_put(hdev); skb_pull(skb, len); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 67bc2c2f8783..657ec736bbc6 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -232,3 +232,22 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) mgr->state = READ_LOC_AMP_ASSOC; hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); } + +void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, + struct hci_conn *hcon) +{ + struct hci_cp_create_phy_link cp; + + cp.phy_handle = hcon->handle; + + BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon, + hcon->handle); + + if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len, + &cp.key_type)) { + BT_DBG("Cannot create link key"); + return; + } + + hci_send_cmd(hdev, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp); +} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a4240f7a142e..bd06ef95693d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1698,6 +1698,11 @@ static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) BT_DBG("%s status 0x%2.2x", hdev->name, status); } +static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) +{ + BT_DBG("%s status 0x%2.2x", hdev->name, status); +} + static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2512,6 +2517,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_le_start_enc(hdev, ev->status); break; + case HCI_OP_CREATE_PHY_LINK: + hci_cs_create_phylink(hdev, ev->status); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; -- cgit v1.2.3 From 93c284ee901f7d7bdd09087e92abefb7496c3777 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:20 +0300 Subject: Bluetooth: AMP: Write remote AMP Assoc When receiving HCI Command Status after HCI Create Physical Link execute HCI Write Remote AMP Assoc command to AMP controller. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 + include/net/bluetooth/hci_core.h | 2 + net/bluetooth/amp.c | 80 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 29 +++++++++++++++ 4 files changed, 113 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index cadb3d032856..8f8032965eaf 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -39,5 +39,7 @@ void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, struct hci_conn *hcon); +void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); +void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle); #endif /* __AMP_H */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 464eae340419..ea1f9340324d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -127,6 +127,8 @@ struct le_scan_params { struct amp_assoc { __u16 len; __u16 offset; + __u16 rem_len; + __u16 len_so_far; __u8 data[HCI_MAX_AMP_ASSOC_SIZE]; }; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 657ec736bbc6..5895ad0d2ac9 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -233,6 +233,86 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); } + +/* Write AMP Assoc data fragments, returns true with last fragment written*/ +static bool amp_write_rem_assoc_frag(struct hci_dev *hdev, + struct hci_conn *hcon) +{ + struct hci_cp_write_remote_amp_assoc *cp; + struct amp_mgr *mgr = hcon->amp_mgr; + struct amp_ctrl *ctrl; + u16 frag_len, len; + + ctrl = amp_ctrl_lookup(mgr, hcon->remote_id); + if (!ctrl) + return false; + + if (!ctrl->assoc_rem_len) { + BT_DBG("all fragments are written"); + ctrl->assoc_rem_len = ctrl->assoc_len; + ctrl->assoc_len_so_far = 0; + + amp_ctrl_put(ctrl); + return true; + } + + frag_len = min_t(u16, 248, ctrl->assoc_rem_len); + len = frag_len + sizeof(*cp); + + cp = kzalloc(len, GFP_KERNEL); + if (!cp) { + amp_ctrl_put(ctrl); + return false; + } + + BT_DBG("hcon %p ctrl %p frag_len %u assoc_len %u rem_len %u", + hcon, ctrl, frag_len, ctrl->assoc_len, ctrl->assoc_rem_len); + + cp->phy_handle = hcon->handle; + cp->len_so_far = cpu_to_le16(ctrl->assoc_len_so_far); + cp->rem_len = cpu_to_le16(ctrl->assoc_rem_len); + memcpy(cp->frag, ctrl->assoc, frag_len); + + ctrl->assoc_len_so_far += frag_len; + ctrl->assoc_rem_len -= frag_len; + + amp_ctrl_put(ctrl); + + hci_send_cmd(hdev, HCI_OP_WRITE_REMOTE_AMP_ASSOC, len, cp); + + kfree(cp); + + return false; +} + +void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle) +{ + struct hci_conn *hcon; + + BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle); + + hcon = hci_conn_hash_lookup_handle(hdev, handle); + if (!hcon) + return; + + amp_write_rem_assoc_frag(hdev, hcon); +} + +void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle) +{ + struct hci_conn *hcon; + + BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle); + + hcon = hci_conn_hash_lookup_handle(hdev, handle); + if (!hcon) + return; + + BT_DBG("%s phy handle 0x%2.2x hcon %p", hdev->name, handle, hcon); + + amp_write_rem_assoc_frag(hdev, hcon); +} + void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, struct hci_conn *hcon) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index bd06ef95693d..0b7ba1e39d45 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1215,6 +1215,20 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status); } +static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_write_remote_amp_assoc *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x phy_handle 0x%2.2x", + hdev->name, rp->status, rp->phy_handle); + + if (rp->status) + return; + + amp_write_rem_assoc_continue(hdev, rp->phy_handle); +} + static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%2.2x", hdev->name, status); @@ -1700,7 +1714,18 @@ static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) { + struct hci_cp_create_phy_link *cp; + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK); + if (!cp) + return; + + amp_write_remote_assoc(hdev, cp->phy_handle); } static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) @@ -2436,6 +2461,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_write_le_host_supported(hdev, skb); break; + case HCI_OP_WRITE_REMOTE_AMP_ASSOC: + hci_cc_write_remote_amp_assoc(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; -- cgit v1.2.3 From 2766be48a7181d7f2a84831ca7e7be248fb6fdb5 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:21 +0300 Subject: Bluetooth: A2MP: Add fallback to normal l2cap init sequence When there is no remote AMP controller found fallback to normal L2CAP sequence. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/a2mp.c | 28 ++++++++++++++++++++++++++++ net/bluetooth/l2cap_core.c | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index aba830fc762f..0967f9e33750 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -769,5 +769,6 @@ int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void l2cap_chan_del(struct l2cap_chan *chan, int err); +void l2cap_send_conn_req(struct l2cap_chan *chan); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 773e8fc41670..28d1246958be 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -180,6 +180,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb, u16 len = le16_to_cpu(hdr->len); struct a2mp_cl *cl; u16 ext_feat; + bool found = false; if (len < sizeof(*rsp)) return -EINVAL; @@ -210,6 +211,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb, if (cl->id != HCI_BREDR_ID && cl->type == HCI_AMP) { struct a2mp_info_req req; + found = true; req.id = cl->id; a2mp_send(mgr, A2MP_GETINFO_REQ, __next_ident(mgr), sizeof(req), &req); @@ -219,6 +221,32 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb, cl = (void *) skb_pull(skb, sizeof(*cl)); } + /* Fall back to L2CAP init sequence */ + if (!found) { + struct l2cap_conn *conn = mgr->l2cap_conn; + struct l2cap_chan *chan; + + mutex_lock(&conn->chan_lock); + + list_for_each_entry(chan, &conn->chan_l, list) { + + BT_DBG("chan %p state %s", chan, + state_to_string(chan->state)); + + if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) + continue; + + l2cap_chan_lock(chan); + + if (chan->state == BT_CONNECT) + l2cap_send_conn_req(chan); + + l2cap_chan_unlock(chan); + } + + mutex_unlock(&conn->chan_lock); + } + return 0; } diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 781a085ae5bc..a347522b2144 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -958,7 +958,7 @@ static bool __amp_capable(struct l2cap_chan *chan) return false; } -static void l2cap_send_conn_req(struct l2cap_chan *chan) +void l2cap_send_conn_req(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; struct l2cap_conn_req req; -- cgit v1.2.3 From 9495b2ee757f7747d7c28f9ba8d7edc53005ec2d Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:22 +0300 Subject: Bluetooth: AMP: Process Chan Selected event Channel Selected event indicates that link information data is available. Read it with Read Local AMP Assoc command. The data shall be sent in the A2MP Create Physical Link Request. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 3 +++ include/net/bluetooth/amp.h | 2 ++ include/net/bluetooth/l2cap.h | 2 ++ net/bluetooth/a2mp.c | 41 ++++++++++++++++++++++++++++++++++++++++- net/bluetooth/amp.c | 15 +++++++++++++++ net/bluetooth/hci_event.c | 21 +++++++++++++++++++++ 6 files changed, 83 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 9fda7c94913f..e776ab2dc572 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -22,6 +22,7 @@ enum amp_mgr_state { READ_LOC_AMP_INFO, READ_LOC_AMP_ASSOC, + READ_LOC_AMP_ASSOC_FINAL, }; struct amp_mgr { @@ -134,6 +135,7 @@ extern struct mutex amp_mgr_list_lock; void amp_mgr_get(struct amp_mgr *mgr); int amp_mgr_put(struct amp_mgr *mgr); +u8 __next_ident(struct amp_mgr *mgr); struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct sk_buff *skb); struct amp_mgr *amp_mgr_lookup_by_state(u8 state); @@ -141,5 +143,6 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data); void a2mp_discover_amp(struct l2cap_chan *chan); void a2mp_send_getinfo_rsp(struct hci_dev *hdev); void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status); +void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status); #endif /* __A2MP_H */ diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 8f8032965eaf..70496c07afaa 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -37,6 +37,8 @@ int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type); void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr); void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); +void amp_read_loc_assoc_final_data(struct hci_dev *hdev, + struct hci_conn *hcon); void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, struct hci_conn *hcon); void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 0967f9e33750..ab58b81fb7c5 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -508,6 +508,8 @@ struct l2cap_chan { __u32 remote_acc_lat; __u32 remote_flush_to; + __u8 ctrl_id; + struct delayed_work chan_timer; struct delayed_work retrans_timer; struct delayed_work monitor_timer; diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 28d1246958be..375a67f501d0 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -67,7 +67,7 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) kfree(cmd); } -static u8 __next_ident(struct amp_mgr *mgr) +u8 __next_ident(struct amp_mgr *mgr) { if (++mgr->ident == 0) mgr->ident = 1; @@ -420,6 +420,8 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id); + mgr->bredr_chan->ctrl_id = rsp->id; + amp_create_phylink(hdev, mgr, hcon); done: @@ -876,6 +878,43 @@ void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status) kfree(rsp); } +void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status) +{ + struct amp_mgr *mgr; + struct amp_assoc *loc_assoc = &hdev->loc_assoc; + struct a2mp_physlink_req *req; + struct l2cap_chan *bredr_chan; + size_t len; + + mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC_FINAL); + if (!mgr) + return; + + len = sizeof(*req) + loc_assoc->len; + + BT_DBG("%s mgr %p assoc_len %zu", hdev->name, mgr, len); + + req = kzalloc(len, GFP_KERNEL); + if (!req) { + amp_mgr_put(mgr); + return; + } + + bredr_chan = mgr->bredr_chan; + if (!bredr_chan) + goto clean; + + req->local_id = hdev->id; + req->remote_id = bredr_chan->ctrl_id; + memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len); + + a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req); + +clean: + amp_mgr_put(mgr); + kfree(req); +} + void a2mp_discover_amp(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 5895ad0d2ac9..4f7b2647d5e9 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -233,6 +233,21 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); } +void amp_read_loc_assoc_final_data(struct hci_dev *hdev, + struct hci_conn *hcon) +{ + struct hci_cp_read_local_amp_assoc cp; + struct amp_mgr *mgr = hcon->amp_mgr; + + cp.phy_handle = hcon->handle; + cp.len_so_far = cpu_to_le16(0); + cp.max_len = cpu_to_le16(hdev->amp_assoc_size); + + mgr->state = READ_LOC_AMP_ASSOC_FINAL; + + /* Read Local AMP Assoc final link information data */ + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); +} /* Write AMP Assoc data fragments, returns true with last fragment written*/ static bool amp_write_rem_assoc_frag(struct hci_dev *hdev, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0b7ba1e39d45..d702ba1c171c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -901,6 +901,7 @@ static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev, a2mp_rsp: /* Send A2MP Rsp when all fragments are received */ a2mp_send_getampassoc_rsp(hdev, rp->status); + a2mp_send_create_phy_link_req(hdev, rp->status); } static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, @@ -3641,6 +3642,22 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) } } +static void hci_chan_selected_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_channel_selected *ev = (void *) skb->data; + struct hci_conn *hcon; + + BT_DBG("%s handle 0x%2.2x", hdev->name, ev->phy_handle); + + skb_pull(skb, sizeof(*ev)); + + hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle); + if (!hcon) + return; + + amp_read_loc_assoc_final_data(hdev, hcon); +} + void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_event_hdr *hdr = (void *) skb->data; @@ -3805,6 +3822,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_le_meta_evt(hdev, skb); break; + case HCI_EV_CHANNEL_SELECTED: + hci_chan_selected_evt(hdev, skb); + break; + case HCI_EV_REMOTE_OOB_DATA_REQUEST: hci_remote_oob_data_request_evt(hdev, skb); break; -- cgit v1.2.3 From dffa387110025801862d7ad09f4e850d06ff55a9 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:23 +0300 Subject: Bluetooth: AMP: Accept Physical Link When receiving A2MP Create Physical Link message execute HCI Accept Physical Link command to AMP controller. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 ++ net/bluetooth/a2mp.c | 5 +---- net/bluetooth/amp.c | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 70496c07afaa..1b06d7b01359 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -41,6 +41,8 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev, struct hci_conn *hcon); void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, struct hci_conn *hcon); +void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, + struct hci_conn *hcon); void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 375a67f501d0..dbfdbbb9707c 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -453,12 +453,9 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, goto send_rsp; } - /* TODO process physlink create */ - hcon = phylink_add(hdev, mgr, req->local_id); if (hcon) { - BT_DBG("hcon %p", hcon); - + amp_accept_phylink(hdev, mgr, hcon); rsp.status = A2MP_STATUS_SUCCESS; } else { rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 4f7b2647d5e9..845e43073c40 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -346,3 +346,22 @@ void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, hci_send_cmd(hdev, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp); } + +void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, + struct hci_conn *hcon) +{ + struct hci_cp_accept_phy_link cp; + + cp.phy_handle = hcon->handle; + + BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon, + hcon->handle); + + if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len, + &cp.key_type)) { + BT_DBG("Cannot create link key"); + return; + } + + hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp); +} -- cgit v1.2.3 From 0b26ab9dce74f8ac77d7eef0d683ab1d527e45b1 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:24 +0300 Subject: Bluetooth: AMP: Handle Accept phylink command status evt When receiving HCI Command Status event for Accept Physical Link execute HCI Write Remote AMP Assoc with data saved from A2MP Create Physical Link Request. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 1 + net/bluetooth/a2mp.c | 32 ++++++++++++++++++++++++++++++++ net/bluetooth/amp.c | 2 +- net/bluetooth/hci_event.c | 20 ++++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 1b06d7b01359..b1e54903dd42 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -25,6 +25,7 @@ struct amp_ctrl { }; int amp_ctrl_put(struct amp_ctrl *ctrl); +void amp_ctrl_get(struct amp_ctrl *ctrl); struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr); struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id); void amp_ctrl_list_flush(struct amp_mgr *mgr); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index dbfdbbb9707c..47565d28b27f 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -438,6 +438,7 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_physlink_rsp rsp; struct hci_dev *hdev; struct hci_conn *hcon; + struct amp_ctrl *ctrl; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; @@ -453,6 +454,37 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, goto send_rsp; } + ctrl = amp_ctrl_lookup(mgr, rsp.remote_id); + if (!ctrl) { + ctrl = amp_ctrl_add(mgr); + if (ctrl) { + amp_ctrl_get(ctrl); + } else { + rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; + goto send_rsp; + } + } + + if (ctrl) { + u8 *assoc, assoc_len = le16_to_cpu(hdr->len) - sizeof(*req); + + ctrl->id = rsp.remote_id; + + assoc = kzalloc(assoc_len, GFP_KERNEL); + if (!assoc) { + amp_ctrl_put(ctrl); + return -ENOMEM; + } + + memcpy(assoc, req->amp_assoc, assoc_len); + ctrl->assoc = assoc; + ctrl->assoc_len = assoc_len; + ctrl->assoc_rem_len = assoc_len; + ctrl->assoc_len_so_far = 0; + + amp_ctrl_put(ctrl); + } + hcon = phylink_add(hdev, mgr, req->local_id); if (hcon) { amp_accept_phylink(hdev, mgr, hcon); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 845e43073c40..5dab2d1c7c82 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -19,7 +19,7 @@ #include /* Remote AMP Controllers interface */ -static void amp_ctrl_get(struct amp_ctrl *ctrl) +void amp_ctrl_get(struct amp_ctrl *ctrl) { BT_DBG("ctrl %p orig refcnt %d", ctrl, atomic_read(&ctrl->kref.refcount)); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d702ba1c171c..7e716698fe64 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1729,6 +1729,22 @@ static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) amp_write_remote_assoc(hdev, cp->phy_handle); } +static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_accept_phy_link *cp; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_ACCEPT_PHY_LINK); + if (!cp) + return; + + amp_write_remote_assoc(hdev, cp->phy_handle); +} + static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2551,6 +2567,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_create_phylink(hdev, ev->status); break; + case HCI_OP_ACCEPT_PHY_LINK: + hci_cs_accept_phylink(hdev, ev->status); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; -- cgit v1.2.3 From d945df256a7b2446227fafae2f89db85597412ef Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 25 Sep 2012 12:49:46 +0300 Subject: bluetooth: Remove unneeded batostr function batostr is not needed anymore since for printing Bluetooth addresses we use %pMR specifier. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/bluetooth.h | 1 - net/bluetooth/lib.c | 14 -------------- 2 files changed, 15 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index ede036977ae8..2554b3f5222a 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -180,7 +180,6 @@ static inline void bacpy(bdaddr_t *dst, bdaddr_t *src) } void baswap(bdaddr_t *dst, bdaddr_t *src); -char *batostr(bdaddr_t *ba); /* Common socket structures and functions */ diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index e1c97527e16c..b3fbc73516c4 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -41,20 +41,6 @@ void baswap(bdaddr_t *dst, bdaddr_t *src) } EXPORT_SYMBOL(baswap); -char *batostr(bdaddr_t *ba) -{ - static char str[2][18]; - static int i = 1; - - i ^= 1; - sprintf(str[i], "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", - ba->b[5], ba->b[4], ba->b[3], - ba->b[2], ba->b[1], ba->b[0]); - - return str[i]; -} -EXPORT_SYMBOL(batostr); - /* Bluetooth error codes to Unix errno mapping */ int bt_to_errno(__u16 code) { -- cgit v1.2.3 From dfee1ebc0e363ff1dc233c4a5246bf3e7f5c5ca6 Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Mon, 1 Oct 2012 19:37:31 +0200 Subject: Bluetooth: remove unused member of hci_dev. This patch removes core_data member from hci_dev struct as it is unused. Signed-off-by: Rami Rosen Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ea1f9340324d..90ae4f0a4fdc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -262,8 +262,6 @@ struct hci_dev { struct sk_buff_head driver_init; - void *core_data; - atomic_t promisc; struct dentry *debugfs; -- cgit v1.2.3 From fa4ebc66c432d0e0ec947cb754d4144c4a681f28 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 5 Oct 2012 16:56:55 +0300 Subject: Bluetooth: AMP: Factor out amp_ctrl_add Add ctrl_id parameter to amp_ctrl_add since we always set it after function ctrl is created. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 +- net/bluetooth/a2mp.c | 8 ++------ net/bluetooth/amp.c | 7 ++++--- 3 files changed, 7 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index b1e54903dd42..ae2c3e536473 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -26,7 +26,7 @@ struct amp_ctrl { int amp_ctrl_put(struct amp_ctrl *ctrl); void amp_ctrl_get(struct amp_ctrl *ctrl); -struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr); +struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id); struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id); void amp_ctrl_list_flush(struct amp_mgr *mgr); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index d4946b591b71..88a4b583d218 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -316,12 +316,10 @@ static int a2mp_getinfo_rsp(struct amp_mgr *mgr, struct sk_buff *skb, if (rsp->status) return -EINVAL; - ctrl = amp_ctrl_add(mgr); + ctrl = amp_ctrl_add(mgr, rsp->id); if (!ctrl) return -ENOMEM; - ctrl->id = rsp->id; - req.id = rsp->id; a2mp_send(mgr, A2MP_GETAMPASSOC_REQ, __next_ident(mgr), sizeof(req), &req); @@ -461,7 +459,7 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, ctrl = amp_ctrl_lookup(mgr, rsp.remote_id); if (!ctrl) { - ctrl = amp_ctrl_add(mgr); + ctrl = amp_ctrl_add(mgr, rsp.remote_id); if (ctrl) { amp_ctrl_get(ctrl); } else { @@ -474,8 +472,6 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, size_t assoc_len = le16_to_cpu(hdr->len) - sizeof(*req); u8 *assoc; - ctrl->id = rsp.remote_id; - assoc = kzalloc(assoc_len, GFP_KERNEL); if (!assoc) { amp_ctrl_put(ctrl); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index b6e1c3ac74f1..2fc5562a84b9 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -45,7 +45,7 @@ int amp_ctrl_put(struct amp_ctrl *ctrl) return kref_put(&ctrl->kref, &_ctrl_destroy); } -struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr) +struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id) { struct amp_ctrl *ctrl; @@ -53,12 +53,13 @@ struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr) if (!ctrl) return NULL; + kref_init(&ctrl->kref); + ctrl->id = id; + mutex_lock(&mgr->amp_ctrls_lock); list_add(&ctrl->list, &mgr->amp_ctrls); mutex_unlock(&mgr->amp_ctrls_lock); - kref_init(&ctrl->kref); - BT_DBG("mgr %p ctrl %p", mgr, ctrl); return ctrl; -- cgit v1.2.3 From a0c234fe8972aa6a5afe2db6c27a3f5d5fbd88e7 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 5 Oct 2012 16:56:56 +0300 Subject: Bluetooth: AMP: Factor out phylink_add Add direction parameter to phylink_add since it is anyway set later. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 +- net/bluetooth/a2mp.c | 4 ++-- net/bluetooth/amp.c | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index ae2c3e536473..2e7c79ea0463 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -31,7 +31,7 @@ struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id); void amp_ctrl_list_flush(struct amp_mgr *mgr); struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, - u8 remote_id); + u8 remote_id, bool out); int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 88a4b583d218..3ff4dc928bf5 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -417,7 +417,7 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, if (!hdev) return -EINVAL; - hcon = phylink_add(hdev, mgr, rsp->id); + hcon = phylink_add(hdev, mgr, rsp->id, true); if (!hcon) goto done; @@ -487,7 +487,7 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, amp_ctrl_put(ctrl); } - hcon = phylink_add(hdev, mgr, req->local_id); + hcon = phylink_add(hdev, mgr, req->local_id, false); if (hcon) { amp_accept_phylink(hdev, mgr, hcon); rsp.status = A2MP_STATUS_SUCCESS; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 2fc5562a84b9..59da0f15818e 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -108,7 +108,7 @@ static u8 __next_handle(struct amp_mgr *mgr) } struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, - u8 remote_id) + u8 remote_id, bool out) { bdaddr_t *dst = mgr->l2cap_conn->dst; struct hci_conn *hcon; @@ -117,12 +117,14 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, if (!hcon) return NULL; + BT_DBG("hcon %p dst %pMR", hcon, dst); + hcon->state = BT_CONNECT; - hcon->out = true; hcon->attempt++; hcon->handle = __next_handle(mgr); hcon->remote_id = remote_id; hcon->amp_mgr = mgr; + hcon->out = out; return hcon; } -- cgit v1.2.3 From 0b4558e388e72b6d088a057833bafb816ff8af85 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 5 Oct 2012 16:56:58 +0300 Subject: Bluetooth: Adjust L2CAP Max PDU size for AMP packets Maximum PDU size is defined by new BT Spec as 1492 octets. Signed-off-by: Andrei Emeltchenko Reviewed-by: Mat Martineau Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index ab58b81fb7c5..7002f0d656ed 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -38,7 +38,7 @@ #define L2CAP_DEFAULT_MAX_TX 3 #define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */ #define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */ -#define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */ +#define L2CAP_DEFAULT_MAX_PDU_SIZE 1492 /* Sized for AMP packet */ #define L2CAP_DEFAULT_ACK_TO 200 #define L2CAP_DEFAULT_MAX_SDU_SIZE 0xFFFF #define L2CAP_DEFAULT_SDU_ITIME 0xFFFFFFFF -- cgit v1.2.3 From 8936fa6d1c202abeb94c51c68897342e8714dd69 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 8 Oct 2012 11:14:41 +0300 Subject: Bluetooth: L2CAP: Fix using default Flush Timeout for EFS There are two Flush Timeouts: one is old Flush Timeot Option which is 2 octets and the second is Flush Timeout inside EFS which is 4 octets long. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 3 ++- net/bluetooth/l2cap_core.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7002f0d656ed..caab98c45121 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -32,7 +32,8 @@ /* L2CAP defaults */ #define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_MIN_MTU 48 -#define L2CAP_DEFAULT_FLUSH_TO 0xffff +#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF +#define L2CAP_EFS_DEFAULT_FLUSH_TO 0xFFFFFFFF #define L2CAP_DEFAULT_TX_WINDOW 63 #define L2CAP_DEFAULT_EXT_WINDOW 0x3FFF #define L2CAP_DEFAULT_MAX_TX 3 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d605bbfddec1..d42cdb145c6d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -504,7 +504,7 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->local_msdu = L2CAP_DEFAULT_MAX_SDU_SIZE; chan->local_sdu_itime = L2CAP_DEFAULT_SDU_ITIME; chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT; - chan->local_flush_to = L2CAP_DEFAULT_FLUSH_TO; + chan->local_flush_to = L2CAP_EFS_DEFAULT_FLUSH_TO; l2cap_chan_hold(chan); @@ -2727,7 +2727,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) efs.msdu = cpu_to_le16(chan->local_msdu); efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); efs.acc_lat = __constant_cpu_to_le32(L2CAP_DEFAULT_ACC_LAT); - efs.flush_to = __constant_cpu_to_le32(L2CAP_DEFAULT_FLUSH_TO); + efs.flush_to = __constant_cpu_to_le32(L2CAP_EFS_DEFAULT_FLUSH_TO); break; case L2CAP_MODE_STREAMING: @@ -2744,7 +2744,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) } l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs), - (unsigned long) &efs); + (unsigned long) &efs); } static void l2cap_ack_timeout(struct work_struct *work) -- cgit v1.2.3 From 53502d69be49e3dd5bc95ab0f2deeaea260bd617 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 10 Oct 2012 17:38:27 +0300 Subject: Bluetooth: AMP: Handle AMP_LINK timeout When AMP_LINK timeouts execute HCI_OP_DISCONN_PHY_LINK as analog to HCI_OP_DISCONNECT for ACL_LINK. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 32 +++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 90ae4f0a4fdc..dfa108c4abec 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -285,6 +285,8 @@ struct hci_dev { int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg); }; +#define HCI_PHY_HANDLE(handle) (handle & 0xff) + struct hci_conn { struct list_head list; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 53202f6733ae..64875794dd9b 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -130,6 +130,20 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason) hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp); } +static void hci_amp_disconn(struct hci_conn *conn, __u8 reason) +{ + struct hci_cp_disconn_phy_link cp; + + BT_DBG("hcon %p", conn); + + conn->state = BT_DISCONN; + + cp.phy_handle = HCI_PHY_HANDLE(conn->handle); + cp.reason = reason; + hci_send_cmd(conn->hdev, HCI_OP_DISCONN_PHY_LINK, + sizeof(cp), &cp); +} + static void hci_add_sco(struct hci_conn *conn, __u16 handle) { struct hci_dev *hdev = conn->hdev; @@ -230,11 +244,24 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status) } } +static void hci_conn_disconnect(struct hci_conn *conn) +{ + __u8 reason = hci_proto_disconn_ind(conn); + + switch (conn->type) { + case ACL_LINK: + hci_acl_disconn(conn, reason); + break; + case AMP_LINK: + hci_amp_disconn(conn, reason); + break; + } +} + static void hci_conn_timeout(struct work_struct *work) { struct hci_conn *conn = container_of(work, struct hci_conn, disc_work.work); - __u8 reason; BT_DBG("hcon %p state %s", conn, state_to_string(conn->state)); @@ -253,8 +280,7 @@ static void hci_conn_timeout(struct work_struct *work) break; case BT_CONFIG: case BT_CONNECTED: - reason = hci_proto_disconn_ind(conn); - hci_acl_disconn(conn, reason); + hci_conn_disconnect(conn); break; default: conn->state = BT_CLOSED; -- cgit v1.2.3 From 42c4e53e7ac3d4069105e852d1ee24e6ee9e57b8 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 10 Oct 2012 17:38:28 +0300 Subject: Bluetooth: AMP: Add handle to hci_chan structure hci_chan will be identified by handle used in logical link creation process. This handle is used in AMP ACL-U packet handle field. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_conn.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index dfa108c4abec..b697ef342020 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -350,7 +350,7 @@ struct hci_conn { struct hci_chan { struct list_head list; - + __u16 handle; struct hci_conn *conn; struct sk_buff_head data_q; unsigned int sent; @@ -567,6 +567,7 @@ void hci_conn_check_pending(struct hci_dev *hdev); struct hci_chan *hci_chan_create(struct hci_conn *conn); void hci_chan_del(struct hci_chan *chan); void hci_chan_list_flush(struct hci_conn *conn); +struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 dst_type, __u8 sec_level, __u8 auth_type); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 64875794dd9b..fe646211c61f 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -989,3 +989,35 @@ void hci_chan_list_flush(struct hci_conn *conn) list_for_each_entry_safe(chan, n, &conn->chan_list, list) hci_chan_del(chan); } + +static struct hci_chan *__hci_chan_lookup_handle(struct hci_conn *hcon, + __u16 handle) +{ + struct hci_chan *hchan; + + list_for_each_entry(hchan, &hcon->chan_list, list) { + if (hchan->handle == handle) + return hchan; + } + + return NULL; +} + +struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *hcon; + struct hci_chan *hchan = NULL; + + rcu_read_lock(); + + list_for_each_entry_rcu(hcon, &h->list, list) { + hchan = __hci_chan_lookup_handle(hcon, handle); + if (hchan) + break; + } + + rcu_read_unlock(); + + return hchan; +} -- cgit v1.2.3 From bd1eb66ba4eee21de3be24212b135f57101ad930 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 10 Oct 2012 17:38:30 +0300 Subject: Bluetooth: AMP: Handle AMP_LINK connection AMP_LINK represents physical link between AMP controllers. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 13 +++++++++++++ net/bluetooth/hci_core.c | 22 +++++++++++++++++++--- net/bluetooth/hci_event.c | 1 + 3 files changed, 33 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b697ef342020..d5ed054d77cf 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -73,6 +73,7 @@ struct discovery_state { struct hci_conn_hash { struct list_head list; unsigned int acl_num; + unsigned int amp_num; unsigned int sco_num; unsigned int le_num; }; @@ -449,6 +450,9 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) case ACL_LINK: h->acl_num++; break; + case AMP_LINK: + h->amp_num++; + break; case LE_LINK: h->le_num++; break; @@ -470,6 +474,9 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) case ACL_LINK: h->acl_num--; break; + case AMP_LINK: + h->amp_num--; + break; case LE_LINK: h->le_num--; break; @@ -486,6 +493,8 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type) switch (type) { case ACL_LINK: return h->acl_num; + case AMP_LINK: + return h->amp_num; case LE_LINK: return h->le_num; case SCO_LINK: @@ -801,6 +810,10 @@ static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason) sco_disconn_cfm(conn, reason); break; + /* L2CAP would be handled for BREDR chan */ + case AMP_LINK: + break; + default: BT_ERR("unknown link type %d", conn->type); break; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index bd26cb52aaa9..2e72c410fb47 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2379,6 +2379,9 @@ static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, case ACL_LINK: cnt = hdev->acl_cnt; break; + case AMP_LINK: + cnt = hdev->block_cnt; + break; case SCO_LINK: case ESCO_LINK: cnt = hdev->sco_cnt; @@ -2508,11 +2511,19 @@ static void hci_sched_acl_blk(struct hci_dev *hdev) struct hci_chan *chan; struct sk_buff *skb; int quote; + u8 type; __check_timeout(hdev, cnt); + BT_DBG("%s", hdev->name); + + if (hdev->dev_type == HCI_AMP) + type = AMP_LINK; + else + type = ACL_LINK; + while (hdev->block_cnt > 0 && - (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { + (chan = hci_chan_sent(hdev, type, "e))) { u32 priority = (skb_peek(&chan->data_q))->priority; while (quote > 0 && (skb = skb_peek(&chan->data_q))) { int blocks; @@ -2545,14 +2556,19 @@ static void hci_sched_acl_blk(struct hci_dev *hdev) } if (cnt != hdev->block_cnt) - hci_prio_recalculate(hdev, ACL_LINK); + hci_prio_recalculate(hdev, type); } static void hci_sched_acl(struct hci_dev *hdev) { BT_DBG("%s", hdev->name); - if (!hci_conn_num(hdev, ACL_LINK)) + /* No ACL link over BR/EDR controller */ + if (!hci_conn_num(hdev, ACL_LINK) && hdev->dev_type == HCI_BREDR) + return; + + /* No AMP link over AMP controller */ + if (!hci_conn_num(hdev, AMP_LINK) && hdev->dev_type == HCI_AMP) return; switch (hdev->flow_ctl_mode) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 5c0b6c161a01..0383635f91fb 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2733,6 +2733,7 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb) switch (conn->type) { case ACL_LINK: + case AMP_LINK: hdev->block_cnt += block_count; if (hdev->block_cnt > hdev->num_blocks) hdev->block_cnt = hdev->num_blocks; -- cgit v1.2.3 From 716e4ab5c966327988e21e6137c14e457cfca690 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 10 Oct 2012 17:38:31 +0300 Subject: Bluetooth: AMP: Hanlde AMP_LINK case in conn_put Handle AMP link when setting up disconnect timeout. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d5ed054d77cf..9fe8e2dec870 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -605,7 +605,10 @@ static inline void hci_conn_put(struct hci_conn *conn) if (atomic_dec_and_test(&conn->refcnt)) { unsigned long timeo; - if (conn->type == ACL_LINK || conn->type == LE_LINK) { + + switch (conn->type) { + case ACL_LINK: + case LE_LINK: del_timer(&conn->idle_timer); if (conn->state == BT_CONNECTED) { timeo = conn->disc_timeout; @@ -614,12 +617,20 @@ static inline void hci_conn_put(struct hci_conn *conn) } else { timeo = msecs_to_jiffies(10); } - } else { + break; + + case AMP_LINK: + timeo = conn->disc_timeout; + break; + + default: timeo = msecs_to_jiffies(10); + break; } + cancel_delayed_work(&conn->disc_work); queue_delayed_work(conn->hdev->workqueue, - &conn->disc_work, timeo); + &conn->disc_work, timeo); } } -- cgit v1.2.3 From 2dc4e5105f012bda7eef2f459ed3d5299ded9672 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Fri, 12 Oct 2012 19:35:24 +0800 Subject: Bluetooth: Add chan->ops->defer() When DEFER_SETUP is set defer() will trigger an authorization request to the userspace. l2cap_chan_no_defer() is meant to be used when one does not want to support DEFER_SETUP (A2MP for example). Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 5 +++++ net/bluetooth/a2mp.c | 1 + net/bluetooth/l2cap_core.c | 10 +++------- net/bluetooth/l2cap_sock.c | 10 ++++++++++ 4 files changed, 19 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index caab98c45121..6e23afdf65c1 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -541,6 +541,7 @@ struct l2cap_ops { void (*state_change) (struct l2cap_chan *chan, int state); void (*ready) (struct l2cap_chan *chan); + void (*defer) (struct l2cap_chan *chan); struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, unsigned long len, int nb); }; @@ -748,6 +749,10 @@ static inline void l2cap_chan_no_ready(struct l2cap_chan *chan) { } +static inline void l2cap_chan_no_defer(struct l2cap_chan *chan) +{ +} + extern bool disable_ertm; int l2cap_init_sockets(void); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 3ff4dc928bf5..7bf9a10d8e46 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -699,6 +699,7 @@ static struct l2cap_ops a2mp_chan_ops = { .new_connection = l2cap_chan_no_new_connection, .teardown = l2cap_chan_no_teardown, .ready = l2cap_chan_no_ready, + .defer = l2cap_chan_no_defer, }; static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9d84050bed2c..314d95580d73 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1120,11 +1120,9 @@ static void l2cap_conn_start(struct l2cap_conn *conn) lock_sock(sk); if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { - struct sock *parent = bt_sk(sk)->parent; rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND); rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND); - if (parent) - parent->sk_data_ready(parent, 0); + chan->ops->defer(chan); } else { __l2cap_state_change(chan, BT_CONFIG); @@ -3460,7 +3458,7 @@ static void __l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, __l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHOR_PEND; - parent->sk_data_ready(parent, 0); + chan->ops->defer(chan); } else { __l2cap_state_change(chan, BT_CONFIG); result = L2CAP_CR_SUCCESS; @@ -5523,11 +5521,9 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (!status) { if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { - struct sock *parent = bt_sk(sk)->parent; res = L2CAP_CR_PEND; stat = L2CAP_CS_AUTHOR_PEND; - if (parent) - parent->sk_data_ready(parent, 0); + chan->ops->defer(chan); } else { __l2cap_state_change(chan, BT_CONFIG); res = L2CAP_CR_SUCCESS; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index d5093b853b05..5fae2bd879a1 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1081,6 +1081,15 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan) release_sock(sk); } +static void l2cap_sock_defer_cb(struct l2cap_chan *chan) +{ + struct sock *sk = chan->data; + struct sock *parent = bt_sk(sk)->parent; + + if (parent) + parent->sk_data_ready(parent, 0); +} + static struct l2cap_ops l2cap_chan_ops = { .name = "L2CAP Socket Interface", .new_connection = l2cap_sock_new_connection_cb, @@ -1089,6 +1098,7 @@ static struct l2cap_ops l2cap_chan_ops = { .teardown = l2cap_sock_teardown_cb, .state_change = l2cap_sock_state_change_cb, .ready = l2cap_sock_ready_cb, + .defer = l2cap_sock_defer_cb, .alloc_skb = l2cap_sock_alloc_skb_cb, }; -- cgit v1.2.3 From d73a098804b4d1d254b1caf1d114e5b707dee060 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 15 Oct 2012 11:58:40 +0300 Subject: Bluetooth: AMP: Handle complete frames in l2cap Check flags type in switch statement and handle new frame type ACL_COMPLETE used for High Speed data over AMP. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 1 + net/bluetooth/l2cap_core.c | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 77b6a197a6f6..88cbbda61027 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -198,6 +198,7 @@ enum { #define ACL_START_NO_FLUSH 0x00 #define ACL_CONT 0x01 #define ACL_START 0x02 +#define ACL_COMPLETE 0x03 #define ACL_ACTIVE_BCAST 0x04 #define ACL_PICO_BCAST 0x08 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fca407e17f16..8faa3121bb44 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5568,6 +5568,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) { struct l2cap_conn *conn = hcon->l2cap_data; + struct l2cap_hdr *hdr; + int len; if (!conn) conn = l2cap_conn_add(hcon, 0); @@ -5577,10 +5579,10 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); - if (!(flags & ACL_CONT)) { - struct l2cap_hdr *hdr; - int len; - + switch (flags) { + case ACL_START: + case ACL_START_NO_FLUSH: + case ACL_COMPLETE: if (conn->rx_len) { BT_ERR("Unexpected start frame (len %d)", skb->len); kfree_skb(conn->rx_skb); @@ -5622,7 +5624,9 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), skb->len); conn->rx_len = len - skb->len; - } else { + break; + + case ACL_CONT: BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); if (!conn->rx_len) { @@ -5650,6 +5654,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) l2cap_recv_frame(conn, conn->rx_skb); conn->rx_skb = NULL; } + break; } drop: -- cgit v1.2.3 From 6863255bd0e48bc41ae5a066d5c771801e92735a Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 15 Oct 2012 14:52:41 +0200 Subject: cfg80211/mac80211: avoid state mishmash on deauth Avoid situation when we are on associate state in mac80211 and on disassociate state in cfg80211. This can results on crash during modules unload (like showed on this thread: http://marc.info/?t=134373976300001&r=1&w=2) and possibly other problems. Reported-by: Pedro Francisco Cc: stable@vger.kernel.org Signed-off-by: Stanislaw Gruszka Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 1 + net/mac80211/mlme.c | 5 +++-- net/wireless/mlme.c | 12 +++--------- 3 files changed, 7 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1b4989082244..f8cd4cf3fad8 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1218,6 +1218,7 @@ struct cfg80211_deauth_request { const u8 *ie; size_t ie_len; u16 reason_code; + bool local_state_change; }; /** diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e714ed8bb198..e510a33fec76 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3549,6 +3549,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; + bool tx = !req->local_state_change; mutex_lock(&ifmgd->mtx); @@ -3565,12 +3566,12 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, if (ifmgd->associated && ether_addr_equal(ifmgd->associated->bssid, req->bssid)) { ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, - req->reason_code, true, frame_buf); + req->reason_code, tx, frame_buf); } else { drv_mgd_prepare_tx(sdata->local, sdata); ieee80211_send_deauth_disassoc(sdata, req->bssid, IEEE80211_STYPE_DEAUTH, - req->reason_code, true, + req->reason_code, tx, frame_buf); } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 8016fee0752b..904a7f368325 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -457,20 +457,14 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, .reason_code = reason, .ie = ie, .ie_len = ie_len, + .local_state_change = local_state_change, }; ASSERT_WDEV_LOCK(wdev); - if (local_state_change) { - if (wdev->current_bss && - ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - } - + if (local_state_change && (!wdev->current_bss || + !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))) return 0; - } return rdev->ops->deauth(&rdev->wiphy, dev, &req); } -- cgit v1.2.3 From f706adfeade767d2194c9f39c0f75e944b0bdd23 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 18 Oct 2012 13:16:19 +0300 Subject: Bluetooth: AMP: Get amp_mgr reference in HS hci_conn When assigning amp_mgr in hci_conn (type AMP_LINK) get also reference. In hci_conn_del those references would be put for both conn types AMP_LINK and ACL_LINK associated with amp_mgr. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 +- net/bluetooth/a2mp.c | 4 +++- net/bluetooth/amp.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index e776ab2dc572..42f21766c538 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -133,7 +133,7 @@ struct a2mp_physlink_rsp { extern struct list_head amp_mgr_list; extern struct mutex amp_mgr_list_lock; -void amp_mgr_get(struct amp_mgr *mgr); +struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr); int amp_mgr_put(struct amp_mgr *mgr); u8 __next_ident(struct amp_mgr *mgr); struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 7bf9a10d8e46..d5136cfb57e2 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -751,11 +751,13 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) } /* AMP Manager functions */ -void amp_mgr_get(struct amp_mgr *mgr) +struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr) { BT_DBG("mgr %p orig refcnt %d", mgr, atomic_read(&mgr->kref.refcount)); kref_get(&mgr->kref); + + return mgr; } static void amp_mgr_destroy(struct kref *kref) diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 59da0f15818e..231d7ef53ecb 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -123,7 +123,7 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, hcon->attempt++; hcon->handle = __next_handle(mgr); hcon->remote_id = remote_id; - hcon->amp_mgr = mgr; + hcon->amp_mgr = amp_mgr_get(mgr); hcon->out = out; return hcon; -- cgit v1.2.3 From 49655bb8a51565f0375a4f783334c9de78134be5 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 29 Sep 2012 20:29:49 +0200 Subject: bcma: just do the necessary things in early register on SoCs Some parts of the initialization for chip common and the pcie core are accessing the sprom struct, but it is not initialized at that stage. Just do the necessary thing in the early register on SoCs and not the complete initialization to read out the nvram from the flash chip. After it is possible to read out the nvram, the sprom should be parsed from it and the full initialization of the cores should be run. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_chipcommon.c | 23 ++++++++++++++++++----- drivers/bcma/driver_chipcommon_pmu.c | 5 ++++- drivers/bcma/driver_mips.c | 26 +++++++++++++++++++------- drivers/bcma/main.c | 8 ++++---- include/linux/bcma/bcma_driver_chipcommon.h | 3 +++ include/linux/bcma/bcma_driver_mips.h | 3 +++ 6 files changed, 51 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c index a4c3ebcc4c86..ffd74e51f02d 100644 --- a/drivers/bcma/driver_chipcommon.c +++ b/drivers/bcma/driver_chipcommon.c @@ -22,12 +22,9 @@ static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset, return value; } -void bcma_core_chipcommon_init(struct bcma_drv_cc *cc) +void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc) { - u32 leddc_on = 10; - u32 leddc_off = 90; - - if (cc->setup_done) + if (cc->early_setup_done) return; if (cc->core->id.rev >= 11) @@ -36,6 +33,22 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc) if (cc->core->id.rev >= 35) cc->capabilities_ext = bcma_cc_read32(cc, BCMA_CC_CAP_EXT); + if (cc->capabilities & BCMA_CC_CAP_PMU) + bcma_pmu_early_init(cc); + + cc->early_setup_done = true; +} + +void bcma_core_chipcommon_init(struct bcma_drv_cc *cc) +{ + u32 leddc_on = 10; + u32 leddc_off = 90; + + if (cc->setup_done) + return; + + bcma_core_chipcommon_early_init(cc); + if (cc->core->id.rev >= 20) { bcma_cc_write32(cc, BCMA_CC_GPIOPULLUP, 0); bcma_cc_write32(cc, BCMA_CC_GPIOPULLDOWN, 0); diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index 201faf106b3f..a63ddd9c70eb 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -144,7 +144,7 @@ static void bcma_pmu_workarounds(struct bcma_drv_cc *cc) } } -void bcma_pmu_init(struct bcma_drv_cc *cc) +void bcma_pmu_early_init(struct bcma_drv_cc *cc) { u32 pmucap; @@ -153,7 +153,10 @@ void bcma_pmu_init(struct bcma_drv_cc *cc) bcma_debug(cc->core->bus, "Found rev %u PMU (capabilities 0x%08X)\n", cc->pmu.rev, pmucap); +} +void bcma_pmu_init(struct bcma_drv_cc *cc) +{ if (cc->pmu.rev == 1) bcma_cc_mask32(cc, BCMA_CC_PMU_CTL, ~BCMA_CC_PMU_CTL_NOILPONW); diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index cc65b45b4368..f44f1fb67011 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -212,16 +212,33 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore) } } +void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) +{ + struct bcma_bus *bus = mcore->core->bus; + + if (mcore->early_setup_done) + return; + + bcma_chipco_serial_init(&bus->drv_cc); + bcma_core_mips_flash_detect(mcore); + + mcore->early_setup_done = true; +} + void bcma_core_mips_init(struct bcma_drv_mips *mcore) { struct bcma_bus *bus; struct bcma_device *core; bus = mcore->core->bus; + if (mcore->setup_done) + return; + bcma_info(bus, "Initializing MIPS core...\n"); - if (!mcore->setup_done) - mcore->assigned_irqs = 1; + bcma_core_mips_early_init(mcore); + + mcore->assigned_irqs = 1; /* Assign IRQs to all cores on the bus */ list_for_each_entry(core, &bus->cores, list) { @@ -256,10 +273,5 @@ void bcma_core_mips_init(struct bcma_drv_mips *mcore) bcma_info(bus, "IRQ reconfiguration done\n"); bcma_core_mips_dump_irq(bus); - if (mcore->setup_done) - return; - - bcma_chipco_serial_init(&bus->drv_cc); - bcma_core_mips_flash_detect(mcore); mcore->setup_done = true; } diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 432aeeedfd5e..bea2d7cfa6c2 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -274,18 +274,18 @@ int __init bcma_bus_early_register(struct bcma_bus *bus, return -1; } - /* Init CC core */ + /* Early init CC core */ core = bcma_find_core(bus, bcma_cc_core_id(bus)); if (core) { bus->drv_cc.core = core; - bcma_core_chipcommon_init(&bus->drv_cc); + bcma_core_chipcommon_early_init(&bus->drv_cc); } - /* Init MIPS core */ + /* Early init MIPS core */ core = bcma_find_core(bus, BCMA_CORE_MIPS_74K); if (core) { bus->drv_mips.core = core; - bcma_core_mips_init(&bus->drv_mips); + bcma_core_mips_early_init(&bus->drv_mips); } bcma_info(bus, "Early bus registered\n"); diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 1cf1749440ac..fbde7cbd2d7d 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -552,6 +552,7 @@ struct bcma_drv_cc { u32 capabilities; u32 capabilities_ext; u8 setup_done:1; + u8 early_setup_done:1; /* Fast Powerup Delay constant */ u16 fast_pwrup_delay; struct bcma_chipcommon_pmu pmu; @@ -583,6 +584,7 @@ struct bcma_drv_cc { bcma_cc_write32(cc, offset, (bcma_cc_read32(cc, offset) & (mask)) | (set)) extern void bcma_core_chipcommon_init(struct bcma_drv_cc *cc); +extern void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc); extern void bcma_chipco_suspend(struct bcma_drv_cc *cc); extern void bcma_chipco_resume(struct bcma_drv_cc *cc); @@ -606,6 +608,7 @@ u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value); /* PMU support */ extern void bcma_pmu_init(struct bcma_drv_cc *cc); +extern void bcma_pmu_early_init(struct bcma_drv_cc *cc); extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value); diff --git a/include/linux/bcma/bcma_driver_mips.h b/include/linux/bcma/bcma_driver_mips.h index c0043645cdcb..0baf8a56b794 100644 --- a/include/linux/bcma/bcma_driver_mips.h +++ b/include/linux/bcma/bcma_driver_mips.h @@ -35,13 +35,16 @@ struct bcma_device; struct bcma_drv_mips { struct bcma_device *core; u8 setup_done:1; + u8 early_setup_done:1; unsigned int assigned_irqs; }; #ifdef CONFIG_BCMA_DRIVER_MIPS extern void bcma_core_mips_init(struct bcma_drv_mips *mcore); +extern void bcma_core_mips_early_init(struct bcma_drv_mips *mcore); #else static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { } +static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) { } #endif extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore); -- cgit v1.2.3 From 360dc31e9cc3f74d0d91a9d19b154c12592f4ffc Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 29 Sep 2012 20:33:49 +0200 Subject: bcma: mark pflash as present when available Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_mips.c | 1 + include/linux/bcma/bcma_driver_chipcommon.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index f44f1fb67011..c0fb1d45c6db 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -190,6 +190,7 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore) break; case BCMA_CC_FLASHT_PARA: bcma_debug(bus, "Found parallel flash\n"); + bus->drv_cc.pflash.present = true; bus->drv_cc.pflash.window = 0x1c000000; bus->drv_cc.pflash.window_size = 0x02000000; diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index fbde7cbd2d7d..79993969953a 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -510,6 +510,7 @@ struct bcma_chipcommon_pmu { #ifdef CONFIG_BCMA_DRIVER_MIPS struct bcma_pflash { + bool present; u8 buswidth; u32 window; u32 window_size; -- cgit v1.2.3 From cc787081bcab5a83051c2a936927634d066e7284 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 29 Sep 2012 20:33:50 +0200 Subject: bcma: add and use constants for the flash windows Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_chipcommon_sflash.c | 4 ++-- drivers/bcma/driver_mips.c | 4 ++-- include/linux/bcma/bcma_regs.h | 5 ++++- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/bcma/driver_chipcommon_sflash.c b/drivers/bcma/driver_chipcommon_sflash.c index 2c4eec2ca5a0..a0993fcd13c2 100644 --- a/drivers/bcma/driver_chipcommon_sflash.c +++ b/drivers/bcma/driver_chipcommon_sflash.c @@ -12,7 +12,7 @@ static struct resource bcma_sflash_resource = { .name = "bcma_sflash", - .start = BCMA_SFLASH, + .start = BCMA_SOC_FLASH2, .end = 0, .flags = IORESOURCE_MEM | IORESOURCE_READONLY, }; @@ -116,7 +116,7 @@ int bcma_sflash_init(struct bcma_drv_cc *cc) return -ENOTSUPP; } - sflash->window = BCMA_SFLASH; + sflash->window = BCMA_SOC_FLASH2; sflash->blocksize = e->blocksize; sflash->numblocks = e->numblocks; sflash->size = sflash->blocksize * sflash->numblocks; diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index c0fb1d45c6db..d8e2fba482e6 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -191,8 +191,8 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore) case BCMA_CC_FLASHT_PARA: bcma_debug(bus, "Found parallel flash\n"); bus->drv_cc.pflash.present = true; - bus->drv_cc.pflash.window = 0x1c000000; - bus->drv_cc.pflash.window_size = 0x02000000; + bus->drv_cc.pflash.window = BCMA_SOC_FLASH2; + bus->drv_cc.pflash.window_size = BCMA_SOC_FLASH2_SZ; if ((bcma_read32(bus->drv_cc.core, BCMA_CC_FLASH_CFG) & BCMA_CC_FLASH_CFG_DS) == 0) diff --git a/include/linux/bcma/bcma_regs.h b/include/linux/bcma/bcma_regs.h index 6c9cb93ae3de..7e8104bb7a7e 100644 --- a/include/linux/bcma/bcma_regs.h +++ b/include/linux/bcma/bcma_regs.h @@ -85,6 +85,9 @@ * (2 ZettaBytes), high 32 bits */ -#define BCMA_SFLASH 0x1c000000 +#define BCMA_SOC_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */ +#define BCMA_SOC_FLASH1_SZ 0x00400000 /* MIPS Size of Flash Region 1 */ +#define BCMA_SOC_FLASH2 0x1c000000 /* Flash Region 2 (region 1 shadowed here) */ +#define BCMA_SOC_FLASH2_SZ 0x02000000 /* Size of Flash Region 2 */ #endif /* LINUX_BCMA_REGS_H_ */ -- cgit v1.2.3 From e661b75a44cc811426ea005c3cb858e45bd73d57 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 29 Sep 2012 20:33:51 +0200 Subject: bcma: mark nflash if it is the boot flash There are some devices which are able to boot from nand flash and other are using a serial flash for booting. Add a bool to indicate that the device is booted from that flash chip and not from some other chip also connected to the SoC. This is needed to find the nvram, as it is stored on the flash the devices booted from. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_chipcommon_nflash.c | 3 +++ include/linux/bcma/bcma_driver_chipcommon.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/bcma/driver_chipcommon_nflash.c b/drivers/bcma/driver_chipcommon_nflash.c index 9042781edec3..dbda91e4dff5 100644 --- a/drivers/bcma/driver_chipcommon_nflash.c +++ b/drivers/bcma/driver_chipcommon_nflash.c @@ -32,6 +32,9 @@ int bcma_nflash_init(struct bcma_drv_cc *cc) } cc->nflash.present = true; + if (cc->core->id.rev == 38 && + (cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT)) + cc->nflash.boot = true; /* Prepare platform device, but don't register it yet. It's too early, * malloc (required by device_private_init) is not available yet. */ diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 79993969953a..145f3c56227f 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -533,6 +533,7 @@ struct mtd_info; struct bcma_nflash { bool present; + bool boot; /* This is the flash the SoC boots from */ struct mtd_info *mtd; }; -- cgit v1.2.3 From 54c974984e8840c9e20390ce16e3d9f4ea674499 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 29 Sep 2012 20:36:17 +0200 Subject: ssb: move parallel flash config into an own struct This is a preparing step for adding serial flash support. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- arch/mips/bcm47xx/nvram.c | 4 ++-- arch/mips/bcm47xx/wgt634u.c | 8 ++++---- drivers/ssb/driver_mipscore.c | 14 +++++++------- include/linux/ssb/ssb_driver_mips.h | 9 ++++++--- 4 files changed, 19 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c index d43ceff5be47..48a4c70b3842 100644 --- a/arch/mips/bcm47xx/nvram.c +++ b/arch/mips/bcm47xx/nvram.c @@ -43,8 +43,8 @@ static void early_nvram_init(void) #ifdef CONFIG_BCM47XX_SSB case BCM47XX_BUS_TYPE_SSB: mcore_ssb = &bcm47xx_bus.ssb.mipscore; - base = mcore_ssb->flash_window; - lim = mcore_ssb->flash_window_size; + base = mcore_ssb->pflash.window; + lim = mcore_ssb->pflash.window_size; break; #endif #ifdef CONFIG_BCM47XX_BCMA diff --git a/arch/mips/bcm47xx/wgt634u.c b/arch/mips/bcm47xx/wgt634u.c index e9f9ec8d443b..e80d585731aa 100644 --- a/arch/mips/bcm47xx/wgt634u.c +++ b/arch/mips/bcm47xx/wgt634u.c @@ -156,10 +156,10 @@ static int __init wgt634u_init(void) SSB_CHIPCO_IRQ_GPIO); } - wgt634u_flash_data.width = mcore->flash_buswidth; - wgt634u_flash_resource.start = mcore->flash_window; - wgt634u_flash_resource.end = mcore->flash_window - + mcore->flash_window_size + wgt634u_flash_data.width = mcore->pflash.buswidth; + wgt634u_flash_resource.start = mcore->pflash.window; + wgt634u_flash_resource.end = mcore->pflash.window + + mcore->pflash.window_size - 1; return platform_add_devices(wgt634u_devices, ARRAY_SIZE(wgt634u_devices)); diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c index c6250867a95d..dcad2c40836e 100644 --- a/drivers/ssb/driver_mipscore.c +++ b/drivers/ssb/driver_mipscore.c @@ -192,9 +192,9 @@ static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) /* When there is no chipcommon on the bus there is 4MB flash */ if (!bus->chipco.dev) { - mcore->flash_buswidth = 2; - mcore->flash_window = SSB_FLASH1; - mcore->flash_window_size = SSB_FLASH1_SZ; + mcore->pflash.buswidth = 2; + mcore->pflash.window = SSB_FLASH1; + mcore->pflash.window_size = SSB_FLASH1_SZ; return; } @@ -206,13 +206,13 @@ static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) break; case SSB_CHIPCO_FLASHT_PARA: pr_debug("Found parallel flash\n"); - mcore->flash_window = SSB_FLASH2; - mcore->flash_window_size = SSB_FLASH2_SZ; + mcore->pflash.window = SSB_FLASH2; + mcore->pflash.window_size = SSB_FLASH2_SZ; if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG) & SSB_CHIPCO_CFG_DS16) == 0) - mcore->flash_buswidth = 1; + mcore->pflash.buswidth = 1; else - mcore->flash_buswidth = 2; + mcore->pflash.buswidth = 2; break; } } diff --git a/include/linux/ssb/ssb_driver_mips.h b/include/linux/ssb/ssb_driver_mips.h index 5f44e9740cd2..5d057db53071 100644 --- a/include/linux/ssb/ssb_driver_mips.h +++ b/include/linux/ssb/ssb_driver_mips.h @@ -13,6 +13,11 @@ struct ssb_serial_port { unsigned int reg_shift; }; +struct ssb_pflash { + u8 buswidth; + u32 window; + u32 window_size; +}; struct ssb_mipscore { struct ssb_device *dev; @@ -20,9 +25,7 @@ struct ssb_mipscore { int nr_serial_ports; struct ssb_serial_port serial_ports[4]; - u8 flash_buswidth; - u32 flash_window; - u32 flash_window_size; + struct ssb_pflash pflash; }; extern void ssb_mipscore_init(struct ssb_mipscore *mcore); -- cgit v1.2.3 From d954162c54ea81cee12acec1ee5fa19214d0de96 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 29 Sep 2012 20:36:18 +0200 Subject: ssb: add attribute to indicate a parallel flash is available Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/ssb/driver_mipscore.c | 2 ++ include/linux/ssb/ssb_driver_mips.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c index dcad2c40836e..b918ba922306 100644 --- a/drivers/ssb/driver_mipscore.c +++ b/drivers/ssb/driver_mipscore.c @@ -192,6 +192,7 @@ static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) /* When there is no chipcommon on the bus there is 4MB flash */ if (!bus->chipco.dev) { + mcore->pflash.present = true; mcore->pflash.buswidth = 2; mcore->pflash.window = SSB_FLASH1; mcore->pflash.window_size = SSB_FLASH1_SZ; @@ -206,6 +207,7 @@ static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) break; case SSB_CHIPCO_FLASHT_PARA: pr_debug("Found parallel flash\n"); + mcore->pflash.present = true; mcore->pflash.window = SSB_FLASH2; mcore->pflash.window_size = SSB_FLASH2_SZ; if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG) diff --git a/include/linux/ssb/ssb_driver_mips.h b/include/linux/ssb/ssb_driver_mips.h index 5d057db53071..07a9c7a2e088 100644 --- a/include/linux/ssb/ssb_driver_mips.h +++ b/include/linux/ssb/ssb_driver_mips.h @@ -14,6 +14,7 @@ struct ssb_serial_port { }; struct ssb_pflash { + bool present; u8 buswidth; u32 window; u32 window_size; -- cgit v1.2.3 From dfae714361ba75323914da19eb411aaae53d6af0 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 29 Sep 2012 20:40:18 +0200 Subject: bcma: add an extra pcie core struct The BCM4706 has two PCIe host controller on the bcma bus. For PCIe client mode it is assumed that there is only one PCIe controller so the PCIe driver, like b43 and brcmsmac are accessing the first PCIe controller when they want to issue a operation on the host controller. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci_host.c | 4 ++++ drivers/bcma/main.c | 25 ++++++++++++++++++++--- drivers/net/wireless/b43/main.c | 2 +- drivers/net/wireless/brcm80211/brcmsmac/aiutils.c | 4 ++-- drivers/net/wireless/brcm80211/brcmsmac/main.c | 2 +- include/linux/bcma/bcma.h | 2 +- 6 files changed, 31 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index 9baf886e82df..63172c9f871a 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -452,6 +452,8 @@ void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc) pc_host->mem_resource.start = BCMA_SOC_PCI_MEM; pc_host->mem_resource.end = BCMA_SOC_PCI_MEM + BCMA_SOC_PCI_MEM_SZ - 1; + pc_host->io_resource.start = 0x100; + pc_host->io_resource.end = 0x47F; pci_membase_1G = BCMA_SOC_PCIE_DMA_H32; pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0, tmp | BCMA_SOC_PCI_MEM); @@ -459,6 +461,8 @@ void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc) pc_host->mem_resource.start = BCMA_SOC_PCI1_MEM; pc_host->mem_resource.end = BCMA_SOC_PCI1_MEM + BCMA_SOC_PCI_MEM_SZ - 1; + pc_host->io_resource.start = 0x480; + pc_host->io_resource.end = 0x7FF; pci_membase_1G = BCMA_SOC_PCIE1_DMA_H32; pc_host->host_cfg_addr = BCMA_SOC_PCI1_CFG; pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0, diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index d6b5c4ca4c43..7f473cf0469e 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -81,6 +81,18 @@ struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid) } EXPORT_SYMBOL_GPL(bcma_find_core); +static struct bcma_device *bcma_find_core_unit(struct bcma_bus *bus, u16 coreid, + u8 unit) +{ + struct bcma_device *core; + + list_for_each_entry(core, &bus->cores, list) { + if (core->id.id == coreid && core->core_unit == unit) + return core; + } + return NULL; +} + static void bcma_release_core_dev(struct device *dev) { struct bcma_device *core = container_of(dev, struct bcma_device, dev); @@ -211,10 +223,17 @@ int __devinit bcma_bus_register(struct bcma_bus *bus) } /* Init PCIE core */ - core = bcma_find_core(bus, BCMA_CORE_PCIE); + core = bcma_find_core_unit(bus, BCMA_CORE_PCIE, 0); + if (core) { + bus->drv_pci[0].core = core; + bcma_core_pci_init(&bus->drv_pci[0]); + } + + /* Init PCIE core */ + core = bcma_find_core_unit(bus, BCMA_CORE_PCIE, 1); if (core) { - bus->drv_pci.core = core; - bcma_core_pci_init(&bus->drv_pci); + bus->drv_pci[1].core = core; + bcma_core_pci_init(&bus->drv_pci[1]); } /* Init GBIT MAC COMMON core */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 73730e94e0ac..7358ea2eb576 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4652,7 +4652,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev) switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: - bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci, + bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci[0], dev->dev->bdev, true); break; #endif diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c index b89f1272b93f..de96290f5ccd 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c @@ -692,7 +692,7 @@ void ai_pci_up(struct si_pub *sih) sii = container_of(sih, struct si_info, pub); if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) - bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci, true); + bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci[0], true); } /* Unconfigure and/or apply various WARs when going down */ @@ -703,7 +703,7 @@ void ai_pci_down(struct si_pub *sih) sii = container_of(sih, struct si_info, pub); if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) - bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci, false); + bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci[0], false); } /* Enable BT-COEX & Ex-PA for 4313 */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 75086b37c817..565c15abbed5 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -5077,7 +5077,7 @@ static int brcms_b_up_prep(struct brcms_hardware *wlc_hw) * Configure pci/pcmcia here instead of in brcms_c_attach() * to allow mfg hotswap: down, hotswap (chip power cycle), up. */ - bcma_core_pci_irq_ctl(&wlc_hw->d11core->bus->drv_pci, wlc_hw->d11core, + bcma_core_pci_irq_ctl(&wlc_hw->d11core->bus->drv_pci[0], wlc_hw->d11core, true); /* diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 4180eb78d575..fd15d9829705 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -251,7 +251,7 @@ struct bcma_bus { u8 num; struct bcma_drv_cc drv_cc; - struct bcma_drv_pci drv_pci; + struct bcma_drv_pci drv_pci[2]; struct bcma_drv_mips drv_mips; struct bcma_drv_gmac_cmn drv_gmac_cmn; -- cgit v1.2.3 From 7e2afc9d072b9f84b314b208a125c2b1ce36b685 Mon Sep 17 00:00:00 2001 From: Arron Wang Date: Thu, 27 Sep 2012 17:32:54 +0800 Subject: NFC: Set local gb and DEP registries Set the local general bytes and default value for NFCIP1 Target/Initiator registries if the protocol is NFC-DEP Signed-off-by: Arron Wang Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544_hci.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ include/net/nfc/hci.h | 3 +++ 2 files changed, 65 insertions(+) (limited to 'include') diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index c9c8570273ab..8b21a8efd751 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -100,6 +100,10 @@ enum pn544_state { #define PN544_SYS_MGMT_INFO_NOTIFICATION 0x02 #define PN544_POLLING_LOOP_MGMT_GATE 0x94 +#define PN544_DEP_MODE 0x01 +#define PN544_DEP_ATR_REQ 0x02 +#define PN544_DEP_ATR_RES 0x03 +#define PN544_DEP_MERGE 0x0D #define PN544_PL_RDPHASES 0x06 #define PN544_PL_EMULATION 0x07 #define PN544_PL_NFCT_DEACTIVATED 0x09 @@ -630,6 +634,9 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, int r; u8 duration[2]; u8 activated; + u8 i_mode = 0x3f; /* Enable all supported modes */ + u8 t_mode = 0x0f; + u8 t_merge = 0x01; /* Enable merge by default */ pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n", __func__, im_protocols, tm_protocols); @@ -667,6 +674,61 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, if (r < 0) return r; + if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { + hdev->gb = nfc_get_local_general_bytes(hdev->ndev, + &hdev->gb_len); + pr_debug("generate local bytes %p", hdev->gb); + if (hdev->gb == NULL || hdev->gb_len == 0) { + im_protocols &= ~NFC_PROTO_NFC_DEP_MASK; + tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK; + } + } + + if (im_protocols & NFC_PROTO_NFC_DEP_MASK) { + r = nfc_hci_send_event(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + PN544_DEP_MODE, &i_mode, 1); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + PN544_DEP_ATR_REQ, hdev->gb, hdev->gb_len); + if (r < 0) + return r; + + r = nfc_hci_send_event(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_READER_REQUESTED, NULL, 0); + if (r < 0) + nfc_hci_send_event(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + } + + if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { + r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_DEP_MODE, &t_mode, 1); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_DEP_ATR_RES, hdev->gb, hdev->gb_len); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_DEP_MERGE, &t_merge, 1); + if (r < 0) + return r; + } + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, NFC_HCI_EVT_READER_REQUESTED, NULL, 0); if (r < 0) diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index e900072950cb..df6523dcd3c8 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -114,6 +114,9 @@ struct nfc_hci_dev { int async_cb_type; data_exchange_cb_t async_cb; void *async_cb_context; + + u8 *gb; + size_t gb_len; }; /* hci device allocation */ -- cgit v1.2.3 From f7a5f6c532f33ba66a7ca19e81ed447a83dab2db Mon Sep 17 00:00:00 2001 From: Arron Wang Date: Thu, 27 Sep 2012 17:32:55 +0800 Subject: NFC: Pass hardware specific HCI event to driver Signed-off-by: Arron Wang Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544_hci.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ include/net/nfc/hci.h | 3 +++ net/nfc/hci/core.c | 12 +++++++++--- 3 files changed, 57 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index 8b21a8efd751..d81242f4a92b 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -112,6 +112,12 @@ enum pn544_state { #define PN544_NFC_WI_MGMT_GATE 0xA1 +#define PN544_HCI_EVT_SND_DATA 0x01 +#define PN544_HCI_EVT_ACTIVATED 0x02 +#define PN544_HCI_EVT_DEACTIVATED 0x03 +#define PN544_HCI_EVT_RCV_DATA 0x04 +#define PN544_HCI_EVT_CONTINUE_MI 0x05 + static struct nfc_hci_gate pn544_gates[] = { {NFC_HCI_ADMIN_GATE, NFC_HCI_INVALID_PIPE}, {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE}, @@ -897,6 +903,44 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, NULL, 0, NULL); } +void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, + struct sk_buff *skb) +{ + struct sk_buff *rgb_skb = NULL; + int r = 0; + + pr_debug("hci event %d", event); + switch (event) { + case PN544_HCI_EVT_ACTIVATED: + if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) + nfc_hci_target_discovered(hdev, gate); + else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { + r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ, + &rgb_skb); + + if (r < 0) + goto exit; + + nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, + NFC_COMM_PASSIVE, rgb_skb->data, + rgb_skb->len); + + kfree_skb(rgb_skb); + } + + break; + case PN544_HCI_EVT_DEACTIVATED: + nfc_hci_send_event(hdev, gate, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + break; + default: + break; + } + +exit: + kfree_skb(skb); +} + static struct nfc_hci_ops pn544_hci_ops = { .open = pn544_hci_open, .close = pn544_hci_close, @@ -907,6 +951,7 @@ static struct nfc_hci_ops pn544_hci_ops = { .complete_target_discovered = pn544_hci_complete_target_discovered, .data_exchange = pn544_hci_data_exchange, .check_presence = pn544_hci_check_presence, + .event_received = pn544_hci_event_received, }; static int __devinit pn544_hci_probe(struct i2c_client *client, diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index df6523dcd3c8..490d323a9ec3 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -47,6 +47,8 @@ struct nfc_hci_ops { data_exchange_cb_t cb, void *cb_context); int (*check_presence)(struct nfc_hci_dev *hdev, struct nfc_target *target); + void (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event, + struct sk_buff *skb); }; /* Pipes */ @@ -222,5 +224,6 @@ int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response, const u8 *param, size_t param_len); int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event, const u8 *param, size_t param_len); +int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate); #endif /* __NET_HCI_H */ diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 5fbb6e40793e..8a9a811b558a 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -182,7 +182,7 @@ static u32 nfc_hci_sak_to_protocol(u8 sak) } } -static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate) +int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate) { struct nfc_target *targets; struct sk_buff *atqa_skb = NULL; @@ -275,6 +275,7 @@ exit: return r; } +EXPORT_SYMBOL(nfc_hci_target_discovered); void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, struct sk_buff *skb) @@ -307,8 +308,13 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, nfc_hci_pipe2gate(hdev, pipe)); break; default: - /* TODO: Unknown events are hardware specific - * pass them to the driver (needs a new hci_ops) */ + if (hdev->ops->event_received) { + hdev->ops->event_received(hdev, + nfc_hci_pipe2gate(hdev, pipe), + event, skb); + return; + } + break; } -- cgit v1.2.3 From c40d17401f89f575a6ff5774abaa0838398b820c Mon Sep 17 00:00:00 2001 From: Arron Wang Date: Thu, 27 Sep 2012 17:32:57 +0800 Subject: NFC: Implement HCI DEP link up and down And implement the corresponding hooks for pn544. Signed-off-by: Arron Wang Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544_hci.c | 39 +++++++++++++++++++++++++++++++++++++++ include/net/nfc/hci.h | 3 +++ net/nfc/hci/core.c | 24 ++++++++++++++++++++++++ 3 files changed, 66 insertions(+) (limited to 'include') diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index 6d564bd8b3b4..4ed2b8356017 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -746,6 +746,43 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, return r; } +static int pn544_hci_dep_link_up(struct nfc_hci_dev *hdev, + struct nfc_target *target, u8 comm_mode, + u8 *gb, size_t gb_len) +{ + struct sk_buff *rgb_skb = NULL; + int r; + + r = nfc_hci_get_param(hdev, target->hci_reader_gate, + PN544_DEP_ATR_RES, &rgb_skb); + if (r < 0) + return r; + + if (rgb_skb->len == 0 || rgb_skb->len > NFC_GB_MAXSIZE) { + r = -EPROTO; + goto exit; + } + print_hex_dump(KERN_DEBUG, "remote gb: ", DUMP_PREFIX_OFFSET, + 16, 1, rgb_skb->data, rgb_skb->len, true); + + r = nfc_set_remote_general_bytes(hdev->ndev, rgb_skb->data, + rgb_skb->len); + + if (r == 0) + r = nfc_dep_link_is_up(hdev->ndev, target->idx, comm_mode, + NFC_RF_INITIATOR); +exit: + kfree_skb(rgb_skb); + return r; +} + +static int pn544_hci_dep_link_down(struct nfc_hci_dev *hdev) +{ + + return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); +} + static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, struct nfc_target *target) { @@ -973,6 +1010,8 @@ static struct nfc_hci_ops pn544_hci_ops = { .hci_ready = pn544_hci_ready, .xmit = pn544_hci_xmit, .start_poll = pn544_hci_start_poll, + .dep_link_up = pn544_hci_dep_link_up, + .dep_link_down = pn544_hci_dep_link_down, .target_from_gate = pn544_hci_target_from_gate, .complete_target_discovered = pn544_hci_complete_target_discovered, .data_exchange = pn544_hci_data_exchange, diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 490d323a9ec3..6b2d75dc7c01 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -38,6 +38,9 @@ struct nfc_hci_ops { int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb); int (*start_poll) (struct nfc_hci_dev *hdev, u32 im_protocols, u32 tm_protocols); + int (*dep_link_up)(struct nfc_hci_dev *hdev, struct nfc_target *target, + u8 comm_mode, u8 *gb, size_t gb_len); + int (*dep_link_down)(struct nfc_hci_dev *hdev); int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate, struct nfc_target *target); int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate, diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 052a0a27ac1a..777deb84aa73 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -546,6 +546,28 @@ static void hci_stop_poll(struct nfc_dev *nfc_dev) NFC_HCI_EVT_END_OPERATION, NULL, 0); } +static int hci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, + __u8 comm_mode, __u8 *gb, size_t gb_len) +{ + 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); + + return 0; +} + +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); + + return 0; +} + static int hci_activate_target(struct nfc_dev *nfc_dev, struct nfc_target *target, u32 protocol) { @@ -731,6 +753,8 @@ static struct nfc_ops hci_nfc_ops = { .dev_down = hci_dev_down, .start_poll = hci_start_poll, .stop_poll = hci_stop_poll, + .dep_link_up = hci_dep_link_up, + .dep_link_down = hci_dep_link_down, .activate_target = hci_activate_target, .deactivate_target = hci_deactivate_target, .im_transceive = hci_transceive, -- cgit v1.2.3 From e81076235b46189a776362ec5e4c6626bf1599ff Mon Sep 17 00:00:00 2001 From: Arron Wang Date: Thu, 27 Sep 2012 17:32:58 +0800 Subject: NFC: Implement HCI DEP send and receive data And implement the corresponding hooks for pn544. Signed-off-by: Arron Wang Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544_hci.c | 36 ++++++++++++++++++++++++++++++++++-- include/net/nfc/hci.h | 3 ++- net/nfc/hci/core.c | 19 +++++++++++++++---- 3 files changed, 51 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index 4ed2b8356017..554faf0a92d7 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -900,7 +900,7 @@ static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb, * <= 0: driver handled the data exchange * 1: driver doesn't especially handle, please do standard processing */ -static int pn544_hci_data_exchange(struct nfc_hci_dev *hdev, +static int pn544_hci_im_transceive(struct nfc_hci_dev *hdev, struct nfc_target *target, struct sk_buff *skb, data_exchange_cb_t cb, void *cb_context) @@ -953,11 +953,26 @@ static int pn544_hci_data_exchange(struct nfc_hci_dev *hdev, return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, PN544_JEWEL_RAW_CMD, skb->data, skb->len, cb, cb_context); + case PN544_RF_READER_NFCIP1_INITIATOR_GATE: + *skb_push(skb, 1) = 0; + + return nfc_hci_send_event(hdev, target->hci_reader_gate, + PN544_HCI_EVT_SND_DATA, skb->data, + skb->len); default: return 1; } } +static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + /* Set default false for multiple information chaining */ + *skb_push(skb, 1) = 0; + + return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_HCI_EVT_SND_DATA, skb->data, skb->len); +} + static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, struct nfc_target *target) { @@ -996,6 +1011,22 @@ void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, nfc_hci_send_event(hdev, gate, NFC_HCI_EVT_END_OPERATION, NULL, 0); break; + case PN544_HCI_EVT_RCV_DATA: + if (skb->len < 2) { + r = -EPROTO; + goto exit; + } + + if (skb->data[0] != 0) { + pr_debug("data0 %d", skb->data[0]); + r = -EPROTO; + goto exit; + } + + skb_pull(skb, 2); + nfc_tm_data_received(hdev->ndev, skb); + + return; default: break; } @@ -1014,7 +1045,8 @@ static struct nfc_hci_ops pn544_hci_ops = { .dep_link_down = pn544_hci_dep_link_down, .target_from_gate = pn544_hci_target_from_gate, .complete_target_discovered = pn544_hci_complete_target_discovered, - .data_exchange = pn544_hci_data_exchange, + .im_transceive = pn544_hci_im_transceive, + .tm_send = pn544_hci_tm_send, .check_presence = pn544_hci_check_presence, .event_received = pn544_hci_event_received, }; diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 6b2d75dc7c01..bc87b8f2d692 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -45,9 +45,10 @@ struct nfc_hci_ops { struct nfc_target *target); int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate, struct nfc_target *target); - int (*data_exchange) (struct nfc_hci_dev *hdev, + int (*im_transceive) (struct nfc_hci_dev *hdev, struct nfc_target *target, struct sk_buff *skb, data_exchange_cb_t cb, void *cb_context); + int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb); int (*check_presence)(struct nfc_hci_dev *hdev, struct nfc_target *target); void (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event, diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 777deb84aa73..c2339c468d91 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -616,8 +616,8 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, switch (target->hci_reader_gate) { case NFC_HCI_RF_READER_A_GATE: case NFC_HCI_RF_READER_B_GATE: - if (hdev->ops->data_exchange) { - r = hdev->ops->data_exchange(hdev, target, skb, cb, + if (hdev->ops->im_transceive) { + r = hdev->ops->im_transceive(hdev, target, skb, cb, cb_context); if (r <= 0) /* handled */ break; @@ -634,8 +634,8 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, skb->len, hci_transceive_cb, hdev); break; default: - if (hdev->ops->data_exchange) { - r = hdev->ops->data_exchange(hdev, target, skb, cb, + if (hdev->ops->im_transceive) { + r = hdev->ops->im_transceive(hdev, target, skb, cb, cb_context); if (r == 1) r = -ENOTSUPP; @@ -650,6 +650,16 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, return r; } +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 + return -ENOTSUPP; +} + static int hci_check_presence(struct nfc_dev *nfc_dev, struct nfc_target *target) { @@ -758,6 +768,7 @@ static struct nfc_ops hci_nfc_ops = { .activate_target = hci_activate_target, .deactivate_target = hci_deactivate_target, .im_transceive = hci_transceive, + .tm_send = hci_tm_send, .check_presence = hci_check_presence, }; -- cgit v1.2.3 From 97f18414af395c547f20300e5d4c81d7190a4155 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 2 Oct 2012 18:44:06 +0200 Subject: NFC: Separate pn544 hci driver in HW dependant and independant parts The driver now has all HCI stuff isolated in one file, and all the hardware link specifics in another. Writing a pn544 driver on top of another hardware link is now just a matter of adding a new file for that new hardware specifics. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/Makefile | 2 +- drivers/nfc/pn544/Makefile | 7 + drivers/nfc/pn544/i2c.c | 500 ++++++++++++++++++ drivers/nfc/pn544/pn544.c | 862 ++++++++++++++++++++++++++++++ drivers/nfc/pn544/pn544.h | 32 ++ drivers/nfc/pn544_hci.c | 1252 -------------------------------------------- include/net/nfc/hci.h | 6 + 7 files changed, 1408 insertions(+), 1253 deletions(-) create mode 100644 drivers/nfc/pn544/Makefile create mode 100644 drivers/nfc/pn544/i2c.c create mode 100644 drivers/nfc/pn544/pn544.c create mode 100644 drivers/nfc/pn544/pn544.h delete mode 100644 drivers/nfc/pn544_hci.c (limited to 'include') diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index bf05831fdf09..36c359043f54 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -2,7 +2,7 @@ # Makefile for nfc devices # -obj-$(CONFIG_PN544_HCI_NFC) += pn544_hci.o +obj-$(CONFIG_PN544_HCI_NFC) += pn544/ obj-$(CONFIG_NFC_PN533) += pn533.o obj-$(CONFIG_NFC_WILINK) += nfcwilink.o diff --git a/drivers/nfc/pn544/Makefile b/drivers/nfc/pn544/Makefile new file mode 100644 index 000000000000..725733881eb3 --- /dev/null +++ b/drivers/nfc/pn544/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for PN544 HCI based NFC driver +# + +obj-$(CONFIG_PN544_HCI_NFC) += pn544_i2c.o + +pn544_i2c-y := pn544.o i2c.o diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c new file mode 100644 index 000000000000..fb430d882352 --- /dev/null +++ b/drivers/nfc/pn544/i2c.c @@ -0,0 +1,500 @@ +/* + * I2C Link Layer for PN544 HCI based Driver + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "pn544.h" + +#define PN544_I2C_FRAME_HEADROOM 1 +#define PN544_I2C_FRAME_TAILROOM 2 + +/* framing in HCI mode */ +#define PN544_HCI_I2C_LLC_LEN 1 +#define PN544_HCI_I2C_LLC_CRC 2 +#define PN544_HCI_I2C_LLC_LEN_CRC (PN544_HCI_I2C_LLC_LEN + \ + PN544_HCI_I2C_LLC_CRC) +#define PN544_HCI_I2C_LLC_MIN_SIZE (1 + PN544_HCI_I2C_LLC_LEN_CRC) +#define PN544_HCI_I2C_LLC_MAX_PAYLOAD 29 +#define PN544_HCI_I2C_LLC_MAX_SIZE (PN544_HCI_I2C_LLC_LEN_CRC + 1 + \ + PN544_HCI_I2C_LLC_MAX_PAYLOAD) + +static struct i2c_device_id pn544_hci_i2c_id_table[] = { + {"pn544", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table); + +#define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c" + +struct pn544_i2c_phy { + struct i2c_client *i2c_dev; + struct nfc_hci_dev *hdev; + + unsigned int gpio_en; + unsigned int gpio_irq; + unsigned int gpio_fw; + unsigned int en_polarity; + + int powered; + + int hard_fault; /* + * < 0 if hardware error occured (e.g. i2c err) + * and prevents normal operation. + */ +}; + +#define I2C_DUMP_SKB(info, skb) \ +do { \ + pr_debug("%s:\n", info); \ + print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \ + 16, 1, (skb)->data, (skb)->len, 0); \ +} while (0) + +static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) +{ + int polarity, retry, ret; + char rset_cmd[] = { 0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5 }; + int count = sizeof(rset_cmd); + + pr_info(DRIVER_DESC ": %s\n", __func__); + dev_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n"); + + /* Disable fw download */ + gpio_set_value(phy->gpio_fw, 0); + + for (polarity = 0; polarity < 2; polarity++) { + phy->en_polarity = polarity; + retry = 3; + while (retry--) { + /* power off */ + gpio_set_value(phy->gpio_en, !phy->en_polarity); + usleep_range(10000, 15000); + + /* power on */ + gpio_set_value(phy->gpio_en, phy->en_polarity); + usleep_range(10000, 15000); + + /* send reset */ + dev_dbg(&phy->i2c_dev->dev, "Sending reset cmd\n"); + ret = i2c_master_send(phy->i2c_dev, rset_cmd, count); + if (ret == count) { + dev_info(&phy->i2c_dev->dev, + "nfc_en polarity : active %s\n", + (polarity == 0 ? "low" : "high")); + goto out; + } + } + } + + dev_err(&phy->i2c_dev->dev, + "Could not detect nfc_en polarity, fallback to active high\n"); + +out: + gpio_set_value(phy->gpio_en, !phy->en_polarity); +} + +static int pn544_hci_i2c_enable(void *phy_id) +{ + struct pn544_i2c_phy *phy = phy_id; + + pr_info(DRIVER_DESC ": %s\n", __func__); + + gpio_set_value(phy->gpio_fw, 0); + gpio_set_value(phy->gpio_en, phy->en_polarity); + usleep_range(10000, 15000); + + phy->powered = 1; + + return 0; +} + +static void pn544_hci_i2c_disable(void *phy_id) +{ + struct pn544_i2c_phy *phy = phy_id; + + pr_info(DRIVER_DESC ": %s\n", __func__); + + gpio_set_value(phy->gpio_fw, 0); + gpio_set_value(phy->gpio_en, !phy->en_polarity); + usleep_range(10000, 15000); + + gpio_set_value(phy->gpio_en, phy->en_polarity); + usleep_range(10000, 15000); + + gpio_set_value(phy->gpio_en, !phy->en_polarity); + usleep_range(10000, 15000); + + phy->powered = 0; +} + +static void pn544_hci_i2c_add_len_crc(struct sk_buff *skb) +{ + u16 crc; + int len; + + len = skb->len + 2; + *skb_push(skb, 1) = len; + + crc = crc_ccitt(0xffff, skb->data, skb->len); + crc = ~crc; + *skb_put(skb, 1) = crc & 0xff; + *skb_put(skb, 1) = crc >> 8; +} + +static void pn544_hci_i2c_remove_len_crc(struct sk_buff *skb) +{ + skb_pull(skb, PN544_I2C_FRAME_HEADROOM); + skb_trim(skb, PN544_I2C_FRAME_TAILROOM); +} + +/* + * Writing a frame must not return the number of written bytes. + * It must return either zero for success, or <0 for error. + * In addition, it must not alter the skb + */ +static int pn544_hci_i2c_write(void *phy_id, struct sk_buff *skb) +{ + int r; + struct pn544_i2c_phy *phy = phy_id; + struct i2c_client *client = phy->i2c_dev; + + if (phy->hard_fault != 0) + return phy->hard_fault; + + usleep_range(3000, 6000); + + pn544_hci_i2c_add_len_crc(skb); + + I2C_DUMP_SKB("i2c frame written", skb); + + r = i2c_master_send(client, skb->data, skb->len); + + if (r == -EREMOTEIO) { /* Retry, chip was in standby */ + usleep_range(6000, 10000); + r = i2c_master_send(client, skb->data, skb->len); + } + + if (r >= 0) { + if (r != skb->len) + r = -EREMOTEIO; + else + r = 0; + } + + pn544_hci_i2c_remove_len_crc(skb); + + return r; +} + +static int check_crc(u8 *buf, int buflen) +{ + int len; + u16 crc; + + len = buf[0] + 1; + crc = crc_ccitt(0xffff, buf, len - 2); + crc = ~crc; + + if (buf[len - 2] != (crc & 0xff) || buf[len - 1] != (crc >> 8)) { + pr_err(PN544_HCI_I2C_DRIVER_NAME + ": CRC error 0x%x != 0x%x 0x%x\n", + crc, buf[len - 1], buf[len - 2]); + + pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__); + print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, + 16, 2, buf, buflen, false); + return -EPERM; + } + return 0; +} + +/* + * Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees + * that i2c bus will be flushed and that next read will start on a new frame. + * returned skb contains only LLC header and payload. + * returns: + * -EREMOTEIO : i2c read error (fatal) + * -EBADMSG : frame was incorrect and discarded + * -ENOMEM : cannot allocate skb, frame dropped + */ +static int pn544_hci_i2c_read(struct pn544_i2c_phy *phy, struct sk_buff **skb) +{ + int r; + u8 len; + u8 tmp[PN544_HCI_I2C_LLC_MAX_SIZE - 1]; + struct i2c_client *client = phy->i2c_dev; + + r = i2c_master_recv(client, &len, 1); + if (r != 1) { + dev_err(&client->dev, "cannot read len byte\n"); + return -EREMOTEIO; + } + + if ((len < (PN544_HCI_I2C_LLC_MIN_SIZE - 1)) || + (len > (PN544_HCI_I2C_LLC_MAX_SIZE - 1))) { + dev_err(&client->dev, "invalid len byte\n"); + r = -EBADMSG; + goto flush; + } + + *skb = alloc_skb(1 + len, GFP_KERNEL); + if (*skb == NULL) { + r = -ENOMEM; + goto flush; + } + + *skb_put(*skb, 1) = len; + + r = i2c_master_recv(client, skb_put(*skb, len), len); + if (r != len) { + kfree_skb(*skb); + return -EREMOTEIO; + } + + I2C_DUMP_SKB("i2c frame read", *skb); + + r = check_crc((*skb)->data, (*skb)->len); + if (r != 0) { + kfree_skb(*skb); + r = -EBADMSG; + goto flush; + } + + skb_pull(*skb, 1); + skb_trim(*skb, (*skb)->len - 2); + + usleep_range(3000, 6000); + + return 0; + +flush: + if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0) + r = -EREMOTEIO; + + usleep_range(3000, 6000); + + return r; +} + +/* + * 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. + * The frame format is len-data-crc, and corruption can occur anywhere while + * transiting on i2c bus, such that we could read an invalid len. + * In order to recover synchronization with the next frame, we must be sure + * to read the real amount of data without using the len byte. We do this by + * assuming the following: + * - the chip will always present only one single complete frame on the bus + * before triggering the interrupt + * - the chip will not present a new frame until we have completely read + * the previous one (or until we have handled the interrupt). + * The tricky case is when we read a corrupted len that is less than the real + * len. We must detect this here in order to determine that we need to flush + * the bus. This is the reason why we check the crc here. + */ +static irqreturn_t pn544_hci_i2c_irq_thread_fn(int irq, void *phy_id) +{ + struct pn544_i2c_phy *phy = phy_id; + struct i2c_client *client; + struct sk_buff *skb = NULL; + int r; + + if (!phy || irq != phy->i2c_dev->irq) { + WARN_ON_ONCE(1); + return IRQ_NONE; + } + + client = phy->i2c_dev; + dev_dbg(&client->dev, "IRQ\n"); + + if (phy->hard_fault != 0) + return IRQ_HANDLED; + + r = pn544_hci_i2c_read(phy, &skb); + if (r == -EREMOTEIO) { + phy->hard_fault = r; + + nfc_hci_recv_frame(phy->hdev, NULL); + + return IRQ_HANDLED; + } else if ((r == -ENOMEM) || (r == -EBADMSG)) { + return IRQ_HANDLED; + } + + nfc_hci_recv_frame(phy->hdev, skb); + + return IRQ_HANDLED; +} + +static struct nfc_phy_ops i2c_phy_ops = { + .write = pn544_hci_i2c_write, + .enable = pn544_hci_i2c_enable, + .disable = pn544_hci_i2c_disable, +}; + +static int __devinit pn544_hci_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pn544_i2c_phy *phy; + struct pn544_nfc_platform_data *pdata; + int r = 0; + + dev_dbg(&client->dev, "%s\n", __func__); + dev_dbg(&client->dev, "IRQ: %d\n", client->irq); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "Need I2C_FUNC_I2C\n"); + return -ENODEV; + } + + phy = kzalloc(sizeof(struct pn544_i2c_phy), GFP_KERNEL); + if (!phy) { + dev_err(&client->dev, + "Cannot allocate memory for pn544 i2c phy.\n"); + r = -ENOMEM; + goto err_phy_alloc; + } + + phy->i2c_dev = client; + i2c_set_clientdata(client, phy); + + pdata = client->dev.platform_data; + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + r = -EINVAL; + goto err_pdata; + } + + if (pdata->request_resources == NULL) { + dev_err(&client->dev, "request_resources() missing\n"); + r = -EINVAL; + goto err_pdata; + } + + r = pdata->request_resources(client); + if (r) { + dev_err(&client->dev, "Cannot get platform resources\n"); + goto err_pdata; + } + + phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); + phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET); + phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ); + + pn544_hci_i2c_platform_init(phy); + + r = request_threaded_irq(client->irq, NULL, pn544_hci_i2c_irq_thread_fn, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + PN544_HCI_I2C_DRIVER_NAME, phy); + if (r < 0) { + dev_err(&client->dev, "Unable to register IRQ handler\n"); + goto err_rti; + } + + r = pn544_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME, + PN544_I2C_FRAME_HEADROOM, PN544_I2C_FRAME_TAILROOM, + PN544_HCI_I2C_LLC_MAX_PAYLOAD, &phy->hdev); + if (r < 0) + goto err_hci; + + return 0; + +err_hci: + free_irq(client->irq, phy); + +err_rti: + if (pdata->free_resources != NULL) + pdata->free_resources(); + +err_pdata: + kfree(phy); + +err_phy_alloc: + return r; +} + +static __devexit int pn544_hci_i2c_remove(struct i2c_client *client) +{ + struct pn544_i2c_phy *phy = i2c_get_clientdata(client); + struct pn544_nfc_platform_data *pdata = client->dev.platform_data; + + dev_dbg(&client->dev, "%s\n", __func__); + + pn544_hci_remove(phy->hdev); + + if (phy->powered) + pn544_hci_i2c_disable(phy); + + free_irq(client->irq, phy); + if (pdata->free_resources) + pdata->free_resources(); + + kfree(phy); + + return 0; +} + +static struct i2c_driver pn544_hci_i2c_driver = { + .driver = { + .name = PN544_HCI_I2C_DRIVER_NAME, + }, + .probe = pn544_hci_i2c_probe, + .id_table = pn544_hci_i2c_id_table, + .remove = __devexit_p(pn544_hci_i2c_remove), +}; + +static int __init pn544_hci_i2c_init(void) +{ + int r; + + pr_debug(DRIVER_DESC ": %s\n", __func__); + + r = i2c_add_driver(&pn544_hci_i2c_driver); + if (r) { + pr_err(PN544_HCI_I2C_DRIVER_NAME + ": driver registration failed\n"); + return r; + } + + return 0; +} + +static void __exit pn544_hci_i2c_exit(void) +{ + i2c_del_driver(&pn544_hci_i2c_driver); +} + +module_init(pn544_hci_i2c_init); +module_exit(pn544_hci_i2c_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c new file mode 100644 index 000000000000..cf344450815c --- /dev/null +++ b/drivers/nfc/pn544/pn544.c @@ -0,0 +1,862 @@ +/* + * HCI based Driver for NXP PN544 NFC Chip + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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. + */ + +#include +#include + +#include +#include +#include + +#include "pn544.h" + +/* Timing restrictions (ms) */ +#define PN544_HCI_RESETVEN_TIME 30 + +#define HCI_MODE 0 +#define FW_MODE 1 + +enum pn544_state { + PN544_ST_COLD, + PN544_ST_FW_READY, + PN544_ST_READY, +}; + +#define FULL_VERSION_LEN 11 + +/* Proprietary commands */ +#define PN544_WRITE 0x3f + +/* Proprietary gates, events, commands and registers */ + +/* NFC_HCI_RF_READER_A_GATE additional registers and commands */ +#define PN544_RF_READER_A_AUTO_ACTIVATION 0x10 +#define PN544_RF_READER_A_CMD_CONTINUE_ACTIVATION 0x12 +#define PN544_MIFARE_CMD 0x21 + +/* Commands that apply to all RF readers */ +#define PN544_RF_READER_CMD_PRESENCE_CHECK 0x30 +#define PN544_RF_READER_CMD_ACTIVATE_NEXT 0x32 + +/* NFC_HCI_ID_MGMT_GATE additional registers */ +#define PN544_ID_MGMT_FULL_VERSION_SW 0x10 + +#define PN544_RF_READER_ISO15693_GATE 0x12 + +#define PN544_RF_READER_F_GATE 0x14 +#define PN544_FELICA_ID 0x04 +#define PN544_FELICA_RAW 0x20 + +#define PN544_RF_READER_JEWEL_GATE 0x15 +#define PN544_JEWEL_RAW_CMD 0x23 + +#define PN544_RF_READER_NFCIP1_INITIATOR_GATE 0x30 +#define PN544_RF_READER_NFCIP1_TARGET_GATE 0x31 + +#define PN544_SYS_MGMT_GATE 0x90 +#define PN544_SYS_MGMT_INFO_NOTIFICATION 0x02 + +#define PN544_POLLING_LOOP_MGMT_GATE 0x94 +#define PN544_DEP_MODE 0x01 +#define PN544_DEP_ATR_REQ 0x02 +#define PN544_DEP_ATR_RES 0x03 +#define PN544_DEP_MERGE 0x0D +#define PN544_PL_RDPHASES 0x06 +#define PN544_PL_EMULATION 0x07 +#define PN544_PL_NFCT_DEACTIVATED 0x09 + +#define PN544_SWP_MGMT_GATE 0xA0 + +#define PN544_NFC_WI_MGMT_GATE 0xA1 + +#define PN544_HCI_EVT_SND_DATA 0x01 +#define PN544_HCI_EVT_ACTIVATED 0x02 +#define PN544_HCI_EVT_DEACTIVATED 0x03 +#define PN544_HCI_EVT_RCV_DATA 0x04 +#define PN544_HCI_EVT_CONTINUE_MI 0x05 + +#define PN544_HCI_CMD_ATTREQUEST 0x12 +#define PN544_HCI_CMD_CONTINUE_ACTIVATION 0x13 + +static struct nfc_hci_gate pn544_gates[] = { + {NFC_HCI_ADMIN_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_SYS_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_SWP_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_POLLING_LOOP_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_NFC_WI_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_JEWEL_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_NFCIP1_INITIATOR_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_NFCIP1_TARGET_GATE, NFC_HCI_INVALID_PIPE} +}; + +/* Largest headroom needed for outgoing custom commands */ +#define PN544_CMDS_HEADROOM 2 + +struct pn544_hci_info { + struct nfc_phy_ops *phy_ops; + void *phy_id; + + struct nfc_hci_dev *hdev; + + enum pn544_state state; + + struct mutex info_lock; + + int async_cb_type; + data_exchange_cb_t async_cb; + void *async_cb_context; +}; + +static int pn544_hci_open(struct nfc_hci_dev *hdev) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + int r = 0; + + mutex_lock(&info->info_lock); + + if (info->state != PN544_ST_COLD) { + r = -EBUSY; + goto out; + } + + r = info->phy_ops->enable(info->phy_id); + + if (r == 0) + info->state = PN544_ST_READY; + +out: + mutex_unlock(&info->info_lock); + return r; +} + +static void pn544_hci_close(struct nfc_hci_dev *hdev) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + + mutex_lock(&info->info_lock); + + if (info->state == PN544_ST_COLD) + goto out; + + info->phy_ops->disable(info->phy_id); + + info->state = PN544_ST_COLD; + +out: + mutex_unlock(&info->info_lock); +} + +static int pn544_hci_ready(struct nfc_hci_dev *hdev) +{ + struct sk_buff *skb; + static struct hw_config { + u8 adr[2]; + u8 value; + } hw_config[] = { + {{0x9f, 0x9a}, 0x00}, + + {{0x98, 0x10}, 0xbc}, + + {{0x9e, 0x71}, 0x00}, + + {{0x98, 0x09}, 0x00}, + + {{0x9e, 0xb4}, 0x00}, + + {{0x9e, 0xd9}, 0xff}, + {{0x9e, 0xda}, 0xff}, + {{0x9e, 0xdb}, 0x23}, + {{0x9e, 0xdc}, 0x21}, + {{0x9e, 0xdd}, 0x22}, + {{0x9e, 0xde}, 0x24}, + + {{0x9c, 0x01}, 0x08}, + + {{0x9e, 0xaa}, 0x01}, + + {{0x9b, 0xd1}, 0x0d}, + {{0x9b, 0xd2}, 0x24}, + {{0x9b, 0xd3}, 0x0a}, + {{0x9b, 0xd4}, 0x22}, + {{0x9b, 0xd5}, 0x08}, + {{0x9b, 0xd6}, 0x1e}, + {{0x9b, 0xdd}, 0x1c}, + + {{0x9b, 0x84}, 0x13}, + {{0x99, 0x81}, 0x7f}, + {{0x99, 0x31}, 0x70}, + + {{0x98, 0x00}, 0x3f}, + + {{0x9f, 0x09}, 0x00}, + + {{0x9f, 0x0a}, 0x05}, + + {{0x9e, 0xd1}, 0xa1}, + {{0x99, 0x23}, 0x00}, + + {{0x9e, 0x74}, 0x80}, + + {{0x9f, 0x28}, 0x10}, + + {{0x9f, 0x35}, 0x14}, + + {{0x9f, 0x36}, 0x60}, + + {{0x9c, 0x31}, 0x00}, + + {{0x9c, 0x32}, 0xc8}, + + {{0x9c, 0x19}, 0x40}, + + {{0x9c, 0x1a}, 0x40}, + + {{0x9c, 0x0c}, 0x00}, + + {{0x9c, 0x0d}, 0x00}, + + {{0x9c, 0x12}, 0x00}, + + {{0x9c, 0x13}, 0x00}, + + {{0x98, 0xa2}, 0x0e}, + + {{0x98, 0x93}, 0x40}, + + {{0x98, 0x7d}, 0x02}, + {{0x98, 0x7e}, 0x00}, + {{0x9f, 0xc8}, 0x01}, + }; + struct hw_config *p = hw_config; + int count = ARRAY_SIZE(hw_config); + struct sk_buff *res_skb; + u8 param[4]; + int r; + + param[0] = 0; + while (count--) { + param[1] = p->adr[0]; + param[2] = p->adr[1]; + param[3] = p->value; + + r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, PN544_WRITE, + param, 4, &res_skb); + if (r < 0) + return r; + + if (res_skb->len != 1) { + kfree_skb(res_skb); + return -EPROTO; + } + + if (res_skb->data[0] != p->value) { + kfree_skb(res_skb); + return -EIO; + } + + kfree_skb(res_skb); + + p++; + } + + param[0] = NFC_HCI_UICC_HOST_ID; + r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, + NFC_HCI_ADMIN_WHITELIST, param, 1); + if (r < 0) + return r; + + param[0] = 0x3d; + r = nfc_hci_set_param(hdev, PN544_SYS_MGMT_GATE, + PN544_SYS_MGMT_INFO_NOTIFICATION, param, 1); + if (r < 0) + return r; + + param[0] = 0x0; + r = nfc_hci_set_param(hdev, NFC_HCI_RF_READER_A_GATE, + PN544_RF_READER_A_AUTO_ACTIVATION, param, 1); + if (r < 0) + return r; + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (r < 0) + return r; + + param[0] = 0x1; + r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, + PN544_PL_NFCT_DEACTIVATED, param, 1); + if (r < 0) + return r; + + param[0] = 0x0; + r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, + PN544_PL_RDPHASES, param, 1); + if (r < 0) + return r; + + r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE, + PN544_ID_MGMT_FULL_VERSION_SW, &skb); + if (r < 0) + return r; + + if (skb->len != FULL_VERSION_LEN) { + kfree_skb(skb); + return -EINVAL; + } + + print_hex_dump(KERN_DEBUG, "FULL VERSION SOFTWARE INFO: ", + DUMP_PREFIX_NONE, 16, 1, + skb->data, FULL_VERSION_LEN, false); + + kfree_skb(skb); + + return 0; +} + +static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + + return info->phy_ops->write(info->phy_id, skb); +} + +static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, + u32 im_protocols, u32 tm_protocols) +{ + u8 phases = 0; + int r; + u8 duration[2]; + u8 activated; + u8 i_mode = 0x3f; /* Enable all supported modes */ + u8 t_mode = 0x0f; + u8 t_merge = 0x01; /* Enable merge by default */ + + pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n", + __func__, im_protocols, tm_protocols); + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (r < 0) + return r; + + duration[0] = 0x18; + duration[1] = 0x6a; + r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, + PN544_PL_EMULATION, duration, 2); + if (r < 0) + return r; + + activated = 0; + r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, + PN544_PL_NFCT_DEACTIVATED, &activated, 1); + if (r < 0) + return r; + + if (im_protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_MIFARE_MASK | + NFC_PROTO_JEWEL_MASK)) + phases |= 1; /* Type A */ + if (im_protocols & NFC_PROTO_FELICA_MASK) { + phases |= (1 << 2); /* Type F 212 */ + phases |= (1 << 3); /* Type F 424 */ + } + + phases |= (1 << 5); /* NFC active */ + + r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, + PN544_PL_RDPHASES, &phases, 1); + if (r < 0) + return r; + + if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { + hdev->gb = nfc_get_local_general_bytes(hdev->ndev, + &hdev->gb_len); + pr_debug("generate local bytes %p", hdev->gb); + if (hdev->gb == NULL || hdev->gb_len == 0) { + im_protocols &= ~NFC_PROTO_NFC_DEP_MASK; + tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK; + } + } + + if (im_protocols & NFC_PROTO_NFC_DEP_MASK) { + r = nfc_hci_send_event(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + PN544_DEP_MODE, &i_mode, 1); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + PN544_DEP_ATR_REQ, hdev->gb, hdev->gb_len); + if (r < 0) + return r; + + r = nfc_hci_send_event(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_READER_REQUESTED, NULL, 0); + if (r < 0) + nfc_hci_send_event(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + } + + if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { + r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_DEP_MODE, &t_mode, 1); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_DEP_ATR_RES, hdev->gb, hdev->gb_len); + if (r < 0) + return r; + + r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_DEP_MERGE, &t_merge, 1); + if (r < 0) + return r; + } + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_READER_REQUESTED, NULL, 0); + if (r < 0) + nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + + return r; +} + +static int pn544_hci_dep_link_up(struct nfc_hci_dev *hdev, + struct nfc_target *target, u8 comm_mode, + u8 *gb, size_t gb_len) +{ + struct sk_buff *rgb_skb = NULL; + int r; + + r = nfc_hci_get_param(hdev, target->hci_reader_gate, + PN544_DEP_ATR_RES, &rgb_skb); + if (r < 0) + return r; + + if (rgb_skb->len == 0 || rgb_skb->len > NFC_GB_MAXSIZE) { + r = -EPROTO; + goto exit; + } + print_hex_dump(KERN_DEBUG, "remote gb: ", DUMP_PREFIX_OFFSET, + 16, 1, rgb_skb->data, rgb_skb->len, true); + + r = nfc_set_remote_general_bytes(hdev->ndev, rgb_skb->data, + rgb_skb->len); + + if (r == 0) + r = nfc_dep_link_is_up(hdev->ndev, target->idx, comm_mode, + NFC_RF_INITIATOR); +exit: + kfree_skb(rgb_skb); + return r; +} + +static int pn544_hci_dep_link_down(struct nfc_hci_dev *hdev) +{ + + return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_INITIATOR_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); +} + +static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, + struct nfc_target *target) +{ + switch (gate) { + case PN544_RF_READER_F_GATE: + target->supported_protocols = NFC_PROTO_FELICA_MASK; + break; + case PN544_RF_READER_JEWEL_GATE: + target->supported_protocols = NFC_PROTO_JEWEL_MASK; + target->sens_res = 0x0c00; + break; + case PN544_RF_READER_NFCIP1_INITIATOR_GATE: + target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; + break; + default: + return -EPROTO; + } + + return 0; +} + +static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev, + u8 gate, + struct nfc_target *target) +{ + struct sk_buff *uid_skb; + int r = 0; + + if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) + return r; + + if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) { + r = nfc_hci_send_cmd(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + PN544_HCI_CMD_CONTINUE_ACTIVATION, NULL, 0, NULL); + if (r < 0) + return r; + + target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE; + } else if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) { + if (target->nfcid1_len != 4 && target->nfcid1_len != 7 && + target->nfcid1_len != 10) + return -EPROTO; + + r = nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, + PN544_RF_READER_CMD_ACTIVATE_NEXT, + target->nfcid1, target->nfcid1_len, NULL); + } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) { + r = nfc_hci_get_param(hdev, PN544_RF_READER_F_GATE, + PN544_FELICA_ID, &uid_skb); + if (r < 0) + return r; + + if (uid_skb->len != 8) { + kfree_skb(uid_skb); + return -EPROTO; + } + + r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE, + PN544_RF_READER_CMD_ACTIVATE_NEXT, + uid_skb->data, uid_skb->len, NULL); + kfree_skb(uid_skb); + + r = nfc_hci_send_cmd(hdev, + PN544_RF_READER_NFCIP1_INITIATOR_GATE, + PN544_HCI_CMD_CONTINUE_ACTIVATION, + NULL, 0, NULL); + if (r < 0) + return r; + + target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE; + target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; + } else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) { + /* + * TODO: maybe other ISO 14443 require some kind of continue + * activation, but for now we've seen only this one below. + */ + if (target->sens_res == 0x4403) /* Type 4 Mifare DESFire */ + r = nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, + PN544_RF_READER_A_CMD_CONTINUE_ACTIVATION, + NULL, 0, NULL); + } + + return r; +} + +#define PN544_CB_TYPE_READER_F 1 + +static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb, + int err) +{ + struct pn544_hci_info *info = context; + + switch (info->async_cb_type) { + case PN544_CB_TYPE_READER_F: + if (err == 0) + skb_pull(skb, 1); + info->async_cb(info->async_cb_context, skb, err); + break; + default: + if (err == 0) + kfree_skb(skb); + break; + } +} + +#define MIFARE_CMD_AUTH_KEY_A 0x60 +#define MIFARE_CMD_AUTH_KEY_B 0x61 +#define MIFARE_CMD_HEADER 2 +#define MIFARE_UID_LEN 4 +#define MIFARE_KEY_LEN 6 +#define MIFARE_CMD_LEN 12 +/* + * Returns: + * <= 0: driver handled the data exchange + * 1: driver doesn't especially handle, please do standard processing + */ +static int pn544_hci_im_transceive(struct nfc_hci_dev *hdev, + struct nfc_target *target, + struct sk_buff *skb, data_exchange_cb_t cb, + void *cb_context) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + + pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__, + target->hci_reader_gate); + + switch (target->hci_reader_gate) { + case NFC_HCI_RF_READER_A_GATE: + if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) { + /* + * It seems that pn544 is inverting key and UID for + * MIFARE authentication commands. + */ + if (skb->len == MIFARE_CMD_LEN && + (skb->data[0] == MIFARE_CMD_AUTH_KEY_A || + skb->data[0] == MIFARE_CMD_AUTH_KEY_B)) { + u8 uid[MIFARE_UID_LEN]; + u8 *data = skb->data + MIFARE_CMD_HEADER; + + memcpy(uid, data + MIFARE_KEY_LEN, + MIFARE_UID_LEN); + memmove(data + MIFARE_UID_LEN, data, + MIFARE_KEY_LEN); + memcpy(data, uid, MIFARE_UID_LEN); + } + + return nfc_hci_send_cmd_async(hdev, + target->hci_reader_gate, + PN544_MIFARE_CMD, + skb->data, skb->len, + cb, cb_context); + } else + return 1; + case PN544_RF_READER_F_GATE: + *skb_push(skb, 1) = 0; + *skb_push(skb, 1) = 0; + + info->async_cb_type = PN544_CB_TYPE_READER_F; + info->async_cb = cb; + info->async_cb_context = cb_context; + + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + PN544_FELICA_RAW, skb->data, + skb->len, + pn544_hci_data_exchange_cb, info); + case PN544_RF_READER_JEWEL_GATE: + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + PN544_JEWEL_RAW_CMD, skb->data, + skb->len, cb, cb_context); + case PN544_RF_READER_NFCIP1_INITIATOR_GATE: + *skb_push(skb, 1) = 0; + + return nfc_hci_send_event(hdev, target->hci_reader_gate, + PN544_HCI_EVT_SND_DATA, skb->data, + skb->len); + default: + return 1; + } +} + +static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + /* Set default false for multiple information chaining */ + *skb_push(skb, 1) = 0; + + return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_HCI_EVT_SND_DATA, skb->data, skb->len); +} + +static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, + struct nfc_target *target) +{ + pr_debug("supported protocol %d", target->supported_protocols); + if (target->supported_protocols & (NFC_PROTO_ISO14443_MASK | + NFC_PROTO_ISO14443_B_MASK)) { + return nfc_hci_send_cmd(hdev, target->hci_reader_gate, + PN544_RF_READER_CMD_PRESENCE_CHECK, + NULL, 0, NULL); + } else if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) { + if (target->nfcid1_len != 4 && target->nfcid1_len != 7 && + target->nfcid1_len != 10) + return -EOPNOTSUPP; + + return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, + PN544_RF_READER_CMD_ACTIVATE_NEXT, + target->nfcid1, target->nfcid1_len, NULL); + } else if (target->supported_protocols & NFC_PROTO_JEWEL_MASK) { + return nfc_hci_send_cmd(hdev, target->hci_reader_gate, + PN544_JEWEL_RAW_CMD, NULL, 0, NULL); + } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) { + return nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE, + PN544_FELICA_RAW, NULL, 0, NULL); + } else if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) { + return nfc_hci_send_cmd(hdev, target->hci_reader_gate, + PN544_HCI_CMD_ATTREQUEST, + NULL, 0, NULL); + } + + return 0; +} + +void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, + struct sk_buff *skb) +{ + struct sk_buff *rgb_skb = NULL; + int r = 0; + + pr_debug("hci event %d", event); + switch (event) { + case PN544_HCI_EVT_ACTIVATED: + if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) + nfc_hci_target_discovered(hdev, gate); + else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { + r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ, + &rgb_skb); + + if (r < 0) + goto exit; + + nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, + NFC_COMM_PASSIVE, rgb_skb->data, + rgb_skb->len); + + kfree_skb(rgb_skb); + } + + break; + case PN544_HCI_EVT_DEACTIVATED: + nfc_hci_send_event(hdev, gate, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + break; + case PN544_HCI_EVT_RCV_DATA: + if (skb->len < 2) { + r = -EPROTO; + goto exit; + } + + if (skb->data[0] != 0) { + pr_debug("data0 %d", skb->data[0]); + r = -EPROTO; + goto exit; + } + + skb_pull(skb, 2); + nfc_tm_data_received(hdev->ndev, skb); + + return; + default: + break; + } + +exit: + kfree_skb(skb); +} + +static struct nfc_hci_ops pn544_hci_ops = { + .open = pn544_hci_open, + .close = pn544_hci_close, + .hci_ready = pn544_hci_ready, + .xmit = pn544_hci_xmit, + .start_poll = pn544_hci_start_poll, + .dep_link_up = pn544_hci_dep_link_up, + .dep_link_down = pn544_hci_dep_link_down, + .target_from_gate = pn544_hci_target_from_gate, + .complete_target_discovered = pn544_hci_complete_target_discovered, + .im_transceive = pn544_hci_im_transceive, + .tm_send = pn544_hci_tm_send, + .check_presence = pn544_hci_check_presence, + .event_received = pn544_hci_event_received, +}; + +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, + struct nfc_hci_dev **hdev) +{ + struct pn544_hci_info *info; + u32 protocols; + struct nfc_hci_init_data init_data; + int r; + + info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL); + if (!info) { + pr_err("Cannot allocate memory for pn544_hci_info.\n"); + r = -ENOMEM; + goto err_info_alloc; + } + + info->phy_ops = phy_ops; + info->phy_id = phy_id; + info->state = PN544_ST_COLD; + mutex_init(&info->info_lock); + + init_data.gate_count = ARRAY_SIZE(pn544_gates); + + memcpy(init_data.gates, pn544_gates, sizeof(pn544_gates)); + + /* + * TODO: Session id must include the driver name + some bus addr + * persistent info to discriminate 2 identical chips + */ + strcpy(init_data.session_id, "ID544HCI"); + + protocols = NFC_PROTO_JEWEL_MASK | + NFC_PROTO_MIFARE_MASK | + NFC_PROTO_FELICA_MASK | + NFC_PROTO_ISO14443_MASK | + NFC_PROTO_ISO14443_B_MASK | + NFC_PROTO_NFC_DEP_MASK; + + info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, + protocols, llc_name, + phy_headroom + PN544_CMDS_HEADROOM, + phy_tailroom, phy_payload); + if (!info->hdev) { + pr_err("Cannot allocate nfc hdev.\n"); + r = -ENOMEM; + goto err_alloc_hdev; + } + + nfc_hci_set_clientdata(info->hdev, info); + + r = nfc_hci_register_device(info->hdev); + if (r) + goto err_regdev; + + *hdev = info->hdev; + + return 0; + +err_regdev: + nfc_hci_free_device(info->hdev); + +err_alloc_hdev: + kfree(info); + +err_info_alloc: + return r; +} + +void pn544_hci_remove(struct nfc_hci_dev *hdev) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); + + nfc_hci_unregister_device(hdev); + nfc_hci_free_device(hdev); + kfree(info); +} diff --git a/drivers/nfc/pn544/pn544.h b/drivers/nfc/pn544/pn544.h new file mode 100644 index 000000000000..f47c6454914b --- /dev/null +++ b/drivers/nfc/pn544/pn544.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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. + */ + +#ifndef __LOCAL_PN544_H_ +#define __LOCAL_PN544_H_ + +#include + +#define DRIVER_DESC "HCI NFC driver for PN544" + +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, + struct nfc_hci_dev **hdev); +void pn544_hci_remove(struct nfc_hci_dev *hdev); + +#endif /* __LOCAL_PN544_H_ */ diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c deleted file mode 100644 index 70858b5f81e4..000000000000 --- a/drivers/nfc/pn544_hci.c +++ /dev/null @@ -1,1252 +0,0 @@ -/* - * HCI based Driver for NXP PN544 NFC Chip - * - * Copyright (C) 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#define DRIVER_DESC "HCI NFC driver for PN544" - -#define PN544_HCI_DRIVER_NAME "pn544_hci" - -/* Timing restrictions (ms) */ -#define PN544_HCI_RESETVEN_TIME 30 - -static struct i2c_device_id pn544_hci_id_table[] = { - {"pn544", 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, pn544_hci_id_table); - -#define HCI_MODE 0 -#define FW_MODE 1 - -/* framing in HCI mode */ -#define PN544_HCI_LLC_LEN 1 -#define PN544_HCI_LLC_CRC 2 -#define PN544_HCI_LLC_LEN_CRC (PN544_HCI_LLC_LEN + PN544_HCI_LLC_CRC) -#define PN544_HCI_LLC_MIN_SIZE (1 + PN544_HCI_LLC_LEN_CRC) -#define PN544_HCI_LLC_MAX_PAYLOAD 29 -#define PN544_HCI_LLC_MAX_SIZE (PN544_HCI_LLC_LEN_CRC + 1 + \ - PN544_HCI_LLC_MAX_PAYLOAD) - -enum pn544_state { - PN544_ST_COLD, - PN544_ST_FW_READY, - PN544_ST_READY, -}; - -#define FULL_VERSION_LEN 11 - -/* Proprietary commands */ -#define PN544_WRITE 0x3f - -/* Proprietary gates, events, commands and registers */ - -/* NFC_HCI_RF_READER_A_GATE additional registers and commands */ -#define PN544_RF_READER_A_AUTO_ACTIVATION 0x10 -#define PN544_RF_READER_A_CMD_CONTINUE_ACTIVATION 0x12 -#define PN544_MIFARE_CMD 0x21 - -/* Commands that apply to all RF readers */ -#define PN544_RF_READER_CMD_PRESENCE_CHECK 0x30 -#define PN544_RF_READER_CMD_ACTIVATE_NEXT 0x32 - -/* NFC_HCI_ID_MGMT_GATE additional registers */ -#define PN544_ID_MGMT_FULL_VERSION_SW 0x10 - -#define PN544_RF_READER_ISO15693_GATE 0x12 - -#define PN544_RF_READER_F_GATE 0x14 -#define PN544_FELICA_ID 0x04 -#define PN544_FELICA_RAW 0x20 - -#define PN544_RF_READER_JEWEL_GATE 0x15 -#define PN544_JEWEL_RAW_CMD 0x23 - -#define PN544_RF_READER_NFCIP1_INITIATOR_GATE 0x30 -#define PN544_RF_READER_NFCIP1_TARGET_GATE 0x31 - -#define PN544_SYS_MGMT_GATE 0x90 -#define PN544_SYS_MGMT_INFO_NOTIFICATION 0x02 - -#define PN544_POLLING_LOOP_MGMT_GATE 0x94 -#define PN544_DEP_MODE 0x01 -#define PN544_DEP_ATR_REQ 0x02 -#define PN544_DEP_ATR_RES 0x03 -#define PN544_DEP_MERGE 0x0D -#define PN544_PL_RDPHASES 0x06 -#define PN544_PL_EMULATION 0x07 -#define PN544_PL_NFCT_DEACTIVATED 0x09 - -#define PN544_SWP_MGMT_GATE 0xA0 - -#define PN544_NFC_WI_MGMT_GATE 0xA1 - -#define PN544_HCI_EVT_SND_DATA 0x01 -#define PN544_HCI_EVT_ACTIVATED 0x02 -#define PN544_HCI_EVT_DEACTIVATED 0x03 -#define PN544_HCI_EVT_RCV_DATA 0x04 -#define PN544_HCI_EVT_CONTINUE_MI 0x05 - -#define PN544_HCI_CMD_ATTREQUEST 0x12 -#define PN544_HCI_CMD_CONTINUE_ACTIVATION 0x13 - -static struct nfc_hci_gate pn544_gates[] = { - {NFC_HCI_ADMIN_GATE, NFC_HCI_INVALID_PIPE}, - {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE}, - {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE}, - {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_INVALID_PIPE}, - {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE}, - {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE}, - {PN544_SYS_MGMT_GATE, NFC_HCI_INVALID_PIPE}, - {PN544_SWP_MGMT_GATE, NFC_HCI_INVALID_PIPE}, - {PN544_POLLING_LOOP_MGMT_GATE, NFC_HCI_INVALID_PIPE}, - {PN544_NFC_WI_MGMT_GATE, NFC_HCI_INVALID_PIPE}, - {PN544_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE}, - {PN544_RF_READER_JEWEL_GATE, NFC_HCI_INVALID_PIPE}, - {PN544_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE}, - {PN544_RF_READER_NFCIP1_INITIATOR_GATE, NFC_HCI_INVALID_PIPE}, - {PN544_RF_READER_NFCIP1_TARGET_GATE, NFC_HCI_INVALID_PIPE} -}; - -/* Largest headroom needed for outgoing custom commands */ -#define PN544_CMDS_HEADROOM 2 -#define PN544_FRAME_HEADROOM 1 -#define PN544_FRAME_TAILROOM 2 - -struct pn544_hci_info { - struct i2c_client *i2c_dev; - struct nfc_hci_dev *hdev; - - enum pn544_state state; - - struct mutex info_lock; - - unsigned int gpio_en; - unsigned int gpio_irq; - unsigned int gpio_fw; - unsigned int en_polarity; - - int hard_fault; /* - * < 0 if hardware error occured (e.g. i2c err) - * and prevents normal operation. - */ - int async_cb_type; - data_exchange_cb_t async_cb; - void *async_cb_context; -}; - -static void pn544_hci_platform_init(struct pn544_hci_info *info) -{ - int polarity, retry, ret; - char rset_cmd[] = { 0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5 }; - int count = sizeof(rset_cmd); - - pr_info(DRIVER_DESC ": %s\n", __func__); - dev_info(&info->i2c_dev->dev, "Detecting nfc_en polarity\n"); - - /* Disable fw download */ - gpio_set_value(info->gpio_fw, 0); - - for (polarity = 0; polarity < 2; polarity++) { - info->en_polarity = polarity; - retry = 3; - while (retry--) { - /* power off */ - gpio_set_value(info->gpio_en, !info->en_polarity); - usleep_range(10000, 15000); - - /* power on */ - gpio_set_value(info->gpio_en, info->en_polarity); - usleep_range(10000, 15000); - - /* send reset */ - dev_dbg(&info->i2c_dev->dev, "Sending reset cmd\n"); - ret = i2c_master_send(info->i2c_dev, rset_cmd, count); - if (ret == count) { - dev_info(&info->i2c_dev->dev, - "nfc_en polarity : active %s\n", - (polarity == 0 ? "low" : "high")); - goto out; - } - } - } - - dev_err(&info->i2c_dev->dev, - "Could not detect nfc_en polarity, fallback to active high\n"); - -out: - gpio_set_value(info->gpio_en, !info->en_polarity); -} - -static int pn544_hci_enable(struct pn544_hci_info *info, int mode) -{ - pr_info(DRIVER_DESC ": %s\n", __func__); - - gpio_set_value(info->gpio_fw, 0); - gpio_set_value(info->gpio_en, info->en_polarity); - usleep_range(10000, 15000); - - return 0; -} - -static void pn544_hci_disable(struct pn544_hci_info *info) -{ - pr_info(DRIVER_DESC ": %s\n", __func__); - - gpio_set_value(info->gpio_fw, 0); - gpio_set_value(info->gpio_en, !info->en_polarity); - usleep_range(10000, 15000); - - gpio_set_value(info->gpio_en, info->en_polarity); - usleep_range(10000, 15000); - - gpio_set_value(info->gpio_en, !info->en_polarity); - usleep_range(10000, 15000); -} - -static int pn544_hci_i2c_write(struct i2c_client *client, u8 *buf, int len) -{ - int r; - - usleep_range(3000, 6000); - - r = i2c_master_send(client, buf, len); - - if (r == -EREMOTEIO) { /* Retry, chip was in standby */ - usleep_range(6000, 10000); - r = i2c_master_send(client, buf, len); - } - - if (r >= 0) { - if (r != len) - return -EREMOTEIO; - else - return 0; - } - - return r; -} - -static int check_crc(u8 *buf, int buflen) -{ - int len; - u16 crc; - - len = buf[0] + 1; - crc = crc_ccitt(0xffff, buf, len - 2); - crc = ~crc; - - if (buf[len - 2] != (crc & 0xff) || buf[len - 1] != (crc >> 8)) { - pr_err(PN544_HCI_DRIVER_NAME ": CRC error 0x%x != 0x%x 0x%x\n", - crc, buf[len - 1], buf[len - 2]); - - pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__); - print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, - 16, 2, buf, buflen, false); - return -EPERM; - } - return 0; -} - -/* - * Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees - * that i2c bus will be flushed and that next read will start on a new frame. - * returned skb contains only LLC header and payload. - * returns: - * -EREMOTEIO : i2c read error (fatal) - * -EBADMSG : frame was incorrect and discarded - * -ENOMEM : cannot allocate skb, frame dropped - */ -static int pn544_hci_i2c_read(struct i2c_client *client, struct sk_buff **skb) -{ - int r; - u8 len; - u8 tmp[PN544_HCI_LLC_MAX_SIZE - 1]; - - r = i2c_master_recv(client, &len, 1); - if (r != 1) { - dev_err(&client->dev, "cannot read len byte\n"); - return -EREMOTEIO; - } - - if ((len < (PN544_HCI_LLC_MIN_SIZE - 1)) || - (len > (PN544_HCI_LLC_MAX_SIZE - 1))) { - dev_err(&client->dev, "invalid len byte\n"); - r = -EBADMSG; - goto flush; - } - - *skb = alloc_skb(1 + len, GFP_KERNEL); - if (*skb == NULL) { - r = -ENOMEM; - goto flush; - } - - *skb_put(*skb, 1) = len; - - r = i2c_master_recv(client, skb_put(*skb, len), len); - if (r != len) { - kfree_skb(*skb); - return -EREMOTEIO; - } - - r = check_crc((*skb)->data, (*skb)->len); - if (r != 0) { - kfree_skb(*skb); - r = -EBADMSG; - goto flush; - } - - skb_pull(*skb, 1); - skb_trim(*skb, (*skb)->len - 2); - - usleep_range(3000, 6000); - - return 0; - -flush: - if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0) - r = -EREMOTEIO; - - usleep_range(3000, 6000); - - return r; -} - -/* - * 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. - * The frame format is len-data-crc, and corruption can occur anywhere while - * transiting on i2c bus, such that we could read an invalid len. - * In order to recover synchronization with the next frame, we must be sure - * to read the real amount of data without using the len byte. We do this by - * assuming the following: - * - the chip will always present only one single complete frame on the bus - * before triggering the interrupt - * - the chip will not present a new frame until we have completely read - * the previous one (or until we have handled the interrupt). - * The tricky case is when we read a corrupted len that is less than the real - * len. We must detect this here in order to determine that we need to flush - * the bus. This is the reason why we check the crc here. - */ -static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id) -{ - struct pn544_hci_info *info = dev_id; - struct i2c_client *client; - struct sk_buff *skb = NULL; - int r; - - if (!info || irq != info->i2c_dev->irq) { - WARN_ON_ONCE(1); - return IRQ_NONE; - } - - client = info->i2c_dev; - dev_dbg(&client->dev, "IRQ\n"); - - if (info->hard_fault != 0) - return IRQ_HANDLED; - - r = pn544_hci_i2c_read(client, &skb); - if (r == -EREMOTEIO) { - info->hard_fault = r; - - nfc_hci_recv_frame(info->hdev, NULL); - - return IRQ_HANDLED; - } else if ((r == -ENOMEM) || (r == -EBADMSG)) { - return IRQ_HANDLED; - } - - nfc_hci_recv_frame(info->hdev, skb); - - return IRQ_HANDLED; -} - -static int pn544_hci_open(struct nfc_hci_dev *hdev) -{ - struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); - int r = 0; - - mutex_lock(&info->info_lock); - - if (info->state != PN544_ST_COLD) { - r = -EBUSY; - goto out; - } - - r = pn544_hci_enable(info, HCI_MODE); - - if (r == 0) - info->state = PN544_ST_READY; - -out: - mutex_unlock(&info->info_lock); - return r; -} - -static void pn544_hci_close(struct nfc_hci_dev *hdev) -{ - struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); - - mutex_lock(&info->info_lock); - - if (info->state == PN544_ST_COLD) - goto out; - - pn544_hci_disable(info); - - info->state = PN544_ST_COLD; - -out: - mutex_unlock(&info->info_lock); -} - -static int pn544_hci_ready(struct nfc_hci_dev *hdev) -{ - struct sk_buff *skb; - static struct hw_config { - u8 adr[2]; - u8 value; - } hw_config[] = { - {{0x9f, 0x9a}, 0x00}, - - {{0x98, 0x10}, 0xbc}, - - {{0x9e, 0x71}, 0x00}, - - {{0x98, 0x09}, 0x00}, - - {{0x9e, 0xb4}, 0x00}, - - {{0x9e, 0xd9}, 0xff}, - {{0x9e, 0xda}, 0xff}, - {{0x9e, 0xdb}, 0x23}, - {{0x9e, 0xdc}, 0x21}, - {{0x9e, 0xdd}, 0x22}, - {{0x9e, 0xde}, 0x24}, - - {{0x9c, 0x01}, 0x08}, - - {{0x9e, 0xaa}, 0x01}, - - {{0x9b, 0xd1}, 0x0d}, - {{0x9b, 0xd2}, 0x24}, - {{0x9b, 0xd3}, 0x0a}, - {{0x9b, 0xd4}, 0x22}, - {{0x9b, 0xd5}, 0x08}, - {{0x9b, 0xd6}, 0x1e}, - {{0x9b, 0xdd}, 0x1c}, - - {{0x9b, 0x84}, 0x13}, - {{0x99, 0x81}, 0x7f}, - {{0x99, 0x31}, 0x70}, - - {{0x98, 0x00}, 0x3f}, - - {{0x9f, 0x09}, 0x00}, - - {{0x9f, 0x0a}, 0x05}, - - {{0x9e, 0xd1}, 0xa1}, - {{0x99, 0x23}, 0x00}, - - {{0x9e, 0x74}, 0x80}, - - {{0x9f, 0x28}, 0x10}, - - {{0x9f, 0x35}, 0x14}, - - {{0x9f, 0x36}, 0x60}, - - {{0x9c, 0x31}, 0x00}, - - {{0x9c, 0x32}, 0xc8}, - - {{0x9c, 0x19}, 0x40}, - - {{0x9c, 0x1a}, 0x40}, - - {{0x9c, 0x0c}, 0x00}, - - {{0x9c, 0x0d}, 0x00}, - - {{0x9c, 0x12}, 0x00}, - - {{0x9c, 0x13}, 0x00}, - - {{0x98, 0xa2}, 0x0e}, - - {{0x98, 0x93}, 0x40}, - - {{0x98, 0x7d}, 0x02}, - {{0x98, 0x7e}, 0x00}, - {{0x9f, 0xc8}, 0x01}, - }; - struct hw_config *p = hw_config; - int count = ARRAY_SIZE(hw_config); - struct sk_buff *res_skb; - u8 param[4]; - int r; - - param[0] = 0; - while (count--) { - param[1] = p->adr[0]; - param[2] = p->adr[1]; - param[3] = p->value; - - r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, PN544_WRITE, - param, 4, &res_skb); - if (r < 0) - return r; - - if (res_skb->len != 1) { - kfree_skb(res_skb); - return -EPROTO; - } - - if (res_skb->data[0] != p->value) { - kfree_skb(res_skb); - return -EIO; - } - - kfree_skb(res_skb); - - p++; - } - - param[0] = NFC_HCI_UICC_HOST_ID; - r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, - NFC_HCI_ADMIN_WHITELIST, param, 1); - if (r < 0) - return r; - - param[0] = 0x3d; - r = nfc_hci_set_param(hdev, PN544_SYS_MGMT_GATE, - PN544_SYS_MGMT_INFO_NOTIFICATION, param, 1); - if (r < 0) - return r; - - param[0] = 0x0; - r = nfc_hci_set_param(hdev, NFC_HCI_RF_READER_A_GATE, - PN544_RF_READER_A_AUTO_ACTIVATION, param, 1); - if (r < 0) - return r; - - r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, - NFC_HCI_EVT_END_OPERATION, NULL, 0); - if (r < 0) - return r; - - param[0] = 0x1; - r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, - PN544_PL_NFCT_DEACTIVATED, param, 1); - if (r < 0) - return r; - - param[0] = 0x0; - r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, - PN544_PL_RDPHASES, param, 1); - if (r < 0) - return r; - - r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE, - PN544_ID_MGMT_FULL_VERSION_SW, &skb); - if (r < 0) - return r; - - if (skb->len != FULL_VERSION_LEN) { - kfree_skb(skb); - return -EINVAL; - } - - print_hex_dump(KERN_DEBUG, "FULL VERSION SOFTWARE INFO: ", - DUMP_PREFIX_NONE, 16, 1, - skb->data, FULL_VERSION_LEN, false); - - kfree_skb(skb); - - return 0; -} - -static void pn544_hci_add_len_crc(struct sk_buff *skb) -{ - u16 crc; - int len; - - len = skb->len + 2; - *skb_push(skb, 1) = len; - - crc = crc_ccitt(0xffff, skb->data, skb->len); - crc = ~crc; - *skb_put(skb, 1) = crc & 0xff; - *skb_put(skb, 1) = crc >> 8; -} - -static void pn544_hci_remove_len_crc(struct sk_buff *skb) -{ - skb_pull(skb, PN544_FRAME_HEADROOM); - skb_trim(skb, PN544_FRAME_TAILROOM); -} - -static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) -{ - struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); - struct i2c_client *client = info->i2c_dev; - int r; - - if (info->hard_fault != 0) - return info->hard_fault; - - pn544_hci_add_len_crc(skb); - r = pn544_hci_i2c_write(client, skb->data, skb->len); - pn544_hci_remove_len_crc(skb); - - return r; -} - -static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, - u32 im_protocols, u32 tm_protocols) -{ - u8 phases = 0; - int r; - u8 duration[2]; - u8 activated; - u8 i_mode = 0x3f; /* Enable all supported modes */ - u8 t_mode = 0x0f; - u8 t_merge = 0x01; /* Enable merge by default */ - - pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n", - __func__, im_protocols, tm_protocols); - - r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, - NFC_HCI_EVT_END_OPERATION, NULL, 0); - if (r < 0) - return r; - - duration[0] = 0x18; - duration[1] = 0x6a; - r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, - PN544_PL_EMULATION, duration, 2); - if (r < 0) - return r; - - activated = 0; - r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, - PN544_PL_NFCT_DEACTIVATED, &activated, 1); - if (r < 0) - return r; - - if (im_protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_MIFARE_MASK | - NFC_PROTO_JEWEL_MASK)) - phases |= 1; /* Type A */ - if (im_protocols & NFC_PROTO_FELICA_MASK) { - phases |= (1 << 2); /* Type F 212 */ - phases |= (1 << 3); /* Type F 424 */ - } - - phases |= (1 << 5); /* NFC active */ - - r = nfc_hci_set_param(hdev, PN544_POLLING_LOOP_MGMT_GATE, - PN544_PL_RDPHASES, &phases, 1); - if (r < 0) - return r; - - if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { - hdev->gb = nfc_get_local_general_bytes(hdev->ndev, - &hdev->gb_len); - pr_debug("generate local bytes %p", hdev->gb); - if (hdev->gb == NULL || hdev->gb_len == 0) { - im_protocols &= ~NFC_PROTO_NFC_DEP_MASK; - tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK; - } - } - - if (im_protocols & NFC_PROTO_NFC_DEP_MASK) { - r = nfc_hci_send_event(hdev, - PN544_RF_READER_NFCIP1_INITIATOR_GATE, - NFC_HCI_EVT_END_OPERATION, NULL, 0); - if (r < 0) - return r; - - r = nfc_hci_set_param(hdev, - PN544_RF_READER_NFCIP1_INITIATOR_GATE, - PN544_DEP_MODE, &i_mode, 1); - if (r < 0) - return r; - - r = nfc_hci_set_param(hdev, - PN544_RF_READER_NFCIP1_INITIATOR_GATE, - PN544_DEP_ATR_REQ, hdev->gb, hdev->gb_len); - if (r < 0) - return r; - - r = nfc_hci_send_event(hdev, - PN544_RF_READER_NFCIP1_INITIATOR_GATE, - NFC_HCI_EVT_READER_REQUESTED, NULL, 0); - if (r < 0) - nfc_hci_send_event(hdev, - PN544_RF_READER_NFCIP1_INITIATOR_GATE, - NFC_HCI_EVT_END_OPERATION, NULL, 0); - } - - if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { - r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, - PN544_DEP_MODE, &t_mode, 1); - if (r < 0) - return r; - - r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, - PN544_DEP_ATR_RES, hdev->gb, hdev->gb_len); - if (r < 0) - return r; - - r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, - PN544_DEP_MERGE, &t_merge, 1); - if (r < 0) - return r; - } - - r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, - NFC_HCI_EVT_READER_REQUESTED, NULL, 0); - if (r < 0) - nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, - NFC_HCI_EVT_END_OPERATION, NULL, 0); - - return r; -} - -static int pn544_hci_dep_link_up(struct nfc_hci_dev *hdev, - struct nfc_target *target, u8 comm_mode, - u8 *gb, size_t gb_len) -{ - struct sk_buff *rgb_skb = NULL; - int r; - - r = nfc_hci_get_param(hdev, target->hci_reader_gate, - PN544_DEP_ATR_RES, &rgb_skb); - if (r < 0) - return r; - - if (rgb_skb->len == 0 || rgb_skb->len > NFC_GB_MAXSIZE) { - r = -EPROTO; - goto exit; - } - print_hex_dump(KERN_DEBUG, "remote gb: ", DUMP_PREFIX_OFFSET, - 16, 1, rgb_skb->data, rgb_skb->len, true); - - r = nfc_set_remote_general_bytes(hdev->ndev, rgb_skb->data, - rgb_skb->len); - - if (r == 0) - r = nfc_dep_link_is_up(hdev->ndev, target->idx, comm_mode, - NFC_RF_INITIATOR); -exit: - kfree_skb(rgb_skb); - return r; -} - -static int pn544_hci_dep_link_down(struct nfc_hci_dev *hdev) -{ - - return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_INITIATOR_GATE, - NFC_HCI_EVT_END_OPERATION, NULL, 0); -} - -static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, - struct nfc_target *target) -{ - switch (gate) { - case PN544_RF_READER_F_GATE: - target->supported_protocols = NFC_PROTO_FELICA_MASK; - break; - case PN544_RF_READER_JEWEL_GATE: - target->supported_protocols = NFC_PROTO_JEWEL_MASK; - target->sens_res = 0x0c00; - break; - case PN544_RF_READER_NFCIP1_INITIATOR_GATE: - target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; - break; - default: - return -EPROTO; - } - - return 0; -} - -static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev, - u8 gate, - struct nfc_target *target) -{ - struct sk_buff *uid_skb; - int r = 0; - - if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) - return r; - - if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) { - r = nfc_hci_send_cmd(hdev, - PN544_RF_READER_NFCIP1_INITIATOR_GATE, - PN544_HCI_CMD_CONTINUE_ACTIVATION, NULL, 0, NULL); - if (r < 0) - return r; - - target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE; - } else if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) { - if (target->nfcid1_len != 4 && target->nfcid1_len != 7 && - target->nfcid1_len != 10) - return -EPROTO; - - r = nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, - PN544_RF_READER_CMD_ACTIVATE_NEXT, - target->nfcid1, target->nfcid1_len, NULL); - } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) { - r = nfc_hci_get_param(hdev, PN544_RF_READER_F_GATE, - PN544_FELICA_ID, &uid_skb); - if (r < 0) - return r; - - if (uid_skb->len != 8) { - kfree_skb(uid_skb); - return -EPROTO; - } - - r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE, - PN544_RF_READER_CMD_ACTIVATE_NEXT, - uid_skb->data, uid_skb->len, NULL); - kfree_skb(uid_skb); - - r = nfc_hci_send_cmd(hdev, - PN544_RF_READER_NFCIP1_INITIATOR_GATE, - PN544_HCI_CMD_CONTINUE_ACTIVATION, - NULL, 0, NULL); - if (r < 0) - return r; - - target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE; - target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; - } else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) { - /* - * TODO: maybe other ISO 14443 require some kind of continue - * activation, but for now we've seen only this one below. - */ - if (target->sens_res == 0x4403) /* Type 4 Mifare DESFire */ - r = nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, - PN544_RF_READER_A_CMD_CONTINUE_ACTIVATION, - NULL, 0, NULL); - } - - return r; -} - -#define PN544_CB_TYPE_READER_F 1 - -static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb, - int err) -{ - struct pn544_hci_info *info = context; - - switch (info->async_cb_type) { - case PN544_CB_TYPE_READER_F: - if (err == 0) - skb_pull(skb, 1); - info->async_cb(info->async_cb_context, skb, err); - break; - default: - if (err == 0) - kfree_skb(skb); - break; - } -} - -#define MIFARE_CMD_AUTH_KEY_A 0x60 -#define MIFARE_CMD_AUTH_KEY_B 0x61 -#define MIFARE_CMD_HEADER 2 -#define MIFARE_UID_LEN 4 -#define MIFARE_KEY_LEN 6 -#define MIFARE_CMD_LEN 12 -/* - * Returns: - * <= 0: driver handled the data exchange - * 1: driver doesn't especially handle, please do standard processing - */ -static int pn544_hci_im_transceive(struct nfc_hci_dev *hdev, - struct nfc_target *target, - struct sk_buff *skb, data_exchange_cb_t cb, - void *cb_context) -{ - struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); - - pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__, - target->hci_reader_gate); - - switch (target->hci_reader_gate) { - case NFC_HCI_RF_READER_A_GATE: - if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) { - /* - * It seems that pn544 is inverting key and UID for - * MIFARE authentication commands. - */ - if (skb->len == MIFARE_CMD_LEN && - (skb->data[0] == MIFARE_CMD_AUTH_KEY_A || - skb->data[0] == MIFARE_CMD_AUTH_KEY_B)) { - u8 uid[MIFARE_UID_LEN]; - u8 *data = skb->data + MIFARE_CMD_HEADER; - - memcpy(uid, data + MIFARE_KEY_LEN, - MIFARE_UID_LEN); - memmove(data + MIFARE_UID_LEN, data, - MIFARE_KEY_LEN); - memcpy(data, uid, MIFARE_UID_LEN); - } - - return nfc_hci_send_cmd_async(hdev, - target->hci_reader_gate, - PN544_MIFARE_CMD, - skb->data, skb->len, - cb, cb_context); - } else - return 1; - case PN544_RF_READER_F_GATE: - *skb_push(skb, 1) = 0; - *skb_push(skb, 1) = 0; - - info->async_cb_type = PN544_CB_TYPE_READER_F; - info->async_cb = cb; - info->async_cb_context = cb_context; - - return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, - PN544_FELICA_RAW, skb->data, - skb->len, - pn544_hci_data_exchange_cb, info); - case PN544_RF_READER_JEWEL_GATE: - return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, - PN544_JEWEL_RAW_CMD, skb->data, - skb->len, cb, cb_context); - case PN544_RF_READER_NFCIP1_INITIATOR_GATE: - *skb_push(skb, 1) = 0; - - return nfc_hci_send_event(hdev, target->hci_reader_gate, - PN544_HCI_EVT_SND_DATA, skb->data, - skb->len); - default: - return 1; - } -} - -static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb) -{ - /* Set default false for multiple information chaining */ - *skb_push(skb, 1) = 0; - - return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, - PN544_HCI_EVT_SND_DATA, skb->data, skb->len); -} - -static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, - struct nfc_target *target) -{ - pr_debug("supported protocol %d", target->supported_protocols); - if (target->supported_protocols & (NFC_PROTO_ISO14443_MASK | - NFC_PROTO_ISO14443_B_MASK)) { - return nfc_hci_send_cmd(hdev, target->hci_reader_gate, - PN544_RF_READER_CMD_PRESENCE_CHECK, - NULL, 0, NULL); - } else if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) { - if (target->nfcid1_len != 4 && target->nfcid1_len != 7 && - target->nfcid1_len != 10) - return -EOPNOTSUPP; - - return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, - PN544_RF_READER_CMD_ACTIVATE_NEXT, - target->nfcid1, target->nfcid1_len, NULL); - } else if (target->supported_protocols & NFC_PROTO_JEWEL_MASK) { - return nfc_hci_send_cmd(hdev, target->hci_reader_gate, - PN544_JEWEL_RAW_CMD, NULL, 0, NULL); - } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) { - return nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE, - PN544_FELICA_RAW, NULL, 0, NULL); - } else if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) { - return nfc_hci_send_cmd(hdev, target->hci_reader_gate, - PN544_HCI_CMD_ATTREQUEST, - NULL, 0, NULL); - } - - return 0; -} - -void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, - struct sk_buff *skb) -{ - struct sk_buff *rgb_skb = NULL; - int r = 0; - - pr_debug("hci event %d", event); - switch (event) { - case PN544_HCI_EVT_ACTIVATED: - if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) - nfc_hci_target_discovered(hdev, gate); - else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { - r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ, - &rgb_skb); - - if (r < 0) - goto exit; - - nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, - NFC_COMM_PASSIVE, rgb_skb->data, - rgb_skb->len); - - kfree_skb(rgb_skb); - } - - break; - case PN544_HCI_EVT_DEACTIVATED: - nfc_hci_send_event(hdev, gate, - NFC_HCI_EVT_END_OPERATION, NULL, 0); - break; - case PN544_HCI_EVT_RCV_DATA: - if (skb->len < 2) { - r = -EPROTO; - goto exit; - } - - if (skb->data[0] != 0) { - pr_debug("data0 %d", skb->data[0]); - r = -EPROTO; - goto exit; - } - - skb_pull(skb, 2); - nfc_tm_data_received(hdev->ndev, skb); - - return; - default: - break; - } - -exit: - kfree_skb(skb); -} - -static struct nfc_hci_ops pn544_hci_ops = { - .open = pn544_hci_open, - .close = pn544_hci_close, - .hci_ready = pn544_hci_ready, - .xmit = pn544_hci_xmit, - .start_poll = pn544_hci_start_poll, - .dep_link_up = pn544_hci_dep_link_up, - .dep_link_down = pn544_hci_dep_link_down, - .target_from_gate = pn544_hci_target_from_gate, - .complete_target_discovered = pn544_hci_complete_target_discovered, - .im_transceive = pn544_hci_im_transceive, - .tm_send = pn544_hci_tm_send, - .check_presence = pn544_hci_check_presence, - .event_received = pn544_hci_event_received, -}; - -static int __devinit pn544_hci_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct pn544_hci_info *info; - struct pn544_nfc_platform_data *pdata; - int r = 0; - u32 protocols; - struct nfc_hci_init_data init_data; - - dev_dbg(&client->dev, "%s\n", __func__); - dev_dbg(&client->dev, "IRQ: %d\n", client->irq); - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, "Need I2C_FUNC_I2C\n"); - return -ENODEV; - } - - info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL); - if (!info) { - dev_err(&client->dev, - "Cannot allocate memory for pn544_hci_info.\n"); - r = -ENOMEM; - goto err_info_alloc; - } - - info->i2c_dev = client; - info->state = PN544_ST_COLD; - mutex_init(&info->info_lock); - i2c_set_clientdata(client, info); - - pdata = client->dev.platform_data; - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - r = -EINVAL; - goto err_pdata; - } - - if (pdata->request_resources == NULL) { - dev_err(&client->dev, "request_resources() missing\n"); - r = -EINVAL; - goto err_pdata; - } - - r = pdata->request_resources(client); - if (r) { - dev_err(&client->dev, "Cannot get platform resources\n"); - goto err_pdata; - } - - info->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); - info->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET); - info->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ); - - pn544_hci_platform_init(info); - - r = request_threaded_irq(client->irq, NULL, pn544_hci_irq_thread_fn, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, - PN544_HCI_DRIVER_NAME, info); - if (r < 0) { - dev_err(&client->dev, "Unable to register IRQ handler\n"); - goto err_rti; - } - - init_data.gate_count = ARRAY_SIZE(pn544_gates); - - memcpy(init_data.gates, pn544_gates, sizeof(pn544_gates)); - - /* - * TODO: Session id must include the driver name + some bus addr - * persistent info to discriminate 2 identical chips - */ - strcpy(init_data.session_id, "ID544HCI"); - - protocols = NFC_PROTO_JEWEL_MASK | - NFC_PROTO_MIFARE_MASK | - NFC_PROTO_FELICA_MASK | - NFC_PROTO_ISO14443_MASK | - NFC_PROTO_ISO14443_B_MASK | - NFC_PROTO_NFC_DEP_MASK; - - info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, - protocols, LLC_SHDLC_NAME, - PN544_FRAME_HEADROOM + - PN544_CMDS_HEADROOM, - PN544_FRAME_TAILROOM, - PN544_HCI_LLC_MAX_PAYLOAD); - if (!info->hdev) { - dev_err(&client->dev, "Cannot allocate nfc hdev.\n"); - r = -ENOMEM; - goto err_alloc_hdev; - } - - nfc_hci_set_clientdata(info->hdev, info); - - r = nfc_hci_register_device(info->hdev); - if (r) - goto err_regdev; - - return 0; - -err_regdev: - nfc_hci_free_device(info->hdev); - -err_alloc_hdev: - free_irq(client->irq, info); - -err_rti: - if (pdata->free_resources != NULL) - pdata->free_resources(); - -err_pdata: - kfree(info); - -err_info_alloc: - return r; -} - -static __devexit int pn544_hci_remove(struct i2c_client *client) -{ - struct pn544_hci_info *info = i2c_get_clientdata(client); - struct pn544_nfc_platform_data *pdata = client->dev.platform_data; - - dev_dbg(&client->dev, "%s\n", __func__); - - nfc_hci_free_device(info->hdev); - - if (info->state != PN544_ST_COLD) { - if (pdata->disable) - pdata->disable(); - } - - free_irq(client->irq, info); - if (pdata->free_resources) - pdata->free_resources(); - - kfree(info); - - return 0; -} - -static struct i2c_driver pn544_hci_driver = { - .driver = { - .name = PN544_HCI_DRIVER_NAME, - }, - .probe = pn544_hci_probe, - .id_table = pn544_hci_id_table, - .remove = __devexit_p(pn544_hci_remove), -}; - -static int __init pn544_hci_init(void) -{ - int r; - - pr_debug(DRIVER_DESC ": %s\n", __func__); - - r = i2c_add_driver(&pn544_hci_driver); - if (r) { - pr_err(PN544_HCI_DRIVER_NAME ": driver registration failed\n"); - return r; - } - - return 0; -} - -static void __exit pn544_hci_exit(void) -{ - i2c_del_driver(&pn544_hci_driver); -} - -module_init(pn544_hci_init); -module_exit(pn544_hci_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index bc87b8f2d692..639f50af42df 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -24,6 +24,12 @@ #include +struct nfc_phy_ops { + int (*write)(void *dev_id, struct sk_buff *skb); + int (*enable)(void *dev_id); + void (*disable)(void *dev_id); +}; + struct nfc_hci_dev; struct nfc_hci_ops { -- cgit v1.2.3 From 7eda8b8e967781cfa5a04962502f9aa428f67e5f Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Mon, 22 Oct 2012 15:57:58 +0200 Subject: NFC: Use IDR library to assing NFC devices IDs As a consequence the NFC device IDs won't be increasing all the time, as IDR provides the first available ID. Signed-off-by: Samuel Ortiz --- include/net/nfc/nfc.h | 2 +- net/nfc/core.c | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index f05b10682c9d..fce80b2f9be7 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -95,7 +95,7 @@ struct nfc_genl_data { }; struct nfc_dev { - unsigned int idx; + int idx; u32 target_next_idx; struct nfc_target *targets; int n_targets; diff --git a/net/nfc/core.c b/net/nfc/core.c index f1c33f233311..e94363dbbf4a 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -40,6 +40,9 @@ int nfc_devlist_generation; DEFINE_MUTEX(nfc_devlist_mutex); +/* NFC device ID bitmap */ +static DEFINE_IDA(nfc_index_ida); + /** * nfc_dev_up - turn on the NFC device * @@ -760,7 +763,6 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, u32 supported_protocols, int tx_headroom, int tx_tailroom) { - static atomic_t dev_no = ATOMIC_INIT(0); struct nfc_dev *dev; if (!ops->start_poll || !ops->stop_poll || !ops->activate_target || @@ -774,11 +776,6 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, if (!dev) return NULL; - dev->dev.class = &nfc_class; - dev->idx = atomic_inc_return(&dev_no) - 1; - dev_set_name(&dev->dev, "nfc%d", dev->idx); - device_initialize(&dev->dev); - dev->ops = ops; dev->supported_protocols = supported_protocols; dev->tx_headroom = tx_headroom; @@ -814,6 +811,14 @@ int nfc_register_device(struct nfc_dev *dev) pr_debug("dev_name=%s\n", dev_name(&dev->dev)); + dev->idx = ida_simple_get(&nfc_index_ida, 0, 0, GFP_KERNEL); + if (dev->idx < 0) + return dev->idx; + + dev->dev.class = &nfc_class; + dev_set_name(&dev->dev, "nfc%d", dev->idx); + device_initialize(&dev->dev); + mutex_lock(&nfc_devlist_mutex); nfc_devlist_generation++; rc = device_add(&dev->dev); @@ -842,10 +847,12 @@ EXPORT_SYMBOL(nfc_register_device); */ void nfc_unregister_device(struct nfc_dev *dev) { - int rc; + int rc, id; pr_debug("dev_name=%s\n", dev_name(&dev->dev)); + id = dev->idx; + mutex_lock(&nfc_devlist_mutex); nfc_devlist_generation++; @@ -864,6 +871,8 @@ void nfc_unregister_device(struct nfc_dev *dev) pr_debug("The userspace won't be notified that the device %s was removed\n", dev_name(&dev->dev)); + ida_simple_remove(&nfc_index_ida, id); + } EXPORT_SYMBOL(nfc_unregister_device); -- cgit v1.2.3 From 9b395bc3be1cebf0144a127c7e67d56dbdac0930 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 26 Oct 2012 00:36:40 +0200 Subject: mac80211: verify that skb data is present A number of places in the mesh code don't check that the frame data is present and in the skb header when trying to access. Add those checks and the necessary pskb_may_pull() calls. This prevents accessing data that doesn't actually exist. To do this, export ieee80211_get_mesh_hdrlen() to be able to use it in mac80211. Cc: stable@vger.kernel.org Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 9 +++++++++ net/mac80211/rx.c | 32 +++++++++++++++++++++++++++++++- net/wireless/util.c | 3 ++- 3 files changed, 42 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f8cd4cf3fad8..7d5b6000378b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2651,6 +2651,15 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb); */ unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc); +/** + * ieee80211_get_mesh_hdrlen - get mesh extension header length + * @meshhdr: the mesh extension header, only the flags field + * (first byte) will be accessed + * Returns the length of the extension header, which is always at + * least 6 bytes and at most 18 if address 5 and 6 are present. + */ +unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr); + /** * DOC: Data path helpers * diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 99cdee16e31b..265a032dec49 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -531,6 +531,11 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) if (ieee80211_is_action(hdr->frame_control)) { u8 category; + + /* make sure category field is present */ + if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE) + return RX_DROP_MONITOR; + mgmt = (struct ieee80211_mgmt *)hdr; category = mgmt->u.action.category; if (category != WLAN_CATEGORY_MESH_ACTION && @@ -1892,6 +1897,20 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) hdr = (struct ieee80211_hdr *) skb->data; hdrlen = ieee80211_hdrlen(hdr->frame_control); + + /* make sure fixed part of mesh header is there, also checks skb len */ + if (!pskb_may_pull(rx->skb, hdrlen + 6)) + return RX_DROP_MONITOR; + + mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + + /* make sure full mesh header is there, also checks skb len */ + if (!pskb_may_pull(rx->skb, + hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr))) + return RX_DROP_MONITOR; + + /* reload pointers */ + hdr = (struct ieee80211_hdr *) skb->data; mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); /* frame is in RMC, don't forward */ @@ -1915,9 +1934,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) if (is_multicast_ether_addr(hdr->addr1)) { mpp_addr = hdr->addr3; proxied_addr = mesh_hdr->eaddr1; - } else { + } else if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6) { + /* has_a4 already checked in ieee80211_rx_mesh_check */ mpp_addr = hdr->addr4; proxied_addr = mesh_hdr->eaddr2; + } else { + return RX_DROP_MONITOR; } rcu_read_lock(); @@ -2354,6 +2376,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) } break; case WLAN_CATEGORY_SELF_PROTECTED: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.self_prot.action_code))) + break; + switch (mgmt->u.action.u.self_prot.action_code) { case WLAN_SP_MESH_PEERING_OPEN: case WLAN_SP_MESH_PEERING_CLOSE: @@ -2372,6 +2398,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) } break; case WLAN_CATEGORY_MESH_ACTION: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.mesh_action.action_code))) + break; + if (!ieee80211_vif_is_mesh(&sdata->vif)) break; if (mesh_action_is_path_sel(mgmt) && diff --git a/net/wireless/util.c b/net/wireless/util.c index 45a09de1ffe3..2762e8329986 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -309,7 +309,7 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) } EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); -static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) +unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) { int ae = meshhdr->flags & MESH_FLAGS_AE; /* 802.11-2012, 8.2.4.7.3 */ @@ -323,6 +323,7 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) return 18; } } +EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen); int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, enum nl80211_iftype iftype) -- cgit v1.2.3 From 52feb444a90304eb13c03115bb9758101dbb9254 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Wed, 17 Oct 2012 14:43:39 +0200 Subject: NFC: Extend netlink interface for LTO, RW, and MIUX parameters support NFC_CMD_LLC_GET_PARAMS: request LTO, RW, and MIUX parameters for a device NFC_CMD_LLC_SET_PARAMS: set one or more of LTO, RW, and MIUX parameters for a device. LTO must be set before the link is up otherwise -EINPROGRESS is returned. RW and MIUX can be set at anytime and will be passed in subsequent CONNECT and CC messages. If one of the passed parameters is wrong none is set and -EINVAL is returned. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- include/uapi/linux/nfc.h | 15 +++++ net/nfc/llcp/commands.c | 18 ++---- net/nfc/llcp/llcp.c | 14 ++--- net/nfc/llcp/llcp.h | 3 + net/nfc/netlink.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++ net/nfc/nfc.h | 6 ++ 6 files changed, 189 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index d908d17da56d..0e63cee8d810 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -60,6 +60,13 @@ * target mode. * @NFC_EVENT_DEVICE_DEACTIVATED: event emitted when the adapter is deactivated * from target mode. + * @NFC_CMD_LLC_GET_PARAMS: request LTO, RW, and MIUX parameters for a device + * @NFC_CMD_LLC_SET_PARAMS: set one or more of LTO, RW, and MIUX parameters for + * a device. LTO must be set before the link is up otherwise -EINPROGRESS + * is returned. RW and MIUX can be set at anytime and will be passed in + * subsequent CONNECT and CC messages. + * If one of the passed parameters is wrong none is set and -EINVAL is + * returned. */ enum nfc_commands { NFC_CMD_UNSPEC, @@ -77,6 +84,8 @@ enum nfc_commands { NFC_EVENT_TARGET_LOST, NFC_EVENT_TM_ACTIVATED, NFC_EVENT_TM_DEACTIVATED, + NFC_CMD_LLC_GET_PARAMS, + NFC_CMD_LLC_SET_PARAMS, /* private: internal use only */ __NFC_CMD_AFTER_LAST }; @@ -102,6 +111,9 @@ enum nfc_commands { * @NFC_ATTR_RF_MODE: Initiator or target * @NFC_ATTR_IM_PROTOCOLS: Initiator mode protocols to poll for * @NFC_ATTR_TM_PROTOCOLS: Target mode protocols to listen for + * @NFC_ATTR_LLC_PARAM_LTO: Link TimeOut parameter + * @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter + * @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter */ enum nfc_attrs { NFC_ATTR_UNSPEC, @@ -119,6 +131,9 @@ enum nfc_attrs { NFC_ATTR_DEVICE_POWERED, NFC_ATTR_IM_PROTOCOLS, NFC_ATTR_TM_PROTOCOLS, + NFC_ATTR_LLC_PARAM_LTO, + NFC_ATTR_LLC_PARAM_RW, + NFC_ATTR_LLC_PARAM_MIUX, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c index 79415353cc28..ed2d17312d61 100644 --- a/net/nfc/llcp/commands.c +++ b/net/nfc/llcp/commands.c @@ -316,8 +316,7 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) struct sk_buff *skb; u8 *service_name_tlv = NULL, service_name_tlv_length; u8 *miux_tlv = NULL, miux_tlv_length; - u8 *rw_tlv = NULL, rw_tlv_length, rw; - __be16 miux; + u8 *rw_tlv = NULL, rw_tlv_length; int err; u16 size = 0; @@ -335,13 +334,11 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) size += service_name_tlv_length; } - miux = cpu_to_be16(LLCP_MAX_MIUX); - miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, + miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0, &miux_tlv_length); size += miux_tlv_length; - rw = LLCP_MAX_RW; - rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); + rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length); size += rw_tlv_length; pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len); @@ -378,8 +375,7 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) struct nfc_llcp_local *local; struct sk_buff *skb; u8 *miux_tlv = NULL, miux_tlv_length; - u8 *rw_tlv = NULL, rw_tlv_length, rw; - __be16 miux; + u8 *rw_tlv = NULL, rw_tlv_length; int err; u16 size = 0; @@ -389,13 +385,11 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) if (local == NULL) return -ENODEV; - miux = cpu_to_be16(LLCP_MAX_MIUX); - miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, + miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0, &miux_tlv_length); size += miux_tlv_length; - rw = LLCP_MAX_RW; - rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); + rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length); size += rw_tlv_length; skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size); diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 2e23bd348ebd..f6804532047a 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -467,10 +467,9 @@ static u8 nfc_llcp_reserve_sdp_ssap(struct nfc_llcp_local *local) static int nfc_llcp_build_gb(struct nfc_llcp_local *local) { u8 *gb_cur, *version_tlv, version, version_length; - u8 *lto_tlv, lto, lto_length; + u8 *lto_tlv, lto_length; u8 *wks_tlv, wks_length; u8 *miux_tlv, miux_length; - __be16 miux; u8 gb_len = 0; int ret = 0; @@ -479,9 +478,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local) 1, &version_length); gb_len += version_length; - /* 1500 ms */ - lto = 150; - lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, <o, 1, <o_length); + lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &local->lto, 1, <o_length); gb_len += lto_length; pr_debug("Local wks 0x%lx\n", local->local_wks); @@ -489,8 +486,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local) &wks_length); gb_len += wks_length; - miux = cpu_to_be16(LLCP_MAX_MIUX); - miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, + miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0, &miux_length); gb_len += miux_length; @@ -1383,6 +1379,10 @@ int nfc_llcp_register_device(struct nfc_dev *ndev) rwlock_init(&local->connecting_sockets.lock); rwlock_init(&local->raw_sockets.lock); + local->lto = 150; /* 1500 ms */ + local->rw = LLCP_MAX_RW; + local->miux = cpu_to_be16(LLCP_MAX_MIUX); + nfc_llcp_build_gb(local); local->remote_miu = LLCP_DEFAULT_MIU; diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h index 276da3a6a589..0d62366f8cc3 100644 --- a/net/nfc/llcp/llcp.h +++ b/net/nfc/llcp/llcp.h @@ -64,6 +64,9 @@ struct nfc_llcp_local { u32 target_idx; u8 rf_mode; u8 comm_mode; + u8 lto; + u8 rw; + __be16 miux; unsigned long local_wks; /* Well known services */ unsigned long local_sdp; /* Local services */ unsigned long local_sap; /* Local SAPs, not available for discovery */ diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 614cfd0470b7..3568ae16786d 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -29,6 +29,8 @@ #include "nfc.h" +#include "llcp/llcp.h" + static struct genl_multicast_group nfc_genl_event_mcgrp = { .name = NFC_GENL_MCAST_EVENT_NAME, }; @@ -716,6 +718,146 @@ static int nfc_genl_dep_link_down(struct sk_buff *skb, struct genl_info *info) return rc; } +static int nfc_genl_send_params(struct sk_buff *msg, + struct nfc_llcp_local *local, + u32 portid, u32 seq) +{ + void *hdr; + + hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, 0, + NFC_CMD_LLC_GET_PARAMS); + if (!hdr) + return -EMSGSIZE; + + if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, local->dev->idx) || + nla_put_u8(msg, NFC_ATTR_LLC_PARAM_LTO, local->lto) || + nla_put_u8(msg, NFC_ATTR_LLC_PARAM_RW, local->rw) || + nla_put_u16(msg, NFC_ATTR_LLC_PARAM_MIUX, be16_to_cpu(local->miux))) + goto nla_put_failure; + + return genlmsg_end(msg, hdr); + +nla_put_failure: + + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nfc_genl_llc_get_params(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + struct nfc_llcp_local *local; + int rc = 0; + struct sk_buff *msg = NULL; + u32 idx; + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) + return -EINVAL; + + idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(idx); + if (!dev) + return -ENODEV; + + device_lock(&dev->dev); + + local = nfc_llcp_find_local(dev); + if (!local) { + rc = -ENODEV; + goto exit; + } + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) { + rc = -ENOMEM; + goto exit; + } + + rc = nfc_genl_send_params(msg, local, info->snd_portid, info->snd_seq); + +exit: + device_unlock(&dev->dev); + + nfc_put_device(dev); + + if (rc < 0) { + if (msg) + nlmsg_free(msg); + + return rc; + } + + return genlmsg_reply(msg, info); +} + +static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + struct nfc_llcp_local *local; + u8 rw = 0; + u16 miux = 0; + u32 idx; + int rc = 0; + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || + (!info->attrs[NFC_ATTR_LLC_PARAM_LTO] && + !info->attrs[NFC_ATTR_LLC_PARAM_RW] && + !info->attrs[NFC_ATTR_LLC_PARAM_MIUX])) + return -EINVAL; + + if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) { + rw = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_RW]); + + if (rw > LLCP_MAX_RW) + return -EINVAL; + } + + if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) { + miux = nla_get_u16(info->attrs[NFC_ATTR_LLC_PARAM_MIUX]); + + if (miux > LLCP_MAX_MIUX) + return -EINVAL; + } + + idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(idx); + if (!dev) + return -ENODEV; + + device_lock(&dev->dev); + + local = nfc_llcp_find_local(dev); + if (!local) { + nfc_put_device(dev); + rc = -ENODEV; + goto exit; + } + + if (info->attrs[NFC_ATTR_LLC_PARAM_LTO]) { + if (dev->dep_link_up) { + rc = -EINPROGRESS; + goto exit; + } + + local->lto = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_LTO]); + } + + if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) + local->rw = rw; + + if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) + local->miux = cpu_to_be16(miux); + +exit: + device_unlock(&dev->dev); + + nfc_put_device(dev); + + return rc; +} + static struct genl_ops nfc_genl_ops[] = { { .cmd = NFC_CMD_GET_DEVICE, @@ -760,6 +902,16 @@ static struct genl_ops nfc_genl_ops[] = { .done = nfc_genl_dump_targets_done, .policy = nfc_genl_policy, }, + { + .cmd = NFC_CMD_LLC_GET_PARAMS, + .doit = nfc_genl_llc_get_params, + .policy = nfc_genl_policy, + }, + { + .cmd = NFC_CMD_LLC_SET_PARAMS, + .doit = nfc_genl_llc_set_params, + .policy = nfc_genl_policy, + }, }; diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index c5e42b79a418..87d914d2876a 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -56,6 +56,7 @@ void nfc_llcp_unregister_device(struct nfc_dev *dev); int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len); u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len); int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb); +struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev); int __init nfc_llcp_init(void); void nfc_llcp_exit(void); @@ -97,6 +98,11 @@ static inline int nfc_llcp_data_received(struct nfc_dev *dev, return 0; } +static inline struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev) +{ + return NULL; +} + static inline int nfc_llcp_init(void) { return 0; -- cgit v1.2.3