linux/sound/firewire/cmp.c
<<
>>
Prefs
   1/*
   2 * Connection Management Procedures (IEC 61883-1) helper functions
   3 *
   4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
   5 * Licensed under the terms of the GNU General Public License, version 2.
   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
 188
 189static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
 190{
 191        ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
 192                             PCR_P2P_CONN_MASK |
 193                             PCR_CHANNEL_MASK);
 194        ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
 195        ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
 196
 197        return ipcr;
 198}
 199
 200static int get_overhead_id(struct cmp_connection *c)
 201{
 202        int id;
 203
 204        /*
 205         * apply "oPCR overhead ID encoding"
 206         * the encoding table can convert up to 512.
 207         * here the value over 512 is converted as the same way as 512.
 208         */
 209        for (id = 1; id < 16; id++) {
 210                if (c->resources.bandwidth_overhead < (id << 5))
 211                        break;
 212        }
 213        if (id == 16)
 214                id = 0;
 215
 216        return id;
 217}
 218
 219static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
 220{
 221        unsigned int spd, xspd;
 222
 223        /* generate speed and extended speed field value */
 224        if (c->speed > SCODE_400) {
 225                spd  = SCODE_800;
 226                xspd = c->speed - SCODE_800;
 227        } else {
 228                spd = c->speed;
 229                xspd = 0;
 230        }
 231
 232        opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
 233                             PCR_P2P_CONN_MASK |
 234                             OPCR_XSPEED_MASK |
 235                             PCR_CHANNEL_MASK |
 236                             OPCR_SPEED_MASK |
 237                             OPCR_OVERHEAD_ID_MASK);
 238        opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
 239        opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
 240        opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
 241        opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
 242        opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
 243
 244        return opcr;
 245}
 246
 247static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
 248{
 249        if (pcr & cpu_to_be32(PCR_BCAST_CONN |
 250                              PCR_P2P_CONN_MASK)) {
 251                cmp_error(c, "plug is already in use\n");
 252                return -EBUSY;
 253        }
 254        if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
 255                cmp_error(c, "plug is not on-line\n");
 256                return -ECONNREFUSED;
 257        }
 258
 259        return 0;
 260}
 261
 262/**
 263 * cmp_connection_establish - establish a connection to the target
 264 * @c: the connection manager
 265 * @max_payload_bytes: the amount of data (including CIP headers) per packet
 266 *
 267 * This function establishes a point-to-point connection from the local
 268 * computer to the target by allocating isochronous resources (channel and
 269 * bandwidth) and setting the target's input/output plug control register.
 270 * When this function succeeds, the caller is responsible for starting
 271 * transmitting packets.
 272 */
 273int cmp_connection_establish(struct cmp_connection *c,
 274                             unsigned int max_payload_bytes)
 275{
 276        int err;
 277
 278        if (WARN_ON(c->connected))
 279                return -EISCONN;
 280
 281        c->speed = min(c->max_speed,
 282                       fw_parent_device(c->resources.unit)->max_speed);
 283
 284        mutex_lock(&c->mutex);
 285
 286retry_after_bus_reset:
 287        err = fw_iso_resources_allocate(&c->resources,
 288                                        max_payload_bytes, c->speed);
 289        if (err < 0)
 290                goto err_mutex;
 291
 292        if (c->direction == CMP_OUTPUT)
 293                err = pcr_modify(c, opcr_set_modify, pcr_set_check,
 294                                 ABORT_ON_BUS_RESET);
 295        else
 296                err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
 297                                 ABORT_ON_BUS_RESET);
 298
 299        if (err == -EAGAIN) {
 300                fw_iso_resources_free(&c->resources);
 301                goto retry_after_bus_reset;
 302        }
 303        if (err < 0)
 304                goto err_resources;
 305
 306        c->connected = true;
 307
 308        mutex_unlock(&c->mutex);
 309
 310        return 0;
 311
 312err_resources:
 313        fw_iso_resources_free(&c->resources);
 314err_mutex:
 315        mutex_unlock(&c->mutex);
 316
 317        return err;
 318}
 319EXPORT_SYMBOL(cmp_connection_establish);
 320
 321/**
 322 * cmp_connection_update - update the connection after a bus reset
 323 * @c: the connection manager
 324 *
 325 * This function must be called from the driver's .update handler to
 326 * reestablish any connection that might have been active.
 327 *
 328 * Returns zero on success, or a negative error code.  On an error, the
 329 * connection is broken and the caller must stop transmitting iso packets.
 330 */
 331int cmp_connection_update(struct cmp_connection *c)
 332{
 333        int err;
 334
 335        mutex_lock(&c->mutex);
 336
 337        if (!c->connected) {
 338                mutex_unlock(&c->mutex);
 339                return 0;
 340        }
 341
 342        err = fw_iso_resources_update(&c->resources);
 343        if (err < 0)
 344                goto err_unconnect;
 345
 346        if (c->direction == CMP_OUTPUT)
 347                err = pcr_modify(c, opcr_set_modify, pcr_set_check,
 348                                 SUCCEED_ON_BUS_RESET);
 349        else
 350                err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
 351                                 SUCCEED_ON_BUS_RESET);
 352
 353        if (err < 0)
 354                goto err_resources;
 355
 356        mutex_unlock(&c->mutex);
 357
 358        return 0;
 359
 360err_resources:
 361        fw_iso_resources_free(&c->resources);
 362err_unconnect:
 363        c->connected = false;
 364        mutex_unlock(&c->mutex);
 365
 366        return err;
 367}
 368EXPORT_SYMBOL(cmp_connection_update);
 369
 370static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
 371{
 372        return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
 373}
 374
 375/**
 376 * cmp_connection_break - break the connection to the target
 377 * @c: the connection manager
 378 *
 379 * This function deactives the connection in the target's input/output plug
 380 * control register, and frees the isochronous resources of the connection.
 381 * Before calling this function, the caller should cease transmitting packets.
 382 */
 383void cmp_connection_break(struct cmp_connection *c)
 384{
 385        int err;
 386
 387        mutex_lock(&c->mutex);
 388
 389        if (!c->connected) {
 390                mutex_unlock(&c->mutex);
 391                return;
 392        }
 393
 394        err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
 395        if (err < 0)
 396                cmp_error(c, "plug is still connected\n");
 397
 398        fw_iso_resources_free(&c->resources);
 399
 400        c->connected = false;
 401
 402        mutex_unlock(&c->mutex);
 403}
 404EXPORT_SYMBOL(cmp_connection_break);
 405