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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)140 _info(struct modinfo *modinfop)
141 {
142 return (mod_info(&modlinkage, modinfop));
143 }
144
145
146 /* ARGSUSED */
147 static int
tod_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)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
tod_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
tod_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
tod_open(dev_t * devp,int flag,int otyp,cred_t * credp)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
tod_close(dev_t dev,int flag,int otyp,cred_t * credp)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
tod_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)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