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 int cmp_connection_reserve(struct cmp_connection *c, 189 unsigned int max_payload_bytes) 190 { 191 int err; 192 193 mutex_lock(&c->mutex); 194 195 if (WARN_ON(c->resources.allocated)) { 196 err = -EBUSY; 197 goto end; 198 } 199 200 c->speed = min(c->max_speed, 201 fw_parent_device(c->resources.unit)->max_speed); 202 203 err = fw_iso_resources_allocate(&c->resources, max_payload_bytes, 204 c->speed); 205 end: 206 mutex_unlock(&c->mutex); 207 208 return err; 209 } 210 EXPORT_SYMBOL(cmp_connection_reserve); 211 212 void cmp_connection_release(struct cmp_connection *c) 213 { 214 mutex_lock(&c->mutex); 215 fw_iso_resources_free(&c->resources); 216 mutex_unlock(&c->mutex); 217 } 218 EXPORT_SYMBOL(cmp_connection_release); 219 220 static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) 221 { 222 ipcr &= ~cpu_to_be32(PCR_BCAST_CONN | 223 PCR_P2P_CONN_MASK | 224 PCR_CHANNEL_MASK); 225 ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT); 226 ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT); 227 228 return ipcr; 229 } 230 231 static int get_overhead_id(struct cmp_connection *c) 232 { 233 int id; 234 235 /* 236 * apply "oPCR overhead ID encoding" 237 * the encoding table can convert up to 512. 238 * here the value over 512 is converted as the same way as 512. 239 */ 240 for (id = 1; id < 16; id++) { 241 if (c->resources.bandwidth_overhead < (id << 5)) 242 break; 243 } 244 if (id == 16) 245 id = 0; 246 247 return id; 248 } 249 250 static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr) 251 { 252 unsigned int spd, xspd; 253 254 /* generate speed and extended speed field value */ 255 if (c->speed > SCODE_400) { 256 spd = SCODE_800; 257 xspd = c->speed - SCODE_800; 258 } else { 259 spd = c->speed; 260 xspd = 0; 261 } 262 263 opcr &= ~cpu_to_be32(PCR_BCAST_CONN | 264 PCR_P2P_CONN_MASK | 265 OPCR_XSPEED_MASK | 266 PCR_CHANNEL_MASK | 267 OPCR_SPEED_MASK | 268 OPCR_OVERHEAD_ID_MASK); 269 opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT); 270 opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT); 271 opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT); 272 opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT); 273 opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT); 274 275 return opcr; 276 } 277 278 static int pcr_set_check(struct cmp_connection *c, __be32 pcr) 279 { 280 if (pcr & cpu_to_be32(PCR_BCAST_CONN | 281 PCR_P2P_CONN_MASK)) { 282 cmp_error(c, "plug is already in use\n"); 283 return -EBUSY; 284 } 285 if (!(pcr & cpu_to_be32(PCR_ONLINE))) { 286 cmp_error(c, "plug is not on-line\n"); 287 return -ECONNREFUSED; 288 } 289 290 return 0; 291 } 292 293 /** 294 * cmp_connection_establish - establish a connection to the target 295 * @c: the connection manager 296 * 297 * This function establishes a point-to-point connection from the local 298 * computer to the target by allocating isochronous resources (channel and 299 * bandwidth) and setting the target's input/output plug control register. 300 * When this function succeeds, the caller is responsible for starting 301 * transmitting packets. 302 */ 303 int cmp_connection_establish(struct cmp_connection *c) 304 { 305 int err; 306 307 mutex_lock(&c->mutex); 308 309 if (WARN_ON(c->connected)) { 310 mutex_unlock(&c->mutex); 311 return -EISCONN; 312 } 313 314 retry_after_bus_reset: 315 if (c->direction == CMP_OUTPUT) 316 err = pcr_modify(c, opcr_set_modify, pcr_set_check, 317 ABORT_ON_BUS_RESET); 318 else 319 err = pcr_modify(c, ipcr_set_modify, pcr_set_check, 320 ABORT_ON_BUS_RESET); 321 322 if (err == -EAGAIN) { 323 err = fw_iso_resources_update(&c->resources); 324 if (err >= 0) 325 goto retry_after_bus_reset; 326 } 327 if (err >= 0) 328 c->connected = true; 329 330 mutex_unlock(&c->mutex); 331 332 return err; 333 } 334 EXPORT_SYMBOL(cmp_connection_establish); 335 336 static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr) 337 { 338 return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK); 339 } 340 341 /** 342 * cmp_connection_break - break the connection to the target 343 * @c: the connection manager 344 * 345 * This function deactives the connection in the target's input/output plug 346 * control register, and frees the isochronous resources of the connection. 347 * Before calling this function, the caller should cease transmitting packets. 348 */ 349 void cmp_connection_break(struct cmp_connection *c) 350 { 351 int err; 352 353 mutex_lock(&c->mutex); 354 355 if (!c->connected) { 356 mutex_unlock(&c->mutex); 357 return; 358 } 359 360 err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET); 361 if (err < 0) 362 cmp_error(c, "plug is still connected\n"); 363 364 c->connected = false; 365 366 mutex_unlock(&c->mutex); 367 } 368 EXPORT_SYMBOL(cmp_connection_break); 369