diff options
author | Thierry Escande <thierry.escande@linux.intel.com> | 2013-10-04 06:12:03 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2013-10-07 08:11:20 -0400 |
commit | 7227c0216d2f879d548e8028dc0298a6156ae633 (patch) | |
tree | fec015c588e0c69d9b30f92239d8729d54304a58 /drivers/nfc | |
parent | 9f7b57f28cb7513d3245e74cd7536304306d1cc0 (diff) |
NFC: port100: Add target mode support
This implements the target NFC digital operations tg_configure_hw(),
tg_listen(), tg_listen_mdaa(), and tg_send_cmd().
The target mode supports NFC-A technology at 106kbits/s and NFC-F
technologies at 212 and 424kbits/s.
Signed-off-by: Thierry Escande <thierry.escande@linux.intel.com>
Cc: Stephen Tiedemann <stephen.tiedemann@gmail.com>
Tested-by: Cho, Yu-Chen <acho@suse.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/nfc')
-rw-r--r-- | drivers/nfc/port100.c | 321 |
1 files changed, 317 insertions, 4 deletions
diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 99a6dd79393d..8a0571eb2627 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c | |||
@@ -75,6 +75,11 @@ static u8 ack_frame[PORT100_FRAME_ACK_SIZE] = { | |||
75 | #define PORT100_CMD_IN_SET_PROTOCOL 0x02 | 75 | #define PORT100_CMD_IN_SET_PROTOCOL 0x02 |
76 | #define PORT100_CMD_IN_COMM_RF 0x04 | 76 | #define PORT100_CMD_IN_COMM_RF 0x04 |
77 | 77 | ||
78 | #define PORT100_CMD_TG_SET_RF 0x40 | ||
79 | #define PORT100_CMD_TG_SET_PROTOCOL 0x42 | ||
80 | #define PORT100_CMD_TG_SET_RF_OFF 0x46 | ||
81 | #define PORT100_CMD_TG_COMM_RF 0x48 | ||
82 | |||
78 | #define PORT100_CMD_SWITCH_RF 0x06 | 83 | #define PORT100_CMD_SWITCH_RF 0x06 |
79 | 84 | ||
80 | #define PORT100_CMD_RESPONSE(cmd) (cmd + 1) | 85 | #define PORT100_CMD_RESPONSE(cmd) (cmd + 1) |
@@ -87,6 +92,9 @@ static u8 ack_frame[PORT100_FRAME_ACK_SIZE] = { | |||
87 | #define PORT100_CMD_STATUS_OK 0x00 | 92 | #define PORT100_CMD_STATUS_OK 0x00 |
88 | #define PORT100_CMD_STATUS_TIMEOUT 0x80 | 93 | #define PORT100_CMD_STATUS_TIMEOUT 0x80 |
89 | 94 | ||
95 | #define PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK 0x01 | ||
96 | #define PORT100_MDAA_TGT_WAS_ACTIVATED_MASK 0x02 | ||
97 | |||
90 | struct port100; | 98 | struct port100; |
91 | 99 | ||
92 | typedef void (*port100_send_async_complete_t)(struct port100 *dev, void *arg, | 100 | typedef void (*port100_send_async_complete_t)(struct port100 *dev, void *arg, |
@@ -133,6 +141,41 @@ static const struct port100_in_rf_setting in_rf_settings[] = { | |||
133 | }, | 141 | }, |
134 | }; | 142 | }; |
135 | 143 | ||
144 | /** | ||
145 | * Setting sets structure for tg_set_rf command | ||
146 | * | ||
147 | * @tg_set_number: Represents the entry index in the port-100 RF Base Table. | ||
148 | * This table contains multiple RF setting sets required for RF | ||
149 | * communication. this field is used for both send and receive | ||
150 | * settings. | ||
151 | * | ||
152 | * @tg_comm_type: Sets the communication type to be used to send and receive | ||
153 | * data. | ||
154 | */ | ||
155 | struct port100_tg_rf_setting { | ||
156 | u8 tg_set_number; | ||
157 | u8 tg_comm_type; | ||
158 | } __packed; | ||
159 | |||
160 | #define PORT100_COMM_TYPE_TG_106A 0x0B | ||
161 | #define PORT100_COMM_TYPE_TG_212F 0x0C | ||
162 | #define PORT100_COMM_TYPE_TG_424F 0x0D | ||
163 | |||
164 | static const struct port100_tg_rf_setting tg_rf_settings[] = { | ||
165 | [NFC_DIGITAL_RF_TECH_106A] = { | ||
166 | .tg_set_number = 8, | ||
167 | .tg_comm_type = PORT100_COMM_TYPE_TG_106A, | ||
168 | }, | ||
169 | [NFC_DIGITAL_RF_TECH_212F] = { | ||
170 | .tg_set_number = 8, | ||
171 | .tg_comm_type = PORT100_COMM_TYPE_TG_212F, | ||
172 | }, | ||
173 | [NFC_DIGITAL_RF_TECH_424F] = { | ||
174 | .tg_set_number = 8, | ||
175 | .tg_comm_type = PORT100_COMM_TYPE_TG_424F, | ||
176 | }, | ||
177 | }; | ||
178 | |||
136 | #define PORT100_IN_PROT_INITIAL_GUARD_TIME 0x00 | 179 | #define PORT100_IN_PROT_INITIAL_GUARD_TIME 0x00 |
137 | #define PORT100_IN_PROT_ADD_CRC 0x01 | 180 | #define PORT100_IN_PROT_ADD_CRC 0x01 |
138 | #define PORT100_IN_PROT_CHECK_CRC 0x02 | 181 | #define PORT100_IN_PROT_CHECK_CRC 0x02 |
@@ -156,6 +199,13 @@ static const struct port100_in_rf_setting in_rf_settings[] = { | |||
156 | 199 | ||
157 | #define PORT100_IN_MAX_NUM_PROTOCOLS 19 | 200 | #define PORT100_IN_MAX_NUM_PROTOCOLS 19 |
158 | 201 | ||
202 | #define PORT100_TG_PROT_TU 0x00 | ||
203 | #define PORT100_TG_PROT_RF_OFF 0x01 | ||
204 | #define PORT100_TG_PROT_CRM 0x02 | ||
205 | #define PORT100_TG_PROT_END 0x03 | ||
206 | |||
207 | #define PORT100_TG_MAX_NUM_PROTOCOLS 3 | ||
208 | |||
159 | struct port100_protocol { | 209 | struct port100_protocol { |
160 | u8 number; | 210 | u8 number; |
161 | u8 value; | 211 | u8 value; |
@@ -282,6 +332,47 @@ in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = { | |||
282 | }, | 332 | }, |
283 | }; | 333 | }; |
284 | 334 | ||
335 | static struct port100_protocol | ||
336 | tg_protocols[][PORT100_TG_MAX_NUM_PROTOCOLS + 1] = { | ||
337 | [NFC_DIGITAL_FRAMING_NFCA_SHORT] = { | ||
338 | { PORT100_TG_PROT_END, 0 }, | ||
339 | }, | ||
340 | [NFC_DIGITAL_FRAMING_NFCA_STANDARD] = { | ||
341 | { PORT100_TG_PROT_END, 0 }, | ||
342 | }, | ||
343 | [NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A] = { | ||
344 | { PORT100_TG_PROT_END, 0 }, | ||
345 | }, | ||
346 | [NFC_DIGITAL_FRAMING_NFCA_T1T] = { | ||
347 | { PORT100_TG_PROT_END, 0 }, | ||
348 | }, | ||
349 | [NFC_DIGITAL_FRAMING_NFCA_T2T] = { | ||
350 | { PORT100_TG_PROT_END, 0 }, | ||
351 | }, | ||
352 | [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = { | ||
353 | { PORT100_TG_PROT_TU, 1 }, | ||
354 | { PORT100_TG_PROT_RF_OFF, 0 }, | ||
355 | { PORT100_TG_PROT_CRM, 7 }, | ||
356 | { PORT100_TG_PROT_END, 0 }, | ||
357 | }, | ||
358 | [NFC_DIGITAL_FRAMING_NFCF] = { | ||
359 | { PORT100_TG_PROT_END, 0 }, | ||
360 | }, | ||
361 | [NFC_DIGITAL_FRAMING_NFCF_T3T] = { | ||
362 | { PORT100_TG_PROT_END, 0 }, | ||
363 | }, | ||
364 | [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = { | ||
365 | { PORT100_TG_PROT_TU, 1 }, | ||
366 | { PORT100_TG_PROT_RF_OFF, 0 }, | ||
367 | { PORT100_TG_PROT_CRM, 7 }, | ||
368 | { PORT100_TG_PROT_END, 0 }, | ||
369 | }, | ||
370 | [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = { | ||
371 | { PORT100_TG_PROT_RF_OFF, 1 }, | ||
372 | { PORT100_TG_PROT_END, 0 }, | ||
373 | }, | ||
374 | }; | ||
375 | |||
285 | struct port100 { | 376 | struct port100 { |
286 | struct nfc_digital_dev *nfc_digital_dev; | 377 | struct nfc_digital_dev *nfc_digital_dev; |
287 | 378 | ||
@@ -348,6 +439,14 @@ struct port100_tg_comm_rf_cmd { | |||
348 | u8 data[]; | 439 | u8 data[]; |
349 | } __packed; | 440 | } __packed; |
350 | 441 | ||
442 | struct port100_tg_comm_rf_res { | ||
443 | u8 comm_type; | ||
444 | u8 ar_status; | ||
445 | u8 target_activated; | ||
446 | __le32 status; | ||
447 | u8 data[]; | ||
448 | } __packed; | ||
449 | |||
351 | /* The rule: value + checksum = 0 */ | 450 | /* The rule: value + checksum = 0 */ |
352 | static inline u8 port100_checksum(u16 value) | 451 | static inline u8 port100_checksum(u16 value) |
353 | { | 452 | { |
@@ -1002,17 +1101,176 @@ static int port100_in_send_cmd(struct nfc_digital_dev *ddev, | |||
1002 | port100_in_comm_rf_complete, cb_arg); | 1101 | port100_in_comm_rf_complete, cb_arg); |
1003 | } | 1102 | } |
1004 | 1103 | ||
1104 | static int port100_tg_set_rf(struct nfc_digital_dev *ddev, u8 rf) | ||
1105 | { | ||
1106 | struct port100 *dev = nfc_digital_get_drvdata(ddev); | ||
1107 | struct sk_buff *skb; | ||
1108 | struct sk_buff *resp; | ||
1109 | int rc; | ||
1110 | |||
1111 | if (rf >= NFC_DIGITAL_RF_TECH_LAST) | ||
1112 | return -EINVAL; | ||
1113 | |||
1114 | skb = port100_alloc_skb(dev, sizeof(struct port100_tg_rf_setting)); | ||
1115 | if (!skb) | ||
1116 | return -ENOMEM; | ||
1117 | |||
1118 | memcpy(skb_put(skb, sizeof(struct port100_tg_rf_setting)), | ||
1119 | &tg_rf_settings[rf], | ||
1120 | sizeof(struct port100_tg_rf_setting)); | ||
1121 | |||
1122 | resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_RF, skb); | ||
1123 | |||
1124 | if (IS_ERR(resp)) | ||
1125 | return PTR_ERR(resp); | ||
1126 | |||
1127 | rc = resp->data[0]; | ||
1128 | |||
1129 | dev_kfree_skb(resp); | ||
1130 | |||
1131 | return rc; | ||
1132 | } | ||
1133 | |||
1134 | static int port100_tg_set_framing(struct nfc_digital_dev *ddev, int param) | ||
1135 | { | ||
1136 | struct port100 *dev = nfc_digital_get_drvdata(ddev); | ||
1137 | struct port100_protocol *protocols; | ||
1138 | struct sk_buff *skb; | ||
1139 | struct sk_buff *resp; | ||
1140 | int rc; | ||
1141 | int num_protocols; | ||
1142 | size_t size; | ||
1143 | |||
1144 | if (param >= NFC_DIGITAL_FRAMING_LAST) | ||
1145 | return -EINVAL; | ||
1146 | |||
1147 | protocols = tg_protocols[param]; | ||
1148 | |||
1149 | num_protocols = 0; | ||
1150 | while (protocols[num_protocols].number != PORT100_TG_PROT_END) | ||
1151 | num_protocols++; | ||
1152 | |||
1153 | if (!num_protocols) | ||
1154 | return 0; | ||
1155 | |||
1156 | size = sizeof(struct port100_protocol) * num_protocols; | ||
1157 | |||
1158 | skb = port100_alloc_skb(dev, size); | ||
1159 | if (!skb) | ||
1160 | return -ENOMEM; | ||
1161 | |||
1162 | memcpy(skb_put(skb, size), protocols, size); | ||
1163 | |||
1164 | resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_PROTOCOL, skb); | ||
1165 | |||
1166 | if (IS_ERR(resp)) | ||
1167 | return PTR_ERR(resp); | ||
1168 | |||
1169 | rc = resp->data[0]; | ||
1170 | |||
1171 | dev_kfree_skb(resp); | ||
1172 | |||
1173 | return rc; | ||
1174 | } | ||
1175 | |||
1005 | static int port100_tg_configure_hw(struct nfc_digital_dev *ddev, int type, | 1176 | static int port100_tg_configure_hw(struct nfc_digital_dev *ddev, int type, |
1006 | int param) | 1177 | int param) |
1007 | { | 1178 | { |
1008 | return -EOPNOTSUPP; | 1179 | if (type == NFC_DIGITAL_CONFIG_RF_TECH) |
1180 | return port100_tg_set_rf(ddev, param); | ||
1181 | |||
1182 | if (type == NFC_DIGITAL_CONFIG_FRAMING) | ||
1183 | return port100_tg_set_framing(ddev, param); | ||
1184 | |||
1185 | return -EINVAL; | ||
1186 | } | ||
1187 | |||
1188 | static bool port100_tg_target_activated(struct port100 *dev, u8 tgt_activated) | ||
1189 | { | ||
1190 | u8 mask; | ||
1191 | |||
1192 | switch (dev->cmd_type) { | ||
1193 | case PORT100_CMD_TYPE_0: | ||
1194 | mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK; | ||
1195 | break; | ||
1196 | case PORT100_CMD_TYPE_1: | ||
1197 | mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK | | ||
1198 | PORT100_MDAA_TGT_WAS_ACTIVATED_MASK; | ||
1199 | break; | ||
1200 | default: | ||
1201 | nfc_err(&dev->interface->dev, "Unknonwn command type.\n"); | ||
1202 | return false; | ||
1203 | } | ||
1204 | |||
1205 | return ((tgt_activated & mask) == mask); | ||
1206 | } | ||
1207 | |||
1208 | static void port100_tg_comm_rf_complete(struct port100 *dev, void *arg, | ||
1209 | struct sk_buff *resp) | ||
1210 | { | ||
1211 | u32 status; | ||
1212 | struct port100_cb_arg *cb_arg = arg; | ||
1213 | nfc_digital_cmd_complete_t cb = cb_arg->complete_cb; | ||
1214 | struct port100_tg_comm_rf_res *hdr; | ||
1215 | |||
1216 | if (IS_ERR(resp)) | ||
1217 | goto exit; | ||
1218 | |||
1219 | hdr = (struct port100_tg_comm_rf_res *)resp->data; | ||
1220 | |||
1221 | status = le32_to_cpu(hdr->status); | ||
1222 | |||
1223 | if (cb_arg->mdaa && | ||
1224 | !port100_tg_target_activated(dev, hdr->target_activated)) { | ||
1225 | kfree_skb(resp); | ||
1226 | resp = ERR_PTR(-ETIMEDOUT); | ||
1227 | |||
1228 | goto exit; | ||
1229 | } | ||
1230 | |||
1231 | skb_pull(resp, sizeof(struct port100_tg_comm_rf_res)); | ||
1232 | |||
1233 | if (status != PORT100_CMD_STATUS_OK) { | ||
1234 | kfree_skb(resp); | ||
1235 | |||
1236 | if (status == PORT100_CMD_STATUS_TIMEOUT) | ||
1237 | resp = ERR_PTR(-ETIMEDOUT); | ||
1238 | else | ||
1239 | resp = ERR_PTR(-EIO); | ||
1240 | } | ||
1241 | |||
1242 | exit: | ||
1243 | cb(dev->nfc_digital_dev, cb_arg->complete_arg, resp); | ||
1244 | |||
1245 | kfree(cb_arg); | ||
1009 | } | 1246 | } |
1010 | 1247 | ||
1011 | static int port100_tg_send_cmd(struct nfc_digital_dev *ddev, | 1248 | static int port100_tg_send_cmd(struct nfc_digital_dev *ddev, |
1012 | struct sk_buff *skb, u16 timeout, | 1249 | struct sk_buff *skb, u16 timeout, |
1013 | nfc_digital_cmd_complete_t cb, void *arg) | 1250 | nfc_digital_cmd_complete_t cb, void *arg) |
1014 | { | 1251 | { |
1015 | return -EOPNOTSUPP; | 1252 | struct port100 *dev = nfc_digital_get_drvdata(ddev); |
1253 | struct port100_tg_comm_rf_cmd *hdr; | ||
1254 | struct port100_cb_arg *cb_arg; | ||
1255 | |||
1256 | cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL); | ||
1257 | if (!cb_arg) | ||
1258 | return -ENOMEM; | ||
1259 | |||
1260 | cb_arg->complete_cb = cb; | ||
1261 | cb_arg->complete_arg = arg; | ||
1262 | |||
1263 | skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd)); | ||
1264 | |||
1265 | hdr = (struct port100_tg_comm_rf_cmd *)skb->data; | ||
1266 | |||
1267 | memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd)); | ||
1268 | hdr->guard_time = cpu_to_le16(500); | ||
1269 | hdr->send_timeout = cpu_to_le16(0xFFFF); | ||
1270 | hdr->recv_timeout = cpu_to_le16(timeout); | ||
1271 | |||
1272 | return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb, | ||
1273 | port100_tg_comm_rf_complete, cb_arg); | ||
1016 | } | 1274 | } |
1017 | 1275 | ||
1018 | static int port100_listen_mdaa(struct nfc_digital_dev *ddev, | 1276 | static int port100_listen_mdaa(struct nfc_digital_dev *ddev, |
@@ -1020,13 +1278,68 @@ static int port100_listen_mdaa(struct nfc_digital_dev *ddev, | |||
1020 | u16 timeout, | 1278 | u16 timeout, |
1021 | nfc_digital_cmd_complete_t cb, void *arg) | 1279 | nfc_digital_cmd_complete_t cb, void *arg) |
1022 | { | 1280 | { |
1023 | return -EOPNOTSUPP; | 1281 | struct port100 *dev = nfc_digital_get_drvdata(ddev); |
1282 | struct port100_tg_comm_rf_cmd *hdr; | ||
1283 | struct port100_cb_arg *cb_arg; | ||
1284 | struct sk_buff *skb; | ||
1285 | int rc; | ||
1286 | |||
1287 | rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, | ||
1288 | NFC_DIGITAL_RF_TECH_106A); | ||
1289 | if (rc) | ||
1290 | return rc; | ||
1291 | |||
1292 | rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, | ||
1293 | NFC_DIGITAL_FRAMING_NFCA_NFC_DEP); | ||
1294 | if (rc) | ||
1295 | return rc; | ||
1296 | |||
1297 | cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL); | ||
1298 | if (!cb_arg) | ||
1299 | return -ENOMEM; | ||
1300 | |||
1301 | cb_arg->complete_cb = cb; | ||
1302 | cb_arg->complete_arg = arg; | ||
1303 | cb_arg->mdaa = 1; | ||
1304 | |||
1305 | skb = port100_alloc_skb(dev, 0); | ||
1306 | if (!skb) { | ||
1307 | kfree(cb_arg); | ||
1308 | return -ENOMEM; | ||
1309 | } | ||
1310 | |||
1311 | skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd)); | ||
1312 | hdr = (struct port100_tg_comm_rf_cmd *)skb->data; | ||
1313 | |||
1314 | memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd)); | ||
1315 | |||
1316 | hdr->guard_time = 0; | ||
1317 | hdr->send_timeout = cpu_to_le16(0xFFFF); | ||
1318 | hdr->mdaa = 1; | ||
1319 | hdr->nfca_param[0] = (params->sens_res >> 8) & 0xFF; | ||
1320 | hdr->nfca_param[1] = params->sens_res & 0xFF; | ||
1321 | memcpy(hdr->nfca_param + 2, params->nfcid1, 3); | ||
1322 | hdr->nfca_param[5] = params->sel_res; | ||
1323 | memcpy(hdr->nfcf_param, params->nfcid2, 8); | ||
1324 | hdr->nfcf_param[16] = (params->sc >> 8) & 0xFF; | ||
1325 | hdr->nfcf_param[17] = params->sc & 0xFF; | ||
1326 | hdr->recv_timeout = cpu_to_le16(timeout); | ||
1327 | |||
1328 | return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb, | ||
1329 | port100_tg_comm_rf_complete, cb_arg); | ||
1024 | } | 1330 | } |
1025 | 1331 | ||
1026 | static int port100_listen(struct nfc_digital_dev *ddev, u16 timeout, | 1332 | static int port100_listen(struct nfc_digital_dev *ddev, u16 timeout, |
1027 | nfc_digital_cmd_complete_t cb, void *arg) | 1333 | nfc_digital_cmd_complete_t cb, void *arg) |
1028 | { | 1334 | { |
1029 | return -EOPNOTSUPP; | 1335 | struct port100 *dev = nfc_digital_get_drvdata(ddev); |
1336 | struct sk_buff *skb; | ||
1337 | |||
1338 | skb = port100_alloc_skb(dev, 0); | ||
1339 | if (!skb) | ||
1340 | return -ENOMEM; | ||
1341 | |||
1342 | return port100_tg_send_cmd(ddev, skb, timeout, cb, arg); | ||
1030 | } | 1343 | } |
1031 | 1344 | ||
1032 | static struct nfc_digital_ops port100_digital_ops = { | 1345 | static struct nfc_digital_ops port100_digital_ops = { |