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