Thierry Escande | 562d4d5 | 2013-10-04 12:12:00 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Sony NFC Port-100 Series driver |
| 3 | * Copyright (c) 2013, Intel Corporation. |
| 4 | * |
| 5 | * Partly based/Inspired by Stephen Tiedemann's nfcpy |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify it |
| 8 | * under the terms and conditions of the GNU General Public License, |
| 9 | * version 2, as published by the Free Software Foundation. |
| 10 | * |
| 11 | * This program is distributed in the hope it will be useful, but WITHOUT |
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 14 | * more details. |
| 15 | * |
| 16 | */ |
| 17 | |
| 18 | #include <linux/module.h> |
| 19 | #include <linux/usb.h> |
| 20 | #include <net/nfc/digital.h> |
| 21 | |
| 22 | #define VERSION "0.1" |
| 23 | |
| 24 | #define SONY_VENDOR_ID 0x054c |
| 25 | #define RCS380_PRODUCT_ID 0x06c1 |
| 26 | |
| 27 | #define PORT100_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ |
| 28 | NFC_PROTO_MIFARE_MASK | \ |
| 29 | NFC_PROTO_FELICA_MASK | \ |
| 30 | NFC_PROTO_NFC_DEP_MASK) |
| 31 | |
| 32 | #define PORT100_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \ |
| 33 | NFC_DIGITAL_DRV_CAPS_TG_CRC) |
| 34 | |
Thierry Escande | 0347a6a | 2013-10-04 12:12:01 +0200 | [diff] [blame^] | 35 | /* Standard port100 frame definitions */ |
| 36 | #define PORT100_FRAME_HEADER_LEN (sizeof(struct port100_frame) \ |
| 37 | + 2) /* data[0] CC, data[1] SCC */ |
| 38 | #define PORT100_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/ |
| 39 | |
| 40 | #define PORT100_COMM_RF_HEAD_MAX_LEN (sizeof(struct port100_tg_comm_rf_cmd)) |
| 41 | |
| 42 | /* |
| 43 | * Max extended frame payload len, excluding CC and SCC |
| 44 | * which are already in PORT100_FRAME_HEADER_LEN. |
| 45 | */ |
| 46 | #define PORT100_FRAME_MAX_PAYLOAD_LEN 1001 |
| 47 | |
| 48 | #define PORT100_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2), |
| 49 | Postamble (1) */ |
| 50 | static u8 ack_frame[PORT100_FRAME_ACK_SIZE] = { |
| 51 | 0x00, 0x00, 0xff, 0x00, 0xff, 0x00 |
| 52 | }; |
| 53 | |
| 54 | #define PORT100_FRAME_CHECKSUM(f) (f->data[le16_to_cpu(f->datalen)]) |
| 55 | #define PORT100_FRAME_POSTAMBLE(f) (f->data[le16_to_cpu(f->datalen) + 1]) |
| 56 | |
| 57 | /* start of frame */ |
| 58 | #define PORT100_FRAME_SOF 0x00FF |
| 59 | #define PORT100_FRAME_EXT 0xFFFF |
| 60 | #define PORT100_FRAME_ACK 0x00FF |
| 61 | |
| 62 | /* Port-100 command: in or out */ |
| 63 | #define PORT100_FRAME_DIRECTION(f) (f->data[0]) /* CC */ |
| 64 | #define PORT100_FRAME_DIR_OUT 0xD6 |
| 65 | #define PORT100_FRAME_DIR_IN 0xD7 |
| 66 | |
| 67 | /* Port-100 sub-command */ |
| 68 | #define PORT100_FRAME_CMD(f) (f->data[1]) /* SCC */ |
| 69 | |
| 70 | #define PORT100_CMD_GET_FIRMWARE_VERSION 0x20 |
| 71 | #define PORT100_CMD_GET_COMMAND_TYPE 0x28 |
| 72 | #define PORT100_CMD_SET_COMMAND_TYPE 0x2A |
| 73 | |
| 74 | #define PORT100_CMD_RESPONSE(cmd) (cmd + 1) |
| 75 | |
| 76 | #define PORT100_CMD_TYPE_IS_SUPPORTED(mask, cmd_type) \ |
| 77 | ((mask) & (0x01 << (cmd_type))) |
| 78 | #define PORT100_CMD_TYPE_0 0 |
| 79 | #define PORT100_CMD_TYPE_1 1 |
| 80 | |
| 81 | struct port100; |
| 82 | |
| 83 | typedef void (*port100_send_async_complete_t)(struct port100 *dev, void *arg, |
| 84 | struct sk_buff *resp); |
| 85 | |
Thierry Escande | 562d4d5 | 2013-10-04 12:12:00 +0200 | [diff] [blame] | 86 | struct port100 { |
| 87 | struct nfc_digital_dev *nfc_digital_dev; |
| 88 | |
| 89 | int skb_headroom; |
| 90 | int skb_tailroom; |
| 91 | |
| 92 | struct usb_device *udev; |
| 93 | struct usb_interface *interface; |
Thierry Escande | 0347a6a | 2013-10-04 12:12:01 +0200 | [diff] [blame^] | 94 | |
| 95 | struct urb *out_urb; |
| 96 | struct urb *in_urb; |
| 97 | |
| 98 | struct work_struct cmd_complete_work; |
| 99 | |
| 100 | u8 cmd_type; |
| 101 | |
| 102 | /* The digital stack serializes commands to be sent. There is no need |
| 103 | * for any queuing/locking mechanism at driver level. |
| 104 | */ |
| 105 | struct port100_cmd *cmd; |
Thierry Escande | 562d4d5 | 2013-10-04 12:12:00 +0200 | [diff] [blame] | 106 | }; |
| 107 | |
Thierry Escande | 0347a6a | 2013-10-04 12:12:01 +0200 | [diff] [blame^] | 108 | struct port100_cmd { |
| 109 | u8 code; |
| 110 | int status; |
| 111 | struct sk_buff *req; |
| 112 | struct sk_buff *resp; |
| 113 | int resp_len; |
| 114 | port100_send_async_complete_t complete_cb; |
| 115 | void *complete_cb_context; |
| 116 | }; |
| 117 | |
| 118 | struct port100_frame { |
| 119 | u8 preamble; |
| 120 | __be16 start_frame; |
| 121 | __be16 extended_frame; |
| 122 | __le16 datalen; |
| 123 | u8 datalen_checksum; |
| 124 | u8 data[]; |
| 125 | } __packed; |
| 126 | |
| 127 | struct port100_ack_frame { |
| 128 | u8 preamble; |
| 129 | __be16 start_frame; |
| 130 | __be16 ack_frame; |
| 131 | u8 postambule; |
| 132 | } __packed; |
| 133 | |
| 134 | struct port100_cb_arg { |
| 135 | nfc_digital_cmd_complete_t complete_cb; |
| 136 | void *complete_arg; |
| 137 | u8 mdaa; |
| 138 | }; |
| 139 | |
| 140 | struct port100_tg_comm_rf_cmd { |
| 141 | __le16 guard_time; |
| 142 | __le16 send_timeout; |
| 143 | u8 mdaa; |
| 144 | u8 nfca_param[6]; |
| 145 | u8 nfcf_param[18]; |
| 146 | u8 mf_halted; |
| 147 | u8 arae_flag; |
| 148 | __le16 recv_timeout; |
| 149 | u8 data[]; |
| 150 | } __packed; |
| 151 | |
| 152 | /* The rule: value + checksum = 0 */ |
| 153 | static inline u8 port100_checksum(u16 value) |
| 154 | { |
| 155 | return ~(((u8 *)&value)[0] + ((u8 *)&value)[1]) + 1; |
| 156 | } |
| 157 | |
| 158 | /* The rule: sum(data elements) + checksum = 0 */ |
| 159 | static u8 port100_data_checksum(u8 *data, int datalen) |
| 160 | { |
| 161 | u8 sum = 0; |
| 162 | int i; |
| 163 | |
| 164 | for (i = 0; i < datalen; i++) |
| 165 | sum += data[i]; |
| 166 | |
| 167 | return port100_checksum(sum); |
| 168 | } |
| 169 | |
| 170 | static void port100_tx_frame_init(void *_frame, u8 cmd_code) |
| 171 | { |
| 172 | struct port100_frame *frame = _frame; |
| 173 | |
| 174 | frame->preamble = 0; |
| 175 | frame->start_frame = cpu_to_be16(PORT100_FRAME_SOF); |
| 176 | frame->extended_frame = cpu_to_be16(PORT100_FRAME_EXT); |
| 177 | PORT100_FRAME_DIRECTION(frame) = PORT100_FRAME_DIR_OUT; |
| 178 | PORT100_FRAME_CMD(frame) = cmd_code; |
| 179 | frame->datalen = cpu_to_le16(2); |
| 180 | } |
| 181 | |
| 182 | static void port100_tx_frame_finish(void *_frame) |
| 183 | { |
| 184 | struct port100_frame *frame = _frame; |
| 185 | |
| 186 | frame->datalen_checksum = port100_checksum(le16_to_cpu(frame->datalen)); |
| 187 | |
| 188 | PORT100_FRAME_CHECKSUM(frame) = |
| 189 | port100_data_checksum(frame->data, le16_to_cpu(frame->datalen)); |
| 190 | |
| 191 | PORT100_FRAME_POSTAMBLE(frame) = 0; |
| 192 | } |
| 193 | |
| 194 | static void port100_tx_update_payload_len(void *_frame, int len) |
| 195 | { |
| 196 | struct port100_frame *frame = _frame; |
| 197 | |
| 198 | frame->datalen = cpu_to_le16(le16_to_cpu(frame->datalen) + len); |
| 199 | } |
| 200 | |
| 201 | static bool port100_rx_frame_is_valid(void *_frame) |
| 202 | { |
| 203 | u8 checksum; |
| 204 | struct port100_frame *frame = _frame; |
| 205 | |
| 206 | if (frame->start_frame != cpu_to_be16(PORT100_FRAME_SOF) || |
| 207 | frame->extended_frame != cpu_to_be16(PORT100_FRAME_EXT)) |
| 208 | return false; |
| 209 | |
| 210 | checksum = port100_checksum(le16_to_cpu(frame->datalen)); |
| 211 | if (checksum != frame->datalen_checksum) |
| 212 | return false; |
| 213 | |
| 214 | checksum = port100_data_checksum(frame->data, |
| 215 | le16_to_cpu(frame->datalen)); |
| 216 | if (checksum != PORT100_FRAME_CHECKSUM(frame)) |
| 217 | return false; |
| 218 | |
| 219 | return true; |
| 220 | } |
| 221 | |
| 222 | static bool port100_rx_frame_is_ack(struct port100_ack_frame *frame) |
| 223 | { |
| 224 | return (frame->start_frame == cpu_to_be16(PORT100_FRAME_SOF) && |
| 225 | frame->ack_frame == cpu_to_be16(PORT100_FRAME_ACK)); |
| 226 | } |
| 227 | |
| 228 | static inline int port100_rx_frame_size(void *frame) |
| 229 | { |
| 230 | struct port100_frame *f = frame; |
| 231 | |
| 232 | return sizeof(struct port100_frame) + le16_to_cpu(f->datalen) + |
| 233 | PORT100_FRAME_TAIL_LEN; |
| 234 | } |
| 235 | |
| 236 | static bool port100_rx_frame_is_cmd_response(struct port100 *dev, void *frame) |
| 237 | { |
| 238 | struct port100_frame *f = frame; |
| 239 | |
| 240 | return (PORT100_FRAME_CMD(f) == PORT100_CMD_RESPONSE(dev->cmd->code)); |
| 241 | } |
| 242 | |
| 243 | static void port100_recv_response(struct urb *urb) |
| 244 | { |
| 245 | struct port100 *dev = urb->context; |
| 246 | struct port100_cmd *cmd = dev->cmd; |
| 247 | u8 *in_frame; |
| 248 | |
| 249 | cmd->status = urb->status; |
| 250 | |
| 251 | switch (urb->status) { |
| 252 | case 0: |
| 253 | break; /* success */ |
| 254 | case -ECONNRESET: |
| 255 | case -ENOENT: |
| 256 | nfc_err(&dev->interface->dev, |
| 257 | "The urb has been canceled (status %d)", urb->status); |
| 258 | goto sched_wq; |
| 259 | case -ESHUTDOWN: |
| 260 | default: |
| 261 | nfc_err(&dev->interface->dev, "Urb failure (status %d)", |
| 262 | urb->status); |
| 263 | goto sched_wq; |
| 264 | } |
| 265 | |
| 266 | in_frame = dev->in_urb->transfer_buffer; |
| 267 | |
| 268 | if (!port100_rx_frame_is_valid(in_frame)) { |
| 269 | nfc_err(&dev->interface->dev, "Received an invalid frame"); |
| 270 | cmd->status = -EIO; |
| 271 | goto sched_wq; |
| 272 | } |
| 273 | |
| 274 | print_hex_dump_debug("PORT100 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame, |
| 275 | port100_rx_frame_size(in_frame), false); |
| 276 | |
| 277 | if (!port100_rx_frame_is_cmd_response(dev, in_frame)) { |
| 278 | nfc_err(&dev->interface->dev, |
| 279 | "It's not the response to the last command"); |
| 280 | cmd->status = -EIO; |
| 281 | goto sched_wq; |
| 282 | } |
| 283 | |
| 284 | sched_wq: |
| 285 | schedule_work(&dev->cmd_complete_work); |
| 286 | } |
| 287 | |
| 288 | static int port100_submit_urb_for_response(struct port100 *dev, gfp_t flags) |
| 289 | { |
| 290 | dev->in_urb->complete = port100_recv_response; |
| 291 | |
| 292 | return usb_submit_urb(dev->in_urb, flags); |
| 293 | } |
| 294 | |
| 295 | static void port100_recv_ack(struct urb *urb) |
| 296 | { |
| 297 | struct port100 *dev = urb->context; |
| 298 | struct port100_cmd *cmd = dev->cmd; |
| 299 | struct port100_ack_frame *in_frame; |
| 300 | int rc; |
| 301 | |
| 302 | cmd->status = urb->status; |
| 303 | |
| 304 | switch (urb->status) { |
| 305 | case 0: |
| 306 | break; /* success */ |
| 307 | case -ECONNRESET: |
| 308 | case -ENOENT: |
| 309 | nfc_err(&dev->interface->dev, |
| 310 | "The urb has been stopped (status %d)", urb->status); |
| 311 | goto sched_wq; |
| 312 | case -ESHUTDOWN: |
| 313 | default: |
| 314 | nfc_err(&dev->interface->dev, "Urb failure (status %d)", |
| 315 | urb->status); |
| 316 | goto sched_wq; |
| 317 | } |
| 318 | |
| 319 | in_frame = dev->in_urb->transfer_buffer; |
| 320 | |
| 321 | if (!port100_rx_frame_is_ack(in_frame)) { |
| 322 | nfc_err(&dev->interface->dev, "Received an invalid ack"); |
| 323 | cmd->status = -EIO; |
| 324 | goto sched_wq; |
| 325 | } |
| 326 | |
| 327 | rc = port100_submit_urb_for_response(dev, GFP_ATOMIC); |
| 328 | if (rc) { |
| 329 | nfc_err(&dev->interface->dev, |
| 330 | "usb_submit_urb failed with result %d", rc); |
| 331 | cmd->status = rc; |
| 332 | goto sched_wq; |
| 333 | } |
| 334 | |
| 335 | return; |
| 336 | |
| 337 | sched_wq: |
| 338 | schedule_work(&dev->cmd_complete_work); |
| 339 | } |
| 340 | |
| 341 | static int port100_submit_urb_for_ack(struct port100 *dev, gfp_t flags) |
| 342 | { |
| 343 | dev->in_urb->complete = port100_recv_ack; |
| 344 | |
| 345 | return usb_submit_urb(dev->in_urb, flags); |
| 346 | } |
| 347 | |
| 348 | static int port100_send_ack(struct port100 *dev) |
| 349 | { |
| 350 | int rc; |
| 351 | |
| 352 | dev->out_urb->transfer_buffer = ack_frame; |
| 353 | dev->out_urb->transfer_buffer_length = sizeof(ack_frame); |
| 354 | rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); |
| 355 | |
| 356 | return rc; |
| 357 | } |
| 358 | |
| 359 | static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out, |
| 360 | struct sk_buff *in, int in_len) |
| 361 | { |
| 362 | int rc; |
| 363 | |
| 364 | dev->out_urb->transfer_buffer = out->data; |
| 365 | dev->out_urb->transfer_buffer_length = out->len; |
| 366 | |
| 367 | dev->in_urb->transfer_buffer = in->data; |
| 368 | dev->in_urb->transfer_buffer_length = in_len; |
| 369 | |
| 370 | print_hex_dump_debug("PORT100 TX: ", DUMP_PREFIX_NONE, 16, 1, |
| 371 | out->data, out->len, false); |
| 372 | |
| 373 | rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); |
| 374 | if (rc) |
| 375 | return rc; |
| 376 | |
| 377 | rc = port100_submit_urb_for_ack(dev, GFP_KERNEL); |
| 378 | if (rc) |
| 379 | goto error; |
| 380 | |
| 381 | return 0; |
| 382 | |
| 383 | error: |
| 384 | usb_unlink_urb(dev->out_urb); |
| 385 | return rc; |
| 386 | } |
| 387 | |
| 388 | static void port100_build_cmd_frame(struct port100 *dev, u8 cmd_code, |
| 389 | struct sk_buff *skb) |
| 390 | { |
| 391 | /* payload is already there, just update datalen */ |
| 392 | int payload_len = skb->len; |
| 393 | |
| 394 | skb_push(skb, PORT100_FRAME_HEADER_LEN); |
| 395 | skb_put(skb, PORT100_FRAME_TAIL_LEN); |
| 396 | |
| 397 | port100_tx_frame_init(skb->data, cmd_code); |
| 398 | port100_tx_update_payload_len(skb->data, payload_len); |
| 399 | port100_tx_frame_finish(skb->data); |
| 400 | } |
| 401 | |
| 402 | static void port100_send_async_complete(struct port100 *dev) |
| 403 | { |
| 404 | struct port100_cmd *cmd = dev->cmd; |
| 405 | int status = cmd->status; |
| 406 | |
| 407 | struct sk_buff *req = cmd->req; |
| 408 | struct sk_buff *resp = cmd->resp; |
| 409 | |
| 410 | dev_kfree_skb(req); |
| 411 | |
| 412 | dev->cmd = NULL; |
| 413 | |
| 414 | if (status < 0) { |
| 415 | cmd->complete_cb(dev, cmd->complete_cb_context, |
| 416 | ERR_PTR(status)); |
| 417 | dev_kfree_skb(resp); |
| 418 | goto done; |
| 419 | } |
| 420 | |
| 421 | skb_put(resp, port100_rx_frame_size(resp->data)); |
| 422 | skb_pull(resp, PORT100_FRAME_HEADER_LEN); |
| 423 | skb_trim(resp, resp->len - PORT100_FRAME_TAIL_LEN); |
| 424 | |
| 425 | cmd->complete_cb(dev, cmd->complete_cb_context, resp); |
| 426 | |
| 427 | done: |
| 428 | kfree(cmd); |
| 429 | } |
| 430 | |
| 431 | static int port100_send_cmd_async(struct port100 *dev, u8 cmd_code, |
| 432 | struct sk_buff *req, |
| 433 | port100_send_async_complete_t complete_cb, |
| 434 | void *complete_cb_context) |
| 435 | { |
| 436 | struct port100_cmd *cmd; |
| 437 | struct sk_buff *resp; |
| 438 | int rc; |
| 439 | int resp_len = PORT100_FRAME_HEADER_LEN + |
| 440 | PORT100_FRAME_MAX_PAYLOAD_LEN + |
| 441 | PORT100_FRAME_TAIL_LEN; |
| 442 | |
| 443 | resp = alloc_skb(resp_len, GFP_KERNEL); |
| 444 | if (!resp) |
| 445 | return -ENOMEM; |
| 446 | |
| 447 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); |
| 448 | if (!cmd) { |
| 449 | dev_kfree_skb(resp); |
| 450 | return -ENOMEM; |
| 451 | } |
| 452 | |
| 453 | cmd->code = cmd_code; |
| 454 | cmd->req = req; |
| 455 | cmd->resp = resp; |
| 456 | cmd->resp_len = resp_len; |
| 457 | cmd->complete_cb = complete_cb; |
| 458 | cmd->complete_cb_context = complete_cb_context; |
| 459 | |
| 460 | port100_build_cmd_frame(dev, cmd_code, req); |
| 461 | |
| 462 | dev->cmd = cmd; |
| 463 | |
| 464 | rc = port100_send_frame_async(dev, req, resp, resp_len); |
| 465 | if (rc) { |
| 466 | kfree(cmd); |
| 467 | dev_kfree_skb(resp); |
| 468 | dev->cmd = NULL; |
| 469 | } |
| 470 | |
| 471 | return rc; |
| 472 | } |
| 473 | |
| 474 | struct port100_sync_cmd_response { |
| 475 | struct sk_buff *resp; |
| 476 | struct completion done; |
| 477 | }; |
| 478 | |
| 479 | static void port100_wq_cmd_complete(struct work_struct *work) |
| 480 | { |
| 481 | struct port100 *dev = container_of(work, struct port100, |
| 482 | cmd_complete_work); |
| 483 | |
| 484 | port100_send_async_complete(dev); |
| 485 | } |
| 486 | |
| 487 | static void port100_send_sync_complete(struct port100 *dev, void *_arg, |
| 488 | struct sk_buff *resp) |
| 489 | { |
| 490 | struct port100_sync_cmd_response *arg = _arg; |
| 491 | |
| 492 | arg->resp = resp; |
| 493 | complete(&arg->done); |
| 494 | } |
| 495 | |
| 496 | static struct sk_buff *port100_send_cmd_sync(struct port100 *dev, u8 cmd_code, |
| 497 | struct sk_buff *req) |
| 498 | { |
| 499 | int rc; |
| 500 | struct port100_sync_cmd_response arg; |
| 501 | |
| 502 | init_completion(&arg.done); |
| 503 | |
| 504 | rc = port100_send_cmd_async(dev, cmd_code, req, |
| 505 | port100_send_sync_complete, &arg); |
| 506 | if (rc) { |
| 507 | dev_kfree_skb(req); |
| 508 | return ERR_PTR(rc); |
| 509 | } |
| 510 | |
| 511 | wait_for_completion(&arg.done); |
| 512 | |
| 513 | return arg.resp; |
| 514 | } |
| 515 | |
| 516 | static void port100_send_complete(struct urb *urb) |
| 517 | { |
| 518 | struct port100 *dev = urb->context; |
| 519 | |
| 520 | switch (urb->status) { |
| 521 | case 0: |
| 522 | break; /* success */ |
| 523 | case -ECONNRESET: |
| 524 | case -ENOENT: |
| 525 | nfc_err(&dev->interface->dev, |
| 526 | "The urb has been stopped (status %d)", urb->status); |
| 527 | break; |
| 528 | case -ESHUTDOWN: |
| 529 | default: |
| 530 | nfc_err(&dev->interface->dev, "Urb failure (status %d)", |
| 531 | urb->status); |
| 532 | } |
| 533 | } |
| 534 | |
Thierry Escande | 562d4d5 | 2013-10-04 12:12:00 +0200 | [diff] [blame] | 535 | static void port100_abort_cmd(struct nfc_digital_dev *ddev) |
| 536 | { |
Thierry Escande | 0347a6a | 2013-10-04 12:12:01 +0200 | [diff] [blame^] | 537 | struct port100 *dev = nfc_digital_get_drvdata(ddev); |
| 538 | |
| 539 | /* An ack will cancel the last issued command */ |
| 540 | port100_send_ack(dev); |
| 541 | |
| 542 | /* cancel the urb request */ |
| 543 | usb_kill_urb(dev->in_urb); |
| 544 | } |
| 545 | |
| 546 | static struct sk_buff *port100_alloc_skb(struct port100 *dev, unsigned int size) |
| 547 | { |
| 548 | struct sk_buff *skb; |
| 549 | |
| 550 | skb = alloc_skb(dev->skb_headroom + dev->skb_tailroom + size, |
| 551 | GFP_KERNEL); |
| 552 | if (skb) |
| 553 | skb_reserve(skb, dev->skb_headroom); |
| 554 | |
| 555 | return skb; |
| 556 | } |
| 557 | |
| 558 | static int port100_set_command_type(struct port100 *dev, u8 command_type) |
| 559 | { |
| 560 | struct sk_buff *skb; |
| 561 | struct sk_buff *resp; |
| 562 | int rc; |
| 563 | |
| 564 | skb = port100_alloc_skb(dev, 1); |
| 565 | if (!skb) |
| 566 | return -ENOMEM; |
| 567 | |
| 568 | *skb_put(skb, sizeof(u8)) = command_type; |
| 569 | |
| 570 | resp = port100_send_cmd_sync(dev, PORT100_CMD_SET_COMMAND_TYPE, skb); |
| 571 | if (IS_ERR(resp)) |
| 572 | return PTR_ERR(resp); |
| 573 | |
| 574 | rc = resp->data[0]; |
| 575 | |
| 576 | dev_kfree_skb(resp); |
| 577 | |
| 578 | return rc; |
| 579 | } |
| 580 | |
| 581 | static u64 port100_get_command_type_mask(struct port100 *dev) |
| 582 | { |
| 583 | struct sk_buff *skb; |
| 584 | struct sk_buff *resp; |
| 585 | u64 mask; |
| 586 | |
| 587 | skb = port100_alloc_skb(dev, 0); |
| 588 | if (!skb) |
| 589 | return -ENOMEM; |
| 590 | |
| 591 | resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_COMMAND_TYPE, skb); |
| 592 | if (IS_ERR(resp)) |
| 593 | return PTR_ERR(resp); |
| 594 | |
| 595 | if (resp->len < 8) |
| 596 | mask = 0; |
| 597 | else |
| 598 | mask = be64_to_cpu(*(__be64 *)resp->data); |
| 599 | |
| 600 | dev_kfree_skb(resp); |
| 601 | |
| 602 | return mask; |
| 603 | } |
| 604 | |
| 605 | static u16 port100_get_firmware_version(struct port100 *dev) |
| 606 | { |
| 607 | struct sk_buff *skb; |
| 608 | struct sk_buff *resp; |
| 609 | u16 fw_ver; |
| 610 | |
| 611 | skb = port100_alloc_skb(dev, 0); |
| 612 | if (!skb) |
| 613 | return 0; |
| 614 | |
| 615 | resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_FIRMWARE_VERSION, |
| 616 | skb); |
| 617 | if (IS_ERR(resp)) |
| 618 | return 0; |
| 619 | |
| 620 | fw_ver = le16_to_cpu(*(__le16 *)resp->data); |
| 621 | |
| 622 | dev_kfree_skb(resp); |
| 623 | |
| 624 | return fw_ver; |
Thierry Escande | 562d4d5 | 2013-10-04 12:12:00 +0200 | [diff] [blame] | 625 | } |
| 626 | |
| 627 | static int port100_switch_rf(struct nfc_digital_dev *ddev, bool on) |
| 628 | { |
| 629 | return -EOPNOTSUPP; |
| 630 | } |
| 631 | |
| 632 | static int port100_in_configure_hw(struct nfc_digital_dev *ddev, int type, |
| 633 | int param) |
| 634 | { |
| 635 | return -EOPNOTSUPP; |
| 636 | } |
| 637 | |
| 638 | static int port100_in_send_cmd(struct nfc_digital_dev *ddev, |
| 639 | struct sk_buff *skb, u16 _timeout, |
| 640 | nfc_digital_cmd_complete_t cb, void *arg) |
| 641 | { |
| 642 | return -EOPNOTSUPP; |
| 643 | } |
| 644 | |
| 645 | static int port100_tg_configure_hw(struct nfc_digital_dev *ddev, int type, |
| 646 | int param) |
| 647 | { |
| 648 | return -EOPNOTSUPP; |
| 649 | } |
| 650 | |
| 651 | static int port100_tg_send_cmd(struct nfc_digital_dev *ddev, |
| 652 | struct sk_buff *skb, u16 timeout, |
| 653 | nfc_digital_cmd_complete_t cb, void *arg) |
| 654 | { |
| 655 | return -EOPNOTSUPP; |
| 656 | } |
| 657 | |
| 658 | static int port100_listen_mdaa(struct nfc_digital_dev *ddev, |
| 659 | struct digital_tg_mdaa_params *params, |
| 660 | u16 timeout, |
| 661 | nfc_digital_cmd_complete_t cb, void *arg) |
| 662 | { |
| 663 | return -EOPNOTSUPP; |
| 664 | } |
| 665 | |
| 666 | static int port100_listen(struct nfc_digital_dev *ddev, u16 timeout, |
| 667 | nfc_digital_cmd_complete_t cb, void *arg) |
| 668 | { |
| 669 | return -EOPNOTSUPP; |
| 670 | } |
| 671 | |
| 672 | static struct nfc_digital_ops port100_digital_ops = { |
| 673 | .in_configure_hw = port100_in_configure_hw, |
| 674 | .in_send_cmd = port100_in_send_cmd, |
| 675 | |
| 676 | .tg_listen_mdaa = port100_listen_mdaa, |
| 677 | .tg_listen = port100_listen, |
| 678 | .tg_configure_hw = port100_tg_configure_hw, |
| 679 | .tg_send_cmd = port100_tg_send_cmd, |
| 680 | |
| 681 | .switch_rf = port100_switch_rf, |
| 682 | .abort_cmd = port100_abort_cmd, |
| 683 | }; |
| 684 | |
| 685 | static const struct usb_device_id port100_table[] = { |
| 686 | { .match_flags = USB_DEVICE_ID_MATCH_DEVICE, |
| 687 | .idVendor = SONY_VENDOR_ID, |
| 688 | .idProduct = RCS380_PRODUCT_ID, |
| 689 | }, |
| 690 | { } |
| 691 | }; |
| 692 | MODULE_DEVICE_TABLE(usb, port100_table); |
| 693 | |
| 694 | static int port100_probe(struct usb_interface *interface, |
| 695 | const struct usb_device_id *id) |
| 696 | { |
| 697 | struct port100 *dev; |
| 698 | int rc; |
Thierry Escande | 0347a6a | 2013-10-04 12:12:01 +0200 | [diff] [blame^] | 699 | struct usb_host_interface *iface_desc; |
| 700 | struct usb_endpoint_descriptor *endpoint; |
| 701 | int in_endpoint; |
| 702 | int out_endpoint; |
| 703 | u16 fw_version; |
| 704 | u64 cmd_type_mask; |
| 705 | int i; |
Thierry Escande | 562d4d5 | 2013-10-04 12:12:00 +0200 | [diff] [blame] | 706 | |
| 707 | dev = devm_kzalloc(&interface->dev, sizeof(struct port100), GFP_KERNEL); |
| 708 | if (!dev) |
| 709 | return -ENOMEM; |
| 710 | |
| 711 | dev->udev = usb_get_dev(interface_to_usbdev(interface)); |
| 712 | dev->interface = interface; |
| 713 | usb_set_intfdata(interface, dev); |
| 714 | |
Thierry Escande | 0347a6a | 2013-10-04 12:12:01 +0200 | [diff] [blame^] | 715 | in_endpoint = out_endpoint = 0; |
| 716 | iface_desc = interface->cur_altsetting; |
| 717 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { |
| 718 | endpoint = &iface_desc->endpoint[i].desc; |
| 719 | |
| 720 | if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint)) |
| 721 | in_endpoint = endpoint->bEndpointAddress; |
| 722 | |
| 723 | if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint)) |
| 724 | out_endpoint = endpoint->bEndpointAddress; |
| 725 | } |
| 726 | |
| 727 | if (!in_endpoint || !out_endpoint) { |
| 728 | nfc_err(&interface->dev, |
| 729 | "Could not find bulk-in or bulk-out endpoint\n"); |
| 730 | rc = -ENODEV; |
| 731 | goto error; |
| 732 | } |
| 733 | |
| 734 | dev->in_urb = usb_alloc_urb(0, GFP_KERNEL); |
| 735 | dev->out_urb = usb_alloc_urb(0, GFP_KERNEL); |
| 736 | |
| 737 | if (!dev->in_urb || !dev->out_urb) { |
| 738 | nfc_err(&interface->dev, "Could not allocate USB URBs\n"); |
| 739 | rc = -ENOMEM; |
| 740 | goto error; |
| 741 | } |
| 742 | |
| 743 | usb_fill_bulk_urb(dev->in_urb, dev->udev, |
| 744 | usb_rcvbulkpipe(dev->udev, in_endpoint), |
| 745 | NULL, 0, NULL, dev); |
| 746 | usb_fill_bulk_urb(dev->out_urb, dev->udev, |
| 747 | usb_sndbulkpipe(dev->udev, out_endpoint), |
| 748 | NULL, 0, port100_send_complete, dev); |
| 749 | |
| 750 | dev->skb_headroom = PORT100_FRAME_HEADER_LEN + |
| 751 | PORT100_COMM_RF_HEAD_MAX_LEN; |
| 752 | dev->skb_tailroom = PORT100_FRAME_TAIL_LEN; |
| 753 | |
| 754 | INIT_WORK(&dev->cmd_complete_work, port100_wq_cmd_complete); |
| 755 | |
| 756 | /* The first thing to do with the Port-100 is to set the command type |
| 757 | * to be used. If supported we use command type 1. 0 otherwise. |
| 758 | */ |
| 759 | cmd_type_mask = port100_get_command_type_mask(dev); |
| 760 | if (!cmd_type_mask) { |
| 761 | nfc_err(&interface->dev, |
| 762 | "Could not get supported command types.\n"); |
| 763 | rc = -ENODEV; |
| 764 | goto error; |
| 765 | } |
| 766 | |
| 767 | if (PORT100_CMD_TYPE_IS_SUPPORTED(cmd_type_mask, PORT100_CMD_TYPE_1)) |
| 768 | dev->cmd_type = PORT100_CMD_TYPE_1; |
| 769 | else |
| 770 | dev->cmd_type = PORT100_CMD_TYPE_0; |
| 771 | |
| 772 | rc = port100_set_command_type(dev, dev->cmd_type); |
| 773 | if (rc) { |
| 774 | nfc_err(&interface->dev, |
| 775 | "The device does not support command type %u.\n", |
| 776 | dev->cmd_type); |
| 777 | goto error; |
| 778 | } |
| 779 | |
| 780 | fw_version = port100_get_firmware_version(dev); |
| 781 | if (!fw_version) |
| 782 | nfc_err(&interface->dev, |
| 783 | "Could not get device firmware version.\n"); |
| 784 | |
| 785 | nfc_info(&interface->dev, |
| 786 | "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n", |
| 787 | (fw_version & 0xFF00) >> 8, fw_version & 0xFF); |
Thierry Escande | 562d4d5 | 2013-10-04 12:12:00 +0200 | [diff] [blame] | 788 | |
| 789 | dev->nfc_digital_dev = nfc_digital_allocate_device(&port100_digital_ops, |
| 790 | PORT100_PROTOCOLS, |
| 791 | PORT100_CAPABILITIES, |
| 792 | dev->skb_headroom, |
| 793 | dev->skb_tailroom); |
| 794 | if (!dev->nfc_digital_dev) { |
| 795 | nfc_err(&interface->dev, |
| 796 | "Could not allocate nfc_digital_dev.\n"); |
| 797 | rc = -ENOMEM; |
| 798 | goto error; |
| 799 | } |
| 800 | |
| 801 | nfc_digital_set_parent_dev(dev->nfc_digital_dev, &interface->dev); |
| 802 | nfc_digital_set_drvdata(dev->nfc_digital_dev, dev); |
| 803 | |
| 804 | rc = nfc_digital_register_device(dev->nfc_digital_dev); |
| 805 | if (rc) { |
| 806 | nfc_err(&interface->dev, |
| 807 | "Could not register digital device.\n"); |
| 808 | goto free_nfc_dev; |
| 809 | } |
| 810 | |
| 811 | return 0; |
| 812 | |
| 813 | free_nfc_dev: |
| 814 | nfc_digital_free_device(dev->nfc_digital_dev); |
| 815 | |
| 816 | error: |
Thierry Escande | 0347a6a | 2013-10-04 12:12:01 +0200 | [diff] [blame^] | 817 | usb_free_urb(dev->in_urb); |
| 818 | usb_free_urb(dev->out_urb); |
| 819 | usb_put_dev(dev->udev); |
| 820 | |
Thierry Escande | 562d4d5 | 2013-10-04 12:12:00 +0200 | [diff] [blame] | 821 | return rc; |
| 822 | } |
| 823 | |
| 824 | static void port100_disconnect(struct usb_interface *interface) |
| 825 | { |
| 826 | struct port100 *dev; |
| 827 | |
| 828 | dev = usb_get_intfdata(interface); |
| 829 | usb_set_intfdata(interface, NULL); |
| 830 | |
| 831 | nfc_digital_unregister_device(dev->nfc_digital_dev); |
| 832 | nfc_digital_free_device(dev->nfc_digital_dev); |
| 833 | |
Thierry Escande | 0347a6a | 2013-10-04 12:12:01 +0200 | [diff] [blame^] | 834 | usb_kill_urb(dev->in_urb); |
| 835 | usb_kill_urb(dev->out_urb); |
| 836 | |
| 837 | usb_free_urb(dev->in_urb); |
| 838 | usb_free_urb(dev->out_urb); |
| 839 | |
| 840 | kfree(dev->cmd); |
| 841 | |
Thierry Escande | 562d4d5 | 2013-10-04 12:12:00 +0200 | [diff] [blame] | 842 | nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected"); |
| 843 | } |
| 844 | |
| 845 | static struct usb_driver port100_driver = { |
| 846 | .name = "port100", |
| 847 | .probe = port100_probe, |
| 848 | .disconnect = port100_disconnect, |
| 849 | .id_table = port100_table, |
| 850 | }; |
| 851 | |
| 852 | module_usb_driver(port100_driver); |
| 853 | |
| 854 | MODULE_DESCRIPTION("NFC Port-100 series usb driver ver " VERSION); |
| 855 | MODULE_VERSION(VERSION); |
| 856 | MODULE_LICENSE("GPL"); |