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