/* $NetBSD: pollpal.c,v 1.4 2022/03/28 12:33:22 riastradh Exp $ */ /*- * Copyright (c) 2020 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD: pollpal.c,v 1.4 2022/03/28 12:33:22 riastradh Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Create a device /dev/pollpal * * To use this device you need to do: * mknod /dev/pollpal c 351 0 * */ dev_type_open(pollpal_open); static struct cdevsw pollpal_cdevsw = { .d_open = pollpal_open, .d_close = noclose, .d_read = noread, .d_write = nowrite, .d_ioctl = noioctl, .d_stop = nostop, .d_tty = notty, .d_poll = nopoll, .d_mmap = nommap, .d_kqfilter = nokqfilter, .d_discard = nodiscard, .d_flag = D_OTHER }; static int pollpal_nopen = 0; static int pollpal_close(file_t *); static int pollpal_write(file_t *, off_t *, struct uio *, kauth_cred_t, int); static int pollpal_read(file_t *, off_t *, struct uio *, kauth_cred_t, int); static int pollpal_poll(file_t *, int); const struct fileops pollpal_fileops = { .fo_read = pollpal_read, .fo_write = pollpal_write, .fo_ioctl = fbadop_ioctl, .fo_fcntl = fnullop_fcntl, .fo_poll = pollpal_poll, .fo_stat = fbadop_stat, .fo_close = pollpal_close, .fo_kqfilter = fnullop_kqfilter, .fo_restart = fnullop_restart, }; typedef struct pollpal_softc { kmutex_t lock; kcondvar_t sc_cv; struct selinfo psel; char *buf; size_t buf_len; /* Device can have two states 1.READ_WAITING, 2.WRITE_WAITING. */ enum states { READ_WAITING = 0, WRITE_WAITING = 1 } sc_state; } pal_t; static int check_pal(const char *str, size_t len) { size_t n; n = 0; while (n <= len / 2) { if (str[n] != str[len - n - 1]) return 0; n++; } return 1; } int pollpal_open(dev_t dev, int flag, int mode, struct lwp *l __unused) { struct file *fp; int error, fd; pal_t *pl; error = fd_allocfile(&fp, &fd); if (error) return error; ++pollpal_nopen; pl = kmem_zalloc(sizeof(*pl), KM_SLEEP); pl->sc_state = READ_WAITING; mutex_init(&pl->lock, MUTEX_DEFAULT, IPL_NONE); cv_init(&pl->sc_cv, "sc_cv"); selinit(&pl->psel); return fd_clone(fp, fd, flag, &pollpal_fileops, pl); } int pollpal_close(file_t * fp) { pal_t * pl = fp->f_data; KASSERT(pl != NULL); if (pl->buf != NULL) kmem_free(pl->buf, pl->buf_len); seldestroy(&pl->psel); cv_destroy(&pl->sc_cv); mutex_destroy(&pl->lock); kmem_free(pl, sizeof(*pl)); --pollpal_nopen; return 0; } /* * Device would write only in READ_WAITING state and then update the state to * WRITE_WAITING. */ int pollpal_write(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred, int flag) { pal_t *pl = fp->f_data; int error; error = 0; /* To ensure that only single process can write in device at a time. */ mutex_enter(&pl->lock); cv_broadcast(&pl->sc_cv); switch (pl->sc_state) { case READ_WAITING: pl->sc_state = WRITE_WAITING; selnotify(&pl->psel, POLLOUT | POLLWRNORM, 0); while (pl->sc_state == WRITE_WAITING) { if (pl->buf) { error = cv_wait_sig(&pl->sc_cv, &pl->lock); if (error) goto ret; } pl->buf_len = uio->uio_iov->iov_len; pl->buf = kmem_alloc(pl->buf_len, KM_SLEEP); uiomove(pl->buf, pl->buf_len, uio); printf("Use cat to know the result.\n"); break; } break; case WRITE_WAITING: printf("State: WRITE_WAITING\n"); break; } ret: mutex_exit(&pl->lock); return error; } /* * Device would read only in WRITE_WAITING state and then update the state to * READ_WAITING. */ int pollpal_read(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred, int flags) { pal_t *pl = fp->f_data; int error; error = 0; /* To ensure that only single process can read device at a time. */ mutex_enter(&pl->lock); cv_broadcast(&pl->sc_cv); switch (pl->sc_state) { case READ_WAITING: printf("State: READ_WAITING\n"); printf("You need to write something to the module first!\n"); goto ret; case WRITE_WAITING: pl->sc_state = READ_WAITING; selnotify(&pl->psel, POLLIN | POLLRDNORM, 0); while (pl->sc_state == READ_WAITING) { if (!pl->buf) { error = cv_wait_sig(&pl->sc_cv, &pl->lock); if (error) goto ret; } printf("The string you entered was: "); uiomove(pl->buf, pl->buf_len, uio); printf("\n"); if (check_pal(pl->buf, pl->buf_len)) printf("String '%s' was a palindrome.\n", pl->buf); else printf("String '%s' was not a palindrome.\n", pl->buf); break; } break; } kmem_free(pl->buf, pl->buf_len); pl->buf = NULL; ret: mutex_exit(&pl->lock); return error; } int pollpal_poll(struct file *fp, int events) { pal_t *pl = fp->f_data; int revents; revents = 0; mutex_enter(&pl->lock); switch (pl->sc_state) { case READ_WAITING: if (events & (POLLOUT | POLLWRNORM)) { /* When device is in READ_WAITING state it can write */ revents |= POLLOUT | POLLWRNORM; } else { /* Record the request if it wasn't satisfied. */ selrecord(curlwp, &pl->psel); } break; case WRITE_WAITING: if (events & (POLLIN | POLLRDNORM)) { /* When device is in WRITE_WAITING state it can read. */ revents |= POLLIN | POLLRDNORM; } else { /* Record the request if it wasn't satisfied. */ selrecord(curlwp, &pl->psel); } break; } mutex_exit(&pl->lock); return revents; } MODULE(MODULE_CLASS_MISC, pollpal, NULL); static int pollpal_modcmd(modcmd_t cmd, void *arg __unused) { int cmajor = 351, bmajor = -1; switch (cmd) { case MODULE_CMD_INIT: if (devsw_attach("pollpal", NULL, &bmajor, &pollpal_cdevsw, &cmajor)) return ENXIO; return 0; case MODULE_CMD_FINI: if (pollpal_nopen != 0) return EBUSY; devsw_detach(NULL, &pollpal_cdevsw); return 0; default: return ENOTTY; } }