1 /* $NetBSD: button.c,v 1.7 2014/03/16 05:20:24 dholland 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.7 2014/03/16 05:20:24 dholland 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_flag = 0 105 }; 106 107 static int 108 btn_init(void) 109 { 110 111 LIST_INIT(&btn_event_list); 112 mutex_init(&btn_event_list_lock, MUTEX_DEFAULT, IPL_NONE); 113 mutex_init(&btn_event_queue_lock, MUTEX_DEFAULT, IPL_NONE); 114 cv_init(&btn_event_queue_cv, "btncv"); 115 selinit(&btn_event_queue_selinfo); 116 117 return 0; 118 } 119 120 static int 121 btn_queue_event(button_event_t *bev) 122 { 123 124 if (btn_event_queue_count == BTN_MAX_EVENTS) 125 return (0); 126 127 btn_event_queue[btn_event_queue_head] = *bev; 128 btn_event_queue_head = BTN_NEXT_EVENT(btn_event_queue_head); 129 btn_event_queue_count++; 130 131 return (1); 132 } 133 134 static int 135 btn_get_event(button_event_t *bev) 136 { 137 138 if (btn_event_queue_count == 0) 139 return (0); 140 141 *bev = btn_event_queue[btn_event_queue_tail]; 142 btn_event_queue_tail = BTN_NEXT_EVENT(btn_event_queue_tail); 143 btn_event_queue_count--; 144 145 return (1); 146 } 147 148 static void 149 btn_event_queue_flush(void) 150 { 151 152 btn_event_queue_head = 0; 153 btn_event_queue_tail = 0; 154 btn_event_queue_count = 0; 155 btn_event_queue_flags = 0; 156 } 157 158 int 159 btnopen(dev_t dev, int flag, int mode, struct lwp *l) 160 { 161 int error; 162 163 error = RUN_ONCE(&btn_once, btn_init); 164 if (error) { 165 return error; 166 } 167 168 if (minor(dev) != 0) { 169 return (ENODEV); 170 } 171 172 mutex_enter(&btn_event_queue_lock); 173 if (btn_daemon != NULL) { 174 error = EBUSY; 175 } else { 176 error = 0; 177 btn_daemon = l; 178 btn_event_queue_flush(); 179 } 180 mutex_exit(&btn_event_queue_lock); 181 182 return (error); 183 } 184 185 int 186 btnclose(dev_t dev, int flag, int mode, struct lwp *l) 187 { 188 int count; 189 190 if (minor(dev) != 0) { 191 return (ENODEV); 192 } 193 194 mutex_enter(&btn_event_queue_lock); 195 count = btn_event_queue_count; 196 btn_daemon = NULL; 197 btn_event_queue_flush(); 198 mutex_exit(&btn_event_queue_lock); 199 200 if (count) { 201 printf("WARNING: %d events lost by exiting daemon\n", count); 202 } 203 204 return (0); 205 } 206 207 int 208 btnioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 209 { 210 int error = 0; 211 212 if (minor(dev) != 0) { 213 return (ENODEV); 214 } 215 216 switch (cmd) { 217 case BUTTON_IOC_GET_TYPE: 218 { 219 struct button_type *button_type = (void *)data; 220 strcpy(button_type->button_type, btn_type); 221 break; 222 } 223 224 default: 225 error = ENOTTY; 226 break; 227 } 228 229 return (error); 230 } 231 232 int 233 btnread(dev_t dev, struct uio *uio, int flags) 234 { 235 button_event_t bev; 236 int error; 237 238 if (minor(dev) != 0) { 239 return (ENODEV); 240 } 241 242 if (uio->uio_resid != BUTTON_EVENT_MSG_SIZE) { 243 return (EINVAL); 244 } 245 246 mutex_enter(&btn_event_queue_lock); 247 for (;;) { 248 if (btn_get_event(&bev)) { 249 mutex_exit(&btn_event_queue_lock); 250 return (uiomove(&bev, BUTTON_EVENT_MSG_SIZE, uio)); 251 } 252 253 if (flags & IO_NDELAY) { 254 mutex_exit(&btn_event_queue_lock); 255 return (EWOULDBLOCK); 256 } 257 258 btn_event_queue_flags |= BEVQ_F_WAITING; 259 error = cv_wait_sig(&btn_event_queue_cv, &btn_event_queue_lock); 260 if (error) { 261 mutex_exit(&btn_event_queue_lock); 262 return (error); 263 } 264 } 265 } 266 267 int 268 btnpoll(dev_t dev, int events, struct lwp *l) 269 { 270 int revents; 271 272 if (minor(dev) != 0) { 273 return (ENODEV); 274 } 275 276 revents = events & (POLLOUT | POLLWRNORM); 277 278 /* Attempt to save some work. */ 279 if ((events & (POLLIN | POLLRDNORM)) == 0) 280 return (revents); 281 282 mutex_enter(&btn_event_queue_lock); 283 if (btn_event_queue_count) { 284 revents |= events & (POLLIN | POLLRDNORM); 285 } else { 286 selrecord(l, &btn_event_queue_selinfo); 287 } 288 mutex_exit(&btn_event_queue_lock); 289 290 return (revents); 291 } 292 293 static void 294 filt_btn_rdetach(struct knote *kn) 295 { 296 297 mutex_enter(&btn_event_queue_lock); 298 SLIST_REMOVE(&btn_event_queue_selinfo.sel_klist, 299 kn, knote, kn_selnext); 300 mutex_exit(&btn_event_queue_lock); 301 } 302 303 static int 304 filt_btn_read(struct knote *kn, long hint) 305 { 306 307 mutex_enter(&btn_event_queue_lock); 308 kn->kn_data = btn_event_queue_count; 309 mutex_exit(&btn_event_queue_lock); 310 311 return (kn->kn_data > 0); 312 } 313 314 static const struct filterops btn_read_filtops = 315 { 1, NULL, filt_btn_rdetach, filt_btn_read }; 316 317 static const struct filterops btn_write_filtops = 318 { 1, NULL, filt_btn_rdetach, filt_seltrue }; 319 320 int 321 btnkqfilter(dev_t dev, struct knote *kn) 322 { 323 struct klist *klist; 324 325 if (minor(dev) != 0) { 326 return (ENODEV); 327 } 328 329 switch (kn->kn_filter) { 330 case EVFILT_READ: 331 klist = &btn_event_queue_selinfo.sel_klist; 332 kn->kn_fop = &btn_read_filtops; 333 break; 334 335 case EVFILT_WRITE: 336 klist = &btn_event_queue_selinfo.sel_klist; 337 kn->kn_fop = &btn_write_filtops; 338 break; 339 340 default: 341 return (1); 342 } 343 344 mutex_enter(&btn_event_queue_lock); 345 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 346 mutex_exit(&btn_event_queue_lock); 347 348 return (0); 349 } 350 351 void 352 btn_settype(const char *type) 353 { 354 355 /* 356 * Don't bother locking this; it's going to be set 357 * during autoconfiguration, and then only read from 358 * then on. 359 */ 360 strlcpy(btn_type, type, sizeof(btn_type)); 361 } 362 363 int 364 btn_event_register(struct btn_event *bev) 365 { 366 367 mutex_enter(&btn_event_list_lock); 368 LIST_INSERT_HEAD(&btn_event_list, bev, bev_list); 369 mutex_exit(&btn_event_list_lock); 370 371 return (0); 372 } 373 374 void 375 btn_event_unregister(struct btn_event *bev) 376 { 377 378 mutex_enter(&btn_event_list_lock); 379 LIST_REMOVE(bev, bev_list); 380 mutex_exit(&btn_event_list_lock); 381 } 382 383 void 384 btn_event_send(struct btn_event *bev, int event) 385 { 386 button_event_t btnev; 387 int rv; 388 389 mutex_enter(&btn_event_queue_lock); 390 if (btn_daemon == NULL) { 391 mutex_exit(&btn_event_queue_lock); 392 printf("%s: btn_event_send can't handle me.\n", bev->bev_name); 393 return; 394 } 395 396 btnev.bev_type = BUTTON_EVENT_STATE_CHANGE; 397 btnev.bev_event.bs_state = event; 398 strcpy(btnev.bev_event.bs_name, bev->bev_name); 399 400 rv = btn_queue_event(&btnev); 401 if (rv == 0) { 402 mutex_exit(&btn_event_queue_lock); 403 printf("%s: WARNING: state change event %d lost; " 404 "queue full\n", bev->bev_name, btnev.bev_type); 405 return; 406 } 407 if (btn_event_queue_flags & BEVQ_F_WAITING) { 408 btn_event_queue_flags &= ~BEVQ_F_WAITING; 409 cv_broadcast(&btn_event_queue_cv); 410 } 411 selnotify(&btn_event_queue_selinfo, 0, 0); 412 mutex_exit(&btn_event_queue_lock); 413 } 414