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