1 /* $NetBSD: button.c,v 1.5 2008/03/01 14:16:49 rmind 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.5 2008/03/01 14:16:49 rmind Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/conf.h> 43 #include <sys/systm.h> 44 #include <sys/malloc.h> 45 #include <sys/simplelock.h> 46 #include <sys/queue.h> 47 #include <sys/proc.h> 48 #include <sys/kthread.h> 49 #include <sys/errno.h> 50 #include <sys/fcntl.h> 51 #include <sys/callout.h> 52 #include <sys/kernel.h> 53 #include <sys/poll.h> 54 #include <sys/select.h> 55 #include <sys/vnode.h> 56 57 #include <machine/button.h> 58 59 #include <landisk/dev/buttonvar.h> 60 61 /* 62 * event handler 63 */ 64 static LIST_HEAD(, btn_event) btn_event_list = 65 LIST_HEAD_INITIALIZER(btn_event_list); 66 static struct simplelock btn_event_list_slock = 67 SIMPLELOCK_INITIALIZER; 68 69 static struct lwp *btn_daemon; 70 71 #define BTN_MAX_EVENTS 32 72 73 static struct simplelock btn_event_queue_slock = 74 SIMPLELOCK_INITIALIZER; 75 static button_event_t btn_event_queue[BTN_MAX_EVENTS]; 76 static int btn_event_queue_head; 77 static int btn_event_queue_tail; 78 static int btn_event_queue_count; 79 static int btn_event_queue_flags; 80 static struct selinfo btn_event_queue_selinfo; 81 82 static char btn_type[32]; 83 84 #define BEVQ_F_WAITING 0x01 /* daemon waiting for event */ 85 86 #define BTN_NEXT_EVENT(x) (((x) + 1) / BTN_MAX_EVENTS) 87 88 dev_type_open(btnopen); 89 dev_type_close(btnclose); 90 dev_type_ioctl(btnioctl); 91 dev_type_read(btnread); 92 dev_type_poll(btnpoll); 93 dev_type_kqfilter(btnkqfilter); 94 95 const struct cdevsw button_cdevsw = { 96 btnopen, btnclose, btnread, nowrite, btnioctl, 97 nostop, notty, btnpoll, nommap, btnkqfilter, 98 }; 99 100 static int 101 btn_queue_event(button_event_t *bev) 102 { 103 104 if (btn_event_queue_count == BTN_MAX_EVENTS) 105 return (0); 106 107 btn_event_queue[btn_event_queue_head] = *bev; 108 btn_event_queue_head = BTN_NEXT_EVENT(btn_event_queue_head); 109 btn_event_queue_count++; 110 111 return (1); 112 } 113 114 static int 115 btn_get_event(button_event_t *bev) 116 { 117 118 if (btn_event_queue_count == 0) 119 return (0); 120 121 *bev = btn_event_queue[btn_event_queue_tail]; 122 btn_event_queue_tail = BTN_NEXT_EVENT(btn_event_queue_tail); 123 btn_event_queue_count--; 124 125 return (1); 126 } 127 128 static void 129 btn_event_queue_flush(void) 130 { 131 132 btn_event_queue_head = 0; 133 btn_event_queue_tail = 0; 134 btn_event_queue_count = 0; 135 btn_event_queue_flags = 0; 136 } 137 138 int 139 btnopen(dev_t dev, int flag, int mode, struct lwp *l) 140 { 141 static bool btn_event_queue_selinfo_init; /* XXX */ 142 int error; 143 144 if (!btn_event_queue_selinfo_init) { 145 selinit(&btn_event_queue_selinfo); 146 btn_event_queue_selinfo_init = true; 147 } 148 149 if (minor(dev) != 0) { 150 return (ENODEV); 151 } 152 153 simple_lock(&btn_event_queue_slock); 154 if (btn_daemon != NULL) { 155 error = EBUSY; 156 } else { 157 error = 0; 158 btn_daemon = l; 159 btn_event_queue_flush(); 160 } 161 simple_unlock(&btn_event_queue_slock); 162 163 return (error); 164 } 165 166 int 167 btnclose(dev_t dev, int flag, int mode, struct lwp *l) 168 { 169 int count; 170 171 if (minor(dev) != 0) { 172 return (ENODEV); 173 } 174 175 simple_lock(&btn_event_queue_slock); 176 count = btn_event_queue_count; 177 btn_daemon = NULL; 178 btn_event_queue_flush(); 179 simple_unlock(&btn_event_queue_slock); 180 181 if (count) { 182 printf("WARNING: %d events lost by exiting daemon\n", count); 183 } 184 185 return (0); 186 } 187 188 int 189 btnioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 190 { 191 int error = 0; 192 193 if (minor(dev) != 0) { 194 return (ENODEV); 195 } 196 197 switch (cmd) { 198 case BUTTON_IOC_GET_TYPE: 199 { 200 struct button_type *button_type = (void *)data; 201 strcpy(button_type->button_type, btn_type); 202 break; 203 } 204 205 default: 206 error = ENOTTY; 207 break; 208 } 209 210 return (error); 211 } 212 213 int 214 btnread(dev_t dev, struct uio *uio, int flags) 215 { 216 button_event_t bev; 217 int error; 218 219 if (minor(dev) != 0) { 220 return (ENODEV); 221 } 222 223 if (uio->uio_resid != BUTTON_EVENT_MSG_SIZE) { 224 return (EINVAL); 225 } 226 227 simple_lock(&btn_event_queue_slock); 228 for (;;) { 229 if (btn_get_event(&bev)) { 230 simple_unlock(&btn_event_queue_slock); 231 return (uiomove(&bev, BUTTON_EVENT_MSG_SIZE, uio)); 232 } 233 234 if (flags & IO_NDELAY) { 235 simple_unlock(&btn_event_queue_slock); 236 return (EWOULDBLOCK); 237 } 238 239 btn_event_queue_flags |= BEVQ_F_WAITING; 240 error = ltsleep(&btn_event_queue_count, 241 (PRIBIO|PCATCH), "btnread", 0, &btn_event_queue_slock); 242 if (error) { 243 simple_unlock(&btn_event_queue_slock); 244 return (error); 245 } 246 } 247 } 248 249 int 250 btnpoll(dev_t dev, int events, struct lwp *l) 251 { 252 int revents; 253 254 if (minor(dev) != 0) { 255 return (ENODEV); 256 } 257 258 revents = events & (POLLOUT | POLLWRNORM); 259 260 /* Attempt to save some work. */ 261 if ((events & (POLLIN | POLLRDNORM)) == 0) 262 return (revents); 263 264 simple_lock(&btn_event_queue_slock); 265 if (btn_event_queue_count) { 266 revents |= events & (POLLIN | POLLRDNORM); 267 } else { 268 selrecord(l, &btn_event_queue_selinfo); 269 } 270 simple_unlock(&btn_event_queue_slock); 271 272 return (revents); 273 } 274 275 static void 276 filt_btn_rdetach(struct knote *kn) 277 { 278 279 simple_lock(&btn_event_queue_slock); 280 SLIST_REMOVE(&btn_event_queue_selinfo.sel_klist, 281 kn, knote, kn_selnext); 282 simple_unlock(&btn_event_queue_slock); 283 } 284 285 static int 286 filt_btn_read(struct knote *kn, long hint) 287 { 288 289 simple_lock(&btn_event_queue_slock); 290 kn->kn_data = btn_event_queue_count; 291 simple_unlock(&btn_event_queue_slock); 292 293 return (kn->kn_data > 0); 294 } 295 296 static const struct filterops btn_read_filtops = 297 { 1, NULL, filt_btn_rdetach, filt_btn_read }; 298 299 static const struct filterops btn_write_filtops = 300 { 1, NULL, filt_btn_rdetach, filt_seltrue }; 301 302 int 303 btnkqfilter(dev_t dev, struct knote *kn) 304 { 305 struct klist *klist; 306 307 if (minor(dev) != 0) { 308 return (ENODEV); 309 } 310 311 switch (kn->kn_filter) { 312 case EVFILT_READ: 313 klist = &btn_event_queue_selinfo.sel_klist; 314 kn->kn_fop = &btn_read_filtops; 315 break; 316 317 case EVFILT_WRITE: 318 klist = &btn_event_queue_selinfo.sel_klist; 319 kn->kn_fop = &btn_write_filtops; 320 break; 321 322 default: 323 return (1); 324 } 325 326 simple_lock(&btn_event_queue_slock); 327 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 328 simple_unlock(&btn_event_queue_slock); 329 330 return (0); 331 } 332 333 void 334 btn_settype(const char *type) 335 { 336 337 /* 338 * Don't bother locking this; it's going to be set 339 * during autoconfiguration, and then only read from 340 * then on. 341 */ 342 strlcpy(btn_type, type, sizeof(btn_type)); 343 } 344 345 int 346 btn_event_register(struct btn_event *bev) 347 { 348 349 simple_lock(&btn_event_list_slock); 350 LIST_INSERT_HEAD(&btn_event_list, bev, bev_list); 351 simple_unlock(&btn_event_list_slock); 352 353 return (0); 354 } 355 356 void 357 btn_event_unregister(struct btn_event *bev) 358 { 359 360 simple_lock(&btn_event_list_slock); 361 LIST_REMOVE(bev, bev_list); 362 simple_unlock(&btn_event_list_slock); 363 } 364 365 void 366 btn_event_send(struct btn_event *bev, int event) 367 { 368 button_event_t btnev; 369 int rv; 370 371 simple_lock(&btn_event_queue_slock); 372 if (btn_daemon != NULL) { 373 btnev.bev_type = BUTTON_EVENT_STATE_CHANGE; 374 btnev.bev_event.bs_state = event; 375 strcpy(btnev.bev_event.bs_name, bev->bev_name); 376 377 rv = btn_queue_event(&btnev); 378 if (rv == 0) { 379 simple_unlock(&btn_event_queue_slock); 380 printf("%s: WARNING: state change event %d lost; " 381 "queue full\n", bev->bev_name, btnev.bev_type); 382 } else { 383 if (btn_event_queue_flags & BEVQ_F_WAITING) { 384 btn_event_queue_flags &= ~BEVQ_F_WAITING; 385 simple_unlock(&btn_event_queue_slock); 386 wakeup(&btn_event_queue_count); 387 } else { 388 simple_unlock(&btn_event_queue_slock); 389 } 390 selnotify(&btn_event_queue_selinfo, 0, 0); 391 } 392 return; 393 } 394 simple_unlock(&btn_event_queue_slock); 395 396 printf("%s: btn_event_send can't handle me.\n", bev->bev_name); 397 } 398