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