xref: /dragonfly/sys/dev/sound/clone.c (revision 0cf7fc2c)
12a1ad637SFrançois Tigeot /*-
22a1ad637SFrançois Tigeot  * Copyright (c) 2007 Ariff Abdullah <ariff@FreeBSD.org>
32a1ad637SFrançois Tigeot  * All rights reserved.
42a1ad637SFrançois Tigeot  *
52a1ad637SFrançois Tigeot  * Redistribution and use in source and binary forms, with or without
62a1ad637SFrançois Tigeot  * modification, are permitted provided that the following conditions
72a1ad637SFrançois Tigeot  * are met:
82a1ad637SFrançois Tigeot  * 1. Redistributions of source code must retain the above copyright
92a1ad637SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer.
102a1ad637SFrançois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
112a1ad637SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer in the
122a1ad637SFrançois Tigeot  *    documentation and/or other materials provided with the distribution.
132a1ad637SFrançois Tigeot  *
142a1ad637SFrançois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
152a1ad637SFrançois Tigeot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
162a1ad637SFrançois Tigeot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172a1ad637SFrançois Tigeot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
182a1ad637SFrançois Tigeot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
192a1ad637SFrançois Tigeot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
202a1ad637SFrançois Tigeot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
212a1ad637SFrançois Tigeot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
222a1ad637SFrançois Tigeot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
232a1ad637SFrançois Tigeot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
242a1ad637SFrançois Tigeot  * SUCH DAMAGE.
252a1ad637SFrançois Tigeot  *
262a1ad637SFrançois Tigeot  * $FreeBSD: head/sys/dev/sound/clone.c 193640 2009-06-07 19:12:08Z ariff $
272a1ad637SFrançois Tigeot  */
282a1ad637SFrançois Tigeot 
292a1ad637SFrançois Tigeot #include <sys/param.h>
302a1ad637SFrançois Tigeot #include <sys/systm.h>
312a1ad637SFrançois Tigeot #include <sys/conf.h>
322a1ad637SFrançois Tigeot #include <sys/kernel.h>
332a1ad637SFrançois Tigeot #include <sys/malloc.h>
342a1ad637SFrançois Tigeot #include <sys/proc.h>
355f097292SMatthew Dillon #include <sys/devfs.h>
362a1ad637SFrançois Tigeot 
372a1ad637SFrançois Tigeot #ifdef HAVE_KERNEL_OPTION_HEADERS
382a1ad637SFrançois Tigeot #include "opt_snd.h"
392a1ad637SFrançois Tigeot #endif
402a1ad637SFrançois Tigeot 
412a1ad637SFrançois Tigeot #include <dev/sound/pcm/sound.h>
422a1ad637SFrançois Tigeot #include <dev/sound/clone.h>
432a1ad637SFrançois Tigeot 
44*0cf7fc2cSSascha Wildner DEVFS_DECLARE_CLONE_BITMAP(dsp);
455f097292SMatthew Dillon 
462a1ad637SFrançois Tigeot /*
472a1ad637SFrançois Tigeot  * So here we go again, another clonedevs manager. Unlike default clonedevs,
482a1ad637SFrançois Tigeot  * this clone manager is designed to withstand various abusive behavior
492a1ad637SFrançois Tigeot  * (such as 'while : ; do ls /dev/whatever ; done', etc.), reusable object
502a1ad637SFrançois Tigeot  * after reaching certain expiration threshold, aggressive garbage collector,
512a1ad637SFrançois Tigeot  * transparent device allocator and concurrency handling across multiple
522a1ad637SFrançois Tigeot  * thread/proc. Due to limited information given by dev_clone EVENTHANDLER,
532a1ad637SFrançois Tigeot  * we don't have much clues whether the caller wants a real open() or simply
542a1ad637SFrançois Tigeot  * making fun of us with things like stat(), mtime() etc. Assuming that:
552a1ad637SFrançois Tigeot  * 1) Time window between dev_clone EH <-> real open() should be small
562a1ad637SFrançois Tigeot  * enough and 2) mtime()/stat() etc. always looks like a half way / stalled
572a1ad637SFrançois Tigeot  * operation, we can decide whether a new cdev must be created, old
582a1ad637SFrançois Tigeot  * (expired) cdev can be reused or an existing cdev can be shared.
592a1ad637SFrançois Tigeot  *
602a1ad637SFrançois Tigeot  * Most of the operations and logics are generic enough and can be applied
612a1ad637SFrançois Tigeot  * on other places (such as if_tap, snp, etc).  Perhaps this can be
622a1ad637SFrançois Tigeot  * rearranged to complement clone_*(). However, due to this still being
632a1ad637SFrançois Tigeot  * specific to the sound driver (and as a proof of concept on how it can be
642a1ad637SFrançois Tigeot  * done), si_drv2 is used to keep the pointer of the clone list entry to
652a1ad637SFrançois Tigeot  * avoid expensive lookup.
662a1ad637SFrançois Tigeot  */
672a1ad637SFrançois Tigeot 
682a1ad637SFrançois Tigeot /* clone entry */
692a1ad637SFrançois Tigeot struct snd_clone_entry {
702a1ad637SFrançois Tigeot 	TAILQ_ENTRY(snd_clone_entry) link;
712a1ad637SFrançois Tigeot 	struct snd_clone *parent;
722a1ad637SFrançois Tigeot 	struct cdev *devt;
732a1ad637SFrançois Tigeot 	struct timespec tsp;
742a1ad637SFrançois Tigeot 	uint32_t flags;
752a1ad637SFrançois Tigeot 	pid_t pid;
762a1ad637SFrançois Tigeot 	int unit;
772a1ad637SFrançois Tigeot };
782a1ad637SFrançois Tigeot 
792a1ad637SFrançois Tigeot /* clone manager */
802a1ad637SFrançois Tigeot struct snd_clone {
812a1ad637SFrançois Tigeot 	TAILQ_HEAD(link_head, snd_clone_entry) head;
822a1ad637SFrançois Tigeot 	struct timespec tsp;
832a1ad637SFrançois Tigeot 	int refcount;
842a1ad637SFrançois Tigeot 	int size;
852a1ad637SFrançois Tigeot 	int typemask;
862a1ad637SFrançois Tigeot 	int maxunit;
872a1ad637SFrançois Tigeot 	int deadline;
882a1ad637SFrançois Tigeot 	uint32_t flags;
892a1ad637SFrançois Tigeot };
902a1ad637SFrançois Tigeot 
912a1ad637SFrançois Tigeot #ifdef SND_DIAGNOSTIC
922a1ad637SFrançois Tigeot #define SND_CLONE_ASSERT(x, y)		do {			\
932a1ad637SFrançois Tigeot 	if (!(x))						\
942a1ad637SFrançois Tigeot 		panic y;					\
952a1ad637SFrançois Tigeot } while (0)
962a1ad637SFrançois Tigeot #else
972a1ad637SFrançois Tigeot #define SND_CLONE_ASSERT(...)		KASSERT(__VA_ARGS__)
982a1ad637SFrançois Tigeot #endif
992a1ad637SFrançois Tigeot 
1002a1ad637SFrançois Tigeot /*
1012a1ad637SFrançois Tigeot  * Shamelessly ripped off from vfs_subr.c
1022a1ad637SFrançois Tigeot  * We need at least 1/HZ precision as default timestamping.
1032a1ad637SFrançois Tigeot  */
1042a1ad637SFrançois Tigeot enum { SND_TSP_SEC, SND_TSP_HZ, SND_TSP_USEC, SND_TSP_NSEC };
1052a1ad637SFrançois Tigeot 
1062a1ad637SFrançois Tigeot static int snd_timestamp_precision = SND_TSP_HZ;
1072a1ad637SFrançois Tigeot TUNABLE_INT("hw.snd.timestamp_precision", &snd_timestamp_precision);
1082a1ad637SFrançois Tigeot 
1092a1ad637SFrançois Tigeot void
snd_timestamp(struct timespec * tsp)1102a1ad637SFrançois Tigeot snd_timestamp(struct timespec *tsp)
1112a1ad637SFrançois Tigeot {
1122a1ad637SFrançois Tigeot 	struct timeval tv;
1132a1ad637SFrançois Tigeot 
1142a1ad637SFrançois Tigeot 	switch (snd_timestamp_precision) {
1152a1ad637SFrançois Tigeot 	case SND_TSP_SEC:
1162a1ad637SFrançois Tigeot 		tsp->tv_sec = time_second;
1172a1ad637SFrançois Tigeot 		tsp->tv_nsec = 0;
1182a1ad637SFrançois Tigeot 		break;
1192a1ad637SFrançois Tigeot 	case SND_TSP_HZ:
1202a1ad637SFrançois Tigeot 		getnanouptime(tsp);
1212a1ad637SFrançois Tigeot 		break;
1222a1ad637SFrançois Tigeot 	case SND_TSP_USEC:
1232a1ad637SFrançois Tigeot 		microuptime(&tv);
1242a1ad637SFrançois Tigeot 		TIMEVAL_TO_TIMESPEC(&tv, tsp);
1252a1ad637SFrançois Tigeot 		break;
1262a1ad637SFrançois Tigeot 	case SND_TSP_NSEC:
1272a1ad637SFrançois Tigeot 		nanouptime(tsp);
1282a1ad637SFrançois Tigeot 		break;
1292a1ad637SFrançois Tigeot 	default:
1302a1ad637SFrançois Tigeot 		snd_timestamp_precision = SND_TSP_HZ;
1312a1ad637SFrançois Tigeot 		getnanouptime(tsp);
1322a1ad637SFrançois Tigeot 		break;
1332a1ad637SFrançois Tigeot 	}
1342a1ad637SFrançois Tigeot }
1352a1ad637SFrançois Tigeot 
1362a1ad637SFrançois Tigeot #if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG)
1372a1ad637SFrançois Tigeot static int
sysctl_hw_snd_timestamp_precision(SYSCTL_HANDLER_ARGS)1382a1ad637SFrançois Tigeot sysctl_hw_snd_timestamp_precision(SYSCTL_HANDLER_ARGS)
1392a1ad637SFrançois Tigeot {
1402a1ad637SFrançois Tigeot 	int err, val;
1412a1ad637SFrançois Tigeot 
1422a1ad637SFrançois Tigeot 	val = snd_timestamp_precision;
1432a1ad637SFrançois Tigeot 	err = sysctl_handle_int(oidp, &val, 0, req);
1442a1ad637SFrançois Tigeot 	if (err == 0 && req->newptr != NULL) {
1452a1ad637SFrançois Tigeot 		switch (val) {
1462a1ad637SFrançois Tigeot 		case SND_TSP_SEC:
1472a1ad637SFrançois Tigeot 		case SND_TSP_HZ:
1482a1ad637SFrançois Tigeot 		case SND_TSP_USEC:
1492a1ad637SFrançois Tigeot 		case SND_TSP_NSEC:
1502a1ad637SFrançois Tigeot 			snd_timestamp_precision = val;
1512a1ad637SFrançois Tigeot 			break;
1522a1ad637SFrançois Tigeot 		default:
1532a1ad637SFrançois Tigeot 			break;
1542a1ad637SFrançois Tigeot 		}
1552a1ad637SFrançois Tigeot 	}
1562a1ad637SFrançois Tigeot 
1572a1ad637SFrançois Tigeot 	return (err);
1582a1ad637SFrançois Tigeot }
1592a1ad637SFrançois Tigeot SYSCTL_PROC(_hw_snd, OID_AUTO, timestamp_precision, CTLTYPE_INT | CTLFLAG_RW,
1602a1ad637SFrançois Tigeot     0, sizeof(int), sysctl_hw_snd_timestamp_precision, "I",
1612a1ad637SFrançois Tigeot     "timestamp precision (0=s 1=hz 2=us 3=ns)");
1622a1ad637SFrançois Tigeot #endif
1632a1ad637SFrançois Tigeot 
1642a1ad637SFrançois Tigeot /*
1652a1ad637SFrançois Tigeot  * snd_clone_create() : Return opaque allocated clone manager.
1662a1ad637SFrançois Tigeot  */
1672a1ad637SFrançois Tigeot struct snd_clone *
snd_clone_create(int typemask,int maxunit,int deadline,uint32_t flags)1682a1ad637SFrançois Tigeot snd_clone_create(int typemask, int maxunit, int deadline, uint32_t flags)
1692a1ad637SFrançois Tigeot {
1702a1ad637SFrançois Tigeot 	struct snd_clone *c;
1712a1ad637SFrançois Tigeot 
1722a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(!(typemask & ~SND_CLONE_MAXUNIT),
1732a1ad637SFrançois Tigeot 	    ("invalid typemask: 0x%08x", typemask));
1742a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK),
1752a1ad637SFrançois Tigeot 	    ("invalid clone flags=0x%08x", flags));
1762a1ad637SFrançois Tigeot 
17767931cc4SFrançois Tigeot 	c = kmalloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO);
1782a1ad637SFrançois Tigeot 	c->refcount = 0;
1792a1ad637SFrançois Tigeot 	c->size = 0;
1802a1ad637SFrançois Tigeot 	c->typemask = typemask;
1812a1ad637SFrançois Tigeot 	c->maxunit = (maxunit == -1) ? (~typemask & SND_CLONE_MAXUNIT) :
1822a1ad637SFrançois Tigeot 	    maxunit;
1832a1ad637SFrançois Tigeot 	c->deadline = deadline;
1842a1ad637SFrançois Tigeot 	c->flags = flags;
1852a1ad637SFrançois Tigeot 	snd_timestamp(&c->tsp);
1862a1ad637SFrançois Tigeot 	TAILQ_INIT(&c->head);
1872a1ad637SFrançois Tigeot 
1882a1ad637SFrançois Tigeot 	return (c);
1892a1ad637SFrançois Tigeot }
1902a1ad637SFrançois Tigeot 
1912a1ad637SFrançois Tigeot int
snd_clone_busy(struct snd_clone * c)1922a1ad637SFrançois Tigeot snd_clone_busy(struct snd_clone *c)
1932a1ad637SFrançois Tigeot {
1942a1ad637SFrançois Tigeot 	struct snd_clone_entry *ce;
1952a1ad637SFrançois Tigeot 
1962a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
1972a1ad637SFrançois Tigeot 
1982a1ad637SFrançois Tigeot 	if (c->size == 0)
1992a1ad637SFrançois Tigeot 		return (0);
2002a1ad637SFrançois Tigeot 
2012a1ad637SFrançois Tigeot 	TAILQ_FOREACH(ce, &c->head, link) {
20267931cc4SFrançois Tigeot 		if (ce->flags & SND_CLONE_BUSY)
2032a1ad637SFrançois Tigeot 			return (EBUSY);
2042a1ad637SFrançois Tigeot 	}
2052a1ad637SFrançois Tigeot 
2062a1ad637SFrançois Tigeot 	return (0);
2072a1ad637SFrançois Tigeot }
2082a1ad637SFrançois Tigeot 
2092a1ad637SFrançois Tigeot /*
2102a1ad637SFrançois Tigeot  * snd_clone_enable()/disable() : Suspend/resume clone allocation through
2112a1ad637SFrançois Tigeot  * snd_clone_alloc(). Everything else will not be affected by this.
2122a1ad637SFrançois Tigeot  */
2132a1ad637SFrançois Tigeot int
snd_clone_enable(struct snd_clone * c)2142a1ad637SFrançois Tigeot snd_clone_enable(struct snd_clone *c)
2152a1ad637SFrançois Tigeot {
2162a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
2172a1ad637SFrançois Tigeot 
2182a1ad637SFrançois Tigeot 	if (c->flags & SND_CLONE_ENABLE)
2192a1ad637SFrançois Tigeot 		return (EINVAL);
2202a1ad637SFrançois Tigeot 
2212a1ad637SFrançois Tigeot 	c->flags |= SND_CLONE_ENABLE;
2222a1ad637SFrançois Tigeot 
2232a1ad637SFrançois Tigeot 	return (0);
2242a1ad637SFrançois Tigeot }
2252a1ad637SFrançois Tigeot 
2262a1ad637SFrançois Tigeot int
snd_clone_disable(struct snd_clone * c)2272a1ad637SFrançois Tigeot snd_clone_disable(struct snd_clone *c)
2282a1ad637SFrançois Tigeot {
2292a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
2302a1ad637SFrançois Tigeot 
2312a1ad637SFrançois Tigeot 	if (!(c->flags & SND_CLONE_ENABLE))
2322a1ad637SFrançois Tigeot 		return (EINVAL);
2332a1ad637SFrançois Tigeot 
2342a1ad637SFrançois Tigeot 	c->flags &= ~SND_CLONE_ENABLE;
2352a1ad637SFrançois Tigeot 
2362a1ad637SFrançois Tigeot 	return (0);
2372a1ad637SFrançois Tigeot }
2382a1ad637SFrançois Tigeot 
2392a1ad637SFrançois Tigeot /*
2402a1ad637SFrançois Tigeot  * Getters / Setters. Not worth explaining :)
2412a1ad637SFrançois Tigeot  */
2422a1ad637SFrançois Tigeot int
snd_clone_getsize(struct snd_clone * c)2432a1ad637SFrançois Tigeot snd_clone_getsize(struct snd_clone *c)
2442a1ad637SFrançois Tigeot {
2452a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
2462a1ad637SFrançois Tigeot 
2472a1ad637SFrançois Tigeot 	return (c->size);
2482a1ad637SFrançois Tigeot }
2492a1ad637SFrançois Tigeot 
2502a1ad637SFrançois Tigeot int
snd_clone_getmaxunit(struct snd_clone * c)2512a1ad637SFrançois Tigeot snd_clone_getmaxunit(struct snd_clone *c)
2522a1ad637SFrançois Tigeot {
2532a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
2542a1ad637SFrançois Tigeot 
2552a1ad637SFrançois Tigeot 	return (c->maxunit);
2562a1ad637SFrançois Tigeot }
2572a1ad637SFrançois Tigeot 
2582a1ad637SFrançois Tigeot int
snd_clone_setmaxunit(struct snd_clone * c,int maxunit)2592a1ad637SFrançois Tigeot snd_clone_setmaxunit(struct snd_clone *c, int maxunit)
2602a1ad637SFrançois Tigeot {
2612a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
2622a1ad637SFrançois Tigeot 
2632a1ad637SFrançois Tigeot 	c->maxunit = (maxunit == -1) ? (~c->typemask & SND_CLONE_MAXUNIT) :
2642a1ad637SFrançois Tigeot 	    maxunit;
2652a1ad637SFrançois Tigeot 
2662a1ad637SFrançois Tigeot 	return (c->maxunit);
2672a1ad637SFrançois Tigeot }
2682a1ad637SFrançois Tigeot 
2692a1ad637SFrançois Tigeot int
snd_clone_getdeadline(struct snd_clone * c)2702a1ad637SFrançois Tigeot snd_clone_getdeadline(struct snd_clone *c)
2712a1ad637SFrançois Tigeot {
2722a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
2732a1ad637SFrançois Tigeot 
2742a1ad637SFrançois Tigeot 	return (c->deadline);
2752a1ad637SFrançois Tigeot }
2762a1ad637SFrançois Tigeot 
2772a1ad637SFrançois Tigeot int
snd_clone_setdeadline(struct snd_clone * c,int deadline)2782a1ad637SFrançois Tigeot snd_clone_setdeadline(struct snd_clone *c, int deadline)
2792a1ad637SFrançois Tigeot {
2802a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
2812a1ad637SFrançois Tigeot 
2822a1ad637SFrançois Tigeot 	c->deadline = deadline;
2832a1ad637SFrançois Tigeot 
2842a1ad637SFrançois Tigeot 	return (c->deadline);
2852a1ad637SFrançois Tigeot }
2862a1ad637SFrançois Tigeot 
2872a1ad637SFrançois Tigeot int
snd_clone_gettime(struct snd_clone * c,struct timespec * tsp)2882a1ad637SFrançois Tigeot snd_clone_gettime(struct snd_clone *c, struct timespec *tsp)
2892a1ad637SFrançois Tigeot {
2902a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
2912a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec"));
2922a1ad637SFrançois Tigeot 
2932a1ad637SFrançois Tigeot 	*tsp = c->tsp;
2942a1ad637SFrançois Tigeot 
2952a1ad637SFrançois Tigeot 	return (0);
2962a1ad637SFrançois Tigeot }
2972a1ad637SFrançois Tigeot 
2982a1ad637SFrançois Tigeot uint32_t
snd_clone_getflags(struct snd_clone * c)2992a1ad637SFrançois Tigeot snd_clone_getflags(struct snd_clone *c)
3002a1ad637SFrançois Tigeot {
3012a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
3022a1ad637SFrançois Tigeot 
3032a1ad637SFrançois Tigeot 	return (c->flags);
3042a1ad637SFrançois Tigeot }
3052a1ad637SFrançois Tigeot 
3062a1ad637SFrançois Tigeot uint32_t
snd_clone_setflags(struct snd_clone * c,uint32_t flags)3072a1ad637SFrançois Tigeot snd_clone_setflags(struct snd_clone *c, uint32_t flags)
3082a1ad637SFrançois Tigeot {
3092a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
3102a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK),
3112a1ad637SFrançois Tigeot 	    ("invalid clone flags=0x%08x", flags));
3122a1ad637SFrançois Tigeot 
3132a1ad637SFrançois Tigeot 	c->flags = flags;
3142a1ad637SFrançois Tigeot 
3152a1ad637SFrançois Tigeot 	return (c->flags);
3162a1ad637SFrançois Tigeot }
3172a1ad637SFrançois Tigeot 
3182a1ad637SFrançois Tigeot int
snd_clone_getdevtime(struct cdev * dev,struct timespec * tsp)3192a1ad637SFrançois Tigeot snd_clone_getdevtime(struct cdev *dev, struct timespec *tsp)
3202a1ad637SFrançois Tigeot {
3212a1ad637SFrançois Tigeot 	struct snd_clone_entry *ce;
3222a1ad637SFrançois Tigeot 
3232a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
3242a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec"));
3252a1ad637SFrançois Tigeot 
3262a1ad637SFrançois Tigeot 	ce = dev->si_drv2;
3272a1ad637SFrançois Tigeot 	if (ce == NULL)
3282a1ad637SFrançois Tigeot 		return (ENODEV);
3292a1ad637SFrançois Tigeot 
3302a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
3312a1ad637SFrançois Tigeot 
3322a1ad637SFrançois Tigeot 	*tsp = ce->tsp;
3332a1ad637SFrançois Tigeot 
3342a1ad637SFrançois Tigeot 	return (0);
3352a1ad637SFrançois Tigeot }
3362a1ad637SFrançois Tigeot 
3372a1ad637SFrançois Tigeot uint32_t
snd_clone_getdevflags(struct cdev * dev)3382a1ad637SFrançois Tigeot snd_clone_getdevflags(struct cdev *dev)
3392a1ad637SFrançois Tigeot {
3402a1ad637SFrançois Tigeot 	struct snd_clone_entry *ce;
3412a1ad637SFrançois Tigeot 
3422a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
3432a1ad637SFrançois Tigeot 
3442a1ad637SFrançois Tigeot 	ce = dev->si_drv2;
3452a1ad637SFrançois Tigeot 	if (ce == NULL)
3462a1ad637SFrançois Tigeot 		return (0xffffffff);
3472a1ad637SFrançois Tigeot 
3482a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
3492a1ad637SFrançois Tigeot 
3502a1ad637SFrançois Tigeot 	return (ce->flags);
3512a1ad637SFrançois Tigeot }
3522a1ad637SFrançois Tigeot 
3532a1ad637SFrançois Tigeot uint32_t
snd_clone_setdevflags(struct cdev * dev,uint32_t flags)3542a1ad637SFrançois Tigeot snd_clone_setdevflags(struct cdev *dev, uint32_t flags)
3552a1ad637SFrançois Tigeot {
3562a1ad637SFrançois Tigeot 	struct snd_clone_entry *ce;
3572a1ad637SFrançois Tigeot 
3582a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
3592a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(!(flags & ~SND_CLONE_DEVMASK),
3602a1ad637SFrançois Tigeot 	    ("invalid clone dev flags=0x%08x", flags));
3612a1ad637SFrançois Tigeot 
3622a1ad637SFrançois Tigeot 	ce = dev->si_drv2;
3632a1ad637SFrançois Tigeot 	if (ce == NULL)
3642a1ad637SFrançois Tigeot 		return (0xffffffff);
3652a1ad637SFrançois Tigeot 
3662a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
3672a1ad637SFrançois Tigeot 
3682a1ad637SFrançois Tigeot 	ce->flags = flags;
3692a1ad637SFrançois Tigeot 
3702a1ad637SFrançois Tigeot 	return (ce->flags);
3712a1ad637SFrançois Tigeot }
3722a1ad637SFrançois Tigeot 
3732a1ad637SFrançois Tigeot /* Elapsed time conversion to ms */
3742a1ad637SFrançois Tigeot #define SND_CLONE_ELAPSED(x, y)						\
3752a1ad637SFrançois Tigeot 	((((x)->tv_sec - (y)->tv_sec) * 1000) +				\
3762a1ad637SFrançois Tigeot 	(((y)->tv_nsec > (x)->tv_nsec) ?				\
3772a1ad637SFrançois Tigeot 	(((1000000000L + (x)->tv_nsec -					\
3782a1ad637SFrançois Tigeot 	(y)->tv_nsec) / 1000000) - 1000) :				\
3792a1ad637SFrançois Tigeot 	(((x)->tv_nsec - (y)->tv_nsec) / 1000000)))
3802a1ad637SFrançois Tigeot 
3812a1ad637SFrançois Tigeot #define SND_CLONE_EXPIRED(x, y, z)					\
3822a1ad637SFrançois Tigeot 	((x)->deadline < 1 ||						\
3832a1ad637SFrançois Tigeot 	((y)->tv_sec - (z)->tv_sec) > ((x)->deadline / 1000) ||		\
3842a1ad637SFrançois Tigeot 	SND_CLONE_ELAPSED(y, z) > (x)->deadline)
3852a1ad637SFrançois Tigeot 
3862a1ad637SFrançois Tigeot /*
3872a1ad637SFrançois Tigeot  * snd_clone_gc() : Garbage collector for stalled, expired objects. Refer to
3882a1ad637SFrançois Tigeot  * clone.h for explanations on GC settings.
3892a1ad637SFrançois Tigeot  */
3902a1ad637SFrançois Tigeot int
snd_clone_gc(struct snd_clone * c)3912a1ad637SFrançois Tigeot snd_clone_gc(struct snd_clone *c)
3922a1ad637SFrançois Tigeot {
3932a1ad637SFrançois Tigeot 	struct snd_clone_entry *ce, *tce;
3942a1ad637SFrançois Tigeot 	struct timespec now;
3952a1ad637SFrançois Tigeot 	int pruned;
3965f097292SMatthew Dillon 	int subunit;
3972a1ad637SFrançois Tigeot 
3982a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
3992a1ad637SFrançois Tigeot 
4002a1ad637SFrançois Tigeot 	if (!(c->flags & SND_CLONE_GC_ENABLE) || c->size == 0)
4012a1ad637SFrançois Tigeot 		return (0);
4022a1ad637SFrançois Tigeot 
4032a1ad637SFrançois Tigeot 	snd_timestamp(&now);
4042a1ad637SFrançois Tigeot 
4052a1ad637SFrançois Tigeot 	/*
4062a1ad637SFrançois Tigeot 	 * Bail out if the last clone handler was invoked below the deadline
4072a1ad637SFrançois Tigeot 	 * threshold.
4082a1ad637SFrançois Tigeot 	 */
4092a1ad637SFrançois Tigeot 	if ((c->flags & SND_CLONE_GC_EXPIRED) &&
4102a1ad637SFrançois Tigeot 	    !SND_CLONE_EXPIRED(c, &now, &c->tsp))
4112a1ad637SFrançois Tigeot 		return (0);
4122a1ad637SFrançois Tigeot 
4132a1ad637SFrançois Tigeot 	pruned = 0;
4142a1ad637SFrançois Tigeot 
4152a1ad637SFrançois Tigeot 	/*
4162a1ad637SFrançois Tigeot 	 * Visit each object in reverse order. If the object is still being
4172a1ad637SFrançois Tigeot 	 * referenced by a valid open(), skip it. Look for expired objects
4182a1ad637SFrançois Tigeot 	 * and either revoke its clone invocation status or mercilessly
4192a1ad637SFrançois Tigeot 	 * throw it away.
4202a1ad637SFrançois Tigeot 	 */
42167931cc4SFrançois Tigeot 	TAILQ_FOREACH_REVERSE_MUTABLE(ce, &c->head, link_head, link, tce) {
4222a1ad637SFrançois Tigeot 		if (!(ce->flags & SND_CLONE_BUSY) &&
4232a1ad637SFrançois Tigeot 		    (!(ce->flags & SND_CLONE_INVOKE) ||
4242a1ad637SFrançois Tigeot 		    SND_CLONE_EXPIRED(c, &now, &ce->tsp))) {
42567931cc4SFrançois Tigeot 			if (c->flags & SND_CLONE_GC_REVOKE) {
4262a1ad637SFrançois Tigeot 				ce->flags &= ~SND_CLONE_INVOKE;
4272a1ad637SFrançois Tigeot 				ce->pid = -1;
4282a1ad637SFrançois Tigeot 			} else {
4292a1ad637SFrançois Tigeot 				TAILQ_REMOVE(&c->head, ce, link);
4305f097292SMatthew Dillon 				subunit = PCMSUBUNIT(ce->devt);
4312a1ad637SFrançois Tigeot 				destroy_dev(ce->devt);
4325f097292SMatthew Dillon 				devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(dsp), subunit);
43367931cc4SFrançois Tigeot 				kfree(ce, M_DEVBUF);
4342a1ad637SFrançois Tigeot 				c->size--;
4352a1ad637SFrançois Tigeot 			}
4362a1ad637SFrançois Tigeot 			pruned++;
4372a1ad637SFrançois Tigeot 		}
4382a1ad637SFrançois Tigeot 	}
4392a1ad637SFrançois Tigeot 
4402a1ad637SFrançois Tigeot 	/* return total pruned objects */
4412a1ad637SFrançois Tigeot 	return (pruned);
4422a1ad637SFrançois Tigeot }
4432a1ad637SFrançois Tigeot 
4442a1ad637SFrançois Tigeot void
snd_clone_destroy(struct snd_clone * c)4452a1ad637SFrançois Tigeot snd_clone_destroy(struct snd_clone *c)
4462a1ad637SFrançois Tigeot {
4472a1ad637SFrançois Tigeot 	struct snd_clone_entry *ce, *tmp;
4485f097292SMatthew Dillon 	int subunit;
4492a1ad637SFrançois Tigeot 
4502a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
4512a1ad637SFrançois Tigeot 
4522a1ad637SFrançois Tigeot 	ce = TAILQ_FIRST(&c->head);
4532a1ad637SFrançois Tigeot 	while (ce != NULL) {
4542a1ad637SFrançois Tigeot 		tmp = TAILQ_NEXT(ce, link);
4555f097292SMatthew Dillon 		if (ce->devt != NULL) {
4565f097292SMatthew Dillon 			subunit = PCMSUBUNIT(ce->devt);
4572a1ad637SFrançois Tigeot 			destroy_dev(ce->devt);
4585f097292SMatthew Dillon 			devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(dsp), subunit);
4595f097292SMatthew Dillon 		}
46067931cc4SFrançois Tigeot 		kfree(ce, M_DEVBUF);
4612a1ad637SFrançois Tigeot 		ce = tmp;
4622a1ad637SFrançois Tigeot 	}
4632a1ad637SFrançois Tigeot 
46467931cc4SFrançois Tigeot 	kfree(c, M_DEVBUF);
4652a1ad637SFrançois Tigeot }
4662a1ad637SFrançois Tigeot 
4672a1ad637SFrançois Tigeot /*
4682a1ad637SFrançois Tigeot  * snd_clone_acquire() : The vital part of concurrency management. Must be
4692a1ad637SFrançois Tigeot  * called somewhere at the beginning of open() handler. ENODEV is not really
4702a1ad637SFrançois Tigeot  * fatal since it just tell the caller that this is not cloned stuff.
4712a1ad637SFrançois Tigeot  * EBUSY is *real*, don't forget that!
4722a1ad637SFrançois Tigeot  */
4732a1ad637SFrançois Tigeot int
snd_clone_acquire(struct cdev * dev)4742a1ad637SFrançois Tigeot snd_clone_acquire(struct cdev *dev)
4752a1ad637SFrançois Tigeot {
4762a1ad637SFrançois Tigeot 	struct snd_clone_entry *ce;
4772a1ad637SFrançois Tigeot 
4782a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
4792a1ad637SFrançois Tigeot 
4802a1ad637SFrançois Tigeot 	ce = dev->si_drv2;
4812a1ad637SFrançois Tigeot 	if (ce == NULL)
4822a1ad637SFrançois Tigeot 		return (ENODEV);
4832a1ad637SFrançois Tigeot 
4842a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
4852a1ad637SFrançois Tigeot 
4862a1ad637SFrançois Tigeot 	ce->flags &= ~SND_CLONE_INVOKE;
4872a1ad637SFrançois Tigeot 
4882a1ad637SFrançois Tigeot 	if (ce->flags & SND_CLONE_BUSY)
4892a1ad637SFrançois Tigeot 		return (EBUSY);
4902a1ad637SFrançois Tigeot 
4912a1ad637SFrançois Tigeot 	ce->flags |= SND_CLONE_BUSY;
4922a1ad637SFrançois Tigeot 
4932a1ad637SFrançois Tigeot 	return (0);
4942a1ad637SFrançois Tigeot }
4952a1ad637SFrançois Tigeot 
4962a1ad637SFrançois Tigeot /*
4972a1ad637SFrançois Tigeot  * snd_clone_release() : Release busy status. Must be called somewhere at
4982a1ad637SFrançois Tigeot  * the end of close() handler, or somewhere after fail open().
4992a1ad637SFrançois Tigeot  */
5002a1ad637SFrançois Tigeot int
snd_clone_release(struct cdev * dev)5012a1ad637SFrançois Tigeot snd_clone_release(struct cdev *dev)
5022a1ad637SFrançois Tigeot {
5032a1ad637SFrançois Tigeot 	struct snd_clone_entry *ce;
5042a1ad637SFrançois Tigeot 
5052a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
5062a1ad637SFrançois Tigeot 
5072a1ad637SFrançois Tigeot 	ce = dev->si_drv2;
5082a1ad637SFrançois Tigeot 	if (ce == NULL)
5092a1ad637SFrançois Tigeot 		return (ENODEV);
5102a1ad637SFrançois Tigeot 
5112a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
5122a1ad637SFrançois Tigeot 
5132a1ad637SFrançois Tigeot 	ce->flags &= ~SND_CLONE_INVOKE;
5142a1ad637SFrançois Tigeot 
5152a1ad637SFrançois Tigeot 	if (!(ce->flags & SND_CLONE_BUSY))
5162a1ad637SFrançois Tigeot 		return (EBADF);
5172a1ad637SFrançois Tigeot 
5182a1ad637SFrançois Tigeot 	ce->flags &= ~SND_CLONE_BUSY;
5192a1ad637SFrançois Tigeot 	ce->pid = -1;
5202a1ad637SFrançois Tigeot 
5212a1ad637SFrançois Tigeot 	return (0);
5222a1ad637SFrançois Tigeot }
5232a1ad637SFrançois Tigeot 
5242a1ad637SFrançois Tigeot /*
5252a1ad637SFrançois Tigeot  * snd_clone_ref/unref() : Garbage collector reference counter. To make
5262a1ad637SFrançois Tigeot  * garbage collector run automatically, the sequence must be something like
5272a1ad637SFrançois Tigeot  * this (both in open() and close() handlers):
5282a1ad637SFrançois Tigeot  *
5292a1ad637SFrançois Tigeot  *  open() - 1) snd_clone_acquire()
5302a1ad637SFrançois Tigeot  *           2) .... check check ... if failed, snd_clone_release()
5312a1ad637SFrançois Tigeot  *           3) Success. Call snd_clone_ref()
5322a1ad637SFrançois Tigeot  *
5332a1ad637SFrançois Tigeot  * close() - 1) .... check check check ....
5342a1ad637SFrançois Tigeot  *           2) Success. snd_clone_release()
5352a1ad637SFrançois Tigeot  *           3) snd_clone_unref() . Garbage collector will run at this point
5362a1ad637SFrançois Tigeot  *              if this is the last referenced object.
5372a1ad637SFrançois Tigeot  */
5382a1ad637SFrançois Tigeot int
snd_clone_ref(struct cdev * dev)5392a1ad637SFrançois Tigeot snd_clone_ref(struct cdev *dev)
5402a1ad637SFrançois Tigeot {
5412a1ad637SFrançois Tigeot 	struct snd_clone_entry *ce;
5422a1ad637SFrançois Tigeot 	struct snd_clone *c;
5432a1ad637SFrançois Tigeot 
5442a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
5452a1ad637SFrançois Tigeot 
5462a1ad637SFrançois Tigeot 	ce = dev->si_drv2;
5472a1ad637SFrançois Tigeot 	if (ce == NULL)
5482a1ad637SFrançois Tigeot 		return (0);
5492a1ad637SFrançois Tigeot 
5502a1ad637SFrançois Tigeot 	c = ce->parent;
5512a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL parent"));
5522a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c->refcount >= 0, ("refcount < 0"));
5532a1ad637SFrançois Tigeot 
5542a1ad637SFrançois Tigeot 	return (++c->refcount);
5552a1ad637SFrançois Tigeot }
5562a1ad637SFrançois Tigeot 
5572a1ad637SFrançois Tigeot int
snd_clone_unref(struct cdev * dev)5582a1ad637SFrançois Tigeot snd_clone_unref(struct cdev *dev)
5592a1ad637SFrançois Tigeot {
5602a1ad637SFrançois Tigeot 	struct snd_clone_entry *ce;
5612a1ad637SFrançois Tigeot 	struct snd_clone *c;
5622a1ad637SFrançois Tigeot 
5632a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
5642a1ad637SFrançois Tigeot 
5652a1ad637SFrançois Tigeot 	ce = dev->si_drv2;
5662a1ad637SFrançois Tigeot 	if (ce == NULL)
5672a1ad637SFrançois Tigeot 		return (0);
5682a1ad637SFrançois Tigeot 
5692a1ad637SFrançois Tigeot 	c = ce->parent;
5702a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL parent"));
5712a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c->refcount > 0, ("refcount <= 0"));
5722a1ad637SFrançois Tigeot 
5732a1ad637SFrançois Tigeot 	c->refcount--;
5742a1ad637SFrançois Tigeot 
5752a1ad637SFrançois Tigeot 	/*
5762a1ad637SFrançois Tigeot 	 * Run automatic garbage collector, if needed.
5772a1ad637SFrançois Tigeot 	 */
5782a1ad637SFrançois Tigeot 	if ((c->flags & SND_CLONE_GC_UNREF) &&
5792a1ad637SFrançois Tigeot 	    (!(c->flags & SND_CLONE_GC_LASTREF) ||
5802a1ad637SFrançois Tigeot 	    (c->refcount == 0 && (c->flags & SND_CLONE_GC_LASTREF))))
5812a1ad637SFrançois Tigeot 		(void)snd_clone_gc(c);
5822a1ad637SFrançois Tigeot 
5832a1ad637SFrançois Tigeot 	return (c->refcount);
5842a1ad637SFrançois Tigeot }
5852a1ad637SFrançois Tigeot 
5862a1ad637SFrançois Tigeot void
snd_clone_register(struct snd_clone_entry * ce,struct cdev * dev)5872a1ad637SFrançois Tigeot snd_clone_register(struct snd_clone_entry *ce, struct cdev *dev)
5882a1ad637SFrançois Tigeot {
5892a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(ce != NULL, ("NULL snd_clone_entry"));
5902a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
5912a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(dev->si_drv2 == NULL, ("dev->si_drv2 not NULL"));
5922a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT((ce->flags & SND_CLONE_ALLOC) == SND_CLONE_ALLOC,
5932a1ad637SFrançois Tigeot 	    ("invalid clone alloc flags=0x%08x", ce->flags));
5942a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(ce->devt == NULL, ("ce->devt not NULL"));
59539b4af32SFrançois Tigeot #if 0	/* dev2unit doesn't make any sense on DragonFly */
5962a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(ce->unit == dev2unit(dev),
5972a1ad637SFrançois Tigeot 	    ("invalid unit ce->unit=0x%08x dev2unit=0x%08x",
5982a1ad637SFrançois Tigeot 	    ce->unit, dev2unit(dev)));
59939b4af32SFrançois Tigeot #endif
6002a1ad637SFrançois Tigeot 
6012a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
6022a1ad637SFrançois Tigeot 
6032a1ad637SFrançois Tigeot 	dev->si_drv2 = ce;
6042a1ad637SFrançois Tigeot 	ce->devt = dev;
6052a1ad637SFrançois Tigeot 	ce->flags &= ~SND_CLONE_ALLOC;
6062a1ad637SFrançois Tigeot 	ce->flags |= SND_CLONE_INVOKE;
6072a1ad637SFrançois Tigeot }
6082a1ad637SFrançois Tigeot 
6092a1ad637SFrançois Tigeot struct snd_clone_entry *
snd_clone_alloc(struct snd_clone * c,struct cdev ** dev,int * unit,int tmask)6102a1ad637SFrançois Tigeot snd_clone_alloc(struct snd_clone *c, struct cdev **dev, int *unit, int tmask)
6112a1ad637SFrançois Tigeot {
6122a1ad637SFrançois Tigeot 	struct snd_clone_entry *ce, *after, *bce, *cce, *nce, *tce;
6132a1ad637SFrançois Tigeot 	struct timespec now;
6142a1ad637SFrançois Tigeot 	int cunit, allocunit;
6152a1ad637SFrançois Tigeot 	pid_t curpid;
6162a1ad637SFrançois Tigeot 
6172a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
6182a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(dev != NULL, ("NULL dev pointer"));
6192a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT((c->typemask & tmask) == tmask,
6202a1ad637SFrançois Tigeot 	    ("invalid tmask: typemask=0x%08x tmask=0x%08x",
6212a1ad637SFrançois Tigeot 	    c->typemask, tmask));
6222a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(unit != NULL, ("NULL unit pointer"));
6232a1ad637SFrançois Tigeot 	SND_CLONE_ASSERT(*unit == -1 || !(*unit & (c->typemask | tmask)),
6242a1ad637SFrançois Tigeot 	    ("typemask collision: typemask=0x%08x tmask=0x%08x *unit=%d",
6252a1ad637SFrançois Tigeot 	    c->typemask, tmask, *unit));
6262a1ad637SFrançois Tigeot 
6272a1ad637SFrançois Tigeot 	if (!(c->flags & SND_CLONE_ENABLE) ||
6282a1ad637SFrançois Tigeot 	    (*unit != -1 && *unit > c->maxunit))
6292a1ad637SFrançois Tigeot 		return (NULL);
6302a1ad637SFrançois Tigeot 
6312a1ad637SFrançois Tigeot 	ce = NULL;
6322a1ad637SFrançois Tigeot 	after = NULL;
6332a1ad637SFrançois Tigeot 	bce = NULL;	/* "b"usy candidate */
6342a1ad637SFrançois Tigeot 	cce = NULL;	/* "c"urthread/proc candidate */
6352a1ad637SFrançois Tigeot 	nce = NULL;	/* "n"ull, totally unbusy candidate */
6362a1ad637SFrançois Tigeot 	tce = NULL;	/* Last "t"ry candidate */
6372a1ad637SFrançois Tigeot 	cunit = 0;
6382a1ad637SFrançois Tigeot 	allocunit = (*unit == -1) ? 0 : *unit;
6392a1ad637SFrançois Tigeot 	curpid = curthread->td_proc->p_pid;
6402a1ad637SFrançois Tigeot 
6412a1ad637SFrançois Tigeot 	snd_timestamp(&now);
6422a1ad637SFrançois Tigeot 
6432a1ad637SFrançois Tigeot 	TAILQ_FOREACH(ce, &c->head, link) {
6442a1ad637SFrançois Tigeot 		/*
6452a1ad637SFrançois Tigeot 		 * Sort incrementally according to device type.
6462a1ad637SFrançois Tigeot 		 */
6472a1ad637SFrançois Tigeot 		if (tmask > (ce->unit & c->typemask)) {
6482a1ad637SFrançois Tigeot 			if (cunit == 0)
6492a1ad637SFrançois Tigeot 				after = ce;
6502a1ad637SFrançois Tigeot 			continue;
6512a1ad637SFrançois Tigeot 		} else if (tmask < (ce->unit & c->typemask))
6522a1ad637SFrançois Tigeot 			break;
6532a1ad637SFrançois Tigeot 
6542a1ad637SFrançois Tigeot 		/*
6552a1ad637SFrançois Tigeot 		 * Shoot.. this is where the grumpiness begin. Just
6562a1ad637SFrançois Tigeot 		 * return immediately.
6572a1ad637SFrançois Tigeot 		 */
6582a1ad637SFrançois Tigeot 		if (*unit != -1 && *unit == (ce->unit & ~tmask))
6592a1ad637SFrançois Tigeot 			goto snd_clone_alloc_out;
6602a1ad637SFrançois Tigeot 
6612a1ad637SFrançois Tigeot 		cunit++;
6622a1ad637SFrançois Tigeot 		/*
6632a1ad637SFrançois Tigeot 		 * Simmilar device type. Sort incrementally according
6642a1ad637SFrançois Tigeot 		 * to allocation unit. While here, look for free slot
6652a1ad637SFrançois Tigeot 		 * and possible collision for new / future allocation.
6662a1ad637SFrançois Tigeot 		 */
6672a1ad637SFrançois Tigeot 		if (*unit == -1 && (ce->unit & ~tmask) == allocunit)
6682a1ad637SFrançois Tigeot 			allocunit++;
6692a1ad637SFrançois Tigeot 		if ((ce->unit & ~tmask) < allocunit)
6702a1ad637SFrançois Tigeot 			after = ce;
6712a1ad637SFrançois Tigeot 		/*
6722a1ad637SFrançois Tigeot 		 * Clone logic:
6732a1ad637SFrançois Tigeot 		 *   1. Look for non busy, but keep track of the best
6742a1ad637SFrançois Tigeot 		 *      possible busy cdev.
6752a1ad637SFrançois Tigeot 		 *   2. Look for the best (oldest referenced) entry that is
6762a1ad637SFrançois Tigeot 		 *      in a same process / thread.
6772a1ad637SFrançois Tigeot 		 *   3. Look for the best (oldest referenced), absolute free
6782a1ad637SFrançois Tigeot 		 *      entry.
6792a1ad637SFrançois Tigeot 		 *   4. Lastly, look for the best (oldest referenced)
6802a1ad637SFrançois Tigeot 		 *      any entries that doesn't fit with anything above.
6812a1ad637SFrançois Tigeot 		 */
6822a1ad637SFrançois Tigeot 		if (ce->flags & SND_CLONE_BUSY) {
6832a1ad637SFrançois Tigeot 			if (ce->devt != NULL && (bce == NULL ||
6842a1ad637SFrançois Tigeot 			    timespeccmp(&ce->tsp, &bce->tsp, <)))
6852a1ad637SFrançois Tigeot 				bce = ce;
6862a1ad637SFrançois Tigeot 			continue;
6872a1ad637SFrançois Tigeot 		}
6882a1ad637SFrançois Tigeot 		if (ce->pid == curpid &&
6892a1ad637SFrançois Tigeot 		    (cce == NULL || timespeccmp(&ce->tsp, &cce->tsp, <)))
6902a1ad637SFrançois Tigeot 			cce = ce;
6912a1ad637SFrançois Tigeot 		else if (!(ce->flags & SND_CLONE_INVOKE) &&
6922a1ad637SFrançois Tigeot 		    (nce == NULL || timespeccmp(&ce->tsp, &nce->tsp, <)))
6932a1ad637SFrançois Tigeot 			nce = ce;
6942a1ad637SFrançois Tigeot 		else if (tce == NULL || timespeccmp(&ce->tsp, &tce->tsp, <))
6952a1ad637SFrançois Tigeot 			tce = ce;
6962a1ad637SFrançois Tigeot 	}
6972a1ad637SFrançois Tigeot 	if (*unit != -1)
6982a1ad637SFrançois Tigeot 		goto snd_clone_alloc_new;
6992a1ad637SFrançois Tigeot 	else if (cce != NULL) {
7002a1ad637SFrançois Tigeot 		/* Same proc entry found, go for it */
7012a1ad637SFrançois Tigeot 		ce = cce;
7022a1ad637SFrançois Tigeot 		goto snd_clone_alloc_out;
7032a1ad637SFrançois Tigeot 	} else if (nce != NULL) {
7042a1ad637SFrançois Tigeot 		/*
7052a1ad637SFrançois Tigeot 		 * Next, try absolute free entry. If the calculated
7062a1ad637SFrançois Tigeot 		 * allocunit is smaller, create new entry instead.
7072a1ad637SFrançois Tigeot 		 */
7082a1ad637SFrançois Tigeot 		if (allocunit < (nce->unit & ~tmask))
7092a1ad637SFrançois Tigeot 			goto snd_clone_alloc_new;
7102a1ad637SFrançois Tigeot 		ce = nce;
7112a1ad637SFrançois Tigeot 		goto snd_clone_alloc_out;
7122a1ad637SFrançois Tigeot 	} else if (allocunit > c->maxunit) {
7132a1ad637SFrançois Tigeot 		/*
7142a1ad637SFrançois Tigeot 		 * Maximum allowable unit reached. Try returning any
7152a1ad637SFrançois Tigeot 		 * available cdev and hope for the best. If the lookup is
7162a1ad637SFrançois Tigeot 		 * done for things like stat(), mtime() etc. , things should
7172a1ad637SFrançois Tigeot 		 * be ok. Otherwise, open() handler should do further checks
7182a1ad637SFrançois Tigeot 		 * and decide whether to return correct error code or not.
7192a1ad637SFrançois Tigeot 		 */
7202a1ad637SFrançois Tigeot 		if (tce != NULL) {
7212a1ad637SFrançois Tigeot 			ce = tce;
7222a1ad637SFrançois Tigeot 			goto snd_clone_alloc_out;
7232a1ad637SFrançois Tigeot 		} else if (bce != NULL) {
7242a1ad637SFrançois Tigeot 			ce = bce;
7252a1ad637SFrançois Tigeot 			goto snd_clone_alloc_out;
7262a1ad637SFrançois Tigeot 		}
7272a1ad637SFrançois Tigeot 		return (NULL);
7282a1ad637SFrançois Tigeot 	}
7292a1ad637SFrançois Tigeot 
7302a1ad637SFrançois Tigeot snd_clone_alloc_new:
7312a1ad637SFrançois Tigeot 	/*
7322a1ad637SFrançois Tigeot 	 * No free entries found, and we still haven't reached maximum
7332a1ad637SFrançois Tigeot 	 * allowable units. Allocate, setup a minimal unique entry with busy
7342a1ad637SFrançois Tigeot 	 * status so nobody will monkey on this new entry. Unit magic is set
7352a1ad637SFrançois Tigeot 	 * right here to avoid collision with other contesting handler.
7362a1ad637SFrançois Tigeot 	 * The caller must be carefull here to maintain its own
7372a1ad637SFrançois Tigeot 	 * synchronization, as long as it will not conflict with malloc(9)
7382a1ad637SFrançois Tigeot 	 * operations.
7392a1ad637SFrançois Tigeot 	 *
7402a1ad637SFrançois Tigeot 	 * That said, go figure.
7412a1ad637SFrançois Tigeot 	 */
7424e8e900cSMatthew Dillon 	ce = kmalloc(sizeof(*ce), M_DEVBUF, M_WAITOK | M_ZERO);
7432a1ad637SFrançois Tigeot 	if (ce == NULL) {
7442a1ad637SFrançois Tigeot 		if (*unit != -1)
7452a1ad637SFrançois Tigeot 			return (NULL);
7462a1ad637SFrançois Tigeot 		/*
7472a1ad637SFrançois Tigeot 		 * We're being dense, ignorance is bliss,
7482a1ad637SFrançois Tigeot 		 * Super Regulatory Measure (TM).. TRY AGAIN!
7492a1ad637SFrançois Tigeot 		 */
7502a1ad637SFrançois Tigeot 		if (nce != NULL) {
7512a1ad637SFrançois Tigeot 			ce = nce;
7522a1ad637SFrançois Tigeot 			goto snd_clone_alloc_out;
7532a1ad637SFrançois Tigeot 		} else if (tce != NULL) {
7542a1ad637SFrançois Tigeot 			ce = tce;
7552a1ad637SFrançois Tigeot 			goto snd_clone_alloc_out;
7562a1ad637SFrançois Tigeot 		} else if (bce != NULL) {
7572a1ad637SFrançois Tigeot 			ce = bce;
7582a1ad637SFrançois Tigeot 			goto snd_clone_alloc_out;
7592a1ad637SFrançois Tigeot 		}
7602a1ad637SFrançois Tigeot 		return (NULL);
7612a1ad637SFrançois Tigeot 	}
7622a1ad637SFrançois Tigeot 	/* Setup new entry */
7632a1ad637SFrançois Tigeot 	ce->parent = c;
7642a1ad637SFrançois Tigeot 	ce->unit = tmask | allocunit;
7652a1ad637SFrançois Tigeot 	ce->pid = curpid;
7662a1ad637SFrançois Tigeot 	ce->tsp = now;
7672a1ad637SFrançois Tigeot 	ce->flags |= SND_CLONE_ALLOC;
7682a1ad637SFrançois Tigeot 	if (after != NULL) {
7692a1ad637SFrançois Tigeot 		TAILQ_INSERT_AFTER(&c->head, after, ce, link);
7702a1ad637SFrançois Tigeot 	} else {
7712a1ad637SFrançois Tigeot 		TAILQ_INSERT_HEAD(&c->head, ce, link);
7722a1ad637SFrançois Tigeot 	}
7732a1ad637SFrançois Tigeot 	c->size++;
7742a1ad637SFrançois Tigeot 	c->tsp = now;
7752a1ad637SFrançois Tigeot 	/*
7762a1ad637SFrançois Tigeot 	 * Save new allocation unit for caller which will be used
7772a1ad637SFrançois Tigeot 	 * by make_dev().
7782a1ad637SFrançois Tigeot 	 */
7792a1ad637SFrançois Tigeot 	*unit = allocunit;
7802a1ad637SFrançois Tigeot 
7812a1ad637SFrançois Tigeot 	return (ce);
7822a1ad637SFrançois Tigeot 
7832a1ad637SFrançois Tigeot snd_clone_alloc_out:
7842a1ad637SFrançois Tigeot 	/*
7852a1ad637SFrançois Tigeot 	 * Set, mark, timestamp the entry if this is a truly free entry.
7862a1ad637SFrançois Tigeot 	 * Leave busy entry alone.
7872a1ad637SFrançois Tigeot 	 */
7882a1ad637SFrançois Tigeot 	if (!(ce->flags & SND_CLONE_BUSY)) {
7892a1ad637SFrançois Tigeot 		ce->pid = curpid;
7902a1ad637SFrançois Tigeot 		ce->tsp = now;
7912a1ad637SFrançois Tigeot 		ce->flags |= SND_CLONE_INVOKE;
7922a1ad637SFrançois Tigeot 	}
7932a1ad637SFrançois Tigeot 	c->tsp = now;
7942a1ad637SFrançois Tigeot 	*dev = ce->devt;
7952a1ad637SFrançois Tigeot 
7962a1ad637SFrançois Tigeot 	return (NULL);
7972a1ad637SFrançois Tigeot }
798