[alsa-devel] [PATCH] ASoC: soc-core: Support debugfs entries larger than PAGE_SIZE bytes

Dimitris Papastamos dp at opensource.wolfsonmicro.com
Wed Feb 2 14:58:58 CET 2011


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 at opensource.wolfsonmicro.com>
---
 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 205cbd7..83626f9 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;
 }
-- 
1.7.3.5



More information about the Alsa-devel mailing list