Commit c614475b0ea9f7e6b3f76a46be315579bb899397

Authored by Clemens Ladisch
1 parent 640d9b421d

ALSA: dice: add a proc file to show device information

For easier debugging, add a proc file to show the device's capabilities
and current status.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>

Showing 1 changed file with 246 additions and 0 deletions Side-by-side Diff

sound/firewire/dice.c
... ... @@ -22,6 +22,7 @@
22 22 #include <sound/core.h>
23 23 #include <sound/firewire.h>
24 24 #include <sound/hwdep.h>
  25 +#include <sound/info.h>
25 26 #include <sound/initval.h>
26 27 #include <sound/pcm.h>
27 28 #include <sound/pcm_params.h>
... ... @@ -857,6 +858,249 @@
857 858 return 0;
858 859 }
859 860  
  861 +static int dice_proc_read_mem(struct dice *dice, void *buffer,
  862 + unsigned int offset_q, unsigned int quadlets)
  863 +{
  864 + unsigned int i;
  865 + int err;
  866 +
  867 + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
  868 + DICE_PRIVATE_SPACE + 4 * offset_q,
  869 + buffer, 4 * quadlets, 0);
  870 + if (err < 0)
  871 + return err;
  872 +
  873 + for (i = 0; i < quadlets; ++i)
  874 + be32_to_cpus(&((u32 *)buffer)[i]);
  875 +
  876 + return 0;
  877 +}
  878 +
  879 +static const char *str_from_array(const char *const strs[], unsigned int count,
  880 + unsigned int i)
  881 +{
  882 + if (i < count)
  883 + return strs[i];
  884 + else
  885 + return "(unknown)";
  886 +}
  887 +
  888 +static void dice_proc_fixup_string(char *s, unsigned int size)
  889 +{
  890 + unsigned int i;
  891 +
  892 + for (i = 0; i < size; i += 4)
  893 + cpu_to_le32s((u32 *)(s + i));
  894 +
  895 + for (i = 0; i < size - 2; ++i) {
  896 + if (s[i] == '\0')
  897 + return;
  898 + if (s[i] == '\\' && s[i + 1] == '\\') {
  899 + s[i + 2] = '\0';
  900 + return;
  901 + }
  902 + }
  903 + s[size - 1] = '\0';
  904 +}
  905 +
  906 +static void dice_proc_read(struct snd_info_entry *entry,
  907 + struct snd_info_buffer *buffer)
  908 +{
  909 + static const char *const section_names[5] = {
  910 + "global", "tx", "rx", "ext_sync", "unused2"
  911 + };
  912 + static const char *const clock_sources[] = {
  913 + "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif",
  914 + "wc", "arx1", "arx2", "arx3", "arx4", "internal"
  915 + };
  916 + static const char *const rates[] = {
  917 + "32000", "44100", "48000", "88200", "96000", "176400", "192000",
  918 + "any low", "any mid", "any high", "none"
  919 + };
  920 + struct dice *dice = entry->private_data;
  921 + u32 sections[ARRAY_SIZE(section_names) * 2];
  922 + struct {
  923 + u32 number;
  924 + u32 size;
  925 + } tx_rx_header;
  926 + union {
  927 + struct {
  928 + u32 owner_hi, owner_lo;
  929 + u32 notification;
  930 + char nick_name[NICK_NAME_SIZE];
  931 + u32 clock_select;
  932 + u32 enable;
  933 + u32 status;
  934 + u32 extended_status;
  935 + u32 sample_rate;
  936 + u32 version;
  937 + u32 clock_caps;
  938 + char clock_source_names[CLOCK_SOURCE_NAMES_SIZE];
  939 + } global;
  940 + struct {
  941 + u32 iso;
  942 + u32 number_audio;
  943 + u32 number_midi;
  944 + u32 speed;
  945 + char names[TX_NAMES_SIZE];
  946 + u32 ac3_caps;
  947 + u32 ac3_enable;
  948 + } tx;
  949 + struct {
  950 + u32 iso;
  951 + u32 seq_start;
  952 + u32 number_audio;
  953 + u32 number_midi;
  954 + char names[RX_NAMES_SIZE];
  955 + u32 ac3_caps;
  956 + u32 ac3_enable;
  957 + } rx;
  958 + struct {
  959 + u32 clock_source;
  960 + u32 locked;
  961 + u32 rate;
  962 + u32 adat_user_data;
  963 + } ext_sync;
  964 + } buf;
  965 + unsigned int quadlets, stream, i;
  966 +
  967 + if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0)
  968 + return;
  969 + snd_iprintf(buffer, "sections:\n");
  970 + for (i = 0; i < ARRAY_SIZE(section_names); ++i)
  971 + snd_iprintf(buffer, " %s: offset %u, size %u\n",
  972 + section_names[i],
  973 + sections[i * 2], sections[i * 2 + 1]);
  974 +
  975 + quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4);
  976 + if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0)
  977 + return;
  978 + snd_iprintf(buffer, "global:\n");
  979 + snd_iprintf(buffer, " owner: %04x:%04x%08x\n",
  980 + buf.global.owner_hi >> 16,
  981 + buf.global.owner_hi & 0xffff, buf.global.owner_lo);
  982 + snd_iprintf(buffer, " notification: %08x\n", buf.global.notification);
  983 + dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE);
  984 + snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name);
  985 + snd_iprintf(buffer, " clock select: %s %s\n",
  986 + str_from_array(clock_sources, ARRAY_SIZE(clock_sources),
  987 + buf.global.clock_select & CLOCK_SOURCE_MASK),
  988 + str_from_array(rates, ARRAY_SIZE(rates),
  989 + (buf.global.clock_select & CLOCK_RATE_MASK)
  990 + >> CLOCK_RATE_SHIFT));
  991 + snd_iprintf(buffer, " enable: %u\n", buf.global.enable);
  992 + snd_iprintf(buffer, " status: %slocked %s\n",
  993 + buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un",
  994 + str_from_array(rates, ARRAY_SIZE(rates),
  995 + (buf.global.status &
  996 + STATUS_NOMINAL_RATE_MASK)
  997 + >> CLOCK_RATE_SHIFT));
  998 + snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status);
  999 + snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate);
  1000 + snd_iprintf(buffer, " version: %u.%u.%u.%u\n",
  1001 + (buf.global.version >> 24) & 0xff,
  1002 + (buf.global.version >> 16) & 0xff,
  1003 + (buf.global.version >> 8) & 0xff,
  1004 + (buf.global.version >> 0) & 0xff);
  1005 + if (quadlets >= 90) {
  1006 + snd_iprintf(buffer, " clock caps:");
  1007 + for (i = 0; i <= 6; ++i)
  1008 + if (buf.global.clock_caps & (1 << i))
  1009 + snd_iprintf(buffer, " %s", rates[i]);
  1010 + for (i = 0; i <= 12; ++i)
  1011 + if (buf.global.clock_caps & (1 << (16 + i)))
  1012 + snd_iprintf(buffer, " %s", clock_sources[i]);
  1013 + snd_iprintf(buffer, "\n");
  1014 + dice_proc_fixup_string(buf.global.clock_source_names,
  1015 + CLOCK_SOURCE_NAMES_SIZE);
  1016 + snd_iprintf(buffer, " clock source names: %s\n",
  1017 + buf.global.clock_source_names);
  1018 + }
  1019 +
  1020 + if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0)
  1021 + return;
  1022 + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx));
  1023 + for (stream = 0; stream < tx_rx_header.number; ++stream) {
  1024 + if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 +
  1025 + stream * tx_rx_header.size,
  1026 + quadlets) < 0)
  1027 + break;
  1028 + snd_iprintf(buffer, "tx %u:\n", stream);
  1029 + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso);
  1030 + snd_iprintf(buffer, " audio channels: %u\n",
  1031 + buf.tx.number_audio);
  1032 + snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi);
  1033 + snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed);
  1034 + if (quadlets >= 68) {
  1035 + dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE);
  1036 + snd_iprintf(buffer, " names: %s\n", buf.tx.names);
  1037 + }
  1038 + if (quadlets >= 70) {
  1039 + snd_iprintf(buffer, " ac3 caps: %08x\n",
  1040 + buf.tx.ac3_caps);
  1041 + snd_iprintf(buffer, " ac3 enable: %08x\n",
  1042 + buf.tx.ac3_enable);
  1043 + }
  1044 + }
  1045 +
  1046 + if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0)
  1047 + return;
  1048 + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx));
  1049 + for (stream = 0; stream < tx_rx_header.number; ++stream) {
  1050 + if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 +
  1051 + stream * tx_rx_header.size,
  1052 + quadlets) < 0)
  1053 + break;
  1054 + snd_iprintf(buffer, "rx %u:\n", stream);
  1055 + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso);
  1056 + snd_iprintf(buffer, " sequence start: %u\n",
  1057 + (int)buf.rx.seq_start);
  1058 + snd_iprintf(buffer, " audio channels: %u\n",
  1059 + buf.rx.number_audio);
  1060 + snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi);
  1061 + if (quadlets >= 68) {
  1062 + dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE);
  1063 + snd_iprintf(buffer, " names: %s\n", buf.rx.names);
  1064 + }
  1065 + if (quadlets >= 70) {
  1066 + snd_iprintf(buffer, " ac3 caps: %08x\n",
  1067 + buf.rx.ac3_caps);
  1068 + snd_iprintf(buffer, " ac3 enable: %08x\n",
  1069 + buf.rx.ac3_enable);
  1070 + }
  1071 + }
  1072 +
  1073 + quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4);
  1074 + if (quadlets >= 4) {
  1075 + if (dice_proc_read_mem(dice, &buf.ext_sync,
  1076 + sections[6], 4) < 0)
  1077 + return;
  1078 + snd_iprintf(buffer, "ext status:\n");
  1079 + snd_iprintf(buffer, " clock source: %s\n",
  1080 + str_from_array(clock_sources,
  1081 + ARRAY_SIZE(clock_sources),
  1082 + buf.ext_sync.clock_source));
  1083 + snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked);
  1084 + snd_iprintf(buffer, " rate: %s\n",
  1085 + str_from_array(rates, ARRAY_SIZE(rates),
  1086 + buf.ext_sync.rate));
  1087 + snd_iprintf(buffer, " adat user data: ");
  1088 + if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA)
  1089 + snd_iprintf(buffer, "-\n");
  1090 + else
  1091 + snd_iprintf(buffer, "%x\n",
  1092 + buf.ext_sync.adat_user_data);
  1093 + }
  1094 +}
  1095 +
  1096 +static void dice_create_proc(struct dice *dice)
  1097 +{
  1098 + struct snd_info_entry *entry;
  1099 +
  1100 + if (!snd_card_proc_new(dice->card, "dice", &entry))
  1101 + snd_info_set_text_ops(entry, dice, dice_proc_read);
  1102 +}
  1103 +
860 1104 static void dice_card_free(struct snd_card *card)
861 1105 {
862 1106 struct dice *dice = card->private_data;
... ... @@ -1130,6 +1374,8 @@
1130 1374 err = dice_create_hwdep(dice);
1131 1375 if (err < 0)
1132 1376 goto error;
  1377 +
  1378 + dice_create_proc(dice);
1133 1379  
1134 1380 err = snd_card_register(card);
1135 1381 if (err < 0)