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 #include <sys/types.h> 28 #include <sys/time.h> 29 #include <sys/errno.h> 30 #include <sys/cmn_err.h> 31 #include <sys/param.h> 32 #include <sys/modctl.h> 33 #include <sys/conf.h> 34 #include <sys/open.h> 35 #include <sys/stat.h> 36 #include <sys/clock.h> 37 #include <sys/tod.h> 38 #include <sys/todio.h> 39 #include <sys/ddi.h> 40 #include <sys/sunddi.h> 41 #include <sys/file.h> 42 43 44 #define getsoftc(minor) \ 45 ((struct tod_softc *)ddi_get_soft_state(statep, (minor))) 46 47 /* dev_ops and cb_ops entry point function declarations */ 48 49 static int tod_attach(dev_info_t *, ddi_attach_cmd_t); 50 static int tod_detach(dev_info_t *, ddi_detach_cmd_t); 51 static int tod_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 52 53 static int tod_open(dev_t *, int, int, cred_t *); 54 static int tod_close(dev_t, int, int, cred_t *); 55 static int tod_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 56 57 struct cb_ops tod_cb_ops = { 58 tod_open, 59 tod_close, 60 nodev, 61 nodev, 62 nodev, /* dump */ 63 nodev, 64 nodev, 65 tod_ioctl, 66 nodev, /* devmap */ 67 nodev, 68 ddi_segmap, /* segmap */ 69 nochpoll, 70 ddi_prop_op, 71 NULL, /* for STREAMS drivers */ 72 D_NEW | D_MP /* driver compatibility flag */ 73 }; 74 75 static struct dev_ops tod_dev_ops = { 76 DEVO_REV, /* driver build version */ 77 0, /* device reference count */ 78 tod_getinfo, 79 nulldev, 80 nulldev, /* probe */ 81 tod_attach, 82 tod_detach, 83 nulldev, /* reset */ 84 &tod_cb_ops, 85 (struct bus_ops *)NULL, 86 nulldev, /* power */ 87 ddi_quiesce_not_supported, /* devo_quiesce */ 88 }; 89 90 /* module configuration stuff */ 91 static void *statep; 92 extern struct mod_ops mod_driverops; 93 94 static struct modldrv modldrv = { 95 &mod_driverops, 96 "tod driver", 97 &tod_dev_ops 98 }; 99 100 static struct modlinkage modlinkage = { 101 MODREV_1, 102 &modldrv, 103 0 104 }; 105 106 107 int 108 _init(void) 109 { 110 int e; 111 112 if (e = ddi_soft_state_init(&statep, sizeof (struct tod_softc), 1)) { 113 return (e); 114 } 115 116 if ((e = mod_install(&modlinkage)) != 0) { 117 ddi_soft_state_fini(&statep); 118 } 119 120 return (e); 121 } 122 123 124 int 125 _fini(void) 126 { 127 int e; 128 129 if ((e = mod_remove(&modlinkage)) != 0) { 130 return (e); 131 } 132 133 ddi_soft_state_fini(&statep); 134 135 return (DDI_SUCCESS); 136 } 137 138 139 int 140 _info(struct modinfo *modinfop) 141 { 142 return (mod_info(&modlinkage, modinfop)); 143 } 144 145 146 /* ARGSUSED */ 147 static int 148 tod_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 149 { 150 int inst = getminor((dev_t)arg); 151 int retval = DDI_SUCCESS; 152 struct tod_softc *softc; 153 154 switch (cmd) { 155 156 case DDI_INFO_DEVT2DEVINFO: 157 if ((softc = getsoftc(inst)) == NULL) { 158 *result = (void *)NULL; 159 retval = DDI_FAILURE; 160 } else { 161 *result = (void *)softc->dip; 162 } 163 break; 164 165 case DDI_INFO_DEVT2INSTANCE: 166 *result = (void *)(uintptr_t)inst; 167 break; 168 169 default: 170 retval = DDI_FAILURE; 171 } 172 173 return (retval); 174 } 175 176 static int 177 tod_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 178 { 179 180 int inst; 181 struct tod_softc *softc = NULL; 182 char name[80]; 183 184 switch (cmd) { 185 186 case DDI_ATTACH: 187 inst = ddi_get_instance(dip); 188 /* 189 * Create minor node. The minor device number, inst, has no 190 * meaning. The model number above, which will be added to 191 * the device's softc, is used to direct peculiar behavior. 192 */ 193 (void) sprintf(name, "tod%d", inst); 194 if (ddi_create_minor_node(dip, name, S_IFCHR, inst, 195 DDI_PSEUDO, NULL) == DDI_FAILURE) 196 goto attach_failed; 197 198 /* 199 * Allocate a soft state structure for this instance. 200 */ 201 if (ddi_soft_state_zalloc(statep, inst) != DDI_SUCCESS) 202 goto attach_failed; 203 204 softc = getsoftc(inst); 205 softc->dip = dip; 206 softc->cpr_stage = ~TOD_SUSPENDED; 207 mutex_init(&softc->mutex, NULL, MUTEX_DRIVER, NULL); 208 ddi_report_dev(dip); 209 return (DDI_SUCCESS); 210 211 case DDI_RESUME: 212 inst = ddi_get_instance(dip); 213 softc = getsoftc(inst); 214 mutex_enter(&softc->mutex); 215 softc->cpr_stage = ~TOD_SUSPENDED; 216 mutex_exit(&softc->mutex); 217 return (DDI_SUCCESS); 218 219 default: 220 return (DDI_FAILURE); 221 } 222 223 attach_failed: 224 /* Free soft state, if allocated. remove minor node if added earlier */ 225 if (softc) 226 ddi_soft_state_free(statep, inst); 227 228 ddi_remove_minor_node(dip, NULL); 229 230 return (DDI_FAILURE); 231 } 232 233 static int 234 tod_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 235 { 236 int inst; 237 struct tod_softc *softc; 238 239 switch (cmd) { 240 241 case DDI_DETACH: 242 inst = ddi_get_instance(dip); 243 if ((softc = getsoftc(inst)) == NULL) 244 return (ENXIO); 245 /* 246 * Free the soft state and remove minor node added earlier. 247 */ 248 mutex_destroy(&softc->mutex); 249 ddi_soft_state_free(statep, inst); 250 ddi_remove_minor_node(dip, NULL); 251 return (DDI_SUCCESS); 252 253 case DDI_SUSPEND: 254 inst = ddi_get_instance(dip); 255 softc = getsoftc(inst); 256 mutex_enter(&softc->mutex); 257 softc->cpr_stage = TOD_SUSPENDED; 258 mutex_exit(&softc->mutex); 259 return (DDI_SUCCESS); 260 261 default: 262 return (DDI_FAILURE); 263 264 } 265 } 266 267 /* ARGSUSED */ 268 static int 269 tod_open(dev_t *devp, int flag, int otyp, cred_t *credp) 270 { 271 int inst = getminor(*devp); 272 273 return (getsoftc(inst) == NULL ? ENXIO : 0); 274 } 275 276 277 /* ARGSUSED */ 278 static int 279 tod_close(dev_t dev, int flag, int otyp, cred_t *credp) 280 { 281 int inst = getminor(dev); 282 283 return (getsoftc(inst) == NULL ? ENXIO : 0); 284 } 285 286 287 /* ARGSUSED */ 288 static int 289 tod_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 290 { 291 int inst = getminor(dev); 292 struct tod_softc *softc; 293 timestruc_t ts; 294 295 if ((softc = getsoftc(inst)) == NULL) 296 return (ENXIO); 297 298 mutex_enter(&softc->mutex); 299 while (softc->cpr_stage == TOD_SUSPENDED) { 300 mutex_exit(&softc->mutex); 301 (void) ddi_dev_is_needed(softc->dip, 0, 1); 302 mutex_enter(&softc->mutex); 303 } 304 305 switch (cmd) { 306 307 case TOD_CLEAR_ALARM: 308 mutex_enter(&tod_lock); 309 tod_ops.tod_clear_power_alarm(); 310 mutex_exit(&tod_lock); 311 break; 312 313 case TOD_SET_ALARM: 314 if ((mode & FMODELS) == FNATIVE) { 315 if (ddi_copyin((caddr_t)arg, (caddr_t)&ts.tv_sec, 316 sizeof (ts.tv_sec), mode) != 0) { 317 mutex_exit(&softc->mutex); 318 return (EFAULT); 319 } 320 } else { 321 time32_t time32; 322 323 if (ddi_copyin((caddr_t)arg, 324 &time32, sizeof (time32), mode) != 0) { 325 mutex_exit(&softc->mutex); 326 return (EFAULT); 327 } 328 ts.tv_sec = (time_t)time32; 329 } 330 ts.tv_nsec = 0; 331 332 mutex_enter(&tod_lock); 333 tod_ops.tod_set_power_alarm(ts); 334 mutex_exit(&tod_lock); 335 break; 336 337 case TOD_GET_DATE: 338 mutex_enter(&tod_lock); 339 ts = tod_ops.tod_get(); 340 mutex_exit(&tod_lock); 341 342 if ((mode & FMODELS) == FNATIVE) { 343 if (ddi_copyout((caddr_t)&ts.tv_sec, (caddr_t)arg, 344 sizeof (ts.tv_sec), mode) != 0) { 345 mutex_exit(&softc->mutex); 346 return (EFAULT); 347 } 348 } else { 349 time32_t time32; 350 351 if (TIMEVAL_OVERFLOW(&ts)) { 352 mutex_exit(&softc->mutex); 353 return (EOVERFLOW); 354 } 355 356 time32 = (time32_t)ts.tv_sec; 357 if (ddi_copyout(&time32, 358 (caddr_t)arg, sizeof (time32), mode) != 0) { 359 mutex_exit(&softc->mutex); 360 return (EFAULT); 361 } 362 } 363 break; 364 365 default: 366 mutex_exit(&softc->mutex); 367 return (EINVAL); 368 } 369 370 mutex_exit(&softc->mutex); 371 return (0); 372 } 373