308 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * dice_proc.c - a part of driver for Dice based devices4 *5 * Copyright (c) Clemens Ladisch6 * Copyright (c) 2014 Takashi Sakamoto7 */8 9#include "dice.h"10 11static int dice_proc_read_mem(struct snd_dice *dice, void *buffer,12 unsigned int offset_q, unsigned int quadlets)13{14 unsigned int i;15 int err;16 17 err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,18 DICE_PRIVATE_SPACE + 4 * offset_q,19 buffer, 4 * quadlets, 0);20 if (err < 0)21 return err;22 23 for (i = 0; i < quadlets; ++i)24 be32_to_cpus(&((u32 *)buffer)[i]);25 26 return 0;27}28 29static const char *str_from_array(const char *const strs[], unsigned int count,30 unsigned int i)31{32 if (i < count)33 return strs[i];34 35 return "(unknown)";36}37 38static void dice_proc_fixup_string(char *s, unsigned int size)39{40 unsigned int i;41 42 for (i = 0; i < size; i += 4)43 cpu_to_le32s((u32 *)(s + i));44 45 for (i = 0; i < size - 2; ++i) {46 if (s[i] == '\0')47 return;48 if (s[i] == '\\' && s[i + 1] == '\\') {49 s[i + 2] = '\0';50 return;51 }52 }53 s[size - 1] = '\0';54}55 56static void dice_proc_read(struct snd_info_entry *entry,57 struct snd_info_buffer *buffer)58{59 static const char *const section_names[5] = {60 "global", "tx", "rx", "ext_sync", "unused2"61 };62 static const char *const clock_sources[] = {63 "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif",64 "wc", "arx1", "arx2", "arx3", "arx4", "internal"65 };66 static const char *const rates[] = {67 "32000", "44100", "48000", "88200", "96000", "176400", "192000",68 "any low", "any mid", "any high", "none"69 };70 struct snd_dice *dice = entry->private_data;71 u32 sections[ARRAY_SIZE(section_names) * 2];72 struct {73 u32 number;74 u32 size;75 } tx_rx_header;76 union {77 struct {78 u32 owner_hi, owner_lo;79 u32 notification;80 char nick_name[NICK_NAME_SIZE];81 u32 clock_select;82 u32 enable;83 u32 status;84 u32 extended_status;85 u32 sample_rate;86 u32 version;87 u32 clock_caps;88 char clock_source_names[CLOCK_SOURCE_NAMES_SIZE];89 } global;90 struct {91 u32 iso;92 u32 number_audio;93 u32 number_midi;94 u32 speed;95 char names[TX_NAMES_SIZE];96 u32 ac3_caps;97 u32 ac3_enable;98 } tx;99 struct {100 u32 iso;101 u32 seq_start;102 u32 number_audio;103 u32 number_midi;104 char names[RX_NAMES_SIZE];105 u32 ac3_caps;106 u32 ac3_enable;107 } rx;108 struct {109 u32 clock_source;110 u32 locked;111 u32 rate;112 u32 adat_user_data;113 } ext_sync;114 } buf;115 unsigned int quadlets, stream, i;116 117 if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0)118 return;119 snd_iprintf(buffer, "sections:\n");120 for (i = 0; i < ARRAY_SIZE(section_names); ++i)121 snd_iprintf(buffer, " %s: offset %u, size %u\n",122 section_names[i],123 sections[i * 2], sections[i * 2 + 1]);124 125 quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4);126 if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0)127 return;128 snd_iprintf(buffer, "global:\n");129 snd_iprintf(buffer, " owner: %04x:%04x%08x\n",130 buf.global.owner_hi >> 16,131 buf.global.owner_hi & 0xffff, buf.global.owner_lo);132 snd_iprintf(buffer, " notification: %08x\n", buf.global.notification);133 dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE);134 snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name);135 snd_iprintf(buffer, " clock select: %s %s\n",136 str_from_array(clock_sources, ARRAY_SIZE(clock_sources),137 buf.global.clock_select & CLOCK_SOURCE_MASK),138 str_from_array(rates, ARRAY_SIZE(rates),139 (buf.global.clock_select & CLOCK_RATE_MASK)140 >> CLOCK_RATE_SHIFT));141 snd_iprintf(buffer, " enable: %u\n", buf.global.enable);142 snd_iprintf(buffer, " status: %slocked %s\n",143 buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un",144 str_from_array(rates, ARRAY_SIZE(rates),145 (buf.global.status &146 STATUS_NOMINAL_RATE_MASK)147 >> CLOCK_RATE_SHIFT));148 snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status);149 snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate);150 if (quadlets >= 90) {151 snd_iprintf(buffer, " version: %u.%u.%u.%u\n",152 (buf.global.version >> 24) & 0xff,153 (buf.global.version >> 16) & 0xff,154 (buf.global.version >> 8) & 0xff,155 (buf.global.version >> 0) & 0xff);156 snd_iprintf(buffer, " clock caps:");157 for (i = 0; i <= 6; ++i)158 if (buf.global.clock_caps & (1 << i))159 snd_iprintf(buffer, " %s", rates[i]);160 for (i = 0; i <= 12; ++i)161 if (buf.global.clock_caps & (1 << (16 + i)))162 snd_iprintf(buffer, " %s", clock_sources[i]);163 snd_iprintf(buffer, "\n");164 dice_proc_fixup_string(buf.global.clock_source_names,165 CLOCK_SOURCE_NAMES_SIZE);166 snd_iprintf(buffer, " clock source names: %s\n",167 buf.global.clock_source_names);168 }169 170 if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0)171 return;172 quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4);173 for (stream = 0; stream < tx_rx_header.number; ++stream) {174 if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 +175 stream * tx_rx_header.size,176 quadlets) < 0)177 break;178 snd_iprintf(buffer, "tx %u:\n", stream);179 snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso);180 snd_iprintf(buffer, " audio channels: %u\n",181 buf.tx.number_audio);182 snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi);183 snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed);184 if (quadlets >= 68) {185 dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE);186 snd_iprintf(buffer, " names: %s\n", buf.tx.names);187 }188 if (quadlets >= 70) {189 snd_iprintf(buffer, " ac3 caps: %08x\n",190 buf.tx.ac3_caps);191 snd_iprintf(buffer, " ac3 enable: %08x\n",192 buf.tx.ac3_enable);193 }194 }195 196 if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0)197 return;198 quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4);199 for (stream = 0; stream < tx_rx_header.number; ++stream) {200 if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 +201 stream * tx_rx_header.size,202 quadlets) < 0)203 break;204 snd_iprintf(buffer, "rx %u:\n", stream);205 snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso);206 snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start);207 snd_iprintf(buffer, " audio channels: %u\n",208 buf.rx.number_audio);209 snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi);210 if (quadlets >= 68) {211 dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE);212 snd_iprintf(buffer, " names: %s\n", buf.rx.names);213 }214 if (quadlets >= 70) {215 snd_iprintf(buffer, " ac3 caps: %08x\n",216 buf.rx.ac3_caps);217 snd_iprintf(buffer, " ac3 enable: %08x\n",218 buf.rx.ac3_enable);219 }220 }221 222 quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4);223 if (quadlets >= 4) {224 if (dice_proc_read_mem(dice, &buf.ext_sync,225 sections[6], 4) < 0)226 return;227 snd_iprintf(buffer, "ext status:\n");228 snd_iprintf(buffer, " clock source: %s\n",229 str_from_array(clock_sources,230 ARRAY_SIZE(clock_sources),231 buf.ext_sync.clock_source));232 snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked);233 snd_iprintf(buffer, " rate: %s\n",234 str_from_array(rates, ARRAY_SIZE(rates),235 buf.ext_sync.rate));236 snd_iprintf(buffer, " adat user data: ");237 if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA)238 snd_iprintf(buffer, "-\n");239 else240 snd_iprintf(buffer, "%x\n",241 buf.ext_sync.adat_user_data);242 }243}244 245static void dice_proc_read_formation(struct snd_info_entry *entry,246 struct snd_info_buffer *buffer)247{248 static const char *const rate_labels[] = {249 [SND_DICE_RATE_MODE_LOW] = "low",250 [SND_DICE_RATE_MODE_MIDDLE] = "middle",251 [SND_DICE_RATE_MODE_HIGH] = "high",252 };253 struct snd_dice *dice = entry->private_data;254 int i, j;255 256 snd_iprintf(buffer, "Output stream from unit:\n");257 for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)258 snd_iprintf(buffer, "\t%s", rate_labels[i]);259 snd_iprintf(buffer, "\tMIDI\n");260 for (i = 0; i < MAX_STREAMS; ++i) {261 snd_iprintf(buffer, "Tx %u:", i);262 for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)263 snd_iprintf(buffer, "\t%u", dice->tx_pcm_chs[i][j]);264 snd_iprintf(buffer, "\t%u\n", dice->tx_midi_ports[i]);265 }266 267 snd_iprintf(buffer, "Input stream to unit:\n");268 for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)269 snd_iprintf(buffer, "\t%s", rate_labels[i]);270 snd_iprintf(buffer, "\n");271 for (i = 0; i < MAX_STREAMS; ++i) {272 snd_iprintf(buffer, "Rx %u:", i);273 for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)274 snd_iprintf(buffer, "\t%u", dice->rx_pcm_chs[i][j]);275 snd_iprintf(buffer, "\t%u\n", dice->rx_midi_ports[i]);276 }277}278 279static void add_node(struct snd_dice *dice, struct snd_info_entry *root,280 const char *name,281 void (*op)(struct snd_info_entry *entry,282 struct snd_info_buffer *buffer))283{284 struct snd_info_entry *entry;285 286 entry = snd_info_create_card_entry(dice->card, name, root);287 if (entry)288 snd_info_set_text_ops(entry, dice, op);289}290 291void snd_dice_create_proc(struct snd_dice *dice)292{293 struct snd_info_entry *root;294 295 /*296 * All nodes are automatically removed at snd_card_disconnect(),297 * by following to link list.298 */299 root = snd_info_create_card_entry(dice->card, "firewire",300 dice->card->proc_root);301 if (!root)302 return;303 root->mode = S_IFDIR | 0555;304 305 add_node(dice, root, "dice", dice_proc_read);306 add_node(dice, root, "formation", dice_proc_read_formation);307}308