xref: /illumos-gate/usr/src/uts/sun4/io/tod.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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