18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * seq_buf.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * The seq_buf is a handy tool that allows you to pass a descriptor around
88c2ecf20Sopenharmony_ci * to a buffer that other functions can write to. It is similar to the
98c2ecf20Sopenharmony_ci * seq_file functionality but has some differences.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * To use it, the seq_buf must be initialized with seq_buf_init().
128c2ecf20Sopenharmony_ci * This will set up the counters within the descriptor. You can call
138c2ecf20Sopenharmony_ci * seq_buf_init() more than once to reset the seq_buf to start
148c2ecf20Sopenharmony_ci * from scratch.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
178c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
188c2ecf20Sopenharmony_ci#include <linux/seq_buf.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/**
218c2ecf20Sopenharmony_ci * seq_buf_can_fit - can the new data fit in the current buffer?
228c2ecf20Sopenharmony_ci * @s: the seq_buf descriptor
238c2ecf20Sopenharmony_ci * @len: The length to see if it can fit in the current buffer
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * Returns true if there's enough unused space in the seq_buf buffer
268c2ecf20Sopenharmony_ci * to fit the amount of new data according to @len.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_cistatic bool seq_buf_can_fit(struct seq_buf *s, size_t len)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	return s->len + len <= s->size;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/**
348c2ecf20Sopenharmony_ci * seq_buf_print_seq - move the contents of seq_buf into a seq_file
358c2ecf20Sopenharmony_ci * @m: the seq_file descriptor that is the destination
368c2ecf20Sopenharmony_ci * @s: the seq_buf descriptor that is the source.
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * Returns zero on success, non zero otherwise
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_ciint seq_buf_print_seq(struct seq_file *m, struct seq_buf *s)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	unsigned int len = seq_buf_used(s);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return seq_write(m, s->buffer, len);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/**
488c2ecf20Sopenharmony_ci * seq_buf_vprintf - sequence printing of information.
498c2ecf20Sopenharmony_ci * @s: seq_buf descriptor
508c2ecf20Sopenharmony_ci * @fmt: printf format string
518c2ecf20Sopenharmony_ci * @args: va_list of arguments from a printf() type function
528c2ecf20Sopenharmony_ci *
538c2ecf20Sopenharmony_ci * Writes a vnprintf() format into the sequencce buffer.
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * Returns zero on success, -1 on overflow.
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_ciint seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	int len;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	WARN_ON(s->size == 0);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (s->len < s->size) {
648c2ecf20Sopenharmony_ci		len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
658c2ecf20Sopenharmony_ci		if (s->len + len < s->size) {
668c2ecf20Sopenharmony_ci			s->len += len;
678c2ecf20Sopenharmony_ci			return 0;
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	seq_buf_set_overflow(s);
718c2ecf20Sopenharmony_ci	return -1;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/**
758c2ecf20Sopenharmony_ci * seq_buf_printf - sequence printing of information
768c2ecf20Sopenharmony_ci * @s: seq_buf descriptor
778c2ecf20Sopenharmony_ci * @fmt: printf format string
788c2ecf20Sopenharmony_ci *
798c2ecf20Sopenharmony_ci * Writes a printf() format into the sequence buffer.
808c2ecf20Sopenharmony_ci *
818c2ecf20Sopenharmony_ci * Returns zero on success, -1 on overflow.
828c2ecf20Sopenharmony_ci */
838c2ecf20Sopenharmony_ciint seq_buf_printf(struct seq_buf *s, const char *fmt, ...)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	va_list ap;
868c2ecf20Sopenharmony_ci	int ret;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	va_start(ap, fmt);
898c2ecf20Sopenharmony_ci	ret = seq_buf_vprintf(s, fmt, ap);
908c2ecf20Sopenharmony_ci	va_end(ap);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return ret;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(seq_buf_printf);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci#ifdef CONFIG_BINARY_PRINTF
978c2ecf20Sopenharmony_ci/**
988c2ecf20Sopenharmony_ci * seq_buf_bprintf - Write the printf string from binary arguments
998c2ecf20Sopenharmony_ci * @s: seq_buf descriptor
1008c2ecf20Sopenharmony_ci * @fmt: The format string for the @binary arguments
1018c2ecf20Sopenharmony_ci * @binary: The binary arguments for @fmt.
1028c2ecf20Sopenharmony_ci *
1038c2ecf20Sopenharmony_ci * When recording in a fast path, a printf may be recorded with just
1048c2ecf20Sopenharmony_ci * saving the format and the arguments as they were passed to the
1058c2ecf20Sopenharmony_ci * function, instead of wasting cycles converting the arguments into
1068c2ecf20Sopenharmony_ci * ASCII characters. Instead, the arguments are saved in a 32 bit
1078c2ecf20Sopenharmony_ci * word array that is defined by the format string constraints.
1088c2ecf20Sopenharmony_ci *
1098c2ecf20Sopenharmony_ci * This function will take the format and the binary array and finish
1108c2ecf20Sopenharmony_ci * the conversion into the ASCII string within the buffer.
1118c2ecf20Sopenharmony_ci *
1128c2ecf20Sopenharmony_ci * Returns zero on success, -1 on overflow.
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_ciint seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	unsigned int len = seq_buf_buffer_left(s);
1178c2ecf20Sopenharmony_ci	int ret;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	WARN_ON(s->size == 0);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (s->len < s->size) {
1228c2ecf20Sopenharmony_ci		ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
1238c2ecf20Sopenharmony_ci		if (s->len + ret < s->size) {
1248c2ecf20Sopenharmony_ci			s->len += ret;
1258c2ecf20Sopenharmony_ci			return 0;
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci	seq_buf_set_overflow(s);
1298c2ecf20Sopenharmony_ci	return -1;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci#endif /* CONFIG_BINARY_PRINTF */
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/**
1348c2ecf20Sopenharmony_ci * seq_buf_puts - sequence printing of simple string
1358c2ecf20Sopenharmony_ci * @s: seq_buf descriptor
1368c2ecf20Sopenharmony_ci * @str: simple string to record
1378c2ecf20Sopenharmony_ci *
1388c2ecf20Sopenharmony_ci * Copy a simple string into the sequence buffer.
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci * Returns zero on success, -1 on overflow
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_ciint seq_buf_puts(struct seq_buf *s, const char *str)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	size_t len = strlen(str);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	WARN_ON(s->size == 0);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* Add 1 to len for the trailing null byte which must be there */
1498c2ecf20Sopenharmony_ci	len += 1;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (seq_buf_can_fit(s, len)) {
1528c2ecf20Sopenharmony_ci		memcpy(s->buffer + s->len, str, len);
1538c2ecf20Sopenharmony_ci		/* Don't count the trailing null byte against the capacity */
1548c2ecf20Sopenharmony_ci		s->len += len - 1;
1558c2ecf20Sopenharmony_ci		return 0;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci	seq_buf_set_overflow(s);
1588c2ecf20Sopenharmony_ci	return -1;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci/**
1628c2ecf20Sopenharmony_ci * seq_buf_putc - sequence printing of simple character
1638c2ecf20Sopenharmony_ci * @s: seq_buf descriptor
1648c2ecf20Sopenharmony_ci * @c: simple character to record
1658c2ecf20Sopenharmony_ci *
1668c2ecf20Sopenharmony_ci * Copy a single character into the sequence buffer.
1678c2ecf20Sopenharmony_ci *
1688c2ecf20Sopenharmony_ci * Returns zero on success, -1 on overflow
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_ciint seq_buf_putc(struct seq_buf *s, unsigned char c)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	WARN_ON(s->size == 0);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (seq_buf_can_fit(s, 1)) {
1758c2ecf20Sopenharmony_ci		s->buffer[s->len++] = c;
1768c2ecf20Sopenharmony_ci		return 0;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	seq_buf_set_overflow(s);
1798c2ecf20Sopenharmony_ci	return -1;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/**
1838c2ecf20Sopenharmony_ci * seq_buf_putmem - write raw data into the sequenc buffer
1848c2ecf20Sopenharmony_ci * @s: seq_buf descriptor
1858c2ecf20Sopenharmony_ci * @mem: The raw memory to copy into the buffer
1868c2ecf20Sopenharmony_ci * @len: The length of the raw memory to copy (in bytes)
1878c2ecf20Sopenharmony_ci *
1888c2ecf20Sopenharmony_ci * There may be cases where raw memory needs to be written into the
1898c2ecf20Sopenharmony_ci * buffer and a strcpy() would not work. Using this function allows
1908c2ecf20Sopenharmony_ci * for such cases.
1918c2ecf20Sopenharmony_ci *
1928c2ecf20Sopenharmony_ci * Returns zero on success, -1 on overflow
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_ciint seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	WARN_ON(s->size == 0);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (seq_buf_can_fit(s, len)) {
1998c2ecf20Sopenharmony_ci		memcpy(s->buffer + s->len, mem, len);
2008c2ecf20Sopenharmony_ci		s->len += len;
2018c2ecf20Sopenharmony_ci		return 0;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci	seq_buf_set_overflow(s);
2048c2ecf20Sopenharmony_ci	return -1;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci#define MAX_MEMHEX_BYTES	8U
2088c2ecf20Sopenharmony_ci#define HEX_CHARS		(MAX_MEMHEX_BYTES*2 + 1)
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci/**
2118c2ecf20Sopenharmony_ci * seq_buf_putmem_hex - write raw memory into the buffer in ASCII hex
2128c2ecf20Sopenharmony_ci * @s: seq_buf descriptor
2138c2ecf20Sopenharmony_ci * @mem: The raw memory to write its hex ASCII representation of
2148c2ecf20Sopenharmony_ci * @len: The length of the raw memory to copy (in bytes)
2158c2ecf20Sopenharmony_ci *
2168c2ecf20Sopenharmony_ci * This is similar to seq_buf_putmem() except instead of just copying the
2178c2ecf20Sopenharmony_ci * raw memory into the buffer it writes its ASCII representation of it
2188c2ecf20Sopenharmony_ci * in hex characters.
2198c2ecf20Sopenharmony_ci *
2208c2ecf20Sopenharmony_ci * Returns zero on success, -1 on overflow
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_ciint seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
2238c2ecf20Sopenharmony_ci		       unsigned int len)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	unsigned char hex[HEX_CHARS];
2268c2ecf20Sopenharmony_ci	const unsigned char *data = mem;
2278c2ecf20Sopenharmony_ci	unsigned int start_len;
2288c2ecf20Sopenharmony_ci	int i, j;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	WARN_ON(s->size == 0);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MAX_MEMHEX_BYTES * 2 >= HEX_CHARS);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	while (len) {
2358c2ecf20Sopenharmony_ci		start_len = min(len, MAX_MEMHEX_BYTES);
2368c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
2378c2ecf20Sopenharmony_ci		for (i = 0, j = 0; i < start_len; i++) {
2388c2ecf20Sopenharmony_ci#else
2398c2ecf20Sopenharmony_ci		for (i = start_len-1, j = 0; i >= 0; i--) {
2408c2ecf20Sopenharmony_ci#endif
2418c2ecf20Sopenharmony_ci			hex[j++] = hex_asc_hi(data[i]);
2428c2ecf20Sopenharmony_ci			hex[j++] = hex_asc_lo(data[i]);
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(j == 0 || j/2 > len))
2458c2ecf20Sopenharmony_ci			break;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		/* j increments twice per loop */
2488c2ecf20Sopenharmony_ci		hex[j++] = ' ';
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		seq_buf_putmem(s, hex, j);
2518c2ecf20Sopenharmony_ci		if (seq_buf_has_overflowed(s))
2528c2ecf20Sopenharmony_ci			return -1;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		len -= start_len;
2558c2ecf20Sopenharmony_ci		data += start_len;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/**
2618c2ecf20Sopenharmony_ci * seq_buf_path - copy a path into the sequence buffer
2628c2ecf20Sopenharmony_ci * @s: seq_buf descriptor
2638c2ecf20Sopenharmony_ci * @path: path to write into the sequence buffer.
2648c2ecf20Sopenharmony_ci * @esc: set of characters to escape in the output
2658c2ecf20Sopenharmony_ci *
2668c2ecf20Sopenharmony_ci * Write a path name into the sequence buffer.
2678c2ecf20Sopenharmony_ci *
2688c2ecf20Sopenharmony_ci * Returns the number of written bytes on success, -1 on overflow
2698c2ecf20Sopenharmony_ci */
2708c2ecf20Sopenharmony_ciint seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	char *buf;
2738c2ecf20Sopenharmony_ci	size_t size = seq_buf_get_buf(s, &buf);
2748c2ecf20Sopenharmony_ci	int res = -1;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	WARN_ON(s->size == 0);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (size) {
2798c2ecf20Sopenharmony_ci		char *p = d_path(path, buf, size);
2808c2ecf20Sopenharmony_ci		if (!IS_ERR(p)) {
2818c2ecf20Sopenharmony_ci			char *end = mangle_path(buf, p, esc);
2828c2ecf20Sopenharmony_ci			if (end)
2838c2ecf20Sopenharmony_ci				res = end - buf;
2848c2ecf20Sopenharmony_ci		}
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci	seq_buf_commit(s, res);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return res;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci/**
2928c2ecf20Sopenharmony_ci * seq_buf_to_user - copy the squence buffer to user space
2938c2ecf20Sopenharmony_ci * @s: seq_buf descriptor
2948c2ecf20Sopenharmony_ci * @ubuf: The userspace memory location to copy to
2958c2ecf20Sopenharmony_ci * @cnt: The amount to copy
2968c2ecf20Sopenharmony_ci *
2978c2ecf20Sopenharmony_ci * Copies the sequence buffer into the userspace memory pointed to
2988c2ecf20Sopenharmony_ci * by @ubuf. It starts from the last read position (@s->readpos)
2998c2ecf20Sopenharmony_ci * and writes up to @cnt characters or till it reaches the end of
3008c2ecf20Sopenharmony_ci * the content in the buffer (@s->len), which ever comes first.
3018c2ecf20Sopenharmony_ci *
3028c2ecf20Sopenharmony_ci * On success, it returns a positive number of the number of bytes
3038c2ecf20Sopenharmony_ci * it copied.
3048c2ecf20Sopenharmony_ci *
3058c2ecf20Sopenharmony_ci * On failure it returns -EBUSY if all of the content in the
3068c2ecf20Sopenharmony_ci * sequence has been already read, which includes nothing in the
3078c2ecf20Sopenharmony_ci * sequence (@s->len == @s->readpos).
3088c2ecf20Sopenharmony_ci *
3098c2ecf20Sopenharmony_ci * Returns -EFAULT if the copy to userspace fails.
3108c2ecf20Sopenharmony_ci */
3118c2ecf20Sopenharmony_ciint seq_buf_to_user(struct seq_buf *s, char __user *ubuf, int cnt)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	int len;
3148c2ecf20Sopenharmony_ci	int ret;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (!cnt)
3178c2ecf20Sopenharmony_ci		return 0;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	len = seq_buf_used(s);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (len <= s->readpos)
3228c2ecf20Sopenharmony_ci		return -EBUSY;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	len -= s->readpos;
3258c2ecf20Sopenharmony_ci	if (cnt > len)
3268c2ecf20Sopenharmony_ci		cnt = len;
3278c2ecf20Sopenharmony_ci	ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt);
3288c2ecf20Sopenharmony_ci	if (ret == cnt)
3298c2ecf20Sopenharmony_ci		return -EFAULT;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	cnt -= ret;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	s->readpos += cnt;
3348c2ecf20Sopenharmony_ci	return cnt;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/**
3388c2ecf20Sopenharmony_ci * seq_buf_hex_dump - print formatted hex dump into the sequence buffer
3398c2ecf20Sopenharmony_ci * @s: seq_buf descriptor
3408c2ecf20Sopenharmony_ci * @prefix_str: string to prefix each line with;
3418c2ecf20Sopenharmony_ci *  caller supplies trailing spaces for alignment if desired
3428c2ecf20Sopenharmony_ci * @prefix_type: controls whether prefix of an offset, address, or none
3438c2ecf20Sopenharmony_ci *  is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
3448c2ecf20Sopenharmony_ci * @rowsize: number of bytes to print per line; must be 16 or 32
3458c2ecf20Sopenharmony_ci * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
3468c2ecf20Sopenharmony_ci * @buf: data blob to dump
3478c2ecf20Sopenharmony_ci * @len: number of bytes in the @buf
3488c2ecf20Sopenharmony_ci * @ascii: include ASCII after the hex output
3498c2ecf20Sopenharmony_ci *
3508c2ecf20Sopenharmony_ci * Function is an analogue of print_hex_dump() and thus has similar interface.
3518c2ecf20Sopenharmony_ci *
3528c2ecf20Sopenharmony_ci * linebuf size is maximal length for one line.
3538c2ecf20Sopenharmony_ci * 32 * 3 - maximum bytes per line, each printed into 2 chars + 1 for
3548c2ecf20Sopenharmony_ci *	separating space
3558c2ecf20Sopenharmony_ci * 2 - spaces separating hex dump and ascii representation
3568c2ecf20Sopenharmony_ci * 32 - ascii representation
3578c2ecf20Sopenharmony_ci * 1 - terminating '\0'
3588c2ecf20Sopenharmony_ci *
3598c2ecf20Sopenharmony_ci * Returns zero on success, -1 on overflow
3608c2ecf20Sopenharmony_ci */
3618c2ecf20Sopenharmony_ciint seq_buf_hex_dump(struct seq_buf *s, const char *prefix_str, int prefix_type,
3628c2ecf20Sopenharmony_ci		     int rowsize, int groupsize,
3638c2ecf20Sopenharmony_ci		     const void *buf, size_t len, bool ascii)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	const u8 *ptr = buf;
3668c2ecf20Sopenharmony_ci	int i, linelen, remaining = len;
3678c2ecf20Sopenharmony_ci	unsigned char linebuf[32 * 3 + 2 + 32 + 1];
3688c2ecf20Sopenharmony_ci	int ret;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (rowsize != 16 && rowsize != 32)
3718c2ecf20Sopenharmony_ci		rowsize = 16;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	for (i = 0; i < len; i += rowsize) {
3748c2ecf20Sopenharmony_ci		linelen = min(remaining, rowsize);
3758c2ecf20Sopenharmony_ci		remaining -= rowsize;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
3788c2ecf20Sopenharmony_ci				   linebuf, sizeof(linebuf), ascii);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		switch (prefix_type) {
3818c2ecf20Sopenharmony_ci		case DUMP_PREFIX_ADDRESS:
3828c2ecf20Sopenharmony_ci			ret = seq_buf_printf(s, "%s%p: %s\n",
3838c2ecf20Sopenharmony_ci			       prefix_str, ptr + i, linebuf);
3848c2ecf20Sopenharmony_ci			break;
3858c2ecf20Sopenharmony_ci		case DUMP_PREFIX_OFFSET:
3868c2ecf20Sopenharmony_ci			ret = seq_buf_printf(s, "%s%.8x: %s\n",
3878c2ecf20Sopenharmony_ci					     prefix_str, i, linebuf);
3888c2ecf20Sopenharmony_ci			break;
3898c2ecf20Sopenharmony_ci		default:
3908c2ecf20Sopenharmony_ci			ret = seq_buf_printf(s, "%s%s\n", prefix_str, linebuf);
3918c2ecf20Sopenharmony_ci			break;
3928c2ecf20Sopenharmony_ci		}
3938c2ecf20Sopenharmony_ci		if (ret)
3948c2ecf20Sopenharmony_ci			return ret;
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
398