cregit-Linux how code gets into the kernel

Release 4.7 sound/firewire/fireworks/fireworks_command.c

/*
 * fireworks_command.c - a part of driver for Fireworks based devices
 *
 * Copyright (c) 2013-2014 Takashi Sakamoto
 *
 * Licensed under the terms of the GNU General Public License, version 2.
 */

#include "./fireworks.h"

/*
 * This driver uses transaction version 1 or later to use extended hardware
 * information. Then too old devices are not available.
 *
 * Each commands are not required to have continuous sequence numbers. This
 * number is just used to match command and response.
 *
 * This module support a part of commands. Please see FFADO if you want to see
 * whole commands. But there are some commands which FFADO don't implement.
 *
 * Fireworks also supports AV/C general commands and AV/C Stream Format
 * Information commands. But this module don't use them.
 */


#define KERNEL_SEQNUM_MIN	(SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 2)

#define KERNEL_SEQNUM_MAX	((u32)~0)

/* for clock source and sampling rate */

struct efc_clock {
	
u32 source;
	
u32 sampling_rate;
	
u32 index;
};

/* command categories */

enum efc_category {
	
EFC_CAT_HWINFO		= 0,
	
EFC_CAT_TRANSPORT	= 2,
	
EFC_CAT_HWCTL		= 3,
};

/* hardware info category commands */

enum efc_cmd_hwinfo {
	
EFC_CMD_HWINFO_GET_CAPS		= 0,
	
EFC_CMD_HWINFO_GET_POLLED	= 1,
	
EFC_CMD_HWINFO_SET_RESP_ADDR	= 2
};


enum efc_cmd_transport {
	
EFC_CMD_TRANSPORT_SET_TX_MODE	= 0
};

/* hardware control category commands */

enum efc_cmd_hwctl {
	
EFC_CMD_HWCTL_SET_CLOCK		= 0,
	
EFC_CMD_HWCTL_GET_CLOCK		= 1,
	
EFC_CMD_HWCTL_IDENTIFY		= 5
};

/* return values in response */

enum efr_status {
	
EFR_STATUS_OK			= 0,
	
EFR_STATUS_BAD			= 1,
	
EFR_STATUS_BAD_COMMAND		= 2,
	
EFR_STATUS_COMM_ERR		= 3,
	
EFR_STATUS_BAD_QUAD_COUNT	= 4,
	
EFR_STATUS_UNSUPPORTED		= 5,
	
EFR_STATUS_1394_TIMEOUT		= 6,
	
EFR_STATUS_DSP_TIMEOUT		= 7,
	
EFR_STATUS_BAD_RATE		= 8,
	
EFR_STATUS_BAD_CLOCK		= 9,
	
EFR_STATUS_BAD_CHANNEL		= 10,
	
EFR_STATUS_BAD_PAN		= 11,
	
EFR_STATUS_FLASH_BUSY		= 12,
	
EFR_STATUS_BAD_MIRROR		= 13,
	
EFR_STATUS_BAD_LED		= 14,
	
EFR_STATUS_BAD_PARAMETER	= 15,
	
EFR_STATUS_INCOMPLETE		= 0x80000000
};


static const char *const efr_status_names[] = {
	[EFR_STATUS_OK]			= "OK",
	[EFR_STATUS_BAD]		= "bad",
	[EFR_STATUS_BAD_COMMAND]	= "bad command",
	[EFR_STATUS_COMM_ERR]		= "comm err",
	[EFR_STATUS_BAD_QUAD_COUNT]	= "bad quad count",
	[EFR_STATUS_UNSUPPORTED]	= "unsupported",
	[EFR_STATUS_1394_TIMEOUT]	= "1394 timeout",
	[EFR_STATUS_DSP_TIMEOUT]	= "DSP timeout",
	[EFR_STATUS_BAD_RATE]		= "bad rate",
	[EFR_STATUS_BAD_CLOCK]		= "bad clock",
	[EFR_STATUS_BAD_CHANNEL]	= "bad channel",
	[EFR_STATUS_BAD_PAN]		= "bad pan",
	[EFR_STATUS_FLASH_BUSY]		= "flash busy",
	[EFR_STATUS_BAD_MIRROR]		= "bad mirror",
	[EFR_STATUS_BAD_LED]		= "bad LED",
	[EFR_STATUS_BAD_PARAMETER]	= "bad parameter",
	[EFR_STATUS_BAD_PARAMETER + 1]	= "incomplete"
};


static int efw_transaction(struct snd_efw *efw, unsigned int category, unsigned int command, const __be32 *params, unsigned int param_bytes, const __be32 *resp, unsigned int resp_bytes) { struct snd_efw_transaction *header; __be32 *buf; u32 seqnum; unsigned int buf_bytes, cmd_bytes; int err; /* calculate buffer size*/ buf_bytes = sizeof(struct snd_efw_transaction) + max(param_bytes, resp_bytes); /* keep buffer */ buf = kzalloc(buf_bytes, GFP_KERNEL); if (buf == NULL) return -ENOMEM; /* to keep consistency of sequence number */ spin_lock(&efw->lock); if ((efw->seqnum < KERNEL_SEQNUM_MIN) || (efw->seqnum >= KERNEL_SEQNUM_MAX - 2)) efw->seqnum = KERNEL_SEQNUM_MIN; else efw->seqnum += 2; seqnum = efw->seqnum; spin_unlock(&efw->lock); /* fill transaction header fields */ cmd_bytes = sizeof(struct snd_efw_transaction) + param_bytes; header = (struct snd_efw_transaction *)buf; header->length = cpu_to_be32(cmd_bytes / sizeof(__be32)); header->version = cpu_to_be32(1); header->seqnum = cpu_to_be32(seqnum); header->category = cpu_to_be32(category); header->command = cpu_to_be32(command); header->status = 0; /* fill transaction command parameters */ memcpy(header->params, params, param_bytes); err = snd_efw_transaction_run(efw->unit, buf, cmd_bytes, buf, buf_bytes); if (err < 0) goto end; /* check transaction header fields */ if ((be32_to_cpu(header->version) < 1) || (be32_to_cpu(header->category) != category) || (be32_to_cpu(header->command) != command) || (be32_to_cpu(header->status) != EFR_STATUS_OK)) { dev_err(&efw->unit->device, "EFW command failed [%u/%u]: %s\n", be32_to_cpu(header->category), be32_to_cpu(header->command), efr_status_names[be32_to_cpu(header->status)]); err = -EIO; goto end; } if (resp == NULL) goto end; /* fill transaction response parameters */ memset((void *)resp, 0, resp_bytes); resp_bytes = min_t(unsigned int, resp_bytes, be32_to_cpu(header->length) * sizeof(__be32) - sizeof(struct snd_efw_transaction)); memcpy((void *)resp, &buf[6], resp_bytes); end: kfree(buf); return err; }

Contributors

PersonTokensPropCommitsCommitProp
takashi sakamototakashi sakamoto418100.00%2100.00%
Total418100.00%2100.00%

/* * The address in host system for transaction response is changable when the * device supports. struct hwinfo.flags includes its flag. The default is * MEMORY_SPACE_EFW_RESPONSE. */
int snd_efw_command_set_resp_addr(struct snd_efw *efw, u16 addr_high, u32 addr_low) { __be32 addr[2]; addr[0] = cpu_to_be32(addr_high); addr[1] = cpu_to_be32(addr_low); if (!efw->resp_addr_changable) return -ENOSYS; return efw_transaction(efw, EFC_CAT_HWCTL, EFC_CMD_HWINFO_SET_RESP_ADDR, addr, sizeof(addr), NULL, 0); }

Contributors

PersonTokensPropCommitsCommitProp
takashi sakamototakashi sakamoto74100.00%1100.00%
Total74100.00%1100.00%

/* * This is for timestamp processing. In Windows mode, all 32bit fields of second * CIP header in AMDTP transmit packet is used for 'presentation timestamp'. In * 'no data' packet the value of this field is 0x90ffffff. */
int snd_efw_command_set_tx_mode(struct snd_efw *efw, enum snd_efw_transport_mode mode) { __be32 param = cpu_to_be32(mode); return efw_transaction(efw, EFC_CAT_TRANSPORT, EFC_CMD_TRANSPORT_SET_TX_MODE, &param, sizeof(param), NULL, 0); }

Contributors

PersonTokensPropCommitsCommitProp
takashi sakamototakashi sakamoto44100.00%1100.00%
Total44100.00%1100.00%


int snd_efw_command_get_hwinfo(struct snd_efw *efw, struct snd_efw_hwinfo *hwinfo) { int err; err = efw_transaction(efw, EFC_CAT_HWINFO, EFC_CMD_HWINFO_GET_CAPS, NULL, 0, (__be32 *)hwinfo, sizeof(*hwinfo)); if (err < 0) goto end; be32_to_cpus(&hwinfo->flags); be32_to_cpus(&hwinfo->guid_hi); be32_to_cpus(&hwinfo->guid_lo); be32_to_cpus(&hwinfo->type); be32_to_cpus(&hwinfo->version); be32_to_cpus(&hwinfo->supported_clocks); be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels); be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels); be32_to_cpus(&hwinfo->phys_out); be32_to_cpus(&hwinfo->phys_in); be32_to_cpus(&hwinfo->phys_out_grp_count); be32_to_cpus(&hwinfo->phys_in_grp_count); be32_to_cpus(&hwinfo->midi_out_ports); be32_to_cpus(&hwinfo->midi_in_ports); be32_to_cpus(&hwinfo->max_sample_rate); be32_to_cpus(&hwinfo->min_sample_rate); be32_to_cpus(&hwinfo->dsp_version); be32_to_cpus(&hwinfo->arm_version); be32_to_cpus(&hwinfo->mixer_playback_channels); be32_to_cpus(&hwinfo->mixer_capture_channels); be32_to_cpus(&hwinfo->fpga_version); be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_2x); be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_2x); be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_4x); be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_4x); /* ensure terminated */ hwinfo->vendor_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0'; hwinfo->model_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0'; end: return err; }

Contributors

PersonTokensPropCommitsCommitProp
takashi sakamototakashi sakamoto282100.00%1100.00%
Total282100.00%1100.00%


int snd_efw_command_get_phys_meters(struct snd_efw *efw, struct snd_efw_phys_meters *meters, unsigned int len) { u32 *buf = (u32 *)meters; unsigned int i; int err; err = efw_transaction(efw, EFC_CAT_HWINFO, EFC_CMD_HWINFO_GET_POLLED, NULL, 0, (__be32 *)meters, len); if (err >= 0) for (i = 0; i < len / sizeof(u32); i++) be32_to_cpus(&buf[i]); return err; }

Contributors

PersonTokensPropCommitsCommitProp
takashi sakamototakashi sakamoto95100.00%2100.00%
Total95100.00%2100.00%


static int command_get_clock(struct snd_efw *efw, struct efc_clock *clock) { int err; err = efw_transaction(efw, EFC_CAT_HWCTL, EFC_CMD_HWCTL_GET_CLOCK, NULL, 0, (__be32 *)clock, sizeof(struct efc_clock)); if (err >= 0) { be32_to_cpus(&clock->source); be32_to_cpus(&clock->sampling_rate); be32_to_cpus(&clock->index); } return err; }

Contributors

PersonTokensPropCommitsCommitProp
takashi sakamototakashi sakamoto81100.00%1100.00%
Total81100.00%1100.00%

/* give UINT_MAX if set nothing */
static int command_set_clock(struct snd_efw *efw, unsigned int source, unsigned int rate) { struct efc_clock clock = {0}; int err; /* check arguments */ if ((source == UINT_MAX) && (rate == UINT_MAX)) { err = -EINVAL; goto end; } /* get current status */ err = command_get_clock(efw, &clock); if (err < 0) goto end; /* no need */ if ((clock.source == source) && (clock.sampling_rate == rate)) goto end; /* set params */ if ((source != UINT_MAX) && (clock.source != source)) clock.source = source; if ((rate != UINT_MAX) && (clock.sampling_rate != rate)) clock.sampling_rate = rate; clock.index = 0; cpu_to_be32s(&clock.source); cpu_to_be32s(&clock.sampling_rate); cpu_to_be32s(&clock.index); err = efw_transaction(efw, EFC_CAT_HWCTL, EFC_CMD_HWCTL_SET_CLOCK, (__be32 *)&clock, sizeof(struct efc_clock), NULL, 0); if (err < 0) goto end; /* * With firmware version 5.8, just after changing clock state, these * parameters are not immediately retrieved by get command. In my * trial, there needs to be 100msec to get changed parameters. */ msleep(150); end: return err; }

Contributors

PersonTokensPropCommitsCommitProp
takashi sakamototakashi sakamoto220100.00%1100.00%
Total220100.00%1100.00%


int snd_efw_command_get_clock_source(struct snd_efw *efw, enum snd_efw_clock_source *source) { int err; struct efc_clock clock = {0}; err = command_get_clock(efw, &clock); if (err >= 0) *source = clock.source; return err; }

Contributors

PersonTokensPropCommitsCommitProp
takashi sakamototakashi sakamoto52100.00%1100.00%
Total52100.00%1100.00%


int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate) { int err; struct efc_clock clock = {0}; err = command_get_clock(efw, &clock); if (err >= 0) *rate = clock.sampling_rate; return err; }

Contributors

PersonTokensPropCommitsCommitProp
takashi sakamototakashi sakamoto52100.00%1100.00%
Total52100.00%1100.00%


int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate) { return command_set_clock(efw, UINT_MAX, rate); }

Contributors

PersonTokensPropCommitsCommitProp
takashi sakamototakashi sakamoto24100.00%1100.00%
Total24100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
takashi sakamototakashi sakamoto1619100.00%3100.00%
Total1619100.00%3100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}