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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * Simple beeper support for PC platform, using standard timer 2 beeper.
  28  */
  29 
  30 #include <sys/types.h>
  31 #include <sys/conf.h>
  32 #include <sys/beep.h>
  33 #include <sys/ksynch.h>
  34 #include <sys/ddi.h>
  35 #include <sys/sunddi.h>
  36 #include <sys/modctl.h>
  37 #include <sys/pit.h>
  38 #include <sys/inttypes.h>
  39 
  40 #define PIT_BEEP_UNIT(dev)      (getminor((dev)))
  41 
  42 typedef struct pit_beep_state {
  43         /* Dip of pit_beep device */
  44         dev_info_t      *dip;
  45 
  46 } pit_beep_state_t;
  47 
  48 #define PIT_BEEP_ON     1
  49 #define PIT_BEEP_OFF    0
  50 
  51 /* Pointer to the state structure */
  52 static void *pit_beep_statep;
  53 
  54 static int pit_beep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
  55 static int pit_beep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
  56 static int pit_beep_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
  57     void *arg, void **result);
  58 static void pit_beep_freq(void *arg, int freq);
  59 static void pit_beep_on(void *arg);
  60 static void pit_beep_off(void *arg);
  61 
  62 struct cb_ops pit_beep_cb_ops = {
  63         nulldev,        /* open  */
  64         nulldev,        /* close */
  65         nulldev,        /* strategy */
  66         nulldev,        /* print */
  67         nulldev,        /* dump */
  68         nulldev,        /* read */
  69         nulldev,        /* write */
  70         nulldev,        /* ioctl */
  71         nulldev,        /* devmap */
  72         nulldev,        /* mmap */
  73         nulldev,        /* segmap */
  74         nochpoll,       /* poll */
  75         ddi_prop_op,    /* cb_prop_op */
  76         NULL,           /* streamtab  */
  77         D_MP | D_NEW
  78 };
  79 
  80 
  81 static struct dev_ops pit_beep_ops = {
  82         DEVO_REV,               /* Devo_rev */
  83         0,                      /* Refcnt */
  84         pit_beep_info,          /* Info */
  85         nulldev,                /* Identify */
  86         nulldev,                /* Probe */
  87         pit_beep_attach,        /* Attach */
  88         pit_beep_detach,        /* Detach */
  89         nodev,                  /* Reset */
  90         &pit_beep_cb_ops,   /* Driver operations */
  91         0,                      /* Bus operations */
  92         NULL,                   /* Power */
  93         ddi_quiesce_not_needed,         /* quiesce */
  94 };
  95 
  96 
  97 static struct modldrv modldrv = {
  98         &mod_driverops,             /* This one is a driver */
  99         "Intel Pit_beep Driver",        /* Name of the module. */
 100         &pit_beep_ops,                      /* Driver ops */
 101 };
 102 
 103 
 104 static struct modlinkage modlinkage = {
 105         MODREV_1, (void *)&modldrv, NULL
 106 };
 107 
 108 
 109 
 110 int
 111 _init(void)
 112 {
 113         int error;
 114 
 115         /* Initialize the soft state structures */
 116         if ((error = ddi_soft_state_init(&pit_beep_statep,
 117             sizeof (pit_beep_state_t), 1)) != 0) {
 118 
 119                 return (error);
 120         }
 121 
 122         /* Install the loadable module */
 123         if ((error = mod_install(&modlinkage)) != 0) {
 124                 ddi_soft_state_fini(&pit_beep_statep);
 125         }
 126 
 127         return (error);
 128 }
 129 
 130 
 131 int
 132 _info(struct modinfo *modinfop)
 133 {
 134         return (mod_info(&modlinkage, modinfop));
 135 }
 136 
 137 int
 138 _fini(void)
 139 {
 140         int error;
 141 
 142         error = mod_remove(&modlinkage);
 143 
 144         if (error == 0) {
 145                 /* Release per module resources */
 146                 ddi_soft_state_fini(&pit_beep_statep);
 147         }
 148 
 149         return (error);
 150 }
 151 
 152 /*
 153  * pit_beep_attach:
 154  */
 155 static int
 156 pit_beep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 157 {
 158         switch (cmd) {
 159                 case DDI_ATTACH:
 160                         break;
 161                 case DDI_RESUME:
 162 
 163                         return (DDI_SUCCESS);
 164                 default:
 165 
 166                         return (DDI_FAILURE);
 167         }
 168 
 169         pit_beep_off(dip);
 170 
 171         (void) beep_init((void *)dip, pit_beep_on, pit_beep_off,
 172             pit_beep_freq);
 173 
 174         /* Display information in the banner */
 175         ddi_report_dev(dip);
 176 
 177         return (DDI_SUCCESS);
 178 }
 179 
 180 
 181 /*
 182  * pit_beep_detach:
 183  */
 184 /* ARGSUSED */
 185 static int
 186 pit_beep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 187 {
 188         switch (cmd) {
 189                 case DDI_SUSPEND:
 190 
 191                         /*
 192                          * If a beep is in progress; fail suspend
 193                          */
 194                         if (!beep_busy()) {
 195 
 196                                 return (DDI_SUCCESS);
 197                         } else {
 198 
 199                                 return (DDI_FAILURE);
 200                         }
 201                 default:
 202 
 203                         return (DDI_FAILURE);
 204         }
 205 }
 206 
 207 
 208 /*
 209  * pit_beep_info:
 210  */
 211 /* ARGSUSED */
 212 static int
 213 pit_beep_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
 214     void *arg, void **result)
 215 {
 216         dev_t dev;
 217         pit_beep_state_t  *statep;
 218         int instance, error;
 219 
 220         switch (infocmd) {
 221         case DDI_INFO_DEVT2DEVINFO:
 222                 dev = (dev_t)arg;
 223                 instance = PIT_BEEP_UNIT(dev);
 224 
 225                 if ((statep = ddi_get_soft_state(pit_beep_statep,
 226                     instance)) == NULL) {
 227 
 228                         return (DDI_FAILURE);
 229                 }
 230 
 231                 *result = (void *)statep->dip;
 232 
 233                 error = DDI_SUCCESS;
 234                 break;
 235         case DDI_INFO_DEVT2INSTANCE:
 236                 dev = (dev_t)arg;
 237                 instance = PIT_BEEP_UNIT(dev);
 238 
 239                 *result = (void *)(uintptr_t)instance;
 240 
 241                 error = DDI_SUCCESS;
 242                 break;
 243         default:
 244                 error = DDI_FAILURE;
 245 
 246         }
 247 
 248         return (error);
 249 }
 250 
 251 
 252 /* ARGSUSED */
 253 static void
 254 pit_beep_freq(void *arg, int freq)
 255 {
 256         int counter;
 257 
 258         if (freq == 0)
 259                 counter = 0;
 260         else {
 261                 counter = PIT_HZ / freq;
 262                 if (counter > UINT16_MAX)
 263                         counter = UINT16_MAX;
 264                 else if (counter < 1)
 265                         counter = 1;
 266         }
 267 
 268         outb(PITCTL_PORT, PIT_C2 | PIT_READMODE | PIT_RATEMODE);
 269         outb(PITCTR2_PORT, counter & 0xff);
 270         outb(PITCTR2_PORT, counter >> 8);
 271 }
 272 
 273 
 274 /* ARGSUSED */
 275 static void
 276 pit_beep_on(void *arg)
 277 {
 278         outb(PITAUX_PORT, inb(PITAUX_PORT) | (PITAUX_OUT2 | PITAUX_GATE2));
 279 }
 280 
 281 
 282 /* ARGSUSED */
 283 static void
 284 pit_beep_off(void *arg)
 285 {
 286         outb(PITAUX_PORT, inb(PITAUX_PORT) & ~(PITAUX_OUT2 | PITAUX_GATE2));
 287 }