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/stat.h>
29#include <sys/conf.h>
30#include <sys/modctl.h>
31#include <sys/sunddi.h>
32#include <sys/callb.h>
33#include <sys/strlog.h>
34#include <sys/file.h>
35#include <sys/lom_io.h>
36#include <sys/ddi.h>
37#include <sys/time.h>
38
39#define	LOMIOCALCTL_OLD		_IOW('a', 4, ts_aldata_t)
40#define	LOMIOCALSTATE_OLD	_IOWR('a', 5, ts_aldata_t)
41
42struct tsalarm_softc {
43	dev_info_t *dip;
44	kmutex_t mutex;
45};
46
47#define	getsoftc(minor)	\
48		((struct tsalarm_softc *)ddi_get_soft_state(statep, (minor)))
49/*
50 * Driver entry points
51 */
52
53/* dev_ops and cb_ops entry point function declarations */
54
55static int	tsalarm_attach(dev_info_t *, ddi_attach_cmd_t);
56static int	tsalarm_detach(dev_info_t *, ddi_detach_cmd_t);
57static int	tsalarm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
58
59static int	tsalarm_open(dev_t *, int, int, cred_t *);
60static int	tsalarm_close(dev_t, int, int, cred_t *);
61static int	tsalarm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
62
63static struct cb_ops tsalarm_cb_ops = {
64	tsalarm_open,	/* open */
65	tsalarm_close,	/* close */
66	nodev,		/* strategy() */
67	nodev,		/* print() */
68	nodev,		/* dump() */
69	nodev,		/* read() */
70	nodev,		/* write() */
71	tsalarm_ioctl,	/* ioctl() */
72	nodev,		/* devmap() */
73	nodev,		/* mmap() */
74	ddi_segmap,	/* segmap() */
75	nochpoll,	/* poll() */
76	ddi_prop_op,    /* prop_op() */
77	NULL,		/* cb_str */
78	D_NEW | D_MP	/* cb_flag */
79};
80
81
82static struct dev_ops tsalarm_ops = {
83	DEVO_REV,
84	0,			/* ref count */
85	tsalarm_getinfo,	/* getinfo() */
86	nulldev,		/* identify() */
87	nulldev,		/* probe() */
88	tsalarm_attach,		/* attach() */
89	tsalarm_detach,		/* detach */
90	nodev,			/* reset */
91	&tsalarm_cb_ops,		/* pointer to cb_ops structure */
92	(struct bus_ops *)NULL,
93	nulldev,		/* power() */
94	ddi_quiesce_not_needed,		/* quiesce() */
95};
96
97/*
98 * Loadable module support.
99 */
100extern struct mod_ops mod_driverops;
101static void    *statep;
102
103static struct modldrv modldrv = {
104	&mod_driverops,			/* Type of module. This is a driver */
105	"tsalarm control driver",	/* Name of the module */
106	&tsalarm_ops			/* pointer to the dev_ops structure */
107};
108
109static struct modlinkage modlinkage = {
110	MODREV_1,
111	&modldrv,
112	NULL
113};
114
115extern int rmclomv_alarm_get(int alarm_type, int *alarm_state);
116extern int rmclomv_alarm_set(int alarm_type, int new_state);
117
118int
119_init(void)
120{
121	int    e;
122
123	if (e = ddi_soft_state_init(&statep,
124				sizeof (struct tsalarm_softc), 1)) {
125		return (e);
126	}
127
128	if ((e = mod_install(&modlinkage)) != 0) {
129		ddi_soft_state_fini(&statep);
130	}
131
132	return (e);
133}
134
135
136int
137_fini(void)
138{
139	int e;
140
141	if ((e = mod_remove(&modlinkage)) != 0) {
142		return (e);
143	}
144
145	ddi_soft_state_fini(&statep);
146
147	return (DDI_SUCCESS);
148}
149
150
151int
152_info(struct modinfo *modinfop)
153{
154	return (mod_info(&modlinkage, modinfop));
155}
156
157
158/* ARGSUSED */
159static int
160tsalarm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
161{
162	int	inst = getminor((dev_t)arg);
163	int	retval = DDI_SUCCESS;
164	struct tsalarm_softc *softc;
165
166	switch (cmd) {
167
168	case DDI_INFO_DEVT2DEVINFO:
169		if ((softc = getsoftc(inst)) == NULL) {
170			*result = (void *)NULL;
171			retval = DDI_FAILURE;
172		} else {
173			*result = (void *)softc->dip;
174		}
175		break;
176
177	case DDI_INFO_DEVT2INSTANCE:
178		*result = (void *)(uintptr_t)inst;
179		break;
180
181	default:
182		retval = DDI_FAILURE;
183	}
184
185	return (retval);
186}
187
188static int
189tsalarm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
190{
191
192	int inst;
193	struct tsalarm_softc *softc = NULL;
194
195	switch (cmd) {
196
197	case DDI_ATTACH:
198		inst = ddi_get_instance(dip);
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		mutex_init(&softc->mutex, NULL, MUTEX_DRIVER, NULL);
208		/*
209		 * Create minor node.  The minor device number, inst, has no
210		 * meaning.  The model number above, which will be added to
211		 * the device's softc, is used to direct peculiar behavior.
212		 */
213		if (ddi_create_minor_node(dip, "lom", S_IFCHR, 0,
214		    DDI_PSEUDO, NULL) == DDI_FAILURE)
215			goto attach_failed;
216
217		ddi_report_dev(dip);
218		return (DDI_SUCCESS);
219
220	case DDI_RESUME:
221		return (DDI_SUCCESS);
222
223	default:
224		return (DDI_FAILURE);
225	}
226
227attach_failed:
228	/* Free soft state, if allocated. remove minor node if added earlier */
229	if (softc) {
230		mutex_destroy(&softc->mutex);
231		ddi_soft_state_free(statep, inst);
232	}
233
234	ddi_remove_minor_node(dip, NULL);
235
236	return (DDI_FAILURE);
237}
238
239static int
240tsalarm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
241{
242	int inst;
243	struct tsalarm_softc *softc;
244
245	switch (cmd) {
246
247	case DDI_DETACH:
248		inst = ddi_get_instance(dip);
249		if ((softc = getsoftc(inst)) == NULL)
250			return (DDI_FAILURE);
251		/*
252		 * Free the soft state and remove minor node added earlier.
253		 */
254		ddi_remove_minor_node(dip, NULL);
255		mutex_destroy(&softc->mutex);
256		ddi_soft_state_free(statep, inst);
257		return (DDI_SUCCESS);
258
259	case DDI_SUSPEND:
260		return (DDI_SUCCESS);
261
262	default:
263		return (DDI_FAILURE);
264
265	}
266}
267
268/* ARGSUSED */
269static int
270tsalarm_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 */
279static int
280tsalarm_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 */
289static int
290tsalarm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
291		cred_t *credp, int *rvalp)
292{
293	int		inst = getminor(dev);
294	struct tsalarm_softc *softc;
295	int retval = 0;
296	ts_aldata_t ts_alinfo;
297	int alarm_type, alarm_state = 0;
298
299	if ((softc = getsoftc(inst)) == NULL)
300		return (ENXIO);
301
302	mutex_enter(&softc->mutex);
303
304	switch (cmd) {
305
306	case LOMIOCALSTATE:
307	case LOMIOCALSTATE_OLD:
308		{
309			if (ddi_copyin((caddr_t)arg, (caddr_t)&ts_alinfo,
310			    sizeof (ts_aldata_t), mode) != 0) {
311				retval = EFAULT;
312				goto end;
313			}
314
315			alarm_type = ts_alinfo.alarm_no;
316			if ((alarm_type < ALARM_CRITICAL) ||
317			    (alarm_type > ALARM_USER)) {
318				retval = EINVAL;
319				goto end;
320			}
321
322			retval = rmclomv_alarm_get(alarm_type, &alarm_state);
323
324			if (retval != 0)
325				goto end;
326
327			if ((alarm_state != 0) && (alarm_state != 1)) {
328				retval = EIO;
329				goto end;
330			}
331
332			ts_alinfo.alarm_state = alarm_state;
333			if (ddi_copyout((caddr_t)&ts_alinfo, (caddr_t)arg,
334			    sizeof (ts_aldata_t), mode) != 0) {
335				retval = EFAULT;
336				goto end;
337			}
338
339		}
340		break;
341
342	case LOMIOCALCTL:
343	case LOMIOCALCTL_OLD:
344		{
345			if (ddi_copyin((caddr_t)arg, (caddr_t)&ts_alinfo,
346			    sizeof (ts_aldata_t), mode) != 0) {
347				retval = EFAULT;
348				goto end;
349			}
350
351			alarm_type = ts_alinfo.alarm_no;
352			alarm_state = ts_alinfo.alarm_state;
353
354			if ((alarm_type < ALARM_CRITICAL) ||
355			    (alarm_type > ALARM_USER)) {
356				retval = EINVAL;
357				goto end;
358			}
359			if ((alarm_state < ALARM_OFF) ||
360			    (alarm_state > ALARM_ON)) {
361				retval = EINVAL;
362				goto end;
363			}
364
365			retval = rmclomv_alarm_set(alarm_type, alarm_state);
366		}
367		break;
368
369	default:
370		retval = EINVAL;
371		break;
372	}
373
374end:
375	mutex_exit(&softc->mutex);
376
377	return (retval);
378}
379