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 40 enum bus_reset_handling { 41 ABORT_ON_BUS_RESET, 42 SUCCEED_ON_BUS_RESET, 43 }; 44 45 static __printf(2, 3) 46 void 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 57 static 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 65 static 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 73 static 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 */ 120 int 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 } 153 EXPORT_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 */ 160 int 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 } 174 EXPORT_SYMBOL(cmp_connection_check_used); 175 176 /** 177 * cmp_connection_destroy - free connection manager resources 178 * @c: the connection manager 179 */ 180 void 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 } 186 EXPORT_SYMBOL(cmp_connection_destroy); 187 188 189 static __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 200 static 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 219 static __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 247 static 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 */ 273 int 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 286 retry_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 312 err_resources: 313 fw_iso_resources_free(&c->resources); 314 err_mutex: 315 mutex_unlock(&c->mutex); 316 317 return err; 318 } 319 EXPORT_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 */ 331 int 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 360 err_resources: 361 fw_iso_resources_free(&c->resources); 362 err_unconnect: 363 c->connected = false; 364 mutex_unlock(&c->mutex); 365 366 return err; 367 } 368 EXPORT_SYMBOL(cmp_connection_update); 369 370 static __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 */ 383 void 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 } 404 EXPORT_SYMBOL(cmp_connection_break); 405