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#define IMPR_SPEED_MASK         0xc0000000
  18#define IMPR_SPEED_SHIFT        30
  19#define IMPR_XSPEED_MASK        0x00000060
  20#define IMPR_XSPEED_SHIFT       5
  21#define IMPR_PLUGS_MASK         0x0000001f
  22
  23#define IPCR_ONLINE             0x80000000
  24#define IPCR_BCAST_CONN         0x40000000
  25#define IPCR_P2P_CONN_MASK      0x3f000000
  26#define IPCR_P2P_CONN_SHIFT     24
  27#define IPCR_CHANNEL_MASK       0x003f0000
  28#define IPCR_CHANNEL_SHIFT      16
  29
  30enum bus_reset_handling {
  31        ABORT_ON_BUS_RESET,
  32        SUCCEED_ON_BUS_RESET,
  33};
  34
  35static __printf(2, 3)
  36void cmp_error(struct cmp_connection *c, const char *fmt, ...)
  37{
  38        va_list va;
  39
  40        va_start(va, fmt);
  41        dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
  42                'i', c->pcr_index, &(struct va_format){ fmt, &va });
  43        va_end(va);
  44}
  45
  46static int pcr_modify(struct cmp_connection *c,
  47                      __be32 (*modify)(struct cmp_connection *c, __be32 old),
  48                      int (*check)(struct cmp_connection *c, __be32 pcr),
  49                      enum bus_reset_handling bus_reset_handling)
  50{
  51        struct fw_device *device = fw_parent_device(c->resources.unit);
  52        int generation = c->resources.generation;
  53        int rcode, errors = 0;
  54        __be32 old_arg, buffer[2];
  55        int err;
  56
  57        buffer[0] = c->last_pcr_value;
  58        for (;;) {
  59                old_arg = buffer[0];
  60                buffer[1] = modify(c, buffer[0]);
  61
  62                rcode = fw_run_transaction(
  63                                device->card, TCODE_LOCK_COMPARE_SWAP,
  64                                device->node_id, generation, device->max_speed,
  65                                CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index),
  66                                buffer, 8);
  67
  68                if (rcode == RCODE_COMPLETE) {
  69                        if (buffer[0] == old_arg) /* success? */
  70                                break;
  71
  72                        if (check) {
  73                                err = check(c, buffer[0]);
  74                                if (err < 0)
  75                                        return err;
  76                        }
  77                } else if (rcode == RCODE_GENERATION)
  78                        goto bus_reset;
  79                else if (rcode_is_permanent_error(rcode) || ++errors >= 3)
  80                        goto io_error;
  81        }
  82        c->last_pcr_value = buffer[1];
  83
  84        return 0;
  85
  86io_error:
  87        cmp_error(c, "transaction failed: %s\n", fw_rcode_string(rcode));
  88        return -EIO;
  89
  90bus_reset:
  91        return bus_reset_handling == ABORT_ON_BUS_RESET ? -EAGAIN : 0;
  92}
  93
  94
  95/**
  96 * cmp_connection_init - initializes a connection manager
  97 * @c: the connection manager to initialize
  98 * @unit: a unit of the target device
  99 * @ipcr_index: the index of the iPCR on the target device
 100 */
 101int cmp_connection_init(struct cmp_connection *c,
 102                        struct fw_unit *unit,
 103                        unsigned int ipcr_index)
 104{
 105        __be32 impr_be;
 106        u32 impr;
 107        int err;
 108
 109        err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
 110                                 CSR_REGISTER_BASE + CSR_IMPR,
 111                                 &impr_be, 4);
 112        if (err < 0)
 113                return err;
 114        impr = be32_to_cpu(impr_be);
 115
 116        if (ipcr_index >= (impr & IMPR_PLUGS_MASK))
 117                return -EINVAL;
 118
 119        err = fw_iso_resources_init(&c->resources, unit);
 120        if (err < 0)
 121                return err;
 122
 123        c->connected = false;
 124        mutex_init(&c->mutex);
 125        c->last_pcr_value = cpu_to_be32(0x80000000);
 126        c->pcr_index = ipcr_index;
 127        c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT;
 128        if (c->max_speed == SCODE_BETA)
 129                c->max_speed += (impr & IMPR_XSPEED_MASK) >> IMPR_XSPEED_SHIFT;
 130
 131        return 0;
 132}
 133EXPORT_SYMBOL(cmp_connection_init);
 134
 135/**
 136 * cmp_connection_destroy - free connection manager resources
 137 * @c: the connection manager
 138 */
 139void cmp_connection_destroy(struct cmp_connection *c)
 140{
 141        WARN_ON(c->connected);
 142        mutex_destroy(&c->mutex);
 143        fw_iso_resources_destroy(&c->resources);
 144}
 145EXPORT_SYMBOL(cmp_connection_destroy);
 146
 147
 148static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
 149{
 150        ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN |
 151                             IPCR_P2P_CONN_MASK |
 152                             IPCR_CHANNEL_MASK);
 153        ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT);
 154        ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT);
 155
 156        return ipcr;
 157}
 158
 159static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr)
 160{
 161        if (ipcr & cpu_to_be32(IPCR_BCAST_CONN |
 162                               IPCR_P2P_CONN_MASK)) {
 163                cmp_error(c, "plug is already in use\n");
 164                return -EBUSY;
 165        }
 166        if (!(ipcr & cpu_to_be32(IPCR_ONLINE))) {
 167                cmp_error(c, "plug is not on-line\n");
 168                return -ECONNREFUSED;
 169        }
 170
 171        return 0;
 172}
 173
 174/**
 175 * cmp_connection_establish - establish a connection to the target
 176 * @c: the connection manager
 177 * @max_payload_bytes: the amount of data (including CIP headers) per packet
 178 *
 179 * This function establishes a point-to-point connection from the local
 180 * computer to the target by allocating isochronous resources (channel and
 181 * bandwidth) and setting the target's input plug control register.  When this
 182 * function succeeds, the caller is responsible for starting transmitting
 183 * packets.
 184 */
 185int cmp_connection_establish(struct cmp_connection *c,
 186                             unsigned int max_payload_bytes)
 187{
 188        int err;
 189
 190        if (WARN_ON(c->connected))
 191                return -EISCONN;
 192
 193        c->speed = min(c->max_speed,
 194                       fw_parent_device(c->resources.unit)->max_speed);
 195
 196        mutex_lock(&c->mutex);
 197
 198retry_after_bus_reset:
 199        err = fw_iso_resources_allocate(&c->resources,
 200                                        max_payload_bytes, c->speed);
 201        if (err < 0)
 202                goto err_mutex;
 203
 204        err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
 205                         ABORT_ON_BUS_RESET);
 206        if (err == -EAGAIN) {
 207                fw_iso_resources_free(&c->resources);
 208                goto retry_after_bus_reset;
 209        }
 210        if (err < 0)
 211                goto err_resources;
 212
 213        c->connected = true;
 214
 215        mutex_unlock(&c->mutex);
 216
 217        return 0;
 218
 219err_resources:
 220        fw_iso_resources_free(&c->resources);
 221err_mutex:
 222        mutex_unlock(&c->mutex);
 223
 224        return err;
 225}
 226EXPORT_SYMBOL(cmp_connection_establish);
 227
 228/**
 229 * cmp_connection_update - update the connection after a bus reset
 230 * @c: the connection manager
 231 *
 232 * This function must be called from the driver's .update handler to reestablish
 233 * any connection that might have been active.
 234 *
 235 * Returns zero on success, or a negative error code.  On an error, the
 236 * connection is broken and the caller must stop transmitting iso packets.
 237 */
 238int cmp_connection_update(struct cmp_connection *c)
 239{
 240        int err;
 241
 242        mutex_lock(&c->mutex);
 243
 244        if (!c->connected) {
 245                mutex_unlock(&c->mutex);
 246                return 0;
 247        }
 248
 249        err = fw_iso_resources_update(&c->resources);
 250        if (err < 0)
 251                goto err_unconnect;
 252
 253        err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
 254                         SUCCEED_ON_BUS_RESET);
 255        if (err < 0)
 256                goto err_resources;
 257
 258        mutex_unlock(&c->mutex);
 259
 260        return 0;
 261
 262err_resources:
 263        fw_iso_resources_free(&c->resources);
 264err_unconnect:
 265        c->connected = false;
 266        mutex_unlock(&c->mutex);
 267
 268        return err;
 269}
 270EXPORT_SYMBOL(cmp_connection_update);
 271
 272
 273static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr)
 274{
 275        return ipcr & ~cpu_to_be32(IPCR_BCAST_CONN | IPCR_P2P_CONN_MASK);
 276}
 277
 278/**
 279 * cmp_connection_break - break the connection to the target
 280 * @c: the connection manager
 281 *
 282 * This function deactives the connection in the target's input plug control
 283 * register, and frees the isochronous resources of the connection.  Before
 284 * calling this function, the caller should cease transmitting packets.
 285 */
 286void cmp_connection_break(struct cmp_connection *c)
 287{
 288        int err;
 289
 290        mutex_lock(&c->mutex);
 291
 292        if (!c->connected) {
 293                mutex_unlock(&c->mutex);
 294                return;
 295        }
 296
 297        err = pcr_modify(c, ipcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
 298        if (err < 0)
 299                cmp_error(c, "plug is still connected\n");
 300
 301        fw_iso_resources_free(&c->resources);
 302
 303        c->connected = false;
 304
 305        mutex_unlock(&c->mutex);
 306}
 307EXPORT_SYMBOL(cmp_connection_break);
 308
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.