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 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 * @pcr_index: the index of the iPCR/oPCR on the target device 118 */ 119 int cmp_connection_init(struct cmp_connection *c, 120 struct fw_unit *unit, 121 enum cmp_direction direction, 122 unsigned int pcr_index) 123 { 124 __be32 mpr_be; 125 u32 mpr; 126 int err; 127 128 c->direction = direction; 129 err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, 130 mpr_address(c), &mpr_be, 4, 0); 131 if (err < 0) 132 return err; 133 mpr = be32_to_cpu(mpr_be); 134 135 if (pcr_index >= (mpr & MPR_PLUGS_MASK)) 136 return -EINVAL; 137 138 err = fw_iso_resources_init(&c->resources, unit); 139 if (err < 0) 140 return err; 141 142 c->connected = false; 143 mutex_init(&c->mutex); 144 c->last_pcr_value = cpu_to_be32(0x80000000); 145 c->pcr_index = pcr_index; 146 c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT; 147 if (c->max_speed == SCODE_BETA) 148 c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT; 149 150 return 0; 151 } 152 EXPORT_SYMBOL(cmp_connection_init); 153 154 /** 155 * cmp_connection_check_used - check connection is already esablished or not 156 * @c: the connection manager to be checked 157 */ 158 int cmp_connection_check_used(struct cmp_connection *c, bool *used) 159 { 160 __be32 pcr; 161 int err; 162 163 err = snd_fw_transaction( 164 c->resources.unit, TCODE_READ_QUADLET_REQUEST, 165 pcr_address(c), &pcr, 4, 0); 166 if (err >= 0) 167 *used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN | 168 PCR_P2P_CONN_MASK)); 169 170 return err; 171 } 172 EXPORT_SYMBOL(cmp_connection_check_used); 173 174 /** 175 * cmp_connection_destroy - free connection manager resources 176 * @c: the connection manager 177 */ 178 void cmp_connection_destroy(struct cmp_connection *c) 179 { 180 WARN_ON(c->connected); 181 mutex_destroy(&c->mutex); 182 fw_iso_resources_destroy(&c->resources); 183 } 184 EXPORT_SYMBOL(cmp_connection_destroy); 185 186 187 static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) 188 { 189 ipcr &= ~cpu_to_be32(PCR_BCAST_CONN | 190 PCR_P2P_CONN_MASK | 191 PCR_CHANNEL_MASK); 192 ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT); 193 ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT); 194 195 return ipcr; 196 } 197 198 static int get_overhead_id(struct cmp_connection *c) 199 { 200 int id; 201 202 /* 203 * apply "oPCR overhead ID encoding" 204 * the encoding table can convert up to 512. 205 * here the value over 512 is converted as the same way as 512. 206 */ 207 for (id = 1; id < 16; id++) { 208 if (c->resources.bandwidth_overhead < (id << 5)) 209 break; 210 } 211 if (id == 16) 212 id = 0; 213 214 return id; 215 } 216 217 static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr) 218 { 219 unsigned int spd, xspd; 220 221 /* generate speed and extended speed field value */ 222 if (c->speed > SCODE_400) { 223 spd = SCODE_800; 224 xspd = c->speed - SCODE_800; 225 } else { 226 spd = c->speed; 227 xspd = 0; 228 } 229 230 opcr &= ~cpu_to_be32(PCR_BCAST_CONN | 231 PCR_P2P_CONN_MASK | 232 OPCR_XSPEED_MASK | 233 PCR_CHANNEL_MASK | 234 OPCR_SPEED_MASK | 235 OPCR_OVERHEAD_ID_MASK); 236 opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT); 237 opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT); 238 opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT); 239 opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT); 240 opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT); 241 242 return opcr; 243 } 244 245 static int pcr_set_check(struct cmp_connection *c, __be32 pcr) 246 { 247 if (pcr & cpu_to_be32(PCR_BCAST_CONN | 248 PCR_P2P_CONN_MASK)) { 249 cmp_error(c, "plug is already in use\n"); 250 return -EBUSY; 251 } 252 if (!(pcr & cpu_to_be32(PCR_ONLINE))) { 253 cmp_error(c, "plug is not on-line\n"); 254 return -ECONNREFUSED; 255 } 256 257 return 0; 258 } 259 260 /** 261 * cmp_connection_establish - establish a connection to the target 262 * @c: the connection manager 263 * @max_payload_bytes: the amount of data (including CIP headers) per packet 264 * 265 * This function establishes a point-to-point connection from the local 266 * computer to the target by allocating isochronous resources (channel and 267 * bandwidth) and setting the target's input/output plug control register. 268 * When this function succeeds, the caller is responsible for starting 269 * transmitting packets. 270 */ 271 int cmp_connection_establish(struct cmp_connection *c, 272 unsigned int max_payload_bytes) 273 { 274 int err; 275 276 if (WARN_ON(c->connected)) 277 return -EISCONN; 278 279 c->speed = min(c->max_speed, 280 fw_parent_device(c->resources.unit)->max_speed); 281 282 mutex_lock(&c->mutex); 283 284 retry_after_bus_reset: 285 err = fw_iso_resources_allocate(&c->resources, 286 max_payload_bytes, c->speed); 287 if (err < 0) 288 goto err_mutex; 289 290 if (c->direction == CMP_OUTPUT) 291 err = pcr_modify(c, opcr_set_modify, pcr_set_check, 292 ABORT_ON_BUS_RESET); 293 else 294 err = pcr_modify(c, ipcr_set_modify, pcr_set_check, 295 ABORT_ON_BUS_RESET); 296 297 if (err == -EAGAIN) { 298 fw_iso_resources_free(&c->resources); 299 goto retry_after_bus_reset; 300 } 301 if (err < 0) 302 goto err_resources; 303 304 c->connected = true; 305 306 mutex_unlock(&c->mutex); 307 308 return 0; 309 310 err_resources: 311 fw_iso_resources_free(&c->resources); 312 err_mutex: 313 mutex_unlock(&c->mutex); 314 315 return err; 316 } 317 EXPORT_SYMBOL(cmp_connection_establish); 318 319 /** 320 * cmp_connection_update - update the connection after a bus reset 321 * @c: the connection manager 322 * 323 * This function must be called from the driver's .update handler to 324 * reestablish any connection that might have been active. 325 * 326 * Returns zero on success, or a negative error code. On an error, the 327 * connection is broken and the caller must stop transmitting iso packets. 328 */ 329 int cmp_connection_update(struct cmp_connection *c) 330 { 331 int err; 332 333 mutex_lock(&c->mutex); 334 335 if (!c->connected) { 336 mutex_unlock(&c->mutex); 337 return 0; 338 } 339 340 err = fw_iso_resources_update(&c->resources); 341 if (err < 0) 342 goto err_unconnect; 343 344 if (c->direction == CMP_OUTPUT) 345 err = pcr_modify(c, opcr_set_modify, pcr_set_check, 346 SUCCEED_ON_BUS_RESET); 347 else 348 err = pcr_modify(c, ipcr_set_modify, pcr_set_check, 349 SUCCEED_ON_BUS_RESET); 350 351 if (err < 0) 352 goto err_resources; 353 354 mutex_unlock(&c->mutex); 355 356 return 0; 357 358 err_resources: 359 fw_iso_resources_free(&c->resources); 360 err_unconnect: 361 c->connected = false; 362 mutex_unlock(&c->mutex); 363 364 return err; 365 } 366 EXPORT_SYMBOL(cmp_connection_update); 367 368 static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr) 369 { 370 return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK); 371 } 372 373 /** 374 * cmp_connection_break - break the connection to the target 375 * @c: the connection manager 376 * 377 * This function deactives the connection in the target's input/output plug 378 * control register, and frees the isochronous resources of the connection. 379 * Before calling this function, the caller should cease transmitting packets. 380 */ 381 void cmp_connection_break(struct cmp_connection *c) 382 { 383 int err; 384 385 mutex_lock(&c->mutex); 386 387 if (!c->connected) { 388 mutex_unlock(&c->mutex); 389 return; 390 } 391 392 err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET); 393 if (err < 0) 394 cmp_error(c, "plug is still connected\n"); 395 396 fw_iso_resources_free(&c->resources); 397 398 c->connected = false; 399 400 mutex_unlock(&c->mutex); 401 } 402 EXPORT_SYMBOL(cmp_connection_break); 403