1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * This is the Beep module for supporting keyboard beep for keyboards
  28  * that do not have the beeping feature within themselves
  29  *
  30  */
  31 
  32 #include <sys/types.h>
  33 #include <sys/conf.h>
  34 
  35 #include <sys/ddi.h>
  36 #include <sys/sunddi.h>
  37 #include <sys/modctl.h>
  38 #include <sys/ddi_impldefs.h>
  39 #include <sys/kmem.h>
  40 
  41 #include <sys/beep.h>
  42 #include <sys/inttypes.h>
  43 
  44 /*
  45  * Debug stuff
  46  * BEEP_DEBUG used for errors
  47  * BEEP_DEBUG1 prints when beep_debug > 1 and used for normal messages
  48  */
  49 #ifdef DEBUG
  50 int beep_debug = 0;
  51 #define BEEP_DEBUG(args)        if (beep_debug) cmn_err args
  52 #define BEEP_DEBUG1(args)       if (beep_debug > 1) cmn_err args
  53 #else
  54 #define BEEP_DEBUG(args)
  55 #define BEEP_DEBUG1(args)
  56 #endif
  57 
  58 int beep_queue_size = BEEP_QUEUE_SIZE;
  59 
  60 /*
  61  * Note that mutex_init is not called on the mutex in beep_state,
  62  * But assumes that zeroed memory does not need to call mutex_init,
  63  * as documented in mutex.c
  64  */
  65 
  66 beep_state_t beep_state;
  67 
  68 beep_params_t beep_params[] = {
  69         {BEEP_CONSOLE,  900,    200},
  70         {BEEP_TYPE4,    2000,   0},
  71         {BEEP_DEFAULT,  1000,   200},   /* Must be last */
  72 };
  73 
  74 
  75 /*
  76  * beep_init:
  77  * Allocate the beep_queue structure
  78  * Initialize beep_state structure
  79  * Called from beep driver attach routine
  80  */
  81 
  82 int
  83 beep_init(void *arg,
  84     beep_on_func_t beep_on_func,
  85     beep_off_func_t beep_off_func,
  86     beep_freq_func_t beep_freq_func)
  87 {
  88         beep_entry_t *queue;
  89 
  90         BEEP_DEBUG1((CE_CONT,
  91             "beep_init(0x%lx, 0x%lx, 0x%lx, 0x%lx) : start.",
  92             (unsigned long) arg,
  93             (unsigned long) beep_on_func,
  94             (unsigned long) beep_off_func,
  95             (unsigned long) beep_freq_func));
  96 
  97         mutex_enter(&beep_state.mutex);
  98 
  99         if (beep_state.mode != BEEP_UNINIT) {
 100                 mutex_exit(&beep_state.mutex);
 101                 BEEP_DEBUG((CE_WARN,
 102                     "beep_init : beep_state already initialized."));
 103                 return (DDI_SUCCESS);
 104         }
 105 
 106         queue = kmem_zalloc(sizeof (beep_entry_t) * beep_queue_size,
 107             KM_SLEEP);
 108 
 109         BEEP_DEBUG1((CE_CONT,
 110             "beep_init : beep_queue kmem_zalloc(%d) = 0x%lx.",
 111             (int)sizeof (beep_entry_t) * beep_queue_size,
 112             (unsigned long)queue));
 113 
 114         beep_state.arg = arg;
 115         beep_state.mode = BEEP_OFF;
 116         beep_state.beep_freq = beep_freq_func;
 117         beep_state.beep_on = beep_on_func;
 118         beep_state.beep_off = beep_off_func;
 119         beep_state.timeout_id = 0;
 120 
 121         beep_state.queue_head = 0;
 122         beep_state.queue_tail = 0;
 123         beep_state.queue_size = beep_queue_size;
 124         beep_state.queue = queue;
 125 
 126         mutex_exit(&beep_state.mutex);
 127 
 128         BEEP_DEBUG1((CE_CONT, "beep_init : done."));
 129         return (DDI_SUCCESS);
 130 }
 131 
 132 
 133 int
 134 beep_fini(void)
 135 {
 136         BEEP_DEBUG1((CE_CONT, "beep_fini() : start."));
 137 
 138         (void) beeper_off();
 139 
 140         mutex_enter(&beep_state.mutex);
 141 
 142         if (beep_state.mode == BEEP_UNINIT) {
 143                 mutex_exit(&beep_state.mutex);
 144                 BEEP_DEBUG((CE_WARN,
 145                     "beep_fini : beep_state already uninitialized."));
 146                 return (0);
 147         }
 148 
 149         if (beep_state.queue != NULL)
 150                 kmem_free(beep_state.queue,
 151                     sizeof (beep_entry_t) * beep_state.queue_size);
 152 
 153         beep_state.arg = (void *)NULL;
 154         beep_state.mode = BEEP_UNINIT;
 155         beep_state.beep_freq = (beep_freq_func_t)NULL;
 156         beep_state.beep_on = (beep_on_func_t)NULL;
 157         beep_state.beep_off = (beep_off_func_t)NULL;
 158         beep_state.timeout_id = 0;
 159 
 160         beep_state.queue_head = 0;
 161         beep_state.queue_tail = 0;
 162         beep_state.queue_size = 0;
 163         beep_state.queue = (beep_entry_t *)NULL;
 164 
 165         mutex_exit(&beep_state.mutex);
 166 
 167         BEEP_DEBUG1((CE_CONT, "beep_fini() : done."));
 168 
 169         return (0);
 170 }
 171 
 172 
 173 int
 174 beeper_off(void)
 175 {
 176         BEEP_DEBUG1((CE_CONT, "beeper_off : start."));
 177 
 178         mutex_enter(&beep_state.mutex);
 179 
 180         if (beep_state.mode == BEEP_UNINIT) {
 181                 mutex_exit(&beep_state.mutex);
 182                 return (ENXIO);
 183         }
 184 
 185         if (beep_state.mode == BEEP_TIMED) {
 186                 (void) untimeout(beep_state.timeout_id);
 187                 beep_state.timeout_id = 0;
 188         }
 189 
 190         if (beep_state.mode != BEEP_OFF) {
 191                 beep_state.mode = BEEP_OFF;
 192 
 193                 if (beep_state.beep_off != NULL)
 194                         (*beep_state.beep_off)(beep_state.arg);
 195         }
 196 
 197         beep_state.queue_head = 0;
 198         beep_state.queue_tail = 0;
 199 
 200         mutex_exit(&beep_state.mutex);
 201 
 202         BEEP_DEBUG1((CE_CONT, "beeper_off : done."));
 203 
 204         return (0);
 205 }
 206 
 207 int
 208 beeper_freq(enum beep_type type, int freq)
 209 {
 210         beep_params_t *bp;
 211 
 212         BEEP_DEBUG1((CE_CONT, "beeper_freq(%d, %d) : start", type, freq));
 213 
 214         /*
 215          * The frequency value is limited to the range of [0 - 32767]
 216          */
 217         if (freq < 0 || freq > INT16_MAX)
 218                 return (EINVAL);
 219 
 220         for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) {
 221                 if (bp->type == type)
 222                         break;
 223         }
 224 
 225         if (bp->type != type) {
 226                 BEEP_DEBUG((CE_WARN, "beeper_freq : invalid type."));
 227 
 228                 return (EINVAL);
 229         }
 230 
 231         bp->frequency = freq;
 232 
 233         BEEP_DEBUG1((CE_CONT, "beeper_freq : done."));
 234         return (0);
 235 }
 236 
 237 /*
 238  * beep :
 239  *      Start beeping for period specified by the type value,
 240  *      from the value in the beep_param structure in milliseconds.
 241  */
 242 int
 243 beep(enum beep_type type)
 244 {
 245 
 246         beep_params_t *bp;
 247 
 248         BEEP_DEBUG1((CE_CONT, "beep(%d) : start.", type));
 249 
 250         for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) {
 251                 if (bp->type == type)
 252                         break;
 253         }
 254 
 255         if (bp->type != type) {
 256 
 257                 BEEP_DEBUG((CE_WARN, "beep : invalid type."));
 258 
 259                 /* If type doesn't match, return silently without beeping */
 260                 return (EINVAL);
 261         }
 262 
 263         return (beep_mktone(bp->frequency, bp->duration));
 264 }
 265 
 266 
 267 /*ARGSUSED*/
 268 int
 269 beep_polled(enum beep_type type)
 270 {
 271         /*
 272          * No-op at this time.
 273          *
 274          * Don't think we can make this work in general, as tem_safe
 275          * has a requirement of no mutexes, but kbd sends messages
 276          * through streams.
 277          */
 278 
 279         BEEP_DEBUG1((CE_CONT, "beep_polled(%d)", type));
 280 
 281         return (0);
 282 }
 283 
 284 /*
 285  * beeper_on :
 286  *      Turn the beeper on
 287  */
 288 int
 289 beeper_on(enum beep_type type)
 290 {
 291         beep_params_t *bp;
 292         int status = 0;
 293 
 294         BEEP_DEBUG1((CE_CONT, "beeper_on(%d) : start.", type));
 295 
 296         for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) {
 297                 if (bp->type == type)
 298                         break;
 299         }
 300 
 301         if (bp->type != type) {
 302 
 303                 BEEP_DEBUG((CE_WARN, "beeper_on : invalid type."));
 304 
 305                 /* If type doesn't match, return silently without beeping */
 306                 return (EINVAL);
 307         }
 308 
 309         mutex_enter(&beep_state.mutex);
 310 
 311         if (beep_state.mode == BEEP_UNINIT) {
 312                 status = ENXIO;
 313 
 314         /* Start another beep only if the previous one is over */
 315         } else if (beep_state.mode == BEEP_OFF) {
 316                 if (bp->frequency != 0) {
 317                         beep_state.mode = BEEP_ON;
 318 
 319                         if (beep_state.beep_freq != NULL)
 320                                 (*beep_state.beep_freq)(beep_state.arg,
 321                                     bp->frequency);
 322 
 323                         if (beep_state.beep_on != NULL)
 324                                 (*beep_state.beep_on)(beep_state.arg);
 325                 }
 326         } else {
 327                 status = EBUSY;
 328         }
 329 
 330         mutex_exit(&beep_state.mutex);
 331 
 332         BEEP_DEBUG1((CE_CONT, "beeper_on : done, status %d.", status));
 333 
 334         return (status);
 335 }
 336 
 337 
 338 int
 339 beep_mktone(int frequency, int duration)
 340 {
 341         int next;
 342         int status = 0;
 343 
 344         BEEP_DEBUG1((CE_CONT, "beep_mktone(%d, %d) : start.", frequency,
 345             duration));
 346 
 347         /*
 348          * The frequency value is limited to the range of [0 - 32767]
 349          */
 350         if (frequency < 0 || frequency > INT16_MAX)
 351                 return (EINVAL);
 352 
 353         mutex_enter(&beep_state.mutex);
 354 
 355         if (beep_state.mode == BEEP_UNINIT) {
 356                 status = ENXIO;
 357 
 358         } else if (beep_state.mode == BEEP_TIMED) {
 359 
 360                 /* If already processing a beep, queue this one */
 361 
 362                 if (frequency != 0) {
 363                         next = beep_state.queue_tail + 1;
 364                         if (next == beep_state.queue_size)
 365                                 next = 0;
 366 
 367                         if (next != beep_state.queue_head) {
 368                                 /*
 369                                  * If there is room in the queue,
 370                                  * add this entry
 371                                  */
 372 
 373                                 beep_state.queue[beep_state.queue_tail].
 374                                     frequency = (unsigned short)frequency;
 375 
 376                                 beep_state.queue[beep_state.queue_tail].
 377                                     duration = (unsigned short)duration;
 378 
 379                                 beep_state.queue_tail = next;
 380                         } else {
 381                                 status = EAGAIN;
 382                         }
 383                 }
 384 
 385         } else if (beep_state.mode == BEEP_OFF) {
 386 
 387                 /* Start another beep only if the previous one is over */
 388 
 389                 if (frequency != 0) {
 390                         beep_state.mode = BEEP_TIMED;
 391 
 392                         if (beep_state.beep_freq != NULL)
 393                                 (*beep_state.beep_freq)(beep_state.arg,
 394                                     frequency);
 395 
 396                         if (beep_state.beep_on != NULL)
 397                                 (*beep_state.beep_on)(beep_state.arg);
 398 
 399                         /*
 400                          * Set timeout for ending the beep after the
 401                          * specified time
 402                          */
 403 
 404                         beep_state.timeout_id = timeout(beep_timeout, NULL,
 405                             drv_usectohz(duration * 1000));
 406                 }
 407         } else {
 408                 status = EBUSY;
 409         }
 410 
 411         mutex_exit(&beep_state.mutex);
 412 
 413         BEEP_DEBUG1((CE_CONT, "beep_mktone : done, status %d.", status));
 414 
 415         return (status);
 416 }
 417 
 418 
 419 /*
 420  * Turn the beeper off which had been turned on from beep()
 421  * for a specified period of time
 422  */
 423 /*ARGSUSED*/
 424 void
 425 beep_timeout(void *arg)
 426 {
 427         int frequency;
 428         int duration;
 429         int next;
 430 
 431         BEEP_DEBUG1((CE_CONT, "beeper_timeout : start."));
 432 
 433         mutex_enter(&beep_state.mutex);
 434 
 435         beep_state.timeout_id = 0;
 436 
 437         if (beep_state.mode == BEEP_UNINIT) {
 438                 mutex_exit(&beep_state.mutex);
 439                 BEEP_DEBUG1((CE_CONT, "beep_timeout : uninitialized."));
 440                 return;
 441         }
 442 
 443         if ((beep_state.mode == BEEP_ON) ||
 444             (beep_state.mode == BEEP_TIMED)) {
 445 
 446                 beep_state.mode = BEEP_OFF;
 447 
 448                 if (beep_state.beep_off != NULL)
 449                         (*beep_state.beep_off)(beep_state.arg);
 450         }
 451 
 452         if (beep_state.queue_head != beep_state.queue_tail) {
 453 
 454                 next = beep_state.queue_head;
 455 
 456                 frequency = beep_state.queue[next].frequency;
 457 
 458                 duration = beep_state.queue[next].duration;
 459 
 460                 next++;
 461                 if (next == beep_state.queue_size)
 462                         next = 0;
 463 
 464                 beep_state.queue_head = next;
 465 
 466                 beep_state.mode = BEEP_TIMED;
 467 
 468                 if (frequency != 0) {
 469                         if (beep_state.beep_freq != NULL)
 470                                 (*beep_state.beep_freq)(beep_state.arg,
 471                                     frequency);
 472 
 473                         if (beep_state.beep_on != NULL)
 474                                 (*beep_state.beep_on)(beep_state.arg);
 475                 }
 476 
 477                 /* Set timeout for ending the beep after the specified time */
 478 
 479                 beep_state.timeout_id = timeout(beep_timeout, NULL,
 480                     drv_usectohz(duration * 1000));
 481         }
 482 
 483         mutex_exit(&beep_state.mutex);
 484 
 485         BEEP_DEBUG1((CE_CONT, "beep_timeout : done."));
 486 }
 487 
 488 
 489 /*
 490  * Return true (1) if we are sounding a tone.
 491  */
 492 int
 493 beep_busy(void)
 494 {
 495         int status;
 496 
 497         BEEP_DEBUG1((CE_CONT, "beep_busy : start."));
 498 
 499         mutex_enter(&beep_state.mutex);
 500 
 501         status = beep_state.mode != BEEP_UNINIT &&
 502             beep_state.mode != BEEP_OFF;
 503 
 504         mutex_exit(&beep_state.mutex);
 505 
 506         BEEP_DEBUG1((CE_CONT, "beep_busy : status %d.", status));
 507 
 508         return (status);
 509 }