1 /* 2 * isochronous resources 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/export.h> 12 #include <linux/jiffies.h> 13 #include <linux/mutex.h> 14 #include <linux/sched.h> 15 #include <linux/spinlock.h> 16 #include "iso-resources.h" 17 18 /** 19 * fw_iso_resources_init - initializes a &struct fw_iso_resources 20 * @r: the resource manager to initialize 21 * @unit: the device unit for which the resources will be needed 22 * 23 * If the device does not support all channel numbers, change @r->channels_mask 24 * after calling this function. 25 */ 26 int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit) 27 { 28 r->channels_mask = ~0uLL; 29 r->unit = fw_unit_get(unit); 30 mutex_init(&r->mutex); 31 r->allocated = false; 32 33 return 0; 34 } 35 EXPORT_SYMBOL(fw_iso_resources_init); 36 37 /** 38 * fw_iso_resources_destroy - destroy a resource manager 39 * @r: the resource manager that is no longer needed 40 */ 41 void fw_iso_resources_destroy(struct fw_iso_resources *r) 42 { 43 WARN_ON(r->allocated); 44 mutex_destroy(&r->mutex); 45 fw_unit_put(r->unit); 46 } 47 EXPORT_SYMBOL(fw_iso_resources_destroy); 48 49 static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed) 50 { 51 unsigned int bytes, s400_bytes; 52 53 /* iso packets have three header quadlets and quadlet-aligned payload */ 54 bytes = 3 * 4 + ALIGN(max_payload_bytes, 4); 55 56 /* convert to bandwidth units (quadlets at S1600 = bytes at S400) */ 57 if (speed <= SCODE_400) 58 s400_bytes = bytes * (1 << (SCODE_400 - speed)); 59 else 60 s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400)); 61 62 return s400_bytes; 63 } 64 65 static int current_bandwidth_overhead(struct fw_card *card) 66 { 67 /* 68 * Under the usual pessimistic assumption (cable length 4.5 m), the 69 * isochronous overhead for N cables is 1.797 µs + N * 0.494 µs, or 70 * 88.3 + N * 24.3 in bandwidth units. 71 * 72 * The calculation below tries to deduce N from the current gap count. 73 * If the gap count has been optimized by measuring the actual packet 74 * transmission time, this derived overhead should be near the actual 75 * overhead as well. 76 */ 77 return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512; 78 } 79 80 static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card) 81 { 82 for (;;) { 83 s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64(); 84 if (delay <= 0) 85 return 0; 86 if (schedule_timeout_interruptible(delay) > 0) 87 return -ERESTARTSYS; 88 } 89 } 90 91 /** 92 * fw_iso_resources_allocate - allocate isochronous channel and bandwidth 93 * @r: the resource manager 94 * @max_payload_bytes: the amount of data (including CIP headers) per packet 95 * @speed: the speed (e.g., SCODE_400) at which the packets will be sent 96 * 97 * This function allocates one isochronous channel and enough bandwidth for the 98 * specified packet size. 99 * 100 * Returns the channel number that the caller must use for streaming, or 101 * a negative error code. Due to potentionally long delays, this function is 102 * interruptible and can return -ERESTARTSYS. On success, the caller is 103 * responsible for calling fw_iso_resources_update() on bus resets, and 104 * fw_iso_resources_free() when the resources are not longer needed. 105 */ 106 int fw_iso_resources_allocate(struct fw_iso_resources *r, 107 unsigned int max_payload_bytes, int speed) 108 { 109 struct fw_card *card = fw_parent_device(r->unit)->card; 110 int bandwidth, channel, err; 111 112 if (WARN_ON(r->allocated)) 113 return -EBADFD; 114 115 r->bandwidth = packet_bandwidth(max_payload_bytes, speed); 116 117 retry_after_bus_reset: 118 spin_lock_irq(&card->lock); 119 r->generation = card->generation; 120 r->bandwidth_overhead = current_bandwidth_overhead(card); 121 spin_unlock_irq(&card->lock); 122 123 err = wait_isoch_resource_delay_after_bus_reset(card); 124 if (err < 0) 125 return err; 126 127 mutex_lock(&r->mutex); 128 129 bandwidth = r->bandwidth + r->bandwidth_overhead; 130 fw_iso_resource_manage(card, r->generation, r->channels_mask, 131 &channel, &bandwidth, true); 132 if (channel == -EAGAIN) { 133 mutex_unlock(&r->mutex); 134 goto retry_after_bus_reset; 135 } 136 if (channel >= 0) { 137 r->channel = channel; 138 r->allocated = true; 139 } else { 140 if (channel == -EBUSY) 141 dev_err(&r->unit->device, 142 "isochronous resources exhausted\n"); 143 else 144 dev_err(&r->unit->device, 145 "isochronous resource allocation failed\n"); 146 } 147 148 mutex_unlock(&r->mutex); 149 150 return channel; 151 } 152 EXPORT_SYMBOL(fw_iso_resources_allocate); 153 154 /** 155 * fw_iso_resources_update - update resource allocations after a bus reset 156 * @r: the resource manager 157 * 158 * This function must be called from the driver's .update handler to reallocate 159 * any resources that were allocated before the bus reset. It is safe to call 160 * this function if no resources are currently allocated. 161 * 162 * Returns a negative error code on failure. If this happens, the caller must 163 * stop streaming. 164 */ 165 int fw_iso_resources_update(struct fw_iso_resources *r) 166 { 167 struct fw_card *card = fw_parent_device(r->unit)->card; 168 int bandwidth, channel; 169 170 mutex_lock(&r->mutex); 171 172 if (!r->allocated) { 173 mutex_unlock(&r->mutex); 174 return 0; 175 } 176 177 spin_lock_irq(&card->lock); 178 r->generation = card->generation; 179 r->bandwidth_overhead = current_bandwidth_overhead(card); 180 spin_unlock_irq(&card->lock); 181 182 bandwidth = r->bandwidth + r->bandwidth_overhead; 183 184 fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, 185 &channel, &bandwidth, true); 186 /* 187 * When another bus reset happens, pretend that the allocation 188 * succeeded; we will try again for the new generation later. 189 */ 190 if (channel < 0 && channel != -EAGAIN) { 191 r->allocated = false; 192 if (channel == -EBUSY) 193 dev_err(&r->unit->device, 194 "isochronous resources exhausted\n"); 195 else 196 dev_err(&r->unit->device, 197 "isochronous resource allocation failed\n"); 198 } 199 200 mutex_unlock(&r->mutex); 201 202 return channel; 203 } 204 EXPORT_SYMBOL(fw_iso_resources_update); 205 206 /** 207 * fw_iso_resources_free - frees allocated resources 208 * @r: the resource manager 209 * 210 * This function deallocates the channel and bandwidth, if allocated. 211 */ 212 void fw_iso_resources_free(struct fw_iso_resources *r) 213 { 214 struct fw_card *card = fw_parent_device(r->unit)->card; 215 int bandwidth, channel; 216 217 mutex_lock(&r->mutex); 218 219 if (r->allocated) { 220 bandwidth = r->bandwidth + r->bandwidth_overhead; 221 fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, 222 &channel, &bandwidth, false); 223 if (channel < 0) 224 dev_err(&r->unit->device, 225 "isochronous resource deallocation failed\n"); 226 227 r->allocated = false; 228 } 229 230 mutex_unlock(&r->mutex); 231 } 232 EXPORT_SYMBOL(fw_iso_resources_free); 233