~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/firewire/iso-resources.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php