1*e7bed289Sriastradh /* $NetBSD: pollpal.c,v 1.4 2022/03/28 12:33:22 riastradh Exp $ */
2b902ab58Schristos
3b902ab58Schristos /*-
4b902ab58Schristos * Copyright (c) 2020 The NetBSD Foundation, Inc.
5b902ab58Schristos * All rights reserved.
6b902ab58Schristos *
7b902ab58Schristos * Redistribution and use in source and binary forms, with or without
8b902ab58Schristos * modification, are permitted provided that the following conditions
9b902ab58Schristos * are met:
10b902ab58Schristos * 1. Redistributions of source code must retain the above copyright
11b902ab58Schristos * notice, this list of conditions and the following disclaimer.
12b902ab58Schristos * 2. Redistributions in binary form must reproduce the above copyright
13b902ab58Schristos * notice, this list of conditions and the following disclaimer in the
14b902ab58Schristos * documentation and/or other materials provided with the distribution.
15b902ab58Schristos *
16b902ab58Schristos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17b902ab58Schristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18b902ab58Schristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19b902ab58Schristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20b902ab58Schristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21b902ab58Schristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22b902ab58Schristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23b902ab58Schristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24b902ab58Schristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25b902ab58Schristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26b902ab58Schristos * POSSIBILITY OF SUCH DAMAGE.
27b902ab58Schristos */
28b902ab58Schristos
29b902ab58Schristos #include <sys/cdefs.h>
30*e7bed289Sriastradh __KERNEL_RCSID(0, "$NetBSD: pollpal.c,v 1.4 2022/03/28 12:33:22 riastradh Exp $");
31b902ab58Schristos
32b902ab58Schristos #include <sys/module.h>
33b902ab58Schristos #include <sys/param.h>
34b902ab58Schristos #include <sys/kernel.h>
35b902ab58Schristos #include <sys/systm.h>
360a33e04eSmrg #include <sys/lwp.h>
37b902ab58Schristos
38b902ab58Schristos #include <sys/condvar.h>
39b902ab58Schristos #include <sys/conf.h>
40b902ab58Schristos #include <sys/device.h>
41b902ab58Schristos #include <sys/file.h>
42b902ab58Schristos #include <sys/filedesc.h>
43b902ab58Schristos #include <sys/mutex.h>
44b902ab58Schristos #include <sys/kmem.h>
45b902ab58Schristos #include <sys/poll.h>
46b902ab58Schristos #include <sys/select.h>
47b902ab58Schristos
48b902ab58Schristos /*
49b902ab58Schristos * Create a device /dev/pollpal
50b902ab58Schristos *
51b902ab58Schristos * To use this device you need to do:
52b902ab58Schristos * mknod /dev/pollpal c 351 0
53b902ab58Schristos *
54b902ab58Schristos */
55b902ab58Schristos
56b902ab58Schristos dev_type_open(pollpal_open);
57b902ab58Schristos
58b902ab58Schristos static struct cdevsw pollpal_cdevsw = {
59b902ab58Schristos .d_open = pollpal_open,
60b902ab58Schristos .d_close = noclose,
61b902ab58Schristos .d_read = noread,
62b902ab58Schristos .d_write = nowrite,
63b902ab58Schristos .d_ioctl = noioctl,
64b902ab58Schristos .d_stop = nostop,
65b902ab58Schristos .d_tty = notty,
66b902ab58Schristos .d_poll = nopoll,
67b902ab58Schristos .d_mmap = nommap,
68b902ab58Schristos .d_kqfilter = nokqfilter,
69b902ab58Schristos .d_discard = nodiscard,
70b902ab58Schristos .d_flag = D_OTHER
71b902ab58Schristos };
72b902ab58Schristos
73b902ab58Schristos static int pollpal_nopen = 0;
74b902ab58Schristos
75b902ab58Schristos static int pollpal_close(file_t *);
76b902ab58Schristos static int pollpal_write(file_t *, off_t *, struct uio *, kauth_cred_t,
77b902ab58Schristos int);
78b902ab58Schristos static int pollpal_read(file_t *, off_t *, struct uio *, kauth_cred_t,
79b902ab58Schristos int);
80b902ab58Schristos static int pollpal_poll(file_t *, int);
81b902ab58Schristos
82b902ab58Schristos const struct fileops pollpal_fileops = {
83b902ab58Schristos .fo_read = pollpal_read,
84b902ab58Schristos .fo_write = pollpal_write,
85b902ab58Schristos .fo_ioctl = fbadop_ioctl,
86b902ab58Schristos .fo_fcntl = fnullop_fcntl,
87b902ab58Schristos .fo_poll = pollpal_poll,
88b902ab58Schristos .fo_stat = fbadop_stat,
89b902ab58Schristos .fo_close = pollpal_close,
90b902ab58Schristos .fo_kqfilter = fnullop_kqfilter,
91b902ab58Schristos .fo_restart = fnullop_restart,
92b902ab58Schristos };
93b902ab58Schristos
94b902ab58Schristos typedef struct pollpal_softc {
95b902ab58Schristos kmutex_t lock;
96b902ab58Schristos kcondvar_t sc_cv;
97b902ab58Schristos struct selinfo psel;
98b902ab58Schristos char *buf;
99b902ab58Schristos size_t buf_len;
100b902ab58Schristos /* Device can have two states 1.READ_WAITING, 2.WRITE_WAITING. */
101b902ab58Schristos enum states {
102b902ab58Schristos READ_WAITING = 0,
103b902ab58Schristos WRITE_WAITING = 1
104b902ab58Schristos } sc_state;
105b902ab58Schristos } pal_t;
106b902ab58Schristos
107b902ab58Schristos static int
check_pal(const char * str,size_t len)108b902ab58Schristos check_pal(const char *str, size_t len)
109b902ab58Schristos {
110b902ab58Schristos size_t n;
111b902ab58Schristos
112b902ab58Schristos n = 0;
113b902ab58Schristos
114b902ab58Schristos while (n <= len / 2) {
115b902ab58Schristos if (str[n] != str[len - n - 1])
116b902ab58Schristos return 0;
117b902ab58Schristos
118b902ab58Schristos n++;
119b902ab58Schristos }
120b902ab58Schristos
121b902ab58Schristos return 1;
122b902ab58Schristos }
123b902ab58Schristos
124b902ab58Schristos int
pollpal_open(dev_t dev,int flag,int mode,struct lwp * l __unused)125b902ab58Schristos pollpal_open(dev_t dev, int flag, int mode, struct lwp *l __unused)
126b902ab58Schristos {
127b902ab58Schristos struct file *fp;
128b902ab58Schristos int error, fd;
129b902ab58Schristos pal_t *pl;
130b902ab58Schristos
131b902ab58Schristos error = fd_allocfile(&fp, &fd);
132b902ab58Schristos if (error)
133b902ab58Schristos return error;
134b902ab58Schristos
135b902ab58Schristos ++pollpal_nopen;
136b902ab58Schristos
137b902ab58Schristos pl = kmem_zalloc(sizeof(*pl), KM_SLEEP);
138b902ab58Schristos
139b902ab58Schristos pl->sc_state = READ_WAITING;
140b902ab58Schristos mutex_init(&pl->lock, MUTEX_DEFAULT, IPL_NONE);
141b902ab58Schristos cv_init(&pl->sc_cv, "sc_cv");
142b902ab58Schristos selinit(&pl->psel);
143b902ab58Schristos
144b902ab58Schristos return fd_clone(fp, fd, flag, &pollpal_fileops, pl);
145b902ab58Schristos }
146b902ab58Schristos
147b902ab58Schristos int
pollpal_close(file_t * fp)148b902ab58Schristos pollpal_close(file_t * fp)
149b902ab58Schristos {
150b902ab58Schristos pal_t * pl = fp->f_data;
151b902ab58Schristos KASSERT(pl != NULL);
152b902ab58Schristos
153b902ab58Schristos if (pl->buf != NULL)
154b902ab58Schristos kmem_free(pl->buf, pl->buf_len);
155b902ab58Schristos
156b902ab58Schristos seldestroy(&pl->psel);
157b902ab58Schristos cv_destroy(&pl->sc_cv);
158b902ab58Schristos mutex_destroy(&pl->lock);
159b902ab58Schristos kmem_free(pl, sizeof(*pl));
160b902ab58Schristos
161b902ab58Schristos --pollpal_nopen;
162b902ab58Schristos
163b902ab58Schristos return 0;
164b902ab58Schristos }
165b902ab58Schristos
166b902ab58Schristos /*
167b902ab58Schristos * Device would write only in READ_WAITING state and then update the state to
168b902ab58Schristos * WRITE_WAITING.
169b902ab58Schristos */
170b902ab58Schristos int
pollpal_write(file_t * fp,off_t * offset,struct uio * uio,kauth_cred_t cred,int flag)171b902ab58Schristos pollpal_write(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred,
172b902ab58Schristos int flag)
173b902ab58Schristos {
174b902ab58Schristos pal_t *pl = fp->f_data;
175b902ab58Schristos int error;
176b902ab58Schristos
177b902ab58Schristos error = 0;
178b902ab58Schristos
179b902ab58Schristos /* To ensure that only single process can write in device at a time. */
180b902ab58Schristos mutex_enter(&pl->lock);
181b902ab58Schristos cv_broadcast(&pl->sc_cv);
182b902ab58Schristos switch (pl->sc_state) {
183b902ab58Schristos case READ_WAITING:
184b902ab58Schristos pl->sc_state = WRITE_WAITING;
185b902ab58Schristos selnotify(&pl->psel, POLLOUT | POLLWRNORM, 0);
186b902ab58Schristos while (pl->sc_state == WRITE_WAITING) {
187b902ab58Schristos if (pl->buf) {
188b902ab58Schristos error = cv_wait_sig(&pl->sc_cv, &pl->lock);
189b902ab58Schristos if (error)
190b902ab58Schristos goto ret;
191b902ab58Schristos
192b902ab58Schristos }
193b902ab58Schristos
194b902ab58Schristos pl->buf_len = uio->uio_iov->iov_len;
195b902ab58Schristos pl->buf = kmem_alloc(pl->buf_len, KM_SLEEP);
196b902ab58Schristos uiomove(pl->buf, pl->buf_len, uio);
197b902ab58Schristos printf("Use cat to know the result.\n");
198b902ab58Schristos break;
199b902ab58Schristos }
200b902ab58Schristos break;
201b902ab58Schristos case WRITE_WAITING:
202b902ab58Schristos printf("State: WRITE_WAITING\n");
203b902ab58Schristos break;
204b902ab58Schristos }
205b902ab58Schristos
206b902ab58Schristos ret:
207b902ab58Schristos mutex_exit(&pl->lock);
208b902ab58Schristos return error;
209b902ab58Schristos }
210b902ab58Schristos
211b902ab58Schristos /*
212b902ab58Schristos * Device would read only in WRITE_WAITING state and then update the state to
213b902ab58Schristos * READ_WAITING.
214b902ab58Schristos */
215b902ab58Schristos int
pollpal_read(file_t * fp,off_t * offset,struct uio * uio,kauth_cred_t cred,int flags)216b902ab58Schristos pollpal_read(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred,
217b902ab58Schristos int flags)
218b902ab58Schristos {
219b902ab58Schristos pal_t *pl = fp->f_data;
220b902ab58Schristos int error;
221b902ab58Schristos
222b902ab58Schristos error = 0;
223b902ab58Schristos
224b902ab58Schristos /* To ensure that only single process can read device at a time. */
225b902ab58Schristos mutex_enter(&pl->lock);
226b902ab58Schristos cv_broadcast(&pl->sc_cv);
227b902ab58Schristos switch (pl->sc_state) {
228b902ab58Schristos case READ_WAITING:
229b902ab58Schristos printf("State: READ_WAITING\n");
230b902ab58Schristos printf("You need to write something to the module first!\n");
231b902ab58Schristos goto ret;
232b902ab58Schristos case WRITE_WAITING:
233b902ab58Schristos pl->sc_state = READ_WAITING;
234b902ab58Schristos selnotify(&pl->psel, POLLIN | POLLRDNORM, 0);
235b902ab58Schristos while (pl->sc_state == READ_WAITING) {
236b902ab58Schristos if (!pl->buf) {
237b902ab58Schristos error = cv_wait_sig(&pl->sc_cv, &pl->lock);
238b902ab58Schristos if (error)
239b902ab58Schristos goto ret;
240b902ab58Schristos
241b902ab58Schristos }
242b902ab58Schristos printf("The string you entered was: ");
243b902ab58Schristos uiomove(pl->buf, pl->buf_len, uio);
244b902ab58Schristos printf("\n");
245b902ab58Schristos if (check_pal(pl->buf, pl->buf_len))
246b902ab58Schristos printf("String '%s' was a palindrome.\n",
247b902ab58Schristos pl->buf);
248b902ab58Schristos else
249b902ab58Schristos printf("String '%s' was not a palindrome.\n",
250b902ab58Schristos pl->buf);
251b902ab58Schristos
252b902ab58Schristos break;
253b902ab58Schristos }
254b902ab58Schristos break;
255b902ab58Schristos }
256b902ab58Schristos kmem_free(pl->buf, pl->buf_len);
257b902ab58Schristos pl->buf = NULL;
258b902ab58Schristos
259b902ab58Schristos ret:
260b902ab58Schristos mutex_exit(&pl->lock);
261b902ab58Schristos return error;
262b902ab58Schristos }
263b902ab58Schristos
264b902ab58Schristos int
pollpal_poll(struct file * fp,int events)265b902ab58Schristos pollpal_poll(struct file *fp, int events)
266b902ab58Schristos {
267b902ab58Schristos pal_t *pl = fp->f_data;
268b902ab58Schristos int revents;
269b902ab58Schristos
270b902ab58Schristos revents = 0;
271b902ab58Schristos
272b902ab58Schristos mutex_enter(&pl->lock);
273b902ab58Schristos switch (pl->sc_state) {
274b902ab58Schristos case READ_WAITING:
275b902ab58Schristos if (events & (POLLOUT | POLLWRNORM)) {
276b902ab58Schristos /* When device is in READ_WAITING state it can write */
277b902ab58Schristos revents |= POLLOUT | POLLWRNORM;
278b902ab58Schristos } else {
279b902ab58Schristos /* Record the request if it wasn't satisfied. */
280b902ab58Schristos selrecord(curlwp, &pl->psel);
281b902ab58Schristos }
282b902ab58Schristos break;
283b902ab58Schristos case WRITE_WAITING:
284226be24aSchristos if (events & (POLLIN | POLLRDNORM)) {
285b902ab58Schristos /* When device is in WRITE_WAITING state it can read. */
286226be24aSchristos revents |= POLLIN | POLLRDNORM;
287b902ab58Schristos } else {
288b902ab58Schristos /* Record the request if it wasn't satisfied. */
289b902ab58Schristos selrecord(curlwp, &pl->psel);
290b902ab58Schristos }
291b902ab58Schristos break;
292b902ab58Schristos }
293b902ab58Schristos
294b902ab58Schristos mutex_exit(&pl->lock);
295b902ab58Schristos return revents;
296b902ab58Schristos }
297b902ab58Schristos
298b902ab58Schristos MODULE(MODULE_CLASS_MISC, pollpal, NULL);
299b902ab58Schristos
300b902ab58Schristos static int
pollpal_modcmd(modcmd_t cmd,void * arg __unused)301b902ab58Schristos pollpal_modcmd(modcmd_t cmd, void *arg __unused)
302b902ab58Schristos {
303b902ab58Schristos int cmajor = 351, bmajor = -1;
304b902ab58Schristos
305b902ab58Schristos switch (cmd) {
306b902ab58Schristos case MODULE_CMD_INIT:
307b902ab58Schristos if (devsw_attach("pollpal", NULL, &bmajor, &pollpal_cdevsw,
308b902ab58Schristos &cmajor))
309b902ab58Schristos return ENXIO;
310b902ab58Schristos return 0;
311b902ab58Schristos case MODULE_CMD_FINI:
312b902ab58Schristos if (pollpal_nopen != 0)
313b902ab58Schristos return EBUSY;
314*e7bed289Sriastradh devsw_detach(NULL, &pollpal_cdevsw);
315*e7bed289Sriastradh return 0;
316b902ab58Schristos default:
317b902ab58Schristos return ENOTTY;
318b902ab58Schristos }
319b902ab58Schristos }
320