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