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