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