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 * @max_payload_bytes: the amount of data (including CIP headers) per packet
 297 *
 298 * This function establishes a point-to-point connection from the local
 299 * computer to the target by allocating isochronous resources (channel and
 300 * bandwidth) and setting the target's input/output plug control register.
 301 * When this function succeeds, the caller is responsible for starting
 302 * transmitting packets.
 303 */
 304int cmp_connection_establish(struct cmp_connection *c)
 305{
 306        int err;
 307
 308        mutex_lock(&c->mutex);
 309
 310        if (WARN_ON(c->connected)) {
 311                mutex_unlock(&c->mutex);
 312                return -EISCONN;
 313        }
 314
 315retry_after_bus_reset:
 316        if (c->direction == CMP_OUTPUT)
 317                err = pcr_modify(c, opcr_set_modify, pcr_set_check,
 318                                 ABORT_ON_BUS_RESET);
 319        else
 320                err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
 321                                 ABORT_ON_BUS_RESET);
 322
 323        if (err == -EAGAIN) {
 324                err = fw_iso_resources_update(&c->resources);
 325                if (err >= 0)
 326                        goto retry_after_bus_reset;
 327        }
 328        if (err >= 0)
 329                c->connected = true;
 330
 331        mutex_unlock(&c->mutex);
 332
 333        return err;
 334}
 335EXPORT_SYMBOL(cmp_connection_establish);
 336
 337/**
 338 * cmp_connection_update - update the connection after a bus reset
 339 * @c: the connection manager
 340 *
 341 * This function must be called from the driver's .update handler to
 342 * reestablish any connection that might have been active.
 343 *
 344 * Returns zero on success, or a negative error code.  On an error, the
 345 * connection is broken and the caller must stop transmitting iso packets.
 346 */
 347int cmp_connection_update(struct cmp_connection *c)
 348{
 349        int err;
 350
 351        mutex_lock(&c->mutex);
 352
 353        if (!c->connected) {
 354                mutex_unlock(&c->mutex);
 355                return 0;
 356        }
 357
 358        err = fw_iso_resources_update(&c->resources);
 359        if (err < 0)
 360                goto err_unconnect;
 361
 362        if (c->direction == CMP_OUTPUT)
 363                err = pcr_modify(c, opcr_set_modify, pcr_set_check,
 364                                 SUCCEED_ON_BUS_RESET);
 365        else
 366                err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
 367                                 SUCCEED_ON_BUS_RESET);
 368
 369        if (err < 0)
 370                goto err_unconnect;
 371
 372        mutex_unlock(&c->mutex);
 373
 374        return 0;
 375
 376err_unconnect:
 377        c->connected = false;
 378        mutex_unlock(&c->mutex);
 379
 380        return err;
 381}
 382EXPORT_SYMBOL(cmp_connection_update);
 383
 384static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
 385{
 386        return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
 387}
 388
 389/**
 390 * cmp_connection_break - break the connection to the target
 391 * @c: the connection manager
 392 *
 393 * This function deactives the connection in the target's input/output plug
 394 * control register, and frees the isochronous resources of the connection.
 395 * Before calling this function, the caller should cease transmitting packets.
 396 */
 397void cmp_connection_break(struct cmp_connection *c)
 398{
 399        int err;
 400
 401        mutex_lock(&c->mutex);
 402
 403        if (!c->connected) {
 404                mutex_unlock(&c->mutex);
 405                return;
 406        }
 407
 408        err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
 409        if (err < 0)
 410                cmp_error(c, "plug is still connected\n");
 411
 412        c->connected = false;
 413
 414        mutex_unlock(&c->mutex);
 415}
 416EXPORT_SYMBOL(cmp_connection_break);
 417