linux/net/nfc/llcp/commands.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2011  Intel Corporation. All rights reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, write to the
  16 * Free Software Foundation, Inc.,
  17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18 */
  19
  20#define pr_fmt(fmt) "llcp: %s: " fmt, __func__
  21
  22#include <linux/init.h>
  23#include <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/nfc.h>
  26
  27#include <net/nfc/nfc.h>
  28
  29#include "../nfc.h"
  30#include "llcp.h"
  31
  32static u8 llcp_tlv_length[LLCP_TLV_MAX] = {
  33        0,
  34        1, /* VERSION */
  35        2, /* MIUX */
  36        2, /* WKS */
  37        1, /* LTO */
  38        1, /* RW */
  39        0, /* SN */
  40        1, /* OPT */
  41        0, /* SDREQ */
  42        2, /* SDRES */
  43
  44};
  45
  46static u8 llcp_tlv8(u8 *tlv, u8 type)
  47{
  48        if (tlv[0] != type || tlv[1] != llcp_tlv_length[tlv[0]])
  49                return 0;
  50
  51        return tlv[2];
  52}
  53
  54static u8 llcp_tlv16(u8 *tlv, u8 type)
  55{
  56        if (tlv[0] != type || tlv[1] != llcp_tlv_length[tlv[0]])
  57                return 0;
  58
  59        return be16_to_cpu(*((__be16 *)(tlv + 2)));
  60}
  61
  62
  63static u8 llcp_tlv_version(u8 *tlv)
  64{
  65        return llcp_tlv8(tlv, LLCP_TLV_VERSION);
  66}
  67
  68static u16 llcp_tlv_miux(u8 *tlv)
  69{
  70        return llcp_tlv16(tlv, LLCP_TLV_MIUX) & 0x7f;
  71}
  72
  73static u16 llcp_tlv_wks(u8 *tlv)
  74{
  75        return llcp_tlv16(tlv, LLCP_TLV_WKS);
  76}
  77
  78static u16 llcp_tlv_lto(u8 *tlv)
  79{
  80        return llcp_tlv8(tlv, LLCP_TLV_LTO);
  81}
  82
  83static u8 llcp_tlv_opt(u8 *tlv)
  84{
  85        return llcp_tlv8(tlv, LLCP_TLV_OPT);
  86}
  87
  88static u8 llcp_tlv_rw(u8 *tlv)
  89{
  90        return llcp_tlv8(tlv, LLCP_TLV_RW) & 0xf;
  91}
  92
  93u8 *nfc_llcp_build_tlv(u8 type, u8 *value, u8 value_length, u8 *tlv_length)
  94{
  95        u8 *tlv, length;
  96
  97        pr_debug("type %d\n", type);
  98
  99        if (type >= LLCP_TLV_MAX)
 100                return NULL;
 101
 102        length = llcp_tlv_length[type];
 103        if (length == 0 && value_length == 0)
 104                return NULL;
 105        else
 106                length = value_length;
 107
 108        *tlv_length = 2 + length;
 109        tlv = kzalloc(2 + length, GFP_KERNEL);
 110        if (tlv == NULL)
 111                return tlv;
 112
 113        tlv[0] = type;
 114        tlv[1] = length;
 115        memcpy(tlv + 2, value, length);
 116
 117        return tlv;
 118}
 119
 120int nfc_llcp_parse_tlv(struct nfc_llcp_local *local,
 121                        u8 *tlv_array, u16 tlv_array_len)
 122{
 123        u8 *tlv = tlv_array, type, length, offset = 0;
 124
 125        pr_debug("TLV array length %d\n", tlv_array_len);
 126
 127        if (local == NULL)
 128                return -ENODEV;
 129
 130        while (offset < tlv_array_len) {
 131                type = tlv[0];
 132                length = tlv[1];
 133
 134                pr_debug("type 0x%x length %d\n", type, length);
 135
 136                switch (type) {
 137                case LLCP_TLV_VERSION:
 138                        local->remote_version = llcp_tlv_version(tlv);
 139                        break;
 140                case LLCP_TLV_MIUX:
 141                        local->remote_miu = llcp_tlv_miux(tlv) + 128;
 142                        break;
 143                case LLCP_TLV_WKS:
 144                        local->remote_wks = llcp_tlv_wks(tlv);
 145                        break;
 146                case LLCP_TLV_LTO:
 147                        local->remote_lto = llcp_tlv_lto(tlv) * 10;
 148                        break;
 149                case LLCP_TLV_OPT:
 150                        local->remote_opt = llcp_tlv_opt(tlv);
 151                        break;
 152                case LLCP_TLV_RW:
 153                        local->remote_rw = llcp_tlv_rw(tlv);
 154                        break;
 155                default:
 156                        pr_err("Invalid gt tlv value 0x%x\n", type);
 157                        break;
 158                }
 159
 160                offset += length + 2;
 161                tlv += length + 2;
 162        }
 163
 164        pr_debug("version 0x%x miu %d lto %d opt 0x%x wks 0x%x rw %d\n",
 165                local->remote_version, local->remote_miu,
 166                local->remote_lto, local->remote_opt,
 167                local->remote_wks, local->remote_rw);
 168
 169        return 0;
 170}
 171
 172static struct sk_buff *llcp_add_header(struct sk_buff *pdu,
 173                                        u8 dsap, u8 ssap, u8 ptype)
 174{
 175        u8 header[2];
 176
 177        pr_debug("ptype 0x%x dsap 0x%x ssap 0x%x\n", ptype, dsap, ssap);
 178
 179        header[0] = (u8)((dsap << 2) | (ptype >> 2));
 180        header[1] = (u8)((ptype << 6) | ssap);
 181
 182        pr_debug("header 0x%x 0x%x\n", header[0], header[1]);
 183
 184        memcpy(skb_put(pdu, LLCP_HEADER_SIZE), header, LLCP_HEADER_SIZE);
 185
 186        return pdu;
 187}
 188
 189static struct sk_buff *llcp_add_tlv(struct sk_buff *pdu, u8 *tlv, u8 tlv_length)
 190{
 191        /* XXX Add an skb length check */
 192
 193        if (tlv == NULL)
 194                return NULL;
 195
 196        memcpy(skb_put(pdu, tlv_length), tlv, tlv_length);
 197
 198        return pdu;
 199}
 200
 201static struct sk_buff *llcp_allocate_pdu(struct nfc_llcp_sock *sock,
 202                                                        u8 cmd, u16 size)
 203{
 204        struct sk_buff *skb;
 205        int err;
 206
 207        if (sock->ssap == 0)
 208                return NULL;
 209
 210        skb = nfc_alloc_send_skb(sock->dev, &sock->sk, MSG_DONTWAIT,
 211                                        size + LLCP_HEADER_SIZE, &err);
 212        if (skb == NULL) {
 213                pr_err("Could not allocate PDU\n");
 214                return NULL;
 215        }
 216
 217        skb = llcp_add_header(skb, sock->dsap, sock->ssap, cmd);
 218
 219        return skb;
 220}
 221
 222int nfc_llcp_disconnect(struct nfc_llcp_sock *sock)
 223{
 224        struct sk_buff *skb;
 225        struct nfc_dev *dev;
 226        struct nfc_llcp_local *local;
 227        u16 size = 0;
 228
 229        pr_debug("Sending DISC\n");
 230
 231        local = sock->local;
 232        if (local == NULL)
 233                return -ENODEV;
 234
 235        dev = sock->dev;
 236        if (dev == NULL)
 237                return -ENODEV;
 238
 239        size += LLCP_HEADER_SIZE;
 240        size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
 241
 242        skb = alloc_skb(size, GFP_ATOMIC);
 243        if (skb == NULL)
 244                return -ENOMEM;
 245
 246        skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
 247
 248        skb = llcp_add_header(skb, sock->ssap, sock->dsap, LLCP_PDU_DISC);
 249
 250        skb_queue_tail(&local->tx_queue, skb);
 251
 252        return 0;
 253}
 254
 255int nfc_llcp_send_symm(struct nfc_dev *dev)
 256{
 257        struct sk_buff *skb;
 258        struct nfc_llcp_local *local;
 259        u16 size = 0;
 260
 261        pr_debug("Sending SYMM\n");
 262
 263        local = nfc_llcp_find_local(dev);
 264        if (local == NULL)
 265                return -ENODEV;
 266
 267        size += LLCP_HEADER_SIZE;
 268        size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
 269
 270        skb = alloc_skb(size, GFP_KERNEL);
 271        if (skb == NULL)
 272                return -ENOMEM;
 273
 274        skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
 275
 276        skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM);
 277
 278        return nfc_data_exchange(dev, local->target_idx, skb,
 279                                        nfc_llcp_recv, local);
 280}
 281
 282int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
 283{
 284        struct nfc_llcp_local *local;
 285        struct sk_buff *skb;
 286        u8 *service_name_tlv = NULL, service_name_tlv_length;
 287        int err;
 288        u16 size = 0;
 289
 290        pr_debug("Sending CONNECT\n");
 291
 292        local = sock->local;
 293        if (local == NULL)
 294                return -ENODEV;
 295
 296        if (sock->service_name != NULL) {
 297                service_name_tlv = nfc_llcp_build_tlv(LLCP_TLV_SN,
 298                                        sock->service_name,
 299                                        sock->service_name_len,
 300                                        &service_name_tlv_length);
 301                size += service_name_tlv_length;
 302        }
 303
 304        pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len);
 305
 306        skb = llcp_allocate_pdu(sock, LLCP_PDU_CONNECT, size);
 307        if (skb == NULL) {
 308                err = -ENOMEM;
 309                goto error_tlv;
 310        }
 311
 312        if (service_name_tlv != NULL)
 313                skb = llcp_add_tlv(skb, service_name_tlv,
 314                                        service_name_tlv_length);
 315
 316        skb_queue_tail(&local->tx_queue, skb);
 317
 318        return 0;
 319
 320error_tlv:
 321        pr_err("error %d\n", err);
 322
 323        kfree(service_name_tlv);
 324
 325        return err;
 326}
 327
 328int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
 329{
 330        struct nfc_llcp_local *local;
 331        struct sk_buff *skb;
 332
 333        pr_debug("Sending CC\n");
 334
 335        local = sock->local;
 336        if (local == NULL)
 337                return -ENODEV;
 338
 339        skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, 0);
 340        if (skb == NULL)
 341                return -ENOMEM;
 342
 343        skb_queue_tail(&local->tx_queue, skb);
 344
 345        return 0;
 346}
 347
 348int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason)
 349{
 350        struct sk_buff *skb;
 351        struct nfc_dev *dev;
 352        u16 size = 1; /* Reason code */
 353
 354        pr_debug("Sending DM reason 0x%x\n", reason);
 355
 356        if (local == NULL)
 357                return -ENODEV;
 358
 359        dev = local->dev;
 360        if (dev == NULL)
 361                return -ENODEV;
 362
 363        size += LLCP_HEADER_SIZE;
 364        size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
 365
 366        skb = alloc_skb(size, GFP_KERNEL);
 367        if (skb == NULL)
 368                return -ENOMEM;
 369
 370        skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
 371
 372        skb = llcp_add_header(skb, ssap, dsap, LLCP_PDU_DM);
 373
 374        memcpy(skb_put(skb, 1), &reason, 1);
 375
 376        skb_queue_head(&local->tx_queue, skb);
 377
 378        return 0;
 379}
 380
 381int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock)
 382{
 383        struct sk_buff *skb;
 384        struct nfc_llcp_local *local;
 385
 386        pr_debug("Send DISC\n");
 387
 388        local = sock->local;
 389        if (local == NULL)
 390                return -ENODEV;
 391
 392        skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0);
 393        if (skb == NULL)
 394                return -ENOMEM;
 395
 396        skb_queue_head(&local->tx_queue, skb);
 397
 398        return 0;
 399}
 400