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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2017 Joyent, Inc.
30 * srn Provide apm-like interfaces to Xorg
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>
44 #include <sys/sunddi.h>
48 #include <sys/mkdev.h>
49 #include <sys/promif.h>
50 #include <sys/consdev.h>
51 #include <sys/ddi_impldefs.h>
54 #include <sys/taskq.h>
55 #include <sys/policy.h>
59 * Minor number is instance<<8 + clone minor from range 1-255;
60 * But only one will be allocated
62 #define SRN_MINOR_TO_CLONE(minor) ((minor) & (SRN_MAX_CLONE - 1))
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
];
71 * The soft state of the srn driver. Since there will only be
72 * one of these, just reference it through a static struct.
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 int srn_fault
[SRN_MAX_CLONE
];
84 typedef struct srnstate
*srn_state_t
;
86 kcondvar_t srn_clones_cv
[SRN_MAX_CLONE
];
87 uint_t srn_poll_cnt
[SRN_MAX_CLONE
]; /* count of events for poll */
90 /* Number of seconds to wait for clients to ack a poll */
93 struct pollhead srn_pollhead
[SRN_MAX_CLONE
];
95 static int srn_open(dev_t
*, int, int, cred_t
*);
96 static int srn_close(dev_t
, int, int, cred_t
*);
97 static int srn_ioctl(dev_t
, int, intptr_t, int, cred_t
*, int *);
98 static int srn_chpoll(dev_t
, short, int, short *, struct pollhead
**);
100 static struct cb_ops srn_cb_ops
= {
102 srn_close
, /* close */
103 nodev
, /* strategy */
108 srn_ioctl
, /* ioctl */
112 srn_chpoll
, /* poll */
113 ddi_prop_op
, /* prop_op */
114 NULL
, /* streamtab */
115 D_NEW
| D_MP
/* driver compatibility flag */
118 static int srn_getinfo(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
,
120 static int srn_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
);
121 static int srn_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
);
122 static void srn_notify(int type
, int event
);
124 static struct dev_ops srn_ops
= {
125 DEVO_REV
, /* devo_rev */
127 srn_getinfo
, /* info */
128 nulldev
, /* identify */
130 srn_attach
, /* attach */
131 srn_detach
, /* detach */
133 &srn_cb_ops
, /* driver operations */
134 NULL
, /* bus operations */
136 ddi_quiesce_not_needed
, /* quiesce */
139 static struct modldrv modldrv
= {
145 static struct modlinkage modlinkage
= {
146 MODREV_1
, &modldrv
, 0
149 /* Local functions */
154 return (mod_install(&modlinkage
));
160 return (mod_remove(&modlinkage
));
164 _info(struct modinfo
*modinfop
)
166 return (mod_info(&modlinkage
, modinfop
));
170 srn_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
173 extern void (*srn_signal
)(int, int);
178 if (srn
.srn_instance
!= -1) /* Only allow one instance */
179 return (DDI_FAILURE
);
180 srn
.srn_instance
= ddi_get_instance(dip
);
181 if (ddi_create_minor_node(dip
, "srn", S_IFCHR
,
182 (srn
.srn_instance
<< 8) + 0, DDI_PSEUDO
, 0)
184 return (DDI_FAILURE
);
186 srn
.srn_dip
= dip
; /* srn_init and getinfo depend on it */
188 for (i
= 0; i
< SRN_MAX_CLONE
; i
++)
189 cv_init(&srn_clones_cv
[i
], NULL
, CV_DEFAULT
, NULL
);
191 srn
.srn_instance
= ddi_get_instance(dip
);
192 mutex_enter(&srn_clone_lock
);
193 srn_signal
= srn_notify
;
194 mutex_exit(&srn_clone_lock
);
196 return (DDI_SUCCESS
);
199 return (DDI_FAILURE
);
205 srn_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
208 extern int srn_inuse
;
209 extern void (*srn_signal
)(int, int);
214 mutex_enter(&srn_clone_lock
);
216 mutex_exit(&srn_clone_lock
);
218 mutex_enter(&srn_clone_lock
);
221 mutex_exit(&srn_clone_lock
);
223 for (i
= 0; i
< SRN_MAX_CLONE
; i
++)
224 cv_destroy(&srn_clones_cv
[i
]);
226 ddi_remove_minor_node(dip
, NULL
);
227 srn
.srn_instance
= -1;
228 return (DDI_SUCCESS
);
231 return (DDI_FAILURE
);
237 char *srn_cmd_string
;
242 * Returns true if permission granted by credentials
246 srn_perms(int perm
, cred_t
*cr
)
248 if ((perm
& SU
) && secpolicy_power_mgmt(cr
) == 0) /* privileged? */
250 if ((perm
& SG
) && (crgetgid(cr
) == 0)) /* group 0 is ok */
256 srn_chpoll(dev_t dev
, short events
, int anyyet
, short *reventsp
,
257 struct pollhead
**phpp
)
259 extern struct pollhead srn_pollhead
[];
262 clone
= SRN_MINOR_TO_CLONE(getminor(dev
));
263 if ((events
& (POLLIN
| POLLRDNORM
)) && srn_poll_cnt
[clone
]) {
264 *reventsp
|= (POLLIN
| POLLRDNORM
);
269 if ((*reventsp
== 0 && !anyyet
) || (events
& POLLET
)) {
270 *phpp
= &srn_pollhead
[clone
];
277 srn_getinfo(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
283 case DDI_INFO_DEVT2DEVINFO
:
284 if (srn
.srn_instance
== -1)
285 return (DDI_FAILURE
);
286 *result
= srn
.srn_dip
;
287 return (DDI_SUCCESS
);
289 case DDI_INFO_DEVT2INSTANCE
:
291 instance
= getminor(dev
) >> 8;
292 *result
= (void *)(uintptr_t)instance
;
293 return (DDI_SUCCESS
);
296 return (DDI_FAILURE
);
303 srn_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*cr
)
307 if (otyp
!= OTYP_CHR
)
310 mutex_enter(&srn_clone_lock
);
311 for (clone
= 1; clone
< SRN_MAX_CLONE
- 1; clone
++)
312 if (!srn
.srn_clones
[clone
])
315 if (clone
== SRN_MAX_CLONE
) {
316 mutex_exit(&srn_clone_lock
);
319 srn
.srn_cred
[clone
] = cr
;
320 ASSERT(srn_apm_count
>= 0);
322 srn
.srn_type
[clone
] = SRN_TYPE_APM
;
325 *devp
= makedevice(getmajor(*devp
), (srn
.srn_instance
<< 8) +
327 srn
.srn_clones
[clone
] = 1;
328 srn
.srn_cred
[clone
] = cr
;
330 mutex_exit(&srn_clone_lock
);
331 PMD(PMD_SX
, ("srn open OK\n"))
337 srn_close(dev_t dev
, int flag
, int otyp
, cred_t
*cr
)
341 if (otyp
!= OTYP_CHR
)
344 clone
= SRN_MINOR_TO_CLONE(getminor(dev
));
345 PMD(PMD_SX
, ("srn_close: minor %x, clone %x\n", getminor(dev
),
347 mutex_enter(&srn_clone_lock
);
348 crfree(srn
.srn_cred
[clone
]);
349 srn
.srn_cred
[clone
] = 0;
350 srn_poll_cnt
[clone
] = 0;
351 srn
.srn_fault
[clone
] = 0;
352 if (srn
.srn_pending
[clone
].ae_type
|| srn
.srn_delivered
[clone
]) {
353 srn
.srn_pending
[clone
].ae_type
= 0;
354 srn
.srn_delivered
[clone
] = 0;
355 cv_signal(&srn_clones_cv
[clone
]);
357 switch (srn
.srn_type
[clone
]) {
358 case SRN_TYPE_AUTOSX
:
359 ASSERT(srn_autosx_count
);
363 ASSERT(srn_apm_count
);
370 srn
.srn_clones
[clone
] = 0;
371 mutex_exit(&srn_clone_lock
);
377 srn_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int mode
, cred_t
*cr
, int *rval_p
)
379 int clone
= SRN_MINOR_TO_CLONE(getminor(dev
));
381 PMD(PMD_SX
, ("ioctl: %x: begin\n", cmd
))
384 case SRN_IOC_NEXTEVENT
:
385 case SRN_IOC_SUSPEND
:
393 if (!srn_perms(SU
| SG
, srn
.srn_cred
[clone
])) {
398 PMD(PMD_SX
, ("SRN_IOC_AUTOSX entered\n"))
399 mutex_enter(&srn_clone_lock
);
400 if (!srn
.srn_clones
[clone
]) {
401 PMD(PMD_SX
, (" ioctl !srn_clones--EINVAL\n"))
402 mutex_exit(&srn_clone_lock
);
405 if (srn
.srn_pending
[clone
].ae_type
) {
406 PMD(PMD_SX
, ("AUTOSX while pending--EBUSY\n"))
407 mutex_exit(&srn_clone_lock
);
410 if (srn
.srn_type
[clone
] == SRN_TYPE_AUTOSX
) {
411 PMD(PMD_SX
, ("AUTOSX already--EBUSY\n"))
412 mutex_exit(&srn_clone_lock
);
415 ASSERT(srn
.srn_type
[clone
] == SRN_TYPE_APM
);
416 srn
.srn_type
[clone
] = SRN_TYPE_AUTOSX
;
417 srn
.srn_fault
[clone
] = 0;
419 ASSERT(srn_apm_count
>= 0);
420 ASSERT(srn_autosx_count
>= 0);
422 mutex_exit(&srn_clone_lock
);
423 PMD(PMD_SX
, ("SRN_IOC_AUTOSX returns success\n"))
426 case SRN_IOC_NEXTEVENT
:
428 * return the next suspend or resume event; there should
429 * be one, cause we only get called if we've signalled a
430 * poll data completion
431 * then wake up the kernel thread sleeping for the delivery
433 PMD(PMD_SX
, ("SRN_IOC_NEXTEVENT entered\n"))
434 if (srn
.srn_fault
[clone
]) {
435 PMD(PMD_SX
, ("SRN_IOC_NEXTEVENT clone %d fault "
437 srn
.srn_fault
[clone
] = 0;
439 mutex_enter(&srn_clone_lock
);
440 if (srn_poll_cnt
[clone
] == 0) {
441 mutex_exit(&srn_clone_lock
);
442 PMD(PMD_SX
, ("SRN_IOC_NEXTEVENT clone %d "
443 "EWOULDBLOCK\n", clone
))
444 return (EWOULDBLOCK
);
446 ASSERT(srn
.srn_pending
[clone
].ae_type
);
447 if (ddi_copyout(&srn
.srn_pending
[clone
], (void *)arg
,
448 sizeof (srn_event_info_t
), mode
) != 0) {
449 mutex_exit(&srn_clone_lock
);
450 PMD(PMD_SX
, ("SRN_IOC_NEXTEVENT clone %d EFAULT\n",
454 if (srn
.srn_type
[clone
] == SRN_TYPE_APM
)
455 srn
.srn_delivered
[clone
] =
456 srn
.srn_pending
[clone
].ae_type
;
457 PMD(PMD_SX
, ("SRN_IOC_NEXTEVENT clone %d delivered %x\n",
458 clone
, srn
.srn_pending
[clone
].ae_type
))
459 srn_poll_cnt
[clone
] = 0;
460 mutex_exit(&srn_clone_lock
);
463 case SRN_IOC_SUSPEND
:
465 PMD(PMD_SX
, ("SRN_IOC_SUSPEND entered clone %d\n", clone
))
466 if (srn
.srn_fault
[clone
]) {
467 PMD(PMD_SX
, ("SRN_IOC_SUSPEND clone %d fault "
469 srn
.srn_fault
[clone
] = 0;
471 mutex_enter(&srn_clone_lock
);
472 if (srn
.srn_delivered
[clone
] != SRN_SUSPEND_REQ
) {
473 mutex_exit(&srn_clone_lock
);
474 PMD(PMD_SX
, ("SRN_IOC_SUSPEND EINVAL\n"))
477 srn
.srn_delivered
[clone
] = 0;
478 srn
.srn_pending
[clone
].ae_type
= 0;
479 /* notify the kernel suspend thread to continue */
480 PMD(PMD_SX
, ("SRN_IOC_SUSPEND clone %d ok\n", clone
))
481 cv_signal(&srn_clones_cv
[clone
]);
482 mutex_exit(&srn_clone_lock
);
487 PMD(PMD_SX
, ("SRN_IOC_RESUME entered clone %d\n", clone
))
488 if (srn
.srn_fault
[clone
]) {
489 PMD(PMD_SX
, ("SRN_IOC_RESUME clone %d fault "
491 srn
.srn_fault
[clone
] = 0;
493 mutex_enter(&srn_clone_lock
);
494 if (srn
.srn_delivered
[clone
] != SRN_NORMAL_RESUME
) {
495 mutex_exit(&srn_clone_lock
);
496 PMD(PMD_SX
, ("SRN_IOC_RESUME EINVAL\n"))
499 srn
.srn_delivered
[clone
] = 0;
500 srn
.srn_pending
[clone
].ae_type
= 0;
501 /* notify the kernel resume thread to continue */
502 PMD(PMD_SX
, ("SRN_IOC_RESUME ok for clone %d\n", clone
))
503 cv_signal(&srn_clones_cv
[clone
]);
504 mutex_exit(&srn_clone_lock
);
508 PMD(PMD_SX
, ("srn_ioctl unknown cmd EINVAL\n"))
513 * A very simple handshake with the srn driver,
514 * only one outstanding event at a time.
515 * The OS delivers the event and depending on type,
516 * either blocks waiting for the ack, or drives on
519 srn_notify(int type
, int event
)
522 PMD(PMD_SX
, ("srn_notify entered with type %d, event 0x%x\n",
524 ASSERT(mutex_owned(&srn_clone_lock
));
527 if (srn_apm_count
== 0) {
528 PMD(PMD_SX
, ("no apm types\n"))
531 count
= srn_apm_count
;
533 case SRN_TYPE_AUTOSX
:
534 if (srn_autosx_count
== 0) {
535 PMD(PMD_SX
, ("no autosx types\n"))
538 count
= srn_autosx_count
;
545 PMD(PMD_SX
, ("count %d\n", count
))
546 for (clone
= 0; clone
< SRN_MAX_CLONE
; clone
++) {
547 if (srn
.srn_type
[clone
] == type
) {
549 if (type
== SRN_TYPE_APM
&& !srn
.srn_fault
[clone
]) {
550 ASSERT(srn
.srn_pending
[clone
].ae_type
== 0);
551 ASSERT(srn_poll_cnt
[clone
] == 0);
552 ASSERT(srn
.srn_delivered
[clone
] == 0);
555 srn
.srn_pending
[clone
].ae_type
= event
;
556 srn_poll_cnt
[clone
] = 1;
557 PMD(PMD_SX
, ("pollwake %d\n", clone
))
558 pollwakeup(&srn_pollhead
[clone
], (POLLRDNORM
| POLLIN
));
564 if (type
== SRN_TYPE_AUTOSX
) { /* we don't wait */
565 PMD(PMD_SX
, ("Not waiting for AUTOSX ack\n"))
568 ASSERT(type
== SRN_TYPE_APM
);
569 /* otherwise wait for acks */
572 * We wait until all of the pending events are cleared.
573 * We have to start over every time we do a cv_wait because
574 * we give up the mutex and can be re-entered
576 for (clone
= 1; clone
< SRN_MAX_CLONE
; clone
++) {
577 if (srn
.srn_clones
[clone
] == 0 ||
578 srn
.srn_type
[clone
] != SRN_TYPE_APM
)
580 if (srn
.srn_pending
[clone
].ae_type
&& !srn
.srn_fault
[clone
]) {
581 PMD(PMD_SX
, ("srn_notify waiting for ack for clone %d, "
582 "event %x\n", clone
, event
))
583 if (cv_timedwait(&srn_clones_cv
[clone
],
584 &srn_clone_lock
, ddi_get_lbolt() +
585 drv_usectohz(srn_timeout
* 1000000)) == -1) {
587 * Client didn't respond, mark it as faulted
588 * and continue as if a regular signal.
590 PMD(PMD_SX
, ("srn_notify: clone %d did not "
591 "ack event %x\n", clone
, event
))
592 cmn_err(CE_WARN
, "srn_notify: clone %d did "
593 "not ack event %x\n", clone
, event
);
594 srn
.srn_fault
[clone
] = 1;
599 PMD(PMD_SX
, ("srn_notify done with %x\n", event
))