diff options
author | Dimitris Papastamos <dp@opensource.wolfsonmicro.com> | 2011-02-02 08:58:58 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-02-02 15:48:15 -0500 |
commit | 13fd179f14712cc89b5b3812673891b96e0ab3a2 (patch) | |
tree | 95a39581df65cfc413c33ce8f77e43857fb2b886 /sound | |
parent | a591e969fae8b79484cb94a12aceb9799e06ac22 (diff) |
ASoC: soc-core: Support debugfs entries larger than PAGE_SIZE bytes
For some codecs with large register maps, it was not possible to dump
all registers via the codec_reg file but only up to PAGE_SIZE bytes.
This patch fixes this problem.
Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/soc-core.c | 110 |
1 files changed, 73 insertions, 37 deletions
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 10bc3c23b525..0c789f9150c3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
@@ -87,15 +87,56 @@ static int min_bytes_needed(unsigned long val) | |||
87 | return c; | 87 | return c; |
88 | } | 88 | } |
89 | 89 | ||
90 | /* fill buf which is 'len' bytes with a formatted | ||
91 | * string of the form 'reg: value\n' */ | ||
92 | static int format_register_str(struct snd_soc_codec *codec, | ||
93 | unsigned int reg, char *buf, size_t len) | ||
94 | { | ||
95 | int wordsize = codec->driver->reg_word_size * 2; | ||
96 | int regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; | ||
97 | int ret; | ||
98 | char tmpbuf[len + 1]; | ||
99 | char regbuf[regsize + 1]; | ||
100 | |||
101 | /* since tmpbuf is allocated on the stack, warn the callers if they | ||
102 | * try to abuse this function */ | ||
103 | WARN_ON(len > 63); | ||
104 | |||
105 | /* +2 for ': ' and + 1 for '\n' */ | ||
106 | if (wordsize + regsize + 2 + 1 != len) | ||
107 | return -EINVAL; | ||
108 | |||
109 | ret = snd_soc_read(codec , reg); | ||
110 | if (ret < 0) { | ||
111 | memset(regbuf, 'X', regsize); | ||
112 | regbuf[regsize] = '\0'; | ||
113 | } else { | ||
114 | snprintf(regbuf, regsize + 1, "%.*x", regsize, ret); | ||
115 | } | ||
116 | |||
117 | /* prepare the buffer */ | ||
118 | snprintf(tmpbuf, len + 1, "%.*x: %s\n", wordsize, reg, regbuf); | ||
119 | /* copy it back to the caller without the '\0' */ | ||
120 | memcpy(buf, tmpbuf, len); | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
90 | /* codec register dump */ | 125 | /* codec register dump */ |
91 | static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) | 126 | static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf, |
127 | size_t count, loff_t pos) | ||
92 | { | 128 | { |
93 | int ret, i, step = 1, count = 0; | 129 | int i, step = 1; |
94 | int wordsize, regsize; | 130 | int wordsize, regsize; |
131 | int len; | ||
132 | size_t total = 0; | ||
133 | loff_t p = 0; | ||
95 | 134 | ||
96 | wordsize = codec->driver->reg_word_size * 2; | 135 | wordsize = codec->driver->reg_word_size * 2; |
97 | regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; | 136 | regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; |
98 | 137 | ||
138 | len = wordsize + regsize + 2 + 1; | ||
139 | |||
99 | if (!codec->driver->reg_cache_size) | 140 | if (!codec->driver->reg_cache_size) |
100 | return 0; | 141 | return 0; |
101 | 142 | ||
@@ -105,51 +146,34 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) | |||
105 | for (i = 0; i < codec->driver->reg_cache_size; i += step) { | 146 | for (i = 0; i < codec->driver->reg_cache_size; i += step) { |
106 | if (codec->readable_register && !codec->readable_register(codec, i)) | 147 | if (codec->readable_register && !codec->readable_register(codec, i)) |
107 | continue; | 148 | continue; |
108 | |||
109 | count += sprintf(buf + count, "%.*x: ", regsize, i); | ||
110 | if (count >= PAGE_SIZE - 1) | ||
111 | break; | ||
112 | |||
113 | if (codec->driver->display_register) { | 149 | if (codec->driver->display_register) { |
114 | count += codec->driver->display_register(codec, buf + count, | 150 | count += codec->driver->display_register(codec, buf + count, |
115 | PAGE_SIZE - count, i); | 151 | PAGE_SIZE - count, i); |
116 | } else { | 152 | } else { |
117 | /* If the read fails it's almost certainly due to | 153 | /* only support larger than PAGE_SIZE bytes debugfs |
118 | * the register being volatile and the device being | 154 | * entries for the default case */ |
119 | * powered off. | 155 | if (p >= pos) { |
120 | */ | 156 | if (total + len >= count - 1) |
121 | ret = snd_soc_read(codec, i); | 157 | break; |
122 | if (ret >= 0) | 158 | format_register_str(codec, i, buf + total, len); |
123 | count += snprintf(buf + count, | 159 | total += len; |
124 | PAGE_SIZE - count, | 160 | } |
125 | "%.*x", wordsize, ret); | 161 | p += len; |
126 | else | ||
127 | count += snprintf(buf + count, | ||
128 | PAGE_SIZE - count, | ||
129 | "<no data: %d>", ret); | ||
130 | } | 162 | } |
131 | |||
132 | if (count >= PAGE_SIZE - 1) | ||
133 | break; | ||
134 | |||
135 | count += snprintf(buf + count, PAGE_SIZE - count, "\n"); | ||
136 | if (count >= PAGE_SIZE - 1) | ||
137 | break; | ||
138 | } | 163 | } |
139 | 164 | ||
140 | /* Truncate count; min() would cause a warning */ | 165 | total = min(total, count - 1); |
141 | if (count >= PAGE_SIZE) | ||
142 | count = PAGE_SIZE - 1; | ||
143 | 166 | ||
144 | return count; | 167 | return total; |
145 | } | 168 | } |
169 | |||
146 | static ssize_t codec_reg_show(struct device *dev, | 170 | static ssize_t codec_reg_show(struct device *dev, |
147 | struct device_attribute *attr, char *buf) | 171 | struct device_attribute *attr, char *buf) |
148 | { | 172 | { |
149 | struct snd_soc_pcm_runtime *rtd = | 173 | struct snd_soc_pcm_runtime *rtd = |
150 | container_of(dev, struct snd_soc_pcm_runtime, dev); | 174 | container_of(dev, struct snd_soc_pcm_runtime, dev); |
151 | 175 | ||
152 | return soc_codec_reg_show(rtd->codec, buf); | 176 | return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0); |
153 | } | 177 | } |
154 | 178 | ||
155 | static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); | 179 | static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); |
@@ -188,16 +212,28 @@ static int codec_reg_open_file(struct inode *inode, struct file *file) | |||
188 | } | 212 | } |
189 | 213 | ||
190 | static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, | 214 | static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, |
191 | size_t count, loff_t *ppos) | 215 | size_t count, loff_t *ppos) |
192 | { | 216 | { |
193 | ssize_t ret; | 217 | ssize_t ret; |
194 | struct snd_soc_codec *codec = file->private_data; | 218 | struct snd_soc_codec *codec = file->private_data; |
195 | char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | 219 | char *buf; |
220 | |||
221 | if (*ppos < 0 || !count) | ||
222 | return -EINVAL; | ||
223 | |||
224 | buf = kmalloc(count, GFP_KERNEL); | ||
196 | if (!buf) | 225 | if (!buf) |
197 | return -ENOMEM; | 226 | return -ENOMEM; |
198 | ret = soc_codec_reg_show(codec, buf); | 227 | |
199 | if (ret >= 0) | 228 | ret = soc_codec_reg_show(codec, buf, count, *ppos); |
200 | ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); | 229 | if (ret >= 0) { |
230 | if (copy_to_user(user_buf, buf, ret)) { | ||
231 | kfree(buf); | ||
232 | return -EFAULT; | ||
233 | } | ||
234 | *ppos += ret; | ||
235 | } | ||
236 | |||
201 | kfree(buf); | 237 | kfree(buf); |
202 | return ret; | 238 | return ret; |
203 | } | 239 | } |