root/sound/firewire/iso-resources.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fw_iso_resources_init
  2. fw_iso_resources_destroy
  3. packet_bandwidth
  4. current_bandwidth_overhead
  5. wait_isoch_resource_delay_after_bus_reset
  6. fw_iso_resources_allocate
  7. fw_iso_resources_update
  8. fw_iso_resources_free

   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);

/* [<][>][^][v][top][bottom][index][help] */