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#include <sys/types.h>
27#include <sys/ksynch.h>
28#include <sys/kmem.h>
29#include <sys/file.h>
30#include <sys/errno.h>
31#include <sys/open.h>
32#include <sys/cred.h>
33#include <sys/conf.h>
34#include <sys/uio.h>
35#include <sys/cmn_err.h>
36
37#define	__NSC_GEN__
38#include "nsc_dev.h"
39#include "nsc_ioctl.h"
40#include "nsc_power.h"
41#include "../nsctl.h"
42
43extern nsc_mem_t *_nsc_local_mem;
44static  int null_power(void);
45
46
47typedef struct _nsc_power_s {
48	struct _nsc_power_s *next;	/* chain */
49	char *name;			/* module name */
50	void (*pw_power_lost)(int);	/* callback power lost(rideout) */
51	void (*pw_power_ok)(void);	/* callback power ok */
52	void (*pw_power_down)(void);
53				/* callback power down (shutdown imminent) */
54} _nsc_power_t;
55
56#define	_P(x)	(((long)(&((_nsc_power_t *)0)->x))/sizeof (long))
57
58static nsc_def_t _nsc_power_def[] = {
59	"Power_Lost",	(uintptr_t)null_power,	_P(pw_power_lost),
60	"Power_OK",	(uintptr_t)null_power,	_P(pw_power_ok),
61	"Power_Down",	(uintptr_t)null_power,	_P(pw_power_down),
62	0,		0,			0,
63};
64
65static _nsc_power_t *_power_clients;
66static kmutex_t _power_mutex;
67
68
69static int null_power(void)
70/*
71 * init null_power - dummy power routine for clients that choose not
72 * to implement all the power hooks.
73 *
74 */
75{
76	return (0);
77}
78
79/*
80 * int
81 * _nsc_power
82 *	Call registered clients of the generic power ioctls.
83 *
84 * Calling/Exit State:
85 *	Calls all the registered clients with a message describing the
86 *      current state of the power for the system.
87 */
88int
89_nsc_power(blind_t argp, int *rvp)
90{
91	nsc_power_ctl_t opc;
92	_nsc_power_t *pp;
93
94	*rvp = 0;
95	if (copyin((void *) argp, &opc, sizeof (nsc_power_ctl_t)))
96		return (EFAULT);
97	mutex_enter(&_power_mutex);
98
99	pp = _power_clients;
100	while (pp) {
101		switch ((nsc_power_ops_t)opc.msg) {
102
103	case Power_OK:
104			(*pp->pw_power_ok)();
105			break;
106
107	case Power_Down:
108			(*pp->pw_power_down)();
109			break;
110
111	case Power_Lost:
112			(*pp->pw_power_lost)(opc.arg1);
113			break;
114
115	default:
116			mutex_exit(&_power_mutex);
117			return (EINVAL);
118		}
119
120		pp = pp->next;
121	}
122	mutex_exit(&_power_mutex);
123	return (0);
124}
125
126/*
127 * int
128 * _nsc_init_power (void)
129 *	Initialise power ioctl subsystem.
130 *
131 * Calling/Exit State:
132 *	Called at driver initialisation time to allocate necessary
133 *	data structures.
134 */
135int
136_nsc_init_power(void)
137{
138	mutex_init(&_power_mutex, NULL, MUTEX_DRIVER, NULL);
139	return (0);
140}
141
142/*
143 * int
144 * _nsc_deinit_power (void)
145 *	Initialise power ioctl subsystem.
146 *
147 * Calling/Exit State:
148 *	Called at driver initialisation time to allocate necessary
149 *	data structures.
150 */
151int
152_nsc_deinit_power(void)
153{
154	_nsc_power_t *pp, *npp;
155
156	mutex_enter(&_power_mutex);
157	pp = _power_clients;
158	while (pp) {
159		npp = pp->next;
160		nsc_kmem_free(pp, sizeof (_nsc_power_t));
161		pp = npp;
162	}
163	_power_clients = NULL;
164	mutex_exit(&_power_mutex);
165	mutex_destroy(&_power_mutex);
166	return (0);
167}
168
169/*
170 * blind_t
171 * nsc_register_power (char *name, nsc_def_t *def)
172 *	Register an power ioctl client.
173 *
174 * Calling/Exit State:
175 *	Returns a token for use in future calls to nsc_unregister_power.
176 *      If a client with the same name is already registered then NULL
177 *      is return to indicate failure.
178 *	If registration fails NULL is returned.
179 *
180 * Description:
181 *	Registers an power ioctl client for notifications during subsequent
182 *      ioctl from UPS/PCU management.
183 */
184blind_t
185nsc_register_power(char *name, nsc_def_t *def)
186{
187	_nsc_power_t *entry, *pp;
188
189
190	entry = nsc_kmem_alloc(sizeof (_nsc_power_t), 0, _nsc_local_mem);
191
192	if (entry == NULL)
193		return (NULL);
194	nsc_decode_param(def, _nsc_power_def, (long *)entry);
195
196	mutex_enter(&_power_mutex);
197
198	for (pp = _power_clients; pp; pp = pp->next) {
199		if (strcmp(pp->name, name) == 0) {
200			mutex_exit(&_power_mutex);
201			nsc_kmem_free(entry, sizeof (_nsc_power_t));
202			return (NULL);
203		}
204	}
205	entry->name = name;
206
207	entry->next = _power_clients;
208	_power_clients = entry;
209	mutex_exit(&_power_mutex);
210	return ((blind_t)entry);
211}
212
213/*
214 * int
215 * nsc_unregister_power (blind_t powerp)
216 *	Un-register a power ioctl client.
217 *
218 * Calling/Exit State:
219 *	Returns 0 on success, otherwise returns an error code.
220 *
221 * Description:
222 *	The specified power ioctl client is un-registered if possible.
223 *      Zero is returned on success otherwise an error code.
224 */
225int
226nsc_unregister_power(blind_t powerp)
227{
228	_nsc_power_t **xpp, *entry;
229
230	entry = (_nsc_power_t *)powerp;
231	if (entry == NULL)
232		return (EINVAL);
233
234	mutex_enter(&_power_mutex);
235
236	for (xpp = &_power_clients; *xpp; xpp = &(*xpp)->next)
237		if (*xpp == entry)
238			break;
239
240	if (*xpp == NULL) {
241		mutex_exit(&_power_mutex);
242		return (EALREADY);
243	}
244	*xpp = entry->next;
245	mutex_exit(&_power_mutex);
246	nsc_kmem_free(entry, sizeof (_nsc_power_t));
247
248	return (0);
249}
250