1 /* $NetBSD: button.c,v 1.9 2017/10/25 08:12:37 maya Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: button.c,v 1.9 2017/10/25 08:12:37 maya Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/conf.h> 43 #include <sys/systm.h> 44 #include <sys/queue.h> 45 #include <sys/mutex.h> 46 #include <sys/errno.h> 47 #include <sys/fcntl.h> 48 #include <sys/callout.h> 49 #include <sys/kernel.h> 50 #include <sys/once.h> 51 #include <sys/poll.h> 52 #include <sys/select.h> 53 #include <sys/vnode.h> 54 55 #include <machine/button.h> 56 57 #include <landisk/dev/buttonvar.h> 58 59 /* 60 * event handler 61 */ 62 static ONCE_DECL(btn_once); 63 static LIST_HEAD(, btn_event) btn_event_list; 64 static kmutex_t btn_event_list_lock; 65 66 static struct lwp *btn_daemon; 67 68 #define BTN_MAX_EVENTS 32 69 70 static kmutex_t btn_event_queue_lock; 71 static kcondvar_t btn_event_queue_cv; 72 73 static button_event_t btn_event_queue[BTN_MAX_EVENTS]; 74 static int btn_event_queue_head; 75 static int btn_event_queue_tail; 76 static int btn_event_queue_count; 77 static int btn_event_queue_flags; 78 static struct selinfo btn_event_queue_selinfo; 79 80 static char btn_type[32]; 81 82 #define BEVQ_F_WAITING 0x01 /* daemon waiting for event */ 83 84 #define BTN_NEXT_EVENT(x) (((x) + 1) / BTN_MAX_EVENTS) 85 86 dev_type_open(btnopen); 87 dev_type_close(btnclose); 88 dev_type_ioctl(btnioctl); 89 dev_type_read(btnread); 90 dev_type_poll(btnpoll); 91 dev_type_kqfilter(btnkqfilter); 92 93 const struct cdevsw button_cdevsw = { 94 .d_open = btnopen, 95 .d_close = btnclose, 96 .d_read = btnread, 97 .d_write = nowrite, 98 .d_ioctl = btnioctl, 99 .d_stop = nostop, 100 .d_tty = notty, 101 .d_poll = btnpoll, 102 .d_mmap = nommap, 103 .d_kqfilter = btnkqfilter, 104 .d_discard = nodiscard, 105 .d_flag = 0 106 }; 107 108 static int 109 btn_init(void) 110 { 111 112 LIST_INIT(&btn_event_list); 113 mutex_init(&btn_event_list_lock, MUTEX_DEFAULT, IPL_NONE); 114 mutex_init(&btn_event_queue_lock, MUTEX_DEFAULT, IPL_NONE); 115 cv_init(&btn_event_queue_cv, "btncv"); 116 selinit(&btn_event_queue_selinfo); 117 118 return 0; 119 } 120 121 static int 122 btn_queue_event(button_event_t *bev) 123 { 124 125 if (btn_event_queue_count == BTN_MAX_EVENTS) 126 return (0); 127 128 btn_event_queue[btn_event_queue_head] = *bev; 129 btn_event_queue_head = BTN_NEXT_EVENT(btn_event_queue_head); 130 btn_event_queue_count++; 131 132 return (1); 133 } 134 135 static int 136 btn_get_event(button_event_t *bev) 137 { 138 139 if (btn_event_queue_count == 0) 140 return (0); 141 142 *bev = btn_event_queue[btn_event_queue_tail]; 143 btn_event_queue_tail = BTN_NEXT_EVENT(btn_event_queue_tail); 144 btn_event_queue_count--; 145 146 return (1); 147 } 148 149 static void 150 btn_event_queue_flush(void) 151 { 152 153 btn_event_queue_head = 0; 154 btn_event_queue_tail = 0; 155 btn_event_queue_count = 0; 156 btn_event_queue_flags = 0; 157 } 158 159 int 160 btnopen(dev_t dev, int flag, int mode, struct lwp *l) 161 { 162 int error; 163 164 error = RUN_ONCE(&btn_once, btn_init); 165 if (error) { 166 return error; 167 } 168 169 if (minor(dev) != 0) { 170 return (ENODEV); 171 } 172 173 mutex_enter(&btn_event_queue_lock); 174 if (btn_daemon != NULL) { 175 error = EBUSY; 176 } else { 177 error = 0; 178 btn_daemon = l; 179 btn_event_queue_flush(); 180 } 181 mutex_exit(&btn_event_queue_lock); 182 183 return (error); 184 } 185 186 int 187 btnclose(dev_t dev, int flag, int mode, struct lwp *l) 188 { 189 int count; 190 191 if (minor(dev) != 0) { 192 return (ENODEV); 193 } 194 195 mutex_enter(&btn_event_queue_lock); 196 count = btn_event_queue_count; 197 btn_daemon = NULL; 198 btn_event_queue_flush(); 199 mutex_exit(&btn_event_queue_lock); 200 201 if (count) { 202 printf("WARNING: %d events lost by exiting daemon\n", count); 203 } 204 205 return (0); 206 } 207 208 int 209 btnioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 210 { 211 int error = 0; 212 213 if (minor(dev) != 0) { 214 return (ENODEV); 215 } 216 217 switch (cmd) { 218 case BUTTON_IOC_GET_TYPE: 219 { 220 struct button_type *button_type = (void *)data; 221 strcpy(button_type->button_type, btn_type); 222 break; 223 } 224 225 default: 226 error = ENOTTY; 227 break; 228 } 229 230 return (error); 231 } 232 233 int 234 btnread(dev_t dev, struct uio *uio, int flags) 235 { 236 button_event_t bev; 237 int error; 238 239 if (minor(dev) != 0) { 240 return (ENODEV); 241 } 242 243 if (uio->uio_resid != BUTTON_EVENT_MSG_SIZE) { 244 return (EINVAL); 245 } 246 247 mutex_enter(&btn_event_queue_lock); 248 for (;;) { 249 if (btn_get_event(&bev)) { 250 mutex_exit(&btn_event_queue_lock); 251 return (uiomove(&bev, BUTTON_EVENT_MSG_SIZE, uio)); 252 } 253 254 if (flags & IO_NDELAY) { 255 mutex_exit(&btn_event_queue_lock); 256 return (EWOULDBLOCK); 257 } 258 259 btn_event_queue_flags |= BEVQ_F_WAITING; 260 error = cv_wait_sig(&btn_event_queue_cv, &btn_event_queue_lock); 261 if (error) { 262 mutex_exit(&btn_event_queue_lock); 263 return (error); 264 } 265 } 266 } 267 268 int 269 btnpoll(dev_t dev, int events, struct lwp *l) 270 { 271 int revents; 272 273 if (minor(dev) != 0) { 274 return (ENODEV); 275 } 276 277 revents = events & (POLLOUT | POLLWRNORM); 278 279 /* Attempt to save some work. */ 280 if ((events & (POLLIN | POLLRDNORM)) == 0) 281 return (revents); 282 283 mutex_enter(&btn_event_queue_lock); 284 if (btn_event_queue_count) { 285 revents |= events & (POLLIN | POLLRDNORM); 286 } else { 287 selrecord(l, &btn_event_queue_selinfo); 288 } 289 mutex_exit(&btn_event_queue_lock); 290 291 return (revents); 292 } 293 294 static void 295 filt_btn_rdetach(struct knote *kn) 296 { 297 298 mutex_enter(&btn_event_queue_lock); 299 SLIST_REMOVE(&btn_event_queue_selinfo.sel_klist, 300 kn, knote, kn_selnext); 301 mutex_exit(&btn_event_queue_lock); 302 } 303 304 static int 305 filt_btn_read(struct knote *kn, long hint) 306 { 307 308 mutex_enter(&btn_event_queue_lock); 309 kn->kn_data = btn_event_queue_count; 310 mutex_exit(&btn_event_queue_lock); 311 312 return (kn->kn_data > 0); 313 } 314 315 static const struct filterops btn_read_filtops = { 316 .f_isfd = 1, 317 .f_attach = NULL, 318 .f_detach = filt_btn_rdetach, 319 .f_event = filt_btn_read, 320 }; 321 322 static const struct filterops btn_write_filtops = { 323 .f_isfd = 1, 324 .f_attach = NULL, 325 .f_detach = filt_btn_rdetach, 326 .f_event = filt_seltrue, 327 }; 328 329 int 330 btnkqfilter(dev_t dev, struct knote *kn) 331 { 332 struct klist *klist; 333 334 if (minor(dev) != 0) { 335 return (ENODEV); 336 } 337 338 switch (kn->kn_filter) { 339 case EVFILT_READ: 340 klist = &btn_event_queue_selinfo.sel_klist; 341 kn->kn_fop = &btn_read_filtops; 342 break; 343 344 case EVFILT_WRITE: 345 klist = &btn_event_queue_selinfo.sel_klist; 346 kn->kn_fop = &btn_write_filtops; 347 break; 348 349 default: 350 return (1); 351 } 352 353 mutex_enter(&btn_event_queue_lock); 354 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 355 mutex_exit(&btn_event_queue_lock); 356 357 return (0); 358 } 359 360 void 361 btn_settype(const char *type) 362 { 363 364 /* 365 * Don't bother locking this; it's going to be set 366 * during autoconfiguration, and then only read from 367 * then on. 368 */ 369 strlcpy(btn_type, type, sizeof(btn_type)); 370 } 371 372 int 373 btn_event_register(struct btn_event *bev) 374 { 375 376 mutex_enter(&btn_event_list_lock); 377 LIST_INSERT_HEAD(&btn_event_list, bev, bev_list); 378 mutex_exit(&btn_event_list_lock); 379 380 return (0); 381 } 382 383 void 384 btn_event_unregister(struct btn_event *bev) 385 { 386 387 mutex_enter(&btn_event_list_lock); 388 LIST_REMOVE(bev, bev_list); 389 mutex_exit(&btn_event_list_lock); 390 } 391 392 void 393 btn_event_send(struct btn_event *bev, int event) 394 { 395 button_event_t btnev; 396 int rv; 397 398 mutex_enter(&btn_event_queue_lock); 399 if (btn_daemon == NULL) { 400 mutex_exit(&btn_event_queue_lock); 401 printf("%s: btn_event_send can't handle me.\n", bev->bev_name); 402 return; 403 } 404 405 btnev.bev_type = BUTTON_EVENT_STATE_CHANGE; 406 btnev.bev_event.bs_state = event; 407 strcpy(btnev.bev_event.bs_name, bev->bev_name); 408 409 rv = btn_queue_event(&btnev); 410 if (rv == 0) { 411 mutex_exit(&btn_event_queue_lock); 412 printf("%s: WARNING: state change event %d lost; " 413 "queue full\n", bev->bev_name, btnev.bev_type); 414 return; 415 } 416 if (btn_event_queue_flags & BEVQ_F_WAITING) { 417 btn_event_queue_flags &= ~BEVQ_F_WAITING; 418 cv_broadcast(&btn_event_queue_cv); 419 } 420 selnotify(&btn_event_queue_selinfo, 0, 0); 421 mutex_exit(&btn_event_queue_lock); 422 } 423