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 30 enum bus_reset_handling { 31 ABORT_ON_BUS_RESET, 32 SUCCEED_ON_BUS_RESET, 33 }; 34 35 static __printf(2, 3) 36 void 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 46 static 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 */ 93 int 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 } 125 EXPORT_SYMBOL(cmp_connection_init); 126 127 /** 128 * cmp_connection_destroy - free connection manager resources 129 * @c: the connection manager 130 */ 131 void 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 } 137 EXPORT_SYMBOL(cmp_connection_destroy); 138 139 140 static __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 151 static 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 */ 177 int 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 190 retry_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 211 err_resources: 212 fw_iso_resources_free(&c->resources); 213 err_mutex: 214 mutex_unlock(&c->mutex); 215 216 return err; 217 } 218 EXPORT_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 */ 230 int 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 254 err_resources: 255 fw_iso_resources_free(&c->resources); 256 err_unconnect: 257 c->connected = false; 258 mutex_unlock(&c->mutex); 259 260 return err; 261 } 262 EXPORT_SYMBOL(cmp_connection_update); 263 264 265 static __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 */ 278 void 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 } 299 EXPORT_SYMBOL(cmp_connection_break); 300