linux/sound/firewire/cmp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Connection Management Procedures (IEC 61883-1) helper functions
   4 *
   5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
   6 */
   7
   8#include <linux/device.h>
   9#include <linux/firewire.h>
  10#include <linux/firewire-constants.h>
  11#include <linux/module.h>
  12#include <linux/sched.h>
  13#include "lib.h"
  14#include "iso-resources.h"
  15#include "cmp.h"
  16
  17/* MPR common fields */
  18#define MPR_SPEED_MASK          0xc0000000
  19#define MPR_SPEED_SHIFT         30
  20#define MPR_XSPEED_MASK         0x00000060
  21#define MPR_XSPEED_SHIFT        5
  22#define MPR_PLUGS_MASK          0x0000001f
  23
  24/* PCR common fields */
  25#define PCR_ONLINE              0x80000000
  26#define PCR_BCAST_CONN          0x40000000
  27#define PCR_P2P_CONN_MASK       0x3f000000
  28#define PCR_P2P_CONN_SHIFT      24
  29#define PCR_CHANNEL_MASK        0x003f0000
  30#define PCR_CHANNEL_SHIFT       16
  31
  32/* oPCR specific fields */
  33#define OPCR_XSPEED_MASK        0x00C00000
  34#define OPCR_XSPEED_SHIFT       22
  35#define OPCR_SPEED_MASK         0x0000C000
  36#define OPCR_SPEED_SHIFT        14
  37#define OPCR_OVERHEAD_ID_MASK   0x00003C00
  38#define OPCR_OVERHEAD_ID_SHIFT  10
  39
  40enum bus_reset_handling {
  41        ABORT_ON_BUS_RESET,
  42        SUCCEED_ON_BUS_RESET,
  43};
  44
  45static __printf(2, 3)
  46void cmp_error(struct cmp_connection *c, const char *fmt, ...)
  47{
  48        va_list va;
  49
  50        va_start(va, fmt);
  51        dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
  52                (c->direction == CMP_INPUT) ? 'i' : 'o',
  53                c->pcr_index, &(struct va_format){ fmt, &va });
  54        va_end(va);
  55}
  56
  57static u64 mpr_address(struct cmp_connection *c)
  58{
  59        if (c->direction == CMP_INPUT)
  60                return CSR_REGISTER_BASE + CSR_IMPR;
  61        else
  62                return CSR_REGISTER_BASE + CSR_OMPR;
  63}
  64
  65static u64 pcr_address(struct cmp_connection *c)
  66{
  67        if (c->direction == CMP_INPUT)
  68                return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
  69        else
  70                return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
  71}
  72
  73static int pcr_modify(struct cmp_connection *c,
  74                      __be32 (*modify)(struct cmp_connection *c, __be32 old),
  75                      int (*check)(struct cmp_connection *c, __be32 pcr),
  76                      enum bus_reset_handling bus_reset_handling)
  77{
  78        __be32 old_arg, buffer[2];
  79        int err;
  80
  81        buffer[0] = c->last_pcr_value;
  82        for (;;) {
  83                old_arg = buffer[0];
  84                buffer[1] = modify(c, buffer[0]);
  85
  86                err = snd_fw_transaction(
  87                                c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
  88                                pcr_address(c), buffer, 8,
  89                                FW_FIXED_GENERATION | c->resources.generation);
  90
  91                if (err < 0) {
  92                        if (err == -EAGAIN &&
  93                            bus_reset_handling == SUCCEED_ON_BUS_RESET)
  94                                err = 0;
  95                        return err;
  96                }
  97
  98                if (buffer[0] == old_arg) /* success? */
  99                        break;
 100
 101                if (check) {
 102                        err = check(c, buffer[0]);
 103                        if (err < 0)
 104                                return err;
 105                }
 106        }
 107        c->last_pcr_value = buffer[1];
 108
 109        return 0;
 110}
 111
 112
 113/**
 114 * cmp_connection_init - initializes a connection manager
 115 * @c: the connection manager to initialize
 116 * @unit: a unit of the target device
 117 * @direction: input or output
 118 * @pcr_index: the index of the iPCR/oPCR on the target device
 119 */
 120int cmp_connection_init(struct cmp_connection *c,
 121                        struct fw_unit *unit,
 122                        enum cmp_direction direction,
 123                        unsigned int pcr_index)
 124{
 125        __be32 mpr_be;
 126        u32 mpr;
 127        int err;
 128
 129        c->direction = direction;
 130        err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
 131                                 mpr_address(c), &mpr_be, 4, 0);
 132        if (err < 0)
 133                return err;
 134        mpr = be32_to_cpu(mpr_be);
 135
 136        if (pcr_index >= (mpr & MPR_PLUGS_MASK))
 137                return -EINVAL;
 138
 139        err = fw_iso_resources_init(&c->resources, unit);
 140        if (err < 0)
 141                return err;
 142
 143        c->connected = false;
 144        mutex_init(&c->mutex);
 145        c->last_pcr_value = cpu_to_be32(0x80000000);
 146        c->pcr_index = pcr_index;
 147        c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
 148        if (c->max_speed == SCODE_BETA)
 149                c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
 150
 151        return 0;
 152}
 153EXPORT_SYMBOL(cmp_connection_init);
 154
 155/**
 156 * cmp_connection_check_used - check connection is already esablished or not
 157 * @c: the connection manager to be checked
 158 * @used: the pointer to store the result of checking the connection
 159 */
 160int cmp_connection_check_used(struct cmp_connection *c, bool *used)
 161{
 162        __be32 pcr;
 163        int err;
 164
 165        err = snd_fw_transaction(
 166                        c->resources.unit, TCODE_READ_QUADLET_REQUEST,
 167                        pcr_address(c), &pcr, 4, 0);
 168        if (err >= 0)
 169                *used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN |
 170                                             PCR_P2P_CONN_MASK));
 171
 172        return err;
 173}
 174EXPORT_SYMBOL(cmp_connection_check_used);
 175
 176/**
 177 * cmp_connection_destroy - free connection manager resources
 178 * @c: the connection manager
 179 */
 180void cmp_connection_destroy(struct cmp_connection *c)
 181{
 182        WARN_ON(c->connected);
 183        mutex_destroy(&c->mutex);
 184        fw_iso_resources_destroy(&c->resources);
 185}
 186EXPORT_SYMBOL(cmp_connection_destroy);
 187
 188int cmp_connection_reserve(struct cmp_connection *c,
 189                           unsigned int max_payload_bytes)
 190{
 191        int err;
 192
 193        mutex_lock(&c->mutex);
 194
 195        if (WARN_ON(c->resources.allocated)) {
 196                err = -EBUSY;
 197                goto end;
 198        }
 199
 200        c->speed = min(c->max_speed,
 201                       fw_parent_device(c->resources.unit)->max_speed);
 202
 203        err = fw_iso_resources_allocate(&c->resources, max_payload_bytes,
 204                                        c->speed);
 205end:
 206        mutex_unlock(&c->mutex);
 207
 208        return err;
 209}
 210EXPORT_SYMBOL(cmp_connection_reserve);
 211
 212void cmp_connection_release(struct cmp_connection *c)
 213{
 214        mutex_lock(&c->mutex);
 215        fw_iso_resources_free(&c->resources);
 216        mutex_unlock(&c->mutex);
 217}
 218EXPORT_SYMBOL(cmp_connection_release);
 219
 220static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
 221{
 222        ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
 223                             PCR_P2P_CONN_MASK |
 224                             PCR_CHANNEL_MASK);
 225        ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
 226        ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
 227
 228        return ipcr;
 229}
 230
 231static int get_overhead_id(struct cmp_connection *c)
 232{
 233        int id;
 234
 235        /*
 236         * apply "oPCR overhead ID encoding"
 237         * the encoding table can convert up to 512.
 238         * here the value over 512 is converted as the same way as 512.
 239         */
 240        for (id = 1; id < 16; id++) {
 241                if (c->resources.bandwidth_overhead < (id << 5))
 242                        break;
 243        }
 244        if (id == 16)
 245                id = 0;
 246
 247        return id;
 248}
 249
 250static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
 251{
 252        unsigned int spd, xspd;
 253
 254        /* generate speed and extended speed field value */
 255        if (c->speed > SCODE_400) {
 256                spd  = SCODE_800;
 257                xspd = c->speed - SCODE_800;
 258        } else {
 259                spd = c->speed;
 260                xspd = 0;
 261        }
 262
 263        opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
 264                             PCR_P2P_CONN_MASK |
 265                             OPCR_XSPEED_MASK |
 266                             PCR_CHANNEL_MASK |
 267                             OPCR_SPEED_MASK |
 268                             OPCR_OVERHEAD_ID_MASK);
 269        opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
 270        opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
 271        opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
 272        opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
 273        opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
 274
 275        return opcr;
 276}
 277
 278static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
 279{
 280        if (pcr & cpu_to_be32(PCR_BCAST_CONN |
 281                              PCR_P2P_CONN_MASK)) {
 282                cmp_error(c, "plug is already in use\n");
 283                return -EBUSY;
 284        }
 285        if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
 286                cmp_error(c, "plug is not on-line\n");
 287                return -ECONNREFUSED;
 288        }
 289
 290        return 0;
 291}
 292
 293/**
 294 * cmp_connection_establish - establish a connection to the target
 295 * @c: the connection manager
 296 *
 297 * This function establishes a point-to-point connection from the local
 298 * computer to the target by allocating isochronous resources (channel and
 299 * bandwidth) and setting the target's input/output plug control register.
 300 * When this function succeeds, the caller is responsible for starting
 301 * transmitting packets.
 302 */
 303int cmp_connection_establish(struct cmp_connection *c)
 304{
 305        int err;
 306
 307        mutex_lock(&c->mutex);
 308
 309        if (WARN_ON(c->connected)) {
 310                mutex_unlock(&c->mutex);
 311                return -EISCONN;
 312        }
 313
 314retry_after_bus_reset:
 315        if (c->direction == CMP_OUTPUT)
 316                err = pcr_modify(c, opcr_set_modify, pcr_set_check,
 317                                 ABORT_ON_BUS_RESET);
 318        else
 319                err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
 320                                 ABORT_ON_BUS_RESET);
 321
 322        if (err == -EAGAIN) {
 323                err = fw_iso_resources_update(&c->resources);
 324                if (err >= 0)
 325                        goto retry_after_bus_reset;
 326        }
 327        if (err >= 0)
 328                c->connected = true;
 329
 330        mutex_unlock(&c->mutex);
 331
 332        return err;
 333}
 334EXPORT_SYMBOL(cmp_connection_establish);
 335
 336/**
 337 * cmp_connection_update - update the connection after a bus reset
 338 * @c: the connection manager
 339 *
 340 * This function must be called from the driver's .update handler to
 341 * reestablish any connection that might have been active.
 342 *
 343 * Returns zero on success, or a negative error code.  On an error, the
 344 * connection is broken and the caller must stop transmitting iso packets.
 345 */
 346int cmp_connection_update(struct cmp_connection *c)
 347{
 348        int err;
 349
 350        mutex_lock(&c->mutex);
 351
 352        if (!c->connected) {
 353                mutex_unlock(&c->mutex);
 354                return 0;
 355        }
 356
 357        err = fw_iso_resources_update(&c->resources);
 358        if (err < 0)
 359                goto err_unconnect;
 360
 361        if (c->direction == CMP_OUTPUT)
 362                err = pcr_modify(c, opcr_set_modify, pcr_set_check,
 363                                 SUCCEED_ON_BUS_RESET);
 364        else
 365                err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
 366                                 SUCCEED_ON_BUS_RESET);
 367
 368        if (err < 0)
 369                goto err_unconnect;
 370
 371        mutex_unlock(&c->mutex);
 372
 373        return 0;
 374
 375err_unconnect:
 376        c->connected = false;
 377        mutex_unlock(&c->mutex);
 378
 379        return err;
 380}
 381EXPORT_SYMBOL(cmp_connection_update);
 382
 383static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
 384{
 385        return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
 386}
 387
 388/**
 389 * cmp_connection_break - break the connection to the target
 390 * @c: the connection manager
 391 *
 392 * This function deactives the connection in the target's input/output plug
 393 * control register, and frees the isochronous resources of the connection.
 394 * Before calling this function, the caller should cease transmitting packets.
 395 */
 396void cmp_connection_break(struct cmp_connection *c)
 397{
 398        int err;
 399
 400        mutex_lock(&c->mutex);
 401
 402        if (!c->connected) {
 403                mutex_unlock(&c->mutex);
 404                return;
 405        }
 406
 407        err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
 408        if (err < 0)
 409                cmp_error(c, "plug is still connected\n");
 410
 411        c->connected = false;
 412
 413        mutex_unlock(&c->mutex);
 414}
 415EXPORT_SYMBOL(cmp_connection_break);
 416