162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Isochronous I/O functionality: 462306a36Sopenharmony_ci * - Isochronous DMA context management 562306a36Sopenharmony_ci * - Isochronous bus resource management (channels, bandwidth), client side 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2006 Kristian Hoegsberg <krh@bitplanet.net> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/firewire.h> 1362306a36Sopenharmony_ci#include <linux/firewire-constants.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/spinlock.h> 1862306a36Sopenharmony_ci#include <linux/vmalloc.h> 1962306a36Sopenharmony_ci#include <linux/export.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <asm/byteorder.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "core.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * Isochronous DMA context management 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciint fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci int i; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci buffer->page_count = 0; 3462306a36Sopenharmony_ci buffer->page_count_mapped = 0; 3562306a36Sopenharmony_ci buffer->pages = kmalloc_array(page_count, sizeof(buffer->pages[0]), 3662306a36Sopenharmony_ci GFP_KERNEL); 3762306a36Sopenharmony_ci if (buffer->pages == NULL) 3862306a36Sopenharmony_ci return -ENOMEM; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci for (i = 0; i < page_count; i++) { 4162306a36Sopenharmony_ci buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); 4262306a36Sopenharmony_ci if (buffer->pages[i] == NULL) 4362306a36Sopenharmony_ci break; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci buffer->page_count = i; 4662306a36Sopenharmony_ci if (i < page_count) { 4762306a36Sopenharmony_ci fw_iso_buffer_destroy(buffer, NULL); 4862306a36Sopenharmony_ci return -ENOMEM; 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci return 0; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciint fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card, 5562306a36Sopenharmony_ci enum dma_data_direction direction) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci dma_addr_t address; 5862306a36Sopenharmony_ci int i; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci buffer->direction = direction; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci for (i = 0; i < buffer->page_count; i++) { 6362306a36Sopenharmony_ci address = dma_map_page(card->device, buffer->pages[i], 6462306a36Sopenharmony_ci 0, PAGE_SIZE, direction); 6562306a36Sopenharmony_ci if (dma_mapping_error(card->device, address)) 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci set_page_private(buffer->pages[i], address); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci buffer->page_count_mapped = i; 7162306a36Sopenharmony_ci if (i < buffer->page_count) 7262306a36Sopenharmony_ci return -ENOMEM; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciint fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, 7862306a36Sopenharmony_ci int page_count, enum dma_data_direction direction) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci int ret; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ret = fw_iso_buffer_alloc(buffer, page_count); 8362306a36Sopenharmony_ci if (ret < 0) 8462306a36Sopenharmony_ci return ret; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ret = fw_iso_buffer_map_dma(buffer, card, direction); 8762306a36Sopenharmony_ci if (ret < 0) 8862306a36Sopenharmony_ci fw_iso_buffer_destroy(buffer, card); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return ret; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ciEXPORT_SYMBOL(fw_iso_buffer_init); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_civoid fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, 9562306a36Sopenharmony_ci struct fw_card *card) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int i; 9862306a36Sopenharmony_ci dma_addr_t address; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci for (i = 0; i < buffer->page_count_mapped; i++) { 10162306a36Sopenharmony_ci address = page_private(buffer->pages[i]); 10262306a36Sopenharmony_ci dma_unmap_page(card->device, address, 10362306a36Sopenharmony_ci PAGE_SIZE, buffer->direction); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci for (i = 0; i < buffer->page_count; i++) 10662306a36Sopenharmony_ci __free_page(buffer->pages[i]); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci kfree(buffer->pages); 10962306a36Sopenharmony_ci buffer->pages = NULL; 11062306a36Sopenharmony_ci buffer->page_count = 0; 11162306a36Sopenharmony_ci buffer->page_count_mapped = 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciEXPORT_SYMBOL(fw_iso_buffer_destroy); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Convert DMA address to offset into virtually contiguous buffer. */ 11662306a36Sopenharmony_cisize_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci size_t i; 11962306a36Sopenharmony_ci dma_addr_t address; 12062306a36Sopenharmony_ci ssize_t offset; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci for (i = 0; i < buffer->page_count; i++) { 12362306a36Sopenharmony_ci address = page_private(buffer->pages[i]); 12462306a36Sopenharmony_ci offset = (ssize_t)completed - (ssize_t)address; 12562306a36Sopenharmony_ci if (offset > 0 && offset <= PAGE_SIZE) 12662306a36Sopenharmony_ci return (i << PAGE_SHIFT) + offset; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistruct fw_iso_context *fw_iso_context_create(struct fw_card *card, 13362306a36Sopenharmony_ci int type, int channel, int speed, size_t header_size, 13462306a36Sopenharmony_ci fw_iso_callback_t callback, void *callback_data) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct fw_iso_context *ctx; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ctx = card->driver->allocate_iso_context(card, 13962306a36Sopenharmony_ci type, channel, header_size); 14062306a36Sopenharmony_ci if (IS_ERR(ctx)) 14162306a36Sopenharmony_ci return ctx; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ctx->card = card; 14462306a36Sopenharmony_ci ctx->type = type; 14562306a36Sopenharmony_ci ctx->channel = channel; 14662306a36Sopenharmony_ci ctx->speed = speed; 14762306a36Sopenharmony_ci ctx->header_size = header_size; 14862306a36Sopenharmony_ci ctx->callback.sc = callback; 14962306a36Sopenharmony_ci ctx->callback_data = callback_data; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return ctx; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_create); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_civoid fw_iso_context_destroy(struct fw_iso_context *ctx) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci ctx->card->driver->free_iso_context(ctx); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_destroy); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ciint fw_iso_context_start(struct fw_iso_context *ctx, 16262306a36Sopenharmony_ci int cycle, int sync, int tags) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci return ctx->card->driver->start_iso(ctx, cycle, sync, tags); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_start); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciint fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci return ctx->card->driver->set_iso_channels(ctx, channels); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciint fw_iso_context_queue(struct fw_iso_context *ctx, 17462306a36Sopenharmony_ci struct fw_iso_packet *packet, 17562306a36Sopenharmony_ci struct fw_iso_buffer *buffer, 17662306a36Sopenharmony_ci unsigned long payload) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci return ctx->card->driver->queue_iso(ctx, packet, buffer, payload); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_queue); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_civoid fw_iso_context_queue_flush(struct fw_iso_context *ctx) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci ctx->card->driver->flush_queue_iso(ctx); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_queue_flush); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ciint fw_iso_context_flush_completions(struct fw_iso_context *ctx) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci return ctx->card->driver->flush_iso_completions(ctx); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_flush_completions); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ciint fw_iso_context_stop(struct fw_iso_context *ctx) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci return ctx->card->driver->stop_iso(ctx); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ciEXPORT_SYMBOL(fw_iso_context_stop); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* 20162306a36Sopenharmony_ci * Isochronous bus resource management (channels, bandwidth), client side 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int manage_bandwidth(struct fw_card *card, int irm_id, int generation, 20562306a36Sopenharmony_ci int bandwidth, bool allocate) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci int try, new, old = allocate ? BANDWIDTH_AVAILABLE_INITIAL : 0; 20862306a36Sopenharmony_ci __be32 data[2]; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * On a 1394a IRM with low contention, try < 1 is enough. 21262306a36Sopenharmony_ci * On a 1394-1995 IRM, we need at least try < 2. 21362306a36Sopenharmony_ci * Let's just do try < 5. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci for (try = 0; try < 5; try++) { 21662306a36Sopenharmony_ci new = allocate ? old - bandwidth : old + bandwidth; 21762306a36Sopenharmony_ci if (new < 0 || new > BANDWIDTH_AVAILABLE_INITIAL) 21862306a36Sopenharmony_ci return -EBUSY; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci data[0] = cpu_to_be32(old); 22162306a36Sopenharmony_ci data[1] = cpu_to_be32(new); 22262306a36Sopenharmony_ci switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, 22362306a36Sopenharmony_ci irm_id, generation, SCODE_100, 22462306a36Sopenharmony_ci CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE, 22562306a36Sopenharmony_ci data, 8)) { 22662306a36Sopenharmony_ci case RCODE_GENERATION: 22762306a36Sopenharmony_ci /* A generation change frees all bandwidth. */ 22862306a36Sopenharmony_ci return allocate ? -EAGAIN : bandwidth; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci case RCODE_COMPLETE: 23162306a36Sopenharmony_ci if (be32_to_cpup(data) == old) 23262306a36Sopenharmony_ci return bandwidth; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci old = be32_to_cpup(data); 23562306a36Sopenharmony_ci /* Fall through. */ 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return -EIO; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic int manage_channel(struct fw_card *card, int irm_id, int generation, 24362306a36Sopenharmony_ci u32 channels_mask, u64 offset, bool allocate) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci __be32 bit, all, old; 24662306a36Sopenharmony_ci __be32 data[2]; 24762306a36Sopenharmony_ci int channel, ret = -EIO, retry = 5; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci old = all = allocate ? cpu_to_be32(~0) : 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci for (channel = 0; channel < 32; channel++) { 25262306a36Sopenharmony_ci if (!(channels_mask & 1 << channel)) 25362306a36Sopenharmony_ci continue; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci ret = -EBUSY; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci bit = cpu_to_be32(1 << (31 - channel)); 25862306a36Sopenharmony_ci if ((old & bit) != (all & bit)) 25962306a36Sopenharmony_ci continue; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci data[0] = old; 26262306a36Sopenharmony_ci data[1] = old ^ bit; 26362306a36Sopenharmony_ci switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, 26462306a36Sopenharmony_ci irm_id, generation, SCODE_100, 26562306a36Sopenharmony_ci offset, data, 8)) { 26662306a36Sopenharmony_ci case RCODE_GENERATION: 26762306a36Sopenharmony_ci /* A generation change frees all channels. */ 26862306a36Sopenharmony_ci return allocate ? -EAGAIN : channel; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci case RCODE_COMPLETE: 27162306a36Sopenharmony_ci if (data[0] == old) 27262306a36Sopenharmony_ci return channel; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci old = data[0]; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Is the IRM 1394a-2000 compliant? */ 27762306a36Sopenharmony_ci if ((data[0] & bit) == (data[1] & bit)) 27862306a36Sopenharmony_ci continue; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci fallthrough; /* It's a 1394-1995 IRM, retry */ 28162306a36Sopenharmony_ci default: 28262306a36Sopenharmony_ci if (retry) { 28362306a36Sopenharmony_ci retry--; 28462306a36Sopenharmony_ci channel--; 28562306a36Sopenharmony_ci } else { 28662306a36Sopenharmony_ci ret = -EIO; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return ret; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void deallocate_channel(struct fw_card *card, int irm_id, 29562306a36Sopenharmony_ci int generation, int channel) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci u32 mask; 29862306a36Sopenharmony_ci u64 offset; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci mask = channel < 32 ? 1 << channel : 1 << (channel - 32); 30162306a36Sopenharmony_ci offset = channel < 32 ? CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI : 30262306a36Sopenharmony_ci CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci manage_channel(card, irm_id, generation, mask, offset, false); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/** 30862306a36Sopenharmony_ci * fw_iso_resource_manage() - Allocate or deallocate a channel and/or bandwidth 30962306a36Sopenharmony_ci * @card: card interface for this action 31062306a36Sopenharmony_ci * @generation: bus generation 31162306a36Sopenharmony_ci * @channels_mask: bitmask for channel allocation 31262306a36Sopenharmony_ci * @channel: pointer for returning channel allocation result 31362306a36Sopenharmony_ci * @bandwidth: pointer for returning bandwidth allocation result 31462306a36Sopenharmony_ci * @allocate: whether to allocate (true) or deallocate (false) 31562306a36Sopenharmony_ci * 31662306a36Sopenharmony_ci * In parameters: card, generation, channels_mask, bandwidth, allocate 31762306a36Sopenharmony_ci * Out parameters: channel, bandwidth 31862306a36Sopenharmony_ci * 31962306a36Sopenharmony_ci * This function blocks (sleeps) during communication with the IRM. 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * Allocates or deallocates at most one channel out of channels_mask. 32262306a36Sopenharmony_ci * channels_mask is a bitfield with MSB for channel 63 and LSB for channel 0. 32362306a36Sopenharmony_ci * (Note, the IRM's CHANNELS_AVAILABLE is a big-endian bitfield with MSB for 32462306a36Sopenharmony_ci * channel 0 and LSB for channel 63.) 32562306a36Sopenharmony_ci * Allocates or deallocates as many bandwidth allocation units as specified. 32662306a36Sopenharmony_ci * 32762306a36Sopenharmony_ci * Returns channel < 0 if no channel was allocated or deallocated. 32862306a36Sopenharmony_ci * Returns bandwidth = 0 if no bandwidth was allocated or deallocated. 32962306a36Sopenharmony_ci * 33062306a36Sopenharmony_ci * If generation is stale, deallocations succeed but allocations fail with 33162306a36Sopenharmony_ci * channel = -EAGAIN. 33262306a36Sopenharmony_ci * 33362306a36Sopenharmony_ci * If channel allocation fails, no bandwidth will be allocated either. 33462306a36Sopenharmony_ci * If bandwidth allocation fails, no channel will be allocated either. 33562306a36Sopenharmony_ci * But deallocations of channel and bandwidth are tried independently 33662306a36Sopenharmony_ci * of each other's success. 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_civoid fw_iso_resource_manage(struct fw_card *card, int generation, 33962306a36Sopenharmony_ci u64 channels_mask, int *channel, int *bandwidth, 34062306a36Sopenharmony_ci bool allocate) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci u32 channels_hi = channels_mask; /* channels 31...0 */ 34362306a36Sopenharmony_ci u32 channels_lo = channels_mask >> 32; /* channels 63...32 */ 34462306a36Sopenharmony_ci int irm_id, ret, c = -EINVAL; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci spin_lock_irq(&card->lock); 34762306a36Sopenharmony_ci irm_id = card->irm_node->node_id; 34862306a36Sopenharmony_ci spin_unlock_irq(&card->lock); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (channels_hi) 35162306a36Sopenharmony_ci c = manage_channel(card, irm_id, generation, channels_hi, 35262306a36Sopenharmony_ci CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI, 35362306a36Sopenharmony_ci allocate); 35462306a36Sopenharmony_ci if (channels_lo && c < 0) { 35562306a36Sopenharmony_ci c = manage_channel(card, irm_id, generation, channels_lo, 35662306a36Sopenharmony_ci CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO, 35762306a36Sopenharmony_ci allocate); 35862306a36Sopenharmony_ci if (c >= 0) 35962306a36Sopenharmony_ci c += 32; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci *channel = c; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (allocate && channels_mask != 0 && c < 0) 36462306a36Sopenharmony_ci *bandwidth = 0; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (*bandwidth == 0) 36762306a36Sopenharmony_ci return; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ret = manage_bandwidth(card, irm_id, generation, *bandwidth, allocate); 37062306a36Sopenharmony_ci if (ret < 0) 37162306a36Sopenharmony_ci *bandwidth = 0; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (allocate && ret < 0) { 37462306a36Sopenharmony_ci if (c >= 0) 37562306a36Sopenharmony_ci deallocate_channel(card, irm_id, generation, c); 37662306a36Sopenharmony_ci *channel = ret; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ciEXPORT_SYMBOL(fw_iso_resource_manage); 380