diff options
author | Dimitris Papastamos <dp@opensource.wolfsonmicro.com> | 2011-02-02 13:58:58 +0000 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-02-02 20:48:15 +0000 |
commit | 13fd179f14712cc89b5b3812673891b96e0ab3a2 (patch) | |
tree | 95a39581df65cfc413c33ce8f77e43857fb2b886 /sound/soc/soc-core.c | |
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/soc/soc-core.c')
-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 10bc3c23b52..0c789f9150c 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) return c; } +/* fill buf which is 'len' bytes with a formatted + * string of the form 'reg: value\n' */ +static int format_register_str(struct snd_soc_codec *codec, + unsigned int reg, char *buf, size_t len) +{ + int wordsize = codec->driver->reg_word_size * 2; + int regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; + int ret; + char tmpbuf[len + 1]; + char regbuf[regsize + 1]; + + /* since tmpbuf is allocated on the stack, warn the callers if they + * try to abuse this function */ + WARN_ON(len > 63); + + /* +2 for ': ' and + 1 for '\n' */ + if (wordsize + regsize + 2 + 1 != len) + return -EINVAL; + + ret = snd_soc_read(codec , reg); + if (ret < 0) { + memset(regbuf, 'X', regsize); + regbuf[regsize] = '\0'; + } else { + snprintf(regbuf, regsize + 1, "%.*x", regsize, ret); + } + + /* prepare the buffer */ + snprintf(tmpbuf, len + 1, "%.*x: %s\n", wordsize, reg, regbuf); + /* copy it back to the caller without the '\0' */ + memcpy(buf, tmpbuf, len); + + return 0; +} + /* codec register dump */ -static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) +static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf, + size_t count, loff_t pos) { - int ret, i, step = 1, count = 0; + int i, step = 1; int wordsize, regsize; + int len; + size_t total = 0; + loff_t p = 0; wordsize = codec->driver->reg_word_size * 2; regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; + len = wordsize + regsize + 2 + 1; + if (!codec->driver->reg_cache_size) return 0; @@ -105,51 +146,34 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) for (i = 0; i < codec->driver->reg_cache_size; i += step) { if (codec->readable_register && !codec->readable_register(codec, i)) continue; - - count += sprintf(buf + count, "%.*x: ", regsize, i); - if (count >= PAGE_SIZE - 1) - break; - if (codec->driver->display_register) { count += codec->driver->display_register(codec, buf + count, PAGE_SIZE - count, i); } else { - /* If the read fails it's almost certainly due to - * the register being volatile and the device being - * powered off. - */ - ret = snd_soc_read(codec, i); - if (ret >= 0) - count += snprintf(buf + count, - PAGE_SIZE - count, - "%.*x", wordsize, ret); - else - count += snprintf(buf + count, - PAGE_SIZE - count, - "<no data: %d>", ret); + /* only support larger than PAGE_SIZE bytes debugfs + * entries for the default case */ + if (p >= pos) { + if (total + len >= count - 1) + break; + format_register_str(codec, i, buf + total, len); + total += len; + } + p += len; } - - if (count >= PAGE_SIZE - 1) - break; - - count += snprintf(buf + count, PAGE_SIZE - count, "\n"); - if (count >= PAGE_SIZE - 1) - break; } - /* Truncate count; min() would cause a warning */ - if (count >= PAGE_SIZE) - count = PAGE_SIZE - 1; + total = min(total, count - 1); - return count; + return total; } + static ssize_t codec_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct snd_soc_pcm_runtime *rtd = container_of(dev, struct snd_soc_pcm_runtime, dev); - return soc_codec_reg_show(rtd->codec, buf); + return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0); } 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) } static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { ssize_t ret; struct snd_soc_codec *codec = file->private_data; - char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + char *buf; + + if (*ppos < 0 || !count) + return -EINVAL; + + buf = kmalloc(count, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = soc_codec_reg_show(codec, buf); - if (ret >= 0) - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + ret = soc_codec_reg_show(codec, buf, count, *ppos); + if (ret >= 0) { + if (copy_to_user(user_buf, buf, ret)) { + kfree(buf); + return -EFAULT; + } + *ppos += ret; + } + kfree(buf); return ret; } |