162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * miscellaneous helper functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/firewire.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include "lib.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define ERROR_RETRY_DELAY_MS 20 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/** 1862306a36Sopenharmony_ci * snd_fw_transaction - send a request and wait for its completion 1962306a36Sopenharmony_ci * @unit: the driver's unit on the target device 2062306a36Sopenharmony_ci * @tcode: the transaction code 2162306a36Sopenharmony_ci * @offset: the address in the target's address space 2262306a36Sopenharmony_ci * @buffer: input/output data 2362306a36Sopenharmony_ci * @length: length of @buffer 2462306a36Sopenharmony_ci * @flags: use %FW_FIXED_GENERATION and add the generation value to attempt the 2562306a36Sopenharmony_ci * request only in that generation; use %FW_QUIET to suppress error 2662306a36Sopenharmony_ci * messages 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * Submits an asynchronous request to the target device, and waits for the 2962306a36Sopenharmony_ci * response. The node ID and the current generation are derived from @unit. 3062306a36Sopenharmony_ci * On a bus reset or an error, the transaction is retried a few times. 3162306a36Sopenharmony_ci * Returns zero on success, or a negative error code. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ciint snd_fw_transaction(struct fw_unit *unit, int tcode, 3462306a36Sopenharmony_ci u64 offset, void *buffer, size_t length, 3562306a36Sopenharmony_ci unsigned int flags) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct fw_device *device = fw_parent_device(unit); 3862306a36Sopenharmony_ci int generation, rcode, tries = 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci generation = flags & FW_GENERATION_MASK; 4162306a36Sopenharmony_ci for (;;) { 4262306a36Sopenharmony_ci if (!(flags & FW_FIXED_GENERATION)) { 4362306a36Sopenharmony_ci generation = device->generation; 4462306a36Sopenharmony_ci smp_rmb(); /* node_id vs. generation */ 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci rcode = fw_run_transaction(device->card, tcode, 4762306a36Sopenharmony_ci device->node_id, generation, 4862306a36Sopenharmony_ci device->max_speed, offset, 4962306a36Sopenharmony_ci buffer, length); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (rcode == RCODE_COMPLETE) 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (rcode == RCODE_GENERATION && (flags & FW_FIXED_GENERATION)) 5562306a36Sopenharmony_ci return -EAGAIN; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (rcode_is_permanent_error(rcode) || ++tries >= 3) { 5862306a36Sopenharmony_ci if (!(flags & FW_QUIET)) 5962306a36Sopenharmony_ci dev_err(&unit->device, 6062306a36Sopenharmony_ci "transaction failed: %s\n", 6162306a36Sopenharmony_ci fw_rcode_string(rcode)); 6262306a36Sopenharmony_ci return -EIO; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci msleep(ERROR_RETRY_DELAY_MS); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_fw_transaction); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ciMODULE_DESCRIPTION("FireWire audio helper functions"); 7162306a36Sopenharmony_ciMODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); 7262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 73