1f697b943SJake Freeland /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3f697b943SJake Freeland  *
4f697b943SJake Freeland  * Copyright (c) 2022, Jake Freeland <jfree@freebsd.org>
5f697b943SJake Freeland  *
6f697b943SJake Freeland  * Redistribution and use in source and binary forms, with or without
7f697b943SJake Freeland  * modification, are permitted provided that the following conditions
8f697b943SJake Freeland  * are met:
9f697b943SJake Freeland  * 1. Redistributions of source code must retain the above copyright
10f697b943SJake Freeland  *    notice, this list of conditions and the following disclaimer.
11f697b943SJake Freeland  * 2. Redistributions in binary form must reproduce the above copyright
12f697b943SJake Freeland  *    notice, this list of conditions and the following disclaimer in the
13f697b943SJake Freeland  *    documentation and/or other materials provided with the distribution.
14f697b943SJake Freeland  *
15f697b943SJake Freeland  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16f697b943SJake Freeland  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17f697b943SJake Freeland  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18f697b943SJake Freeland  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19f697b943SJake Freeland  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20f697b943SJake Freeland  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21f697b943SJake Freeland  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22f697b943SJake Freeland  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23f697b943SJake Freeland  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24f697b943SJake Freeland  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25f697b943SJake Freeland  * SUCH DAMAGE.
26f697b943SJake Freeland  */
27f697b943SJake Freeland 
28f697b943SJake Freeland #include <sys/types.h>
29f697b943SJake Freeland #include <linux/fs.h>
30f697b943SJake Freeland 
31f697b943SJake Freeland MALLOC_DEFINE(M_LSATTR, "simple_attr", "Linux Simple Attribute File");
32f697b943SJake Freeland 
33f697b943SJake Freeland struct simple_attr {
34f697b943SJake Freeland 	int (*get)(void *, uint64_t *);
35f697b943SJake Freeland 	int (*set)(void *, uint64_t);
36f697b943SJake Freeland 	void *data;
37f697b943SJake Freeland 	const char *fmt;
38f697b943SJake Freeland 	struct mutex mutex;
39f697b943SJake Freeland };
40f697b943SJake Freeland 
41f697b943SJake Freeland /*
42f697b943SJake Freeland  * simple_attr_open: open and populate simple attribute data
43f697b943SJake Freeland  *
44f697b943SJake Freeland  * @inode: file inode
45f697b943SJake Freeland  * @filp: file pointer
46f697b943SJake Freeland  * @get: ->get() for reading file data
47f697b943SJake Freeland  * @set: ->set() for writing file data
48f697b943SJake Freeland  * @fmt: format specifier for data returned by @get
49f697b943SJake Freeland  *
50f697b943SJake Freeland  * Memory allocate a simple_attr and appropriately initialize its members.
51f697b943SJake Freeland  * The simple_attr must be stored in filp->private_data.
52f697b943SJake Freeland  * Simple attr files do not support seeking. Open the file as nonseekable.
53f697b943SJake Freeland  *
54f697b943SJake Freeland  * Return value: simple attribute file descriptor
55f697b943SJake Freeland  */
56f697b943SJake Freeland int
simple_attr_open(struct inode * inode,struct file * filp,int (* get)(void *,uint64_t *),int (* set)(void *,uint64_t),const char * fmt)57f697b943SJake Freeland simple_attr_open(struct inode *inode, struct file *filp,
58f697b943SJake Freeland     int (*get)(void *, uint64_t *), int (*set)(void *, uint64_t),
59f697b943SJake Freeland     const char *fmt)
60f697b943SJake Freeland {
61f697b943SJake Freeland 	struct simple_attr *sattr;
62f697b943SJake Freeland 	sattr = malloc(sizeof(*sattr), M_LSATTR, M_ZERO | M_NOWAIT);
63f697b943SJake Freeland 	if (sattr == NULL)
64f697b943SJake Freeland 		return (-ENOMEM);
65f697b943SJake Freeland 
66f697b943SJake Freeland 	sattr->get = get;
67f697b943SJake Freeland 	sattr->set = set;
68f697b943SJake Freeland 	sattr->data = inode->i_private;
69f697b943SJake Freeland 	sattr->fmt = fmt;
70f697b943SJake Freeland 	mutex_init(&sattr->mutex);
71f697b943SJake Freeland 
72f697b943SJake Freeland 	filp->private_data = (void *) sattr;
73f697b943SJake Freeland 
74f697b943SJake Freeland 	return (nonseekable_open(inode, filp));
75f697b943SJake Freeland }
76f697b943SJake Freeland 
77f697b943SJake Freeland int
simple_attr_release(struct inode * inode,struct file * filp)78f697b943SJake Freeland simple_attr_release(struct inode *inode, struct file *filp)
79f697b943SJake Freeland {
80f697b943SJake Freeland 	free(filp->private_data, M_LSATTR);
81f697b943SJake Freeland 	return (0);
82f697b943SJake Freeland }
83f697b943SJake Freeland 
84f697b943SJake Freeland /*
85f697b943SJake Freeland  * simple_attr_read: read simple attr data and transfer into buffer
86f697b943SJake Freeland  *
87f697b943SJake Freeland  * @filp: file pointer
88f697b943SJake Freeland  * @buf: kernel space buffer
89f697b943SJake Freeland  * @read_size: number of bytes to be transferred
90f697b943SJake Freeland  * @ppos: starting pointer position for transfer
91f697b943SJake Freeland  *
92f697b943SJake Freeland  * The simple_attr structure is stored in filp->private_data.
93f697b943SJake Freeland  * ->get() retrieves raw file data.
94f697b943SJake Freeland  * The ->fmt specifier can format this data to be human readable.
95f697b943SJake Freeland  * This output is then transferred into the @buf buffer.
96f697b943SJake Freeland  *
97f697b943SJake Freeland  * Return value:
98f697b943SJake Freeland  * On success, number of bytes transferred
99f697b943SJake Freeland  * On failure, negative signed ERRNO
100f697b943SJake Freeland  */
101f697b943SJake Freeland ssize_t
simple_attr_read(struct file * filp,char * buf,size_t read_size,loff_t * ppos)102f697b943SJake Freeland simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos)
103f697b943SJake Freeland {
104f697b943SJake Freeland 	struct simple_attr *sattr;
105f697b943SJake Freeland 	uint64_t data;
106f697b943SJake Freeland 	ssize_t ret;
107f697b943SJake Freeland 	char prebuf[24];
108f697b943SJake Freeland 
109f697b943SJake Freeland 	sattr = filp->private_data;
110f697b943SJake Freeland 
111f697b943SJake Freeland 	if (sattr->get == NULL)
112f697b943SJake Freeland 		return (-EFAULT);
113f697b943SJake Freeland 
114f697b943SJake Freeland 	mutex_lock(&sattr->mutex);
115f697b943SJake Freeland 
116f697b943SJake Freeland 	ret = sattr->get(sattr->data, &data);
117f697b943SJake Freeland 	if (ret)
118f697b943SJake Freeland 		goto unlock;
119f697b943SJake Freeland 
120f697b943SJake Freeland 	scnprintf(prebuf, sizeof(prebuf), sattr->fmt, data);
121f697b943SJake Freeland 
122f697b943SJake Freeland 	ret = strlen(prebuf) + 1;
123f697b943SJake Freeland 	if (*ppos >= ret || read_size < 1) {
124f697b943SJake Freeland 		ret = -EINVAL;
125f697b943SJake Freeland 		goto unlock;
126f697b943SJake Freeland 	}
127f697b943SJake Freeland 
128f697b943SJake Freeland 	read_size = min(ret - *ppos, read_size);
129f697b943SJake Freeland 	ret = strscpy(buf, prebuf + *ppos, read_size);
130f697b943SJake Freeland 
131f697b943SJake Freeland 	/* add 1 for null terminator */
132f697b943SJake Freeland 	if (ret > 0)
133f697b943SJake Freeland 		ret += 1;
134f697b943SJake Freeland 
135f697b943SJake Freeland unlock:
136f697b943SJake Freeland 	mutex_unlock(&sattr->mutex);
137f697b943SJake Freeland 	return (ret);
138f697b943SJake Freeland }
139f697b943SJake Freeland 
140f697b943SJake Freeland /*
141f697b943SJake Freeland  * simple_attr_write: write contents of buffer into simple attribute file
142f697b943SJake Freeland  *
143f697b943SJake Freeland  * @filp: file pointer
144f697b943SJake Freeland  * @buf: kernel space buffer
145f697b943SJake Freeland  * @write_size: number bytes to be transferred
146f697b943SJake Freeland  * @ppos: starting pointer position for transfer
147f697b943SJake Freeland  *
148f697b943SJake Freeland  * The simple_attr structure is stored in filp->private_data.
149f697b943SJake Freeland  * Convert the @buf string to unsigned long long.
150f697b943SJake Freeland  * ->set() writes unsigned long long data into the simple attr file.
151f697b943SJake Freeland  *
152f697b943SJake Freeland  * Return value:
153f697b943SJake Freeland  * On success, number of bytes written to simple attr
154f697b943SJake Freeland  * On failure, negative signed ERRNO
155f697b943SJake Freeland  */
156f697b943SJake Freeland ssize_t
simple_attr_write(struct file * filp,const char * buf,size_t write_size,loff_t * ppos)157f697b943SJake Freeland simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t *ppos)
158f697b943SJake Freeland {
159f697b943SJake Freeland 	struct simple_attr *sattr;
160f697b943SJake Freeland 	unsigned long long data;
161f697b943SJake Freeland 	size_t bufsize;
162f697b943SJake Freeland 	ssize_t ret;
163f697b943SJake Freeland 
164f697b943SJake Freeland 	sattr = filp->private_data;
165f697b943SJake Freeland 	bufsize = strlen(buf) + 1;
166f697b943SJake Freeland 
167f697b943SJake Freeland 	if (sattr->set == NULL)
168f697b943SJake Freeland 		return (-EFAULT);
169f697b943SJake Freeland 
170f697b943SJake Freeland 	if (*ppos >= bufsize || write_size < 1)
171f697b943SJake Freeland 		return (-EINVAL);
172f697b943SJake Freeland 
173f697b943SJake Freeland 	mutex_lock(&sattr->mutex);
174f697b943SJake Freeland 
175f697b943SJake Freeland 	ret = kstrtoull(buf + *ppos, 0, &data);
176f697b943SJake Freeland 	if (ret)
177f697b943SJake Freeland 		goto unlock;
178f697b943SJake Freeland 
179f697b943SJake Freeland 	ret = sattr->set(sattr->data, data);
180f697b943SJake Freeland 	if (ret)
181f697b943SJake Freeland 		goto unlock;
182f697b943SJake Freeland 
183f697b943SJake Freeland 	ret = bufsize - *ppos;
184f697b943SJake Freeland 
185f697b943SJake Freeland unlock:
186f697b943SJake Freeland 	mutex_unlock(&sattr->mutex);
187f697b943SJake Freeland 	return (ret);
188f697b943SJake Freeland }
189