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