1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * dice_proc.c - a part of driver for Dice based devices 4 * 5 * Copyright (c) Clemens Ladisch 6 * Copyright (c) 2014 Takashi Sakamoto 7 */ 8 9 #include "dice.h" 10 11 static 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 29 static 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 38 static 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 56 static 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 else 240 snd_iprintf(buffer, "%x\n", 241 buf.ext_sync.adat_user_data); 242 } 243 } 244 245 static 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 279 static 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 291 void 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