xref: /illumos-gate/usr/src/uts/common/io/srn.c (revision bb57d1f5164aca913cbd286ae1b61c896167cfa7)
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 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * srn	Provide apm-like interfaces to Xorg
31  */
32 
33 #include <sys/types.h>
34 #include <sys/errno.h>
35 #include <sys/modctl.h>
36 #include <sys/conf.h>		/* driver flags and functions */
37 #include <sys/open.h>		/* OTYP_CHR definition */
38 #include <sys/stat.h>		/* S_IFCHR definition */
39 #include <sys/pathname.h>	/* name -> dev_info xlation */
40 #include <sys/kmem.h>		/* memory alloc stuff */
41 #include <sys/debug.h>
42 #include <sys/pm.h>
43 #include <sys/ddi.h>
44 #include <sys/sunddi.h>
45 #include <sys/epm.h>
46 #include <sys/vfs.h>
47 #include <sys/mode.h>
48 #include <sys/mkdev.h>
49 #include <sys/promif.h>
50 #include <sys/consdev.h>
51 #include <sys/ddi_impldefs.h>
52 #include <sys/poll.h>
53 #include <sys/note.h>
54 #include <sys/taskq.h>
55 #include <sys/policy.h>
56 #include <sys/srn.h>
57 
58 /*
59  * Minor number is instance<<8 + clone minor from range 1-255;
60  * But only one will be allocated
61  */
62 #define	SRN_MINOR_TO_CLONE(minor) ((minor) & (SRN_MAX_CLONE - 1))
63 #define	SU		0x002
64 #define	SG		0x004
65 
66 extern kmutex_t	srn_clone_lock;	/* protects srn_clones array */
67 extern kcondvar_t srn_clones_cv[SRN_MAX_CLONE];
68 extern uint_t	srn_poll_cnt[SRN_MAX_CLONE];
69 
70 /*
71  * The soft state of the srn driver.  Since there will only be
72  * one of these, just reference it through a static struct.
73  */
74 static struct srnstate {
75 	dev_info_t	*srn_dip;		/* ptr to our dev_info node */
76 	int		srn_instance;		/* for ddi_get_instance() */
77 	uchar_t		srn_clones[SRN_MAX_CLONE]; /* unique opens	*/
78 	struct cred	*srn_cred[SRN_MAX_CLONE]; /* cred for each open	*/
79 	int		srn_type[SRN_MAX_CLONE]; /* type of handshake */
80 	int		srn_delivered[SRN_MAX_CLONE];
81 	srn_event_info_t srn_pending[SRN_MAX_CLONE];
82 } srn = { NULL, -1};
83 typedef struct srnstate *srn_state_t;
84 
85 kcondvar_t	srn_clones_cv[SRN_MAX_CLONE];
86 uint_t		srn_poll_cnt[SRN_MAX_CLONE];	/* count of events for poll */
87 int		srn_apm_count;
88 int		srn_autosx_count;
89 struct pollhead	srn_pollhead[SRN_MAX_CLONE];
90 
91 static int	srn_open(dev_t *, int, int, cred_t *);
92 static int	srn_close(dev_t, int, int, cred_t *);
93 static int	srn_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
94 static int	srn_chpoll(dev_t, short, int, short *, struct pollhead **);
95 
96 static struct cb_ops srn_cb_ops = {
97 	srn_open,	/* open */
98 	srn_close,	/* close */
99 	nodev,		/* strategy */
100 	nodev,		/* print */
101 	nodev,		/* dump */
102 	nodev,		/* read */
103 	nodev,		/* write */
104 	srn_ioctl,	/* ioctl */
105 	nodev,		/* devmap */
106 	nodev,		/* mmap */
107 	nodev,		/* segmap */
108 	srn_chpoll,	/* poll */
109 	ddi_prop_op,	/* prop_op */
110 	NULL,		/* streamtab */
111 	D_NEW | D_MP	/* driver compatibility flag */
112 };
113 
114 static int srn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
115     void **result);
116 static int srn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
117 static int srn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
118 static void srn_notify(int type, int event);
119 
120 static struct dev_ops srn_ops = {
121 	DEVO_REV,		/* devo_rev */
122 	0,			/* refcnt */
123 	srn_getinfo,		/* info */
124 	nulldev,		/* identify */
125 	nulldev,		/* probe */
126 	srn_attach,		/* attach */
127 	srn_detach,		/* detach */
128 	nodev,			/* reset */
129 	&srn_cb_ops,		/* driver operations */
130 	NULL,			/* bus operations */
131 	NULL			/* power */
132 };
133 
134 static struct modldrv modldrv = {
135 	&mod_driverops,
136 	"srn driver v1.4",
137 	&srn_ops
138 };
139 
140 static struct modlinkage modlinkage = {
141 	MODREV_1, &modldrv, 0
142 };
143 
144 /* Local functions */
145 
146 int
147 _init(void)
148 {
149 	return (mod_install(&modlinkage));
150 }
151 
152 int
153 _fini(void)
154 {
155 	return (mod_remove(&modlinkage));
156 }
157 
158 int
159 _info(struct modinfo *modinfop)
160 {
161 	return (mod_info(&modlinkage, modinfop));
162 }
163 
164 static int
165 srn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
166 {
167 	int		i;
168 	extern void (*srn_signal)(int, int);
169 
170 	switch (cmd) {
171 
172 	case DDI_ATTACH:
173 		if (srn.srn_instance != -1)	/* Only allow one instance */
174 			return (DDI_FAILURE);
175 		srn.srn_instance = ddi_get_instance(dip);
176 		if (ddi_create_minor_node(dip, "srn", S_IFCHR,
177 		    (srn.srn_instance << 8) + 0, DDI_PSEUDO, 0)
178 		    != DDI_SUCCESS) {
179 			return (DDI_FAILURE);
180 		}
181 		srn.srn_dip = dip;	/* srn_init and getinfo depend on it */
182 
183 		for (i = 0; i < SRN_MAX_CLONE; i++)
184 			cv_init(&srn_clones_cv[i], NULL, CV_DEFAULT, NULL);
185 
186 		srn.srn_instance = ddi_get_instance(dip);
187 		mutex_enter(&srn_clone_lock);
188 		srn_signal = srn_notify;
189 		mutex_exit(&srn_clone_lock);
190 		ddi_report_dev(dip);
191 		return (DDI_SUCCESS);
192 
193 	default:
194 		return (DDI_FAILURE);
195 	}
196 }
197 
198 /* ARGSUSED */
199 static int
200 srn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
201 {
202 	int i;
203 	extern int srn_inuse;
204 	extern void (*srn_signal)(int, int);
205 
206 	switch (cmd) {
207 	case DDI_DETACH:
208 
209 		mutex_enter(&srn_clone_lock);
210 		while (srn_inuse) {
211 			mutex_exit(&srn_clone_lock);
212 			delay(1);
213 			mutex_enter(&srn_clone_lock);
214 		}
215 		srn_signal = NULL;
216 		mutex_exit(&srn_clone_lock);
217 
218 		for (i = 0; i < SRN_MAX_CLONE; i++)
219 			cv_destroy(&srn_clones_cv[i]);
220 
221 		ddi_remove_minor_node(dip, NULL);
222 		srn.srn_instance = -1;
223 		return (DDI_SUCCESS);
224 
225 	default:
226 		return (DDI_FAILURE);
227 	}
228 }
229 
230 
231 #ifdef DEBUG
232 char *srn_cmd_string;
233 int srn_cmd;
234 #endif
235 
236 /*
237  * Returns true if permission granted by credentials
238  * XXX
239  */
240 static int
241 srn_perms(int perm, cred_t *cr)
242 {
243 	if ((perm & SU) && secpolicy_power_mgmt(cr) == 0) /* privileged? */
244 		return (1);
245 	if ((perm & SG) && (crgetgid(cr) == 0))	/* group 0 is ok */
246 		return (1);
247 	return (0);
248 }
249 
250 static int
251 srn_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
252 	struct pollhead **phpp)
253 {
254 	extern struct pollhead srn_pollhead[];	/* common/os/sunpm.c */
255 	int	clone;
256 
257 	clone = SRN_MINOR_TO_CLONE(getminor(dev));
258 	if ((events & (POLLIN | POLLRDNORM)) && srn_poll_cnt[clone]) {
259 		*reventsp |= (POLLIN | POLLRDNORM);
260 	} else {
261 		*reventsp = 0;
262 		if (!anyyet) {
263 			*phpp = &srn_pollhead[clone];
264 		}
265 	}
266 	return (0);
267 }
268 
269 /*ARGSUSED*/
270 static int
271 srn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
272 {
273 	dev_t	dev;
274 	int	instance;
275 
276 	switch (infocmd) {
277 	case DDI_INFO_DEVT2DEVINFO:
278 		if (srn.srn_instance == -1)
279 			return (DDI_FAILURE);
280 		*result = srn.srn_dip;
281 		return (DDI_SUCCESS);
282 
283 	case DDI_INFO_DEVT2INSTANCE:
284 		dev = (dev_t)arg;
285 		instance = getminor(dev) >> 8;
286 		*result = (void *)(uintptr_t)instance;
287 		return (DDI_SUCCESS);
288 
289 	default:
290 		return (DDI_FAILURE);
291 	}
292 }
293 
294 
295 /*ARGSUSED1*/
296 static int
297 srn_open(dev_t *devp, int flag, int otyp, cred_t *cr)
298 {
299 	int		clone;
300 
301 	if (otyp != OTYP_CHR)
302 		return (EINVAL);
303 
304 	mutex_enter(&srn_clone_lock);
305 	for (clone = 1; clone < SRN_MAX_CLONE - 1; clone++)
306 		if (!srn.srn_clones[clone])
307 			break;
308 
309 	if (clone == SRN_MAX_CLONE) {
310 		mutex_exit(&srn_clone_lock);
311 		return (ENXIO);
312 	}
313 	srn.srn_cred[clone] = cr;
314 	ASSERT(srn_apm_count >= 0);
315 	srn_apm_count++;
316 	srn.srn_type[clone] = SRN_TYPE_APM;
317 	crhold(cr);
318 
319 	*devp = makedevice(getmajor(*devp), (srn.srn_instance << 8) +
320 	    clone);
321 	srn.srn_clones[clone] = 1;
322 	srn.srn_cred[clone] = cr;
323 	crhold(cr);
324 	mutex_exit(&srn_clone_lock);
325 	PMD(PMD_SX, ("srn open OK\n"))
326 	return (0);
327 }
328 
329 /*ARGSUSED1*/
330 static int
331 srn_close(dev_t dev, int flag, int otyp, cred_t *cr)
332 {
333 	int clone;
334 
335 	if (otyp != OTYP_CHR)
336 		return (EINVAL);
337 
338 	clone = SRN_MINOR_TO_CLONE(getminor(dev));
339 	PMD(PMD_SX, ("srn_close: minor %x, clone %x\n", getminor(dev),
340 	    clone))
341 	mutex_enter(&srn_clone_lock);
342 	crfree(srn.srn_cred[clone]);
343 	srn.srn_cred[clone] = 0;
344 	srn_poll_cnt[clone] = 0;
345 	if (srn.srn_pending[clone].ae_type || srn.srn_delivered[clone]) {
346 		srn.srn_pending[clone].ae_type = 0;
347 		srn.srn_delivered[clone] = 0;
348 		cv_signal(&srn_clones_cv[clone]);
349 	}
350 	switch (srn.srn_type[clone]) {
351 	case SRN_TYPE_AUTOSX:
352 		ASSERT(srn_autosx_count);
353 		srn_autosx_count--;
354 		break;
355 	case SRN_TYPE_APM:
356 		ASSERT(srn_apm_count);
357 		srn_apm_count--;
358 		break;
359 	default:
360 		ASSERT(0);
361 		return (EINVAL);
362 	}
363 	srn.srn_clones[clone] = 0;
364 	mutex_exit(&srn_clone_lock);
365 	return (0);
366 }
367 
368 /*ARGSUSED*/
369 static int
370 srn_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
371 {
372 	int clone = SRN_MINOR_TO_CLONE(getminor(dev));
373 
374 	PMD(PMD_SX, ("ioctl: %x: begin\n", cmd))
375 
376 	switch (cmd) {
377 	case SRN_IOC_NEXTEVENT:
378 	case SRN_IOC_SUSPEND:
379 	case SRN_IOC_RESUME:
380 	case SRN_IOC_AUTOSX:
381 		break;
382 	default:
383 		return (ENOTTY);
384 	}
385 
386 	if (!srn_perms(SU | SG, srn.srn_cred[clone])) {
387 		return (EPERM);
388 	}
389 	switch (cmd) {
390 	case SRN_IOC_AUTOSX:
391 		PMD(PMD_SX, ("SRN_IOC_AUTOSX entered\n"))
392 		mutex_enter(&srn_clone_lock);
393 		if (!srn.srn_clones[clone]) {
394 			PMD(PMD_SX, (" ioctl !srn_clones--EINVAL\n"))
395 			mutex_exit(&srn_clone_lock);
396 			return (EINVAL);
397 		}
398 		if (srn.srn_pending[clone].ae_type) {
399 			PMD(PMD_SX, ("AUTOSX while pending--EBUSY\n"))
400 			mutex_exit(&srn_clone_lock);
401 			return (EBUSY);
402 		}
403 		if (srn.srn_type[clone] == SRN_TYPE_AUTOSX) {
404 			PMD(PMD_SX, ("AUTOSX already--EBUSY\n"))
405 			mutex_exit(&srn_clone_lock);
406 			return (EBUSY);
407 		}
408 		ASSERT(srn.srn_type[clone] == SRN_TYPE_APM);
409 		srn.srn_type[clone] = SRN_TYPE_AUTOSX;
410 		srn_apm_count--;
411 		ASSERT(srn_apm_count >= 0);
412 		ASSERT(srn_autosx_count >= 0);
413 		srn_autosx_count++;
414 		mutex_exit(&srn_clone_lock);
415 		PMD(PMD_SX, ("SRN_IOC_AUTOSX returns success\n"))
416 		return (0);
417 
418 	case SRN_IOC_NEXTEVENT:
419 		/*
420 		 * return the next suspend or resume event;  there should
421 		 * be one, cause we only get called if we've signalled a
422 		 * poll data completion
423 		 * then wake up the kernel thread sleeping for the delivery
424 		 */
425 		PMD(PMD_SX, ("SRN_IOC_NEXTEVENT entered\n"))
426 		mutex_enter(&srn_clone_lock);
427 		if (srn_poll_cnt[clone] == 0) {
428 			mutex_exit(&srn_clone_lock);
429 			PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d "
430 			    "EWOULDBLOCK\n", clone))
431 			return (EWOULDBLOCK);
432 		}
433 		ASSERT(srn.srn_pending[clone].ae_type);
434 		if (ddi_copyout(&srn.srn_pending[clone], (void *)arg,
435 		    sizeof (srn_event_info_t), mode) != 0) {
436 			mutex_exit(&srn_clone_lock);
437 			PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d EFAULT\n",
438 			    clone))
439 			return (EFAULT);
440 		}
441 		if (srn.srn_type[clone] == SRN_TYPE_APM)
442 			srn.srn_delivered[clone] =
443 			    srn.srn_pending[clone].ae_type;
444 		PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d delivered %x\n",
445 		    clone, srn.srn_pending[clone].ae_type))
446 		srn_poll_cnt[clone] = 0;
447 		mutex_exit(&srn_clone_lock);
448 		return (0);
449 
450 	case SRN_IOC_SUSPEND:
451 		/* ack suspend */
452 		PMD(PMD_SX, ("SRN_IOC_SUSPEND entered clone %d\n", clone))
453 		mutex_enter(&srn_clone_lock);
454 		if (srn.srn_delivered[clone] != SRN_SUSPEND_REQ) {
455 			mutex_exit(&srn_clone_lock);
456 			PMD(PMD_SX, ("SRN_IOC_SUSPEND EINVAL\n"))
457 			return (EINVAL);
458 		}
459 		srn.srn_delivered[clone] = 0;
460 		srn.srn_pending[clone].ae_type = 0;
461 		/* notify the kernel suspend thread  to continue */
462 		PMD(PMD_SX, ("SRN_IOC_SUSPEND clone %d ok\n", clone))
463 		cv_signal(&srn_clones_cv[clone]);
464 		mutex_exit(&srn_clone_lock);
465 		return (0);
466 
467 	case SRN_IOC_RESUME:
468 		/* ack resume */
469 		PMD(PMD_SX, ("SRN_IOC_RESUME entered clone %d\n", clone))
470 		mutex_enter(&srn_clone_lock);
471 		if (srn.srn_delivered[clone] != SRN_NORMAL_RESUME) {
472 			mutex_exit(&srn_clone_lock);
473 			PMD(PMD_SX, ("SRN_IOC_RESUME EINVAL\n"))
474 			return (EINVAL);
475 		}
476 		srn.srn_delivered[clone] = 0;
477 		srn.srn_pending[clone].ae_type = 0;
478 		/* notify the kernel resume thread  to continue */
479 		PMD(PMD_SX, ("SRN_IOC_RESUME ok for clone %d\n", clone))
480 		cv_signal(&srn_clones_cv[clone]);
481 		mutex_exit(&srn_clone_lock);
482 		return (0);
483 
484 	default:
485 		PMD(PMD_SX, ("srn_ioctl unknown cmd EINVAL\n"))
486 		return (EINVAL);
487 	}
488 }
489 /*
490  * A very simple handshake with the srn driver,
491  * only one outstanding event at a time.
492  * The OS delivers the event and depending on type,
493  * either blocks waiting for the ack, or drives on
494  */
495 void
496 srn_notify(int type, int event)
497 {
498 	int clone, count;
499 	PMD(PMD_SX, ("srn_notify entered with type %d, event 0x%x\n",
500 	    type, event));
501 	ASSERT(mutex_owned(&srn_clone_lock));
502 	switch (type) {
503 	case SRN_TYPE_APM:
504 		if (srn_apm_count == 0) {
505 			PMD(PMD_SX, ("no apm types\n"))
506 			return;
507 		}
508 		count = srn_apm_count;
509 		break;
510 	case SRN_TYPE_AUTOSX:
511 		if (srn_autosx_count == 0) {
512 			PMD(PMD_SX, ("no autosx types\n"))
513 			return;
514 		}
515 		count = srn_autosx_count;
516 		break;
517 	default:
518 		ASSERT(0);
519 		break;
520 	}
521 	ASSERT(count > 0);
522 	PMD(PMD_SX, ("count %d\n", count))
523 	for (clone = 0; clone < SRN_MAX_CLONE; clone++) {
524 		if (srn.srn_type[clone] == type) {
525 			if (type == SRN_TYPE_APM) {
526 				ASSERT(srn.srn_pending[clone].ae_type == 0);
527 				ASSERT(srn_poll_cnt[clone] == 0);
528 				ASSERT(srn.srn_delivered[clone] == 0);
529 			}
530 			srn.srn_pending[clone].ae_type = event;
531 			srn_poll_cnt[clone] = 1;
532 			PMD(PMD_SX, ("pollwake %d\n", clone))
533 			pollwakeup(&srn_pollhead[clone], (POLLRDNORM | POLLIN));
534 			count--;
535 			if (count == 0)
536 				break;
537 		}
538 	}
539 	if (type == SRN_TYPE_AUTOSX) {		/* we don't wait */
540 		PMD(PMD_SX, ("Not waiting for AUTOSX ack\n"))
541 		return;
542 	}
543 	ASSERT(type == SRN_TYPE_APM);
544 	/* otherwise wait for acks */
545 restart:
546 	/*
547 	 * We wait untill all of the pending events are cleared.
548 	 * We have to start over every time we do a cv_wait because
549 	 * we give up the mutex and can be re-entered
550 	 */
551 	for (clone = 1; clone < SRN_MAX_CLONE; clone++) {
552 		if (srn.srn_clones[clone] == 0 ||
553 		    srn.srn_type[clone] != SRN_TYPE_APM)
554 			continue;
555 		if (srn.srn_pending[clone].ae_type) {
556 			PMD(PMD_SX, ("srn_notify waiting for ack for clone %d, "
557 			    "event %x\n", clone, event))
558 			cv_wait(&srn_clones_cv[clone], &srn_clone_lock);
559 			goto restart;
560 		}
561 	}
562 	PMD(PMD_SX, ("srn_notify done with %x\n", event))
563 }
564