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