xref: /netbsd-src/sys/modules/examples/pollpal/pollpal.c (revision e7bed289116c9b32f7fb79bfcc85271d4154b7e8)
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