aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/nfc/microread/mei.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/nfc/microread/mei.c')
-rw-r--r--drivers/nfc/microread/mei.c241
1 files changed, 241 insertions, 0 deletions
diff --git a/drivers/nfc/microread/mei.c b/drivers/nfc/microread/mei.c
new file mode 100644
index 000000000000..c078e56d7d14
--- /dev/null
+++ b/drivers/nfc/microread/mei.c
@@ -0,0 +1,241 @@
1/*
2 * HCI based Driver for Inside Secure microread NFC Chip
3 *
4 * Copyright (C) 2013 Intel Corporation. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#include <linux/module.h>
22#include <linux/slab.h>
23#include <linux/interrupt.h>
24#include <linux/gpio.h>
25#include <linux/mei_bus.h>
26
27#include <linux/nfc.h>
28#include <net/nfc/hci.h>
29#include <net/nfc/llc.h>
30
31#include "microread.h"
32
33#define MICROREAD_DRIVER_NAME "microread"
34
35#define MICROREAD_UUID UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, 0x94, \
36 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c)
37
38struct mei_nfc_hdr {
39 u8 cmd;
40 u8 status;
41 u16 req_id;
42 u32 reserved;
43 u16 data_size;
44} __attribute__((packed));
45
46#define MEI_NFC_HEADER_SIZE 10
47#define MEI_NFC_MAX_HCI_PAYLOAD 300
48#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
49
50struct microread_mei_phy {
51 struct mei_bus_client *client;
52 struct nfc_hci_dev *hdev;
53
54 int powered;
55
56 int hard_fault; /*
57 * < 0 if hardware error occured (e.g. i2c err)
58 * and prevents normal operation.
59 */
60};
61
62#define MEI_DUMP_SKB_IN(info, skb) \
63do { \
64 pr_debug("%s:\n", info); \
65 print_hex_dump(KERN_DEBUG, "mei in : ", DUMP_PREFIX_OFFSET, \
66 16, 1, (skb)->data, (skb)->len, 0); \
67} while (0)
68
69#define MEI_DUMP_SKB_OUT(info, skb) \
70do { \
71 pr_debug("%s:\n", info); \
72 print_hex_dump(KERN_DEBUG, "mei out: ", DUMP_PREFIX_OFFSET, \
73 16, 1, (skb)->data, (skb)->len, 0); \
74} while (0)
75
76static int microread_mei_enable(void *phy_id)
77{
78 struct microread_mei_phy *phy = phy_id;
79
80 pr_info(DRIVER_DESC ": %s\n", __func__);
81
82 phy->powered = 1;
83
84 return 0;
85}
86
87static void microread_mei_disable(void *phy_id)
88{
89 struct microread_mei_phy *phy = phy_id;
90
91 pr_info(DRIVER_DESC ": %s\n", __func__);
92
93 phy->powered = 0;
94}
95
96/*
97 * Writing a frame must not return the number of written bytes.
98 * It must return either zero for success, or <0 for error.
99 * In addition, it must not alter the skb
100 */
101static int microread_mei_write(void *phy_id, struct sk_buff *skb)
102{
103 struct microread_mei_phy *phy = phy_id;
104 int r;
105
106 MEI_DUMP_SKB_OUT("mei frame sent", skb);
107
108 r = mei_bus_send(phy->client, skb->data, skb->len);
109 if (r > 0)
110 r = 0;
111
112 return r;
113}
114
115static void microread_event_cb(struct mei_bus_client *client, u32 events,
116 void *context)
117{
118 struct microread_mei_phy *phy = context;
119
120 if (phy->hard_fault != 0)
121 return;
122
123 if (events & BIT(MEI_BUS_EVENT_RX)) {
124 struct sk_buff *skb;
125 int reply_size;
126
127 skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL);
128 if (!skb)
129 return;
130
131 reply_size = mei_bus_recv(client, skb->data, MEI_NFC_MAX_READ);
132 if (reply_size < MEI_NFC_HEADER_SIZE) {
133 kfree(skb);
134 return;
135 }
136
137 skb_put(skb, reply_size);
138 skb_pull(skb, MEI_NFC_HEADER_SIZE);
139
140 MEI_DUMP_SKB_IN("mei frame read", skb);
141
142 nfc_hci_recv_frame(phy->hdev, skb);
143 }
144}
145
146static struct nfc_phy_ops mei_phy_ops = {
147 .write = microread_mei_write,
148 .enable = microread_mei_enable,
149 .disable = microread_mei_disable,
150};
151
152static int microread_mei_probe(struct mei_bus_client *client)
153{
154 struct microread_mei_phy *phy;
155 int r;
156
157 pr_info("Probing NFC microread\n");
158
159 phy = kzalloc(sizeof(struct microread_mei_phy), GFP_KERNEL);
160 if (!phy) {
161 pr_err("Cannot allocate memory for microread mei phy.\n");
162 return -ENOMEM;
163 }
164
165 phy->client = client;
166 mei_bus_set_clientdata(client, phy);
167
168 r = mei_bus_register_event_cb(client, microread_event_cb, phy);
169 if (r) {
170 pr_err(MICROREAD_DRIVER_NAME ": event cb registration failed\n");
171 goto err_out;
172 }
173
174 r = microread_probe(phy, &mei_phy_ops, LLC_NOP_NAME,
175 MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD,
176 &phy->hdev);
177 if (r < 0)
178 goto err_out;
179
180 return 0;
181
182err_out:
183 kfree(phy);
184
185 return r;
186}
187
188static int microread_mei_remove(struct mei_bus_client *client)
189{
190 struct microread_mei_phy *phy = mei_bus_get_clientdata(client);
191
192 pr_info("Removing microread\n");
193
194 microread_remove(phy->hdev);
195
196 if (phy->powered)
197 microread_mei_disable(phy);
198
199 kfree(phy);
200
201 return 0;
202}
203
204static struct mei_bus_driver microread_driver = {
205 .driver = {
206 .name = MICROREAD_DRIVER_NAME,
207 },
208 .id = {
209 .name = MICROREAD_DRIVER_NAME,
210 .uuid = MICROREAD_UUID,
211 },
212
213 .probe = microread_mei_probe,
214 .remove = microread_mei_remove,
215};
216
217static int microread_mei_init(void)
218{
219 int r;
220
221 pr_debug(DRIVER_DESC ": %s\n", __func__);
222
223 r = mei_driver_register(&microread_driver);
224 if (r) {
225 pr_err(MICROREAD_DRIVER_NAME ": driver registration failed\n");
226 return r;
227 }
228
229 return 0;
230}
231
232static void microread_mei_exit(void)
233{
234 mei_driver_unregister(&microread_driver);
235}
236
237module_init(microread_mei_init);
238module_exit(microread_mei_exit);
239
240MODULE_LICENSE("GPL");
241MODULE_DESCRIPTION(DRIVER_DESC);