linux/drivers/misc/mei/bus-fixup.c
<<
>>
Prefs
   1/*
   2 *
   3 * Intel Management Engine Interface (Intel MEI) Linux driver
   4 * Copyright (c) 2003-2013, Intel Corporation.
   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 it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 *
  15 */
  16
  17#include <linux/kernel.h>
  18#include <linux/sched.h>
  19#include <linux/module.h>
  20#include <linux/moduleparam.h>
  21#include <linux/device.h>
  22#include <linux/slab.h>
  23#include <linux/uuid.h>
  24
  25#include <linux/mei_cl_bus.h>
  26
  27#include "mei_dev.h"
  28#include "client.h"
  29
  30#define MEI_UUID_NFC_INFO UUID_LE(0xd2de1625, 0x382d, 0x417d, \
  31                        0x48, 0xa4, 0xef, 0xab, 0xba, 0x8a, 0x12, 0x06)
  32
  33static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO;
  34
  35#define MEI_UUID_NFC_HCI UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \
  36                        0x94, 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c)
  37
  38#define MEI_UUID_ANY NULL_UUID_LE
  39
  40/**
  41 * number_of_connections - determine whether an client be on the bus
  42 *    according number of connections
  43 *    We support only clients:
  44 *       1. with single connection
  45 *       2. and fixed clients (max_number_of_connections == 0)
  46 *
  47 * @cldev: me clients device
  48 */
  49static void number_of_connections(struct mei_cl_device *cldev)
  50{
  51        dev_dbg(&cldev->dev, "running hook %s on %pUl\n",
  52                        __func__, mei_me_cl_uuid(cldev->me_cl));
  53
  54        if (cldev->me_cl->props.max_number_of_connections > 1)
  55                cldev->do_match = 0;
  56}
  57
  58/**
  59 * blacklist - blacklist a client from the bus
  60 *
  61 * @cldev: me clients device
  62 */
  63static void blacklist(struct mei_cl_device *cldev)
  64{
  65        dev_dbg(&cldev->dev, "running hook %s on %pUl\n",
  66                        __func__, mei_me_cl_uuid(cldev->me_cl));
  67        cldev->do_match = 0;
  68}
  69
  70struct mei_nfc_cmd {
  71        u8 command;
  72        u8 status;
  73        u16 req_id;
  74        u32 reserved;
  75        u16 data_size;
  76        u8 sub_command;
  77        u8 data[];
  78} __packed;
  79
  80struct mei_nfc_reply {
  81        u8 command;
  82        u8 status;
  83        u16 req_id;
  84        u32 reserved;
  85        u16 data_size;
  86        u8 sub_command;
  87        u8 reply_status;
  88        u8 data[];
  89} __packed;
  90
  91struct mei_nfc_if_version {
  92        u8 radio_version_sw[3];
  93        u8 reserved[3];
  94        u8 radio_version_hw[3];
  95        u8 i2c_addr;
  96        u8 fw_ivn;
  97        u8 vendor_id;
  98        u8 radio_type;
  99} __packed;
 100
 101
 102#define MEI_NFC_CMD_MAINTENANCE 0x00
 103#define MEI_NFC_SUBCMD_IF_VERSION 0x01
 104
 105/* Vendors */
 106#define MEI_NFC_VENDOR_INSIDE 0x00
 107#define MEI_NFC_VENDOR_NXP    0x01
 108
 109/* Radio types */
 110#define MEI_NFC_VENDOR_INSIDE_UREAD 0x00
 111#define MEI_NFC_VENDOR_NXP_PN544    0x01
 112
 113/**
 114 * mei_nfc_if_version - get NFC interface version
 115 *
 116 * @cl: host client (nfc info)
 117 * @ver: NFC interface version to be filled in
 118 *
 119 * Return: 0 on success; < 0 otherwise
 120 */
 121static int mei_nfc_if_version(struct mei_cl *cl,
 122                              struct mei_nfc_if_version *ver)
 123{
 124        struct mei_device *bus;
 125        struct mei_nfc_cmd cmd = {
 126                .command = MEI_NFC_CMD_MAINTENANCE,
 127                .data_size = 1,
 128                .sub_command = MEI_NFC_SUBCMD_IF_VERSION,
 129        };
 130        struct mei_nfc_reply *reply = NULL;
 131        size_t if_version_length;
 132        int bytes_recv, ret;
 133
 134        bus = cl->dev;
 135
 136        WARN_ON(mutex_is_locked(&bus->device_lock));
 137
 138        ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd), 1);
 139        if (ret < 0) {
 140                dev_err(bus->dev, "Could not send IF version cmd\n");
 141                return ret;
 142        }
 143
 144        /* to be sure on the stack we alloc memory */
 145        if_version_length = sizeof(struct mei_nfc_reply) +
 146                sizeof(struct mei_nfc_if_version);
 147
 148        reply = kzalloc(if_version_length, GFP_KERNEL);
 149        if (!reply)
 150                return -ENOMEM;
 151
 152        ret = 0;
 153        bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length);
 154        if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) {
 155                dev_err(bus->dev, "Could not read IF version\n");
 156                ret = -EIO;
 157                goto err;
 158        }
 159
 160        memcpy(ver, reply->data, sizeof(struct mei_nfc_if_version));
 161
 162        dev_info(bus->dev, "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n",
 163                ver->fw_ivn, ver->vendor_id, ver->radio_type);
 164
 165err:
 166        kfree(reply);
 167        return ret;
 168}
 169
 170/**
 171 * mei_nfc_radio_name - derive nfc radio name from the interface version
 172 *
 173 * @ver: NFC radio version
 174 *
 175 * Return: radio name string
 176 */
 177static const char *mei_nfc_radio_name(struct mei_nfc_if_version *ver)
 178{
 179
 180        if (ver->vendor_id == MEI_NFC_VENDOR_INSIDE) {
 181                if (ver->radio_type == MEI_NFC_VENDOR_INSIDE_UREAD)
 182                        return "microread";
 183        }
 184
 185        if (ver->vendor_id == MEI_NFC_VENDOR_NXP) {
 186                if (ver->radio_type == MEI_NFC_VENDOR_NXP_PN544)
 187                        return "pn544";
 188        }
 189
 190        return NULL;
 191}
 192
 193/**
 194 * mei_nfc - The nfc fixup function. The function retrieves nfc radio
 195 *    name and set is as device attribute so we can load
 196 *    the proper device driver for it
 197 *
 198 * @cldev: me client device (nfc)
 199 */
 200static void mei_nfc(struct mei_cl_device *cldev)
 201{
 202        struct mei_device *bus;
 203        struct mei_cl *cl;
 204        struct mei_me_client *me_cl = NULL;
 205        struct mei_nfc_if_version ver;
 206        const char *radio_name = NULL;
 207        int ret;
 208
 209        bus = cldev->bus;
 210
 211        dev_dbg(bus->dev, "running hook %s: %pUl match=%d\n",
 212                __func__, mei_me_cl_uuid(cldev->me_cl), cldev->do_match);
 213
 214        mutex_lock(&bus->device_lock);
 215        /* we need to connect to INFO GUID */
 216        cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY);
 217        if (IS_ERR(cl)) {
 218                ret = PTR_ERR(cl);
 219                cl = NULL;
 220                dev_err(bus->dev, "nfc hook alloc failed %d\n", ret);
 221                goto out;
 222        }
 223
 224        me_cl = mei_me_cl_by_uuid(bus, &mei_nfc_info_guid);
 225        if (!me_cl) {
 226                ret = -ENOTTY;
 227                dev_err(bus->dev, "Cannot find nfc info %d\n", ret);
 228                goto out;
 229        }
 230
 231        ret = mei_cl_connect(cl, me_cl, NULL);
 232        if (ret < 0) {
 233                dev_err(&cldev->dev, "Can't connect to the NFC INFO ME ret = %d\n",
 234                        ret);
 235                goto out;
 236        }
 237
 238        mutex_unlock(&bus->device_lock);
 239
 240        ret = mei_nfc_if_version(cl, &ver);
 241        if (ret)
 242                goto disconnect;
 243
 244        radio_name = mei_nfc_radio_name(&ver);
 245
 246        if (!radio_name) {
 247                ret = -ENOENT;
 248                dev_err(&cldev->dev, "Can't get the NFC interface version ret = %d\n",
 249                        ret);
 250                goto disconnect;
 251        }
 252
 253        dev_dbg(bus->dev, "nfc radio %s\n", radio_name);
 254        strlcpy(cldev->name, radio_name, sizeof(cldev->name));
 255
 256disconnect:
 257        mutex_lock(&bus->device_lock);
 258        if (mei_cl_disconnect(cl) < 0)
 259                dev_err(bus->dev, "Can't disconnect the NFC INFO ME\n");
 260
 261        mei_cl_flush_queues(cl, NULL);
 262
 263out:
 264        mei_cl_unlink(cl);
 265        mutex_unlock(&bus->device_lock);
 266        mei_me_cl_put(me_cl);
 267        kfree(cl);
 268
 269        if (ret)
 270                cldev->do_match = 0;
 271
 272        dev_dbg(bus->dev, "end of fixup match = %d\n", cldev->do_match);
 273}
 274
 275#define MEI_FIXUP(_uuid, _hook) { _uuid, _hook }
 276
 277static struct mei_fixup {
 278
 279        const uuid_le uuid;
 280        void (*hook)(struct mei_cl_device *cldev);
 281} mei_fixups[] = {
 282        MEI_FIXUP(MEI_UUID_ANY, number_of_connections),
 283        MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist),
 284        MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc),
 285};
 286
 287/**
 288 * mei_cl_dev_fixup - run fixup handlers
 289 *
 290 * @cldev: me client device
 291 */
 292void mei_cl_dev_fixup(struct mei_cl_device *cldev)
 293{
 294        struct mei_fixup *f;
 295        const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
 296        int i;
 297
 298        for (i = 0; i < ARRAY_SIZE(mei_fixups); i++) {
 299
 300                f = &mei_fixups[i];
 301                if (uuid_le_cmp(f->uuid, MEI_UUID_ANY) == 0 ||
 302                    uuid_le_cmp(f->uuid, *uuid) == 0)
 303                        f->hook(cldev);
 304        }
 305}
 306
 307