Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * isochronous resources helper functions
0004  *
0005  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
0006  */
0007 
0008 #include <linux/device.h>
0009 #include <linux/firewire.h>
0010 #include <linux/firewire-constants.h>
0011 #include <linux/export.h>
0012 #include <linux/jiffies.h>
0013 #include <linux/mutex.h>
0014 #include <linux/sched.h>
0015 #include <linux/spinlock.h>
0016 #include "iso-resources.h"
0017 
0018 /**
0019  * fw_iso_resources_init - initializes a &struct fw_iso_resources
0020  * @r: the resource manager to initialize
0021  * @unit: the device unit for which the resources will be needed
0022  *
0023  * If the device does not support all channel numbers, change @r->channels_mask
0024  * after calling this function.
0025  */
0026 int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit)
0027 {
0028     r->channels_mask = ~0uLL;
0029     r->unit = unit;
0030     mutex_init(&r->mutex);
0031     r->allocated = false;
0032 
0033     return 0;
0034 }
0035 EXPORT_SYMBOL(fw_iso_resources_init);
0036 
0037 /**
0038  * fw_iso_resources_destroy - destroy a resource manager
0039  * @r: the resource manager that is no longer needed
0040  */
0041 void fw_iso_resources_destroy(struct fw_iso_resources *r)
0042 {
0043     WARN_ON(r->allocated);
0044     mutex_destroy(&r->mutex);
0045 }
0046 EXPORT_SYMBOL(fw_iso_resources_destroy);
0047 
0048 static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
0049 {
0050     unsigned int bytes, s400_bytes;
0051 
0052     /* iso packets have three header quadlets and quadlet-aligned payload */
0053     bytes = 3 * 4 + ALIGN(max_payload_bytes, 4);
0054 
0055     /* convert to bandwidth units (quadlets at S1600 = bytes at S400) */
0056     if (speed <= SCODE_400)
0057         s400_bytes = bytes * (1 << (SCODE_400 - speed));
0058     else
0059         s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400));
0060 
0061     return s400_bytes;
0062 }
0063 
0064 static int current_bandwidth_overhead(struct fw_card *card)
0065 {
0066     /*
0067      * Under the usual pessimistic assumption (cable length 4.5 m), the
0068      * isochronous overhead for N cables is 1.797 µs + N * 0.494 µs, or
0069      * 88.3 + N * 24.3 in bandwidth units.
0070      *
0071      * The calculation below tries to deduce N from the current gap count.
0072      * If the gap count has been optimized by measuring the actual packet
0073      * transmission time, this derived overhead should be near the actual
0074      * overhead as well.
0075      */
0076     return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512;
0077 }
0078 
0079 static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card)
0080 {
0081     for (;;) {
0082         s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64();
0083         if (delay <= 0)
0084             return 0;
0085         if (schedule_timeout_interruptible(delay) > 0)
0086             return -ERESTARTSYS;
0087     }
0088 }
0089 
0090 /**
0091  * fw_iso_resources_allocate - allocate isochronous channel and bandwidth
0092  * @r: the resource manager
0093  * @max_payload_bytes: the amount of data (including CIP headers) per packet
0094  * @speed: the speed (e.g., SCODE_400) at which the packets will be sent
0095  *
0096  * This function allocates one isochronous channel and enough bandwidth for the
0097  * specified packet size.
0098  *
0099  * Returns the channel number that the caller must use for streaming, or
0100  * a negative error code.  Due to potentionally long delays, this function is
0101  * interruptible and can return -ERESTARTSYS.  On success, the caller is
0102  * responsible for calling fw_iso_resources_update() on bus resets, and
0103  * fw_iso_resources_free() when the resources are not longer needed.
0104  */
0105 int fw_iso_resources_allocate(struct fw_iso_resources *r,
0106                   unsigned int max_payload_bytes, int speed)
0107 {
0108     struct fw_card *card = fw_parent_device(r->unit)->card;
0109     int bandwidth, channel, err;
0110 
0111     if (WARN_ON(r->allocated))
0112         return -EBADFD;
0113 
0114     r->bandwidth = packet_bandwidth(max_payload_bytes, speed);
0115 
0116 retry_after_bus_reset:
0117     spin_lock_irq(&card->lock);
0118     r->generation = card->generation;
0119     r->bandwidth_overhead = current_bandwidth_overhead(card);
0120     spin_unlock_irq(&card->lock);
0121 
0122     err = wait_isoch_resource_delay_after_bus_reset(card);
0123     if (err < 0)
0124         return err;
0125 
0126     mutex_lock(&r->mutex);
0127 
0128     bandwidth = r->bandwidth + r->bandwidth_overhead;
0129     fw_iso_resource_manage(card, r->generation, r->channels_mask,
0130                    &channel, &bandwidth, true);
0131     if (channel == -EAGAIN) {
0132         mutex_unlock(&r->mutex);
0133         goto retry_after_bus_reset;
0134     }
0135     if (channel >= 0) {
0136         r->channel = channel;
0137         r->allocated = true;
0138     } else {
0139         if (channel == -EBUSY)
0140             dev_err(&r->unit->device,
0141                 "isochronous resources exhausted\n");
0142         else
0143             dev_err(&r->unit->device,
0144                 "isochronous resource allocation failed\n");
0145     }
0146 
0147     mutex_unlock(&r->mutex);
0148 
0149     return channel;
0150 }
0151 EXPORT_SYMBOL(fw_iso_resources_allocate);
0152 
0153 /**
0154  * fw_iso_resources_update - update resource allocations after a bus reset
0155  * @r: the resource manager
0156  *
0157  * This function must be called from the driver's .update handler to reallocate
0158  * any resources that were allocated before the bus reset.  It is safe to call
0159  * this function if no resources are currently allocated.
0160  *
0161  * Returns a negative error code on failure.  If this happens, the caller must
0162  * stop streaming.
0163  */
0164 int fw_iso_resources_update(struct fw_iso_resources *r)
0165 {
0166     struct fw_card *card = fw_parent_device(r->unit)->card;
0167     int bandwidth, channel;
0168 
0169     mutex_lock(&r->mutex);
0170 
0171     if (!r->allocated) {
0172         mutex_unlock(&r->mutex);
0173         return 0;
0174     }
0175 
0176     spin_lock_irq(&card->lock);
0177     r->generation = card->generation;
0178     r->bandwidth_overhead = current_bandwidth_overhead(card);
0179     spin_unlock_irq(&card->lock);
0180 
0181     bandwidth = r->bandwidth + r->bandwidth_overhead;
0182 
0183     fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
0184                    &channel, &bandwidth, true);
0185     /*
0186      * When another bus reset happens, pretend that the allocation
0187      * succeeded; we will try again for the new generation later.
0188      */
0189     if (channel < 0 && channel != -EAGAIN) {
0190         r->allocated = false;
0191         if (channel == -EBUSY)
0192             dev_err(&r->unit->device,
0193                 "isochronous resources exhausted\n");
0194         else
0195             dev_err(&r->unit->device,
0196                 "isochronous resource allocation failed\n");
0197     }
0198 
0199     mutex_unlock(&r->mutex);
0200 
0201     return channel;
0202 }
0203 EXPORT_SYMBOL(fw_iso_resources_update);
0204 
0205 /**
0206  * fw_iso_resources_free - frees allocated resources
0207  * @r: the resource manager
0208  *
0209  * This function deallocates the channel and bandwidth, if allocated.
0210  */
0211 void fw_iso_resources_free(struct fw_iso_resources *r)
0212 {
0213     struct fw_card *card;
0214     int bandwidth, channel;
0215 
0216     /* Not initialized. */
0217     if (r->unit == NULL)
0218         return;
0219     card = fw_parent_device(r->unit)->card;
0220 
0221     mutex_lock(&r->mutex);
0222 
0223     if (r->allocated) {
0224         bandwidth = r->bandwidth + r->bandwidth_overhead;
0225         fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
0226                        &channel, &bandwidth, false);
0227         if (channel < 0)
0228             dev_err(&r->unit->device,
0229                 "isochronous resource deallocation failed\n");
0230 
0231         r->allocated = false;
0232     }
0233 
0234     mutex_unlock(&r->mutex);
0235 }
0236 EXPORT_SYMBOL(fw_iso_resources_free);