diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2012-01-05 16:36:08 -0500 |
---|---|---|
committer | Clemens Ladisch <clemens@ladisch.de> | 2013-10-20 16:07:57 -0400 |
commit | c614475b0ea9f7e6b3f76a46be315579bb899397 (patch) | |
tree | 6460fc0b88f1cbeefe509e10f2abc6882ccf2455 /sound/firewire | |
parent | 640d9b421d4d8c7593aa8647479a4c7c6fe0ca65 (diff) |
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>
Diffstat (limited to 'sound/firewire')
-rw-r--r-- | sound/firewire/dice.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 3395c8ba7af1..25a96362e1aa 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <sound/core.h> | 22 | #include <sound/core.h> |
23 | #include <sound/firewire.h> | 23 | #include <sound/firewire.h> |
24 | #include <sound/hwdep.h> | 24 | #include <sound/hwdep.h> |
25 | #include <sound/info.h> | ||
25 | #include <sound/initval.h> | 26 | #include <sound/initval.h> |
26 | #include <sound/pcm.h> | 27 | #include <sound/pcm.h> |
27 | #include <sound/pcm_params.h> | 28 | #include <sound/pcm_params.h> |
@@ -857,6 +858,249 @@ static int dice_create_hwdep(struct dice *dice) | |||
857 | return 0; | 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 | static void dice_card_free(struct snd_card *card) | 1104 | static void dice_card_free(struct snd_card *card) |
861 | { | 1105 | { |
862 | struct dice *dice = card->private_data; | 1106 | struct dice *dice = card->private_data; |
@@ -1131,6 +1375,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) | |||
1131 | if (err < 0) | 1375 | if (err < 0) |
1132 | goto error; | 1376 | goto error; |
1133 | 1377 | ||
1378 | dice_create_proc(dice); | ||
1379 | |||
1134 | err = snd_card_register(card); | 1380 | err = snd_card_register(card); |
1135 | if (err < 0) | 1381 | if (err < 0) |
1136 | goto error; | 1382 | goto error; |