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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 * Excalibur fans watchdog module
32 #include <sys/types.h>
33 #include <sys/mkdev.h>
36 #include <sys/modctl.h>
37 #include <sys/sunddi.h>
38 #include <sys/sunndi.h>
39 #include <sys/ksynch.h>
41 #include <sys/errno.h>
44 #include <sys/xcalwd.h>
45 #include <sys/policy.h>
46 #include <sys/platform_module.h>
48 extern struct mod_ops mod_driverops
;
50 #define MINOR_DEVICE_NAME "xcalwd"
53 * Define your per instance state data
55 typedef struct xcalwd_state
{
64 * Pointer to soft states
66 static void *xcalwd_statep
;
71 static int xcalwd_getinfo(dev_info_t
*dip
, ddi_info_cmd_t cmd
,
72 void *arg
, void **resultp
);
73 static int xcalwd_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
);
74 static int xcalwd_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
);
79 static int xcalwd_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*credp
);
80 static int xcalwd_close(dev_t dev
, int flag
, int otyp
, cred_t
*credp
);
81 static int xcalwd_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int mode
,
82 cred_t
*credp
, int *rvalp
);
86 static void xcalwd_timeout(void *arg
);
91 static struct cb_ops xcalwd_cb_ops
= {
92 xcalwd_open
, /* open */
93 xcalwd_close
, /* close */
99 xcalwd_ioctl
, /* ioctl */
103 nochpoll
, /* chpoll */
104 ddi_prop_op
, /* prop_op */
105 NULL
, /* streamtab */
106 D_NEW
| D_MP
| D_64BIT
, /* cb_flag */
108 nodev
, /* int (*cb_aread)() */
109 nodev
/* int (*cb_awrite)() */
115 static struct dev_ops xcalwd_dev_ops
= {
116 DEVO_REV
, /* devo_rev */
118 xcalwd_getinfo
, /* getinfo */
119 nulldev
, /* identify */
121 xcalwd_attach
, /* attach */
122 xcalwd_detach
, /* detach */
123 nodev
, /* devo_reset */
124 &xcalwd_cb_ops
, /* devo_cb_ops */
125 NULL
, /* devo_bus_ops */
126 NULL
, /* devo_power */
127 ddi_quiesce_not_needed
, /* devo_quiesce */
133 static struct modldrv xcalwd_modldrv
= {
134 &mod_driverops
, /* drv_modops */
135 "Excalibur watchdog timer v1.7 ", /* drv_linkinfo */
136 &xcalwd_dev_ops
/* drv_dev_ops */
142 static struct modlinkage xcalwd_modlinkage
= {
154 * Initialize the module state structure
156 error
= ddi_soft_state_init(&xcalwd_statep
,
157 sizeof (xcalwd_state_t
), 0);
163 * Link the driver into the system
165 error
= mod_install(&xcalwd_modlinkage
);
167 ddi_soft_state_fini(&xcalwd_statep
);
178 error
= mod_remove(&xcalwd_modlinkage
);
184 * Cleanup resources allocated in _init
186 ddi_soft_state_fini(&xcalwd_statep
);
191 _info(struct modinfo
*modinfop
)
193 return (mod_info(&xcalwd_modlinkage
, modinfop
));
198 xcalwd_getinfo(dev_info_t
*dip
, ddi_info_cmd_t cmd
,
199 void *arg
, void **resultp
)
202 dev_t dev
= (dev_t
)arg
;
206 retval
= DDI_FAILURE
;
208 case DDI_INFO_DEVT2DEVINFO
:
209 instance
= getminor(dev
);
210 tsp
= ddi_get_soft_state(xcalwd_statep
, instance
);
215 retval
= DDI_SUCCESS
;
218 case DDI_INFO_DEVT2INSTANCE
:
219 *resultp
= (void *)(uintptr_t)getminor(dev
);
220 retval
= DDI_SUCCESS
;
229 xcalwd_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
236 instance
= ddi_get_instance(dip
);
238 if (&plat_fan_blast
== NULL
) {
239 cmn_err(CE_WARN
, "missing plat_fan_blast function");
240 return (DDI_FAILURE
);
243 if (ddi_soft_state_zalloc(xcalwd_statep
, instance
) !=
245 cmn_err(CE_WARN
, "attach could not alloc"
246 "%d state structure", instance
);
247 return (DDI_FAILURE
);
250 tsp
= ddi_get_soft_state(xcalwd_statep
, instance
);
252 cmn_err(CE_WARN
, "get state failed %d",
254 return (DDI_FAILURE
);
257 if (ddi_create_minor_node(dip
, MINOR_DEVICE_NAME
,
258 S_IFCHR
, instance
, DDI_PSEUDO
, NULL
) == DDI_FAILURE
) {
259 cmn_err(CE_WARN
, "create minor node failed\n");
260 return (DDI_FAILURE
);
263 mutex_init(&tsp
->lock
, NULL
, MUTEX_DRIVER
, NULL
);
264 tsp
->started
= B_FALSE
;
269 return (DDI_SUCCESS
);
272 return (DDI_SUCCESS
);
276 return (DDI_FAILURE
);
280 xcalwd_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
287 instance
= ddi_get_instance(dip
);
288 tsp
= ddi_get_soft_state(xcalwd_statep
, instance
);
289 ddi_remove_minor_node(dip
, NULL
);
290 mutex_destroy(&tsp
->lock
);
291 ddi_soft_state_free(xcalwd_statep
, instance
);
292 return (DDI_SUCCESS
);
294 return (DDI_SUCCESS
);
298 return (DDI_FAILURE
);
302 * Watchdog timeout handler that calls plat_fan_blast to take
303 * the failsafe action.
306 xcalwd_timeout(void *arg
)
308 int instance
= (int)(uintptr_t)arg
;
314 tsp
= ddi_get_soft_state(xcalwd_statep
, instance
);
318 mutex_enter(&tsp
->lock
);
319 if (tsp
->started
== B_FALSE
|| tsp
->tid
== 0) {
321 mutex_exit(&tsp
->lock
);
324 mutex_exit(&tsp
->lock
);
331 xcalwd_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*credp
)
335 if (secpolicy_sys_config(credp
, B_FALSE
) != 0)
338 if (otyp
!= OTYP_CHR
)
341 instance
= getminor(*devp
);
345 if (ddi_get_soft_state(xcalwd_statep
, instance
) == NULL
) {
354 xcalwd_close(dev_t dev
, int flag
, int otyp
, cred_t
*credp
)
360 instance
= getminor(dev
);
363 tsp
= ddi_get_soft_state(xcalwd_statep
, instance
);
367 mutex_enter(&tsp
->lock
);
368 if (tsp
->started
== B_FALSE
) {
370 mutex_exit(&tsp
->lock
);
374 * The watchdog is enabled. Cancel the pending timer
375 * and call plat_fan_blast.
377 tsp
->started
= B_FALSE
;
380 mutex_exit(&tsp
->lock
);
382 (void) untimeout(tid
);
389 * These are private ioctls for PICL environmental control plug-in
390 * to use. The plug-in enables the watchdog before performing
391 * altering fan speeds. It also periodically issues a keepalive
392 * to the watchdog to cancel and reinstate the watchdog timer.
393 * The watchdog timeout handler when executed with the watchdog
394 * enabled sets fans to full blast by calling plat_fan_blast.
398 xcalwd_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int flag
,
399 cred_t
*cred_p
, int *rvalp
)
408 if (secpolicy_sys_config(cred_p
, B_FALSE
) != 0)
411 instance
= getminor(dev
);
415 tsp
= ddi_get_soft_state(xcalwd_statep
, instance
);
420 case XCALWD_STOPWATCHDOG
:
422 * cancels any pending timer and disables the timer.
425 mutex_enter(&tsp
->lock
);
426 if (tsp
->started
== B_FALSE
) {
427 mutex_exit(&tsp
->lock
);
431 tsp
->started
= B_FALSE
;
433 mutex_exit(&tsp
->lock
);
435 (void) untimeout(tid
);
437 case XCALWD_STARTWATCHDOG
:
438 if (ddi_copyin((void *)arg
, &intvl
, sizeof (intvl
), flag
))
443 mutex_enter(&tsp
->lock
);
444 o_intvl
= tsp
->intvl
;
445 mutex_exit(&tsp
->lock
);
447 if (ddi_copyout((const void *)&o_intvl
, (void *)arg
,
448 sizeof (o_intvl
), flag
))
451 mutex_enter(&tsp
->lock
);
452 if (tsp
->started
== B_TRUE
) {
453 mutex_exit(&tsp
->lock
);
457 tsp
->tid
= realtime_timeout(xcalwd_timeout
,
458 (void *)(uintptr_t)instance
,
459 drv_usectohz(1000000) * tsp
->intvl
);
460 tsp
->started
= B_TRUE
;
461 mutex_exit(&tsp
->lock
);
463 case XCALWD_KEEPALIVE
:
465 mutex_enter(&tsp
->lock
);
468 mutex_exit(&tsp
->lock
);
470 (void) untimeout(tid
); /* cancel */
472 mutex_enter(&tsp
->lock
);
473 if (tsp
->started
== B_TRUE
) /* reinstate */
474 tsp
->tid
= realtime_timeout(xcalwd_timeout
,
475 (void *)(uintptr_t)instance
,
476 drv_usectohz(1000000) * tsp
->intvl
);
477 mutex_exit(&tsp
->lock
);
479 case XCALWD_GETSTATE
:
480 mutex_enter(&tsp
->lock
);
481 curstate
= tsp
->started
;
482 mutex_exit(&tsp
->lock
);
483 if (ddi_copyout((const void *)&curstate
, (void *)arg
,
484 sizeof (curstate
), flag
))