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