Skip to content

Commit 3774591

Browse files
ivanorlov2206tiwai
authored andcommitted
ALSA: timer: Introduce virtual userspace-driven timers
Implement two ioctl calls in order to support virtual userspace-driven ALSA timers. The first ioctl is SNDRV_TIMER_IOCTL_CREATE, which gets the snd_timer_uinfo struct as a parameter and puts a file descriptor of a virtual timer into the `fd` field of the snd_timer_unfo structure. It also updates the `id` field of the snd_timer_uinfo struct, which provides a unique identifier for the timer (basically, the subdevice number which can be used when creating timer instances). This patch also introduces a tiny id allocator for the userspace-driven timers, which guarantees that we don't have more than 128 of them in the system. Another ioctl is SNDRV_TIMER_IOCTL_TRIGGER, which allows us to trigger the virtual timer (and calls snd_timer_interrupt for the timer under the hood), causing all of the timer instances binded to this timer to execute their callbacks. The maximum amount of ticks available for the timer is 1 for the sake of simplicity of the userspace API. 'start', 'stop', 'open' and 'close' callbacks for the userspace-driven timers are empty since we don't really do any hardware initialization here. Suggested-by: Axel Holzinger <[email protected]> Signed-off-by: Ivan Orlov <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> Link: https://patch.msgid.link/[email protected]
1 parent 8fad71b commit 3774591

File tree

3 files changed

+251
-1
lines changed

3 files changed

+251
-1
lines changed

include/uapi/sound/asound.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,7 @@ struct snd_ump_block_info {
869869
* Timer section - /dev/snd/timer
870870
*/
871871

872-
#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 7)
872+
#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 8)
873873

874874
enum {
875875
SNDRV_TIMER_CLASS_NONE = -1,
@@ -894,6 +894,7 @@ enum {
894894
#define SNDRV_TIMER_GLOBAL_RTC 1 /* unused */
895895
#define SNDRV_TIMER_GLOBAL_HPET 2
896896
#define SNDRV_TIMER_GLOBAL_HRTIMER 3
897+
#define SNDRV_TIMER_GLOBAL_UDRIVEN 4
897898

898899
/* info flags */
899900
#define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */
@@ -974,6 +975,18 @@ struct snd_timer_status {
974975
};
975976
#endif
976977

978+
/*
979+
* This structure describes the userspace-driven timer. Such timers are purely virtual,
980+
* and can only be triggered from software (for instance, by userspace application).
981+
*/
982+
struct snd_timer_uinfo {
983+
/* To pretend being a normal timer, we need to know the resolution in ns. */
984+
__u64 resolution;
985+
int fd;
986+
unsigned int id;
987+
unsigned char reserved[16];
988+
};
989+
977990
#define SNDRV_TIMER_IOCTL_PVERSION _IOR('T', 0x00, int)
978991
#define SNDRV_TIMER_IOCTL_NEXT_DEVICE _IOWR('T', 0x01, struct snd_timer_id)
979992
#define SNDRV_TIMER_IOCTL_TREAD_OLD _IOW('T', 0x02, int)
@@ -990,6 +1003,8 @@ struct snd_timer_status {
9901003
#define SNDRV_TIMER_IOCTL_CONTINUE _IO('T', 0xa2)
9911004
#define SNDRV_TIMER_IOCTL_PAUSE _IO('T', 0xa3)
9921005
#define SNDRV_TIMER_IOCTL_TREAD64 _IOW('T', 0xa4, int)
1006+
#define SNDRV_TIMER_IOCTL_CREATE _IOWR('T', 0xa5, struct snd_timer_uinfo)
1007+
#define SNDRV_TIMER_IOCTL_TRIGGER _IO('T', 0xa6)
9931008

9941009
#if __BITS_PER_LONG == 64
9951010
#define SNDRV_TIMER_IOCTL_TREAD SNDRV_TIMER_IOCTL_TREAD_OLD

sound/core/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,16 @@ config SND_JACK_INJECTION_DEBUG
242242
Say Y if you are debugging via jack injection interface.
243243
If unsure select "N".
244244

245+
config SND_UTIMER
246+
bool "Enable support for userspace-controlled virtual timers"
247+
depends on SND_TIMER
248+
help
249+
Say Y to enable the support of userspace-controlled timers. These
250+
timers are purely virtual, and they are supposed to be triggered
251+
from userspace. They could be quite useful when synchronizing the
252+
sound timing with userspace applications (for instance, when sending
253+
data through snd-aloop).
254+
245255
config SND_VMASTER
246256
bool
247257

sound/core/timer.c

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <linux/module.h>
1414
#include <linux/string.h>
1515
#include <linux/sched/signal.h>
16+
#include <linux/anon_inodes.h>
17+
#include <linux/idr.h>
1618
#include <sound/core.h>
1719
#include <sound/timer.h>
1820
#include <sound/control.h>
@@ -109,6 +111,16 @@ struct snd_timer_status64 {
109111
unsigned char reserved[64]; /* reserved */
110112
};
111113

114+
#ifdef CONFIG_SND_UTIMER
115+
#define SNDRV_UTIMERS_MAX_COUNT 128
116+
/* Internal data structure for keeping the state of the userspace-driven timer */
117+
struct snd_utimer {
118+
char *name;
119+
struct snd_timer *timer;
120+
unsigned int id;
121+
};
122+
#endif
123+
112124
#define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64)
113125

114126
/* list of timers */
@@ -2009,6 +2021,217 @@ enum {
20092021
SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
20102022
};
20112023

2024+
#ifdef CONFIG_SND_UTIMER
2025+
/*
2026+
* Since userspace-driven timers are passed to userspace, we need to have an identifier
2027+
* which will allow us to use them (basically, the subdevice number of udriven timer).
2028+
*/
2029+
static DEFINE_IDA(snd_utimer_ids);
2030+
2031+
static void snd_utimer_put_id(struct snd_utimer *utimer)
2032+
{
2033+
int timer_id = utimer->id;
2034+
2035+
snd_BUG_ON(timer_id < 0 || timer_id >= SNDRV_UTIMERS_MAX_COUNT);
2036+
ida_free(&snd_utimer_ids, timer_id);
2037+
}
2038+
2039+
static int snd_utimer_take_id(void)
2040+
{
2041+
return ida_alloc_max(&snd_utimer_ids, SNDRV_UTIMERS_MAX_COUNT - 1, GFP_KERNEL);
2042+
}
2043+
2044+
static void snd_utimer_free(struct snd_utimer *utimer)
2045+
{
2046+
snd_timer_free(utimer->timer);
2047+
snd_utimer_put_id(utimer);
2048+
kfree(utimer->name);
2049+
kfree(utimer);
2050+
}
2051+
2052+
static int snd_utimer_release(struct inode *inode, struct file *file)
2053+
{
2054+
struct snd_utimer *utimer = (struct snd_utimer *)file->private_data;
2055+
2056+
snd_utimer_free(utimer);
2057+
return 0;
2058+
}
2059+
2060+
static int snd_utimer_trigger(struct file *file)
2061+
{
2062+
struct snd_utimer *utimer = (struct snd_utimer *)file->private_data;
2063+
2064+
snd_timer_interrupt(utimer->timer, utimer->timer->sticks);
2065+
return 0;
2066+
}
2067+
2068+
static long snd_utimer_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
2069+
{
2070+
switch (ioctl) {
2071+
case SNDRV_TIMER_IOCTL_TRIGGER:
2072+
return snd_utimer_trigger(file);
2073+
}
2074+
2075+
return -ENOTTY;
2076+
}
2077+
2078+
static const struct file_operations snd_utimer_fops = {
2079+
.llseek = noop_llseek,
2080+
.release = snd_utimer_release,
2081+
.unlocked_ioctl = snd_utimer_ioctl,
2082+
};
2083+
2084+
static int snd_utimer_start(struct snd_timer *t)
2085+
{
2086+
return 0;
2087+
}
2088+
2089+
static int snd_utimer_stop(struct snd_timer *t)
2090+
{
2091+
return 0;
2092+
}
2093+
2094+
static int snd_utimer_open(struct snd_timer *t)
2095+
{
2096+
return 0;
2097+
}
2098+
2099+
static int snd_utimer_close(struct snd_timer *t)
2100+
{
2101+
return 0;
2102+
}
2103+
2104+
static const struct snd_timer_hardware timer_hw = {
2105+
.flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_WORK,
2106+
.open = snd_utimer_open,
2107+
.close = snd_utimer_close,
2108+
.start = snd_utimer_start,
2109+
.stop = snd_utimer_stop,
2110+
};
2111+
2112+
static int snd_utimer_create(struct snd_timer_uinfo *utimer_info,
2113+
struct snd_utimer **r_utimer)
2114+
{
2115+
struct snd_utimer *utimer;
2116+
struct snd_timer *timer;
2117+
struct snd_timer_id tid;
2118+
int utimer_id;
2119+
int err = 0;
2120+
2121+
if (!utimer_info || utimer_info->resolution == 0)
2122+
return -EINVAL;
2123+
2124+
utimer = kzalloc(sizeof(*utimer), GFP_KERNEL);
2125+
if (!utimer)
2126+
return -ENOMEM;
2127+
2128+
/* We hold the ioctl lock here so we won't get a race condition when allocating id */
2129+
utimer_id = snd_utimer_take_id();
2130+
if (utimer_id < 0) {
2131+
err = utimer_id;
2132+
goto err_take_id;
2133+
}
2134+
2135+
utimer->name = kasprintf(GFP_KERNEL, "snd-utimer%d", utimer_id);
2136+
if (!utimer->name) {
2137+
err = -ENOMEM;
2138+
goto err_get_name;
2139+
}
2140+
2141+
utimer->id = utimer_id;
2142+
2143+
tid.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
2144+
tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
2145+
tid.card = -1;
2146+
tid.device = SNDRV_TIMER_GLOBAL_UDRIVEN;
2147+
tid.subdevice = utimer_id;
2148+
2149+
err = snd_timer_new(NULL, utimer->name, &tid, &timer);
2150+
if (err < 0) {
2151+
pr_err("Can't create userspace-driven timer\n");
2152+
goto err_timer_new;
2153+
}
2154+
2155+
timer->module = THIS_MODULE;
2156+
timer->hw = timer_hw;
2157+
timer->hw.resolution = utimer_info->resolution;
2158+
timer->hw.ticks = 1;
2159+
timer->max_instances = MAX_SLAVE_INSTANCES;
2160+
2161+
utimer->timer = timer;
2162+
2163+
err = snd_timer_global_register(timer);
2164+
if (err < 0) {
2165+
pr_err("Can't register a userspace-driven timer\n");
2166+
goto err_timer_reg;
2167+
}
2168+
2169+
*r_utimer = utimer;
2170+
return 0;
2171+
2172+
err_timer_reg:
2173+
snd_timer_free(timer);
2174+
err_timer_new:
2175+
kfree(utimer->name);
2176+
err_get_name:
2177+
snd_utimer_put_id(utimer);
2178+
err_take_id:
2179+
kfree(utimer);
2180+
2181+
return err;
2182+
}
2183+
2184+
static int snd_utimer_ioctl_create(struct file *file,
2185+
struct snd_timer_uinfo __user *_utimer_info)
2186+
{
2187+
struct snd_utimer *utimer;
2188+
struct snd_timer_uinfo *utimer_info __free(kfree) = NULL;
2189+
int err, timer_fd;
2190+
2191+
utimer_info = memdup_user(_utimer_info, sizeof(*utimer_info));
2192+
if (IS_ERR(utimer_info))
2193+
return PTR_ERR(no_free_ptr(utimer_info));
2194+
2195+
err = snd_utimer_create(utimer_info, &utimer);
2196+
if (err < 0)
2197+
return err;
2198+
2199+
utimer_info->id = utimer->id;
2200+
2201+
timer_fd = anon_inode_getfd(utimer->name, &snd_utimer_fops, utimer, O_RDWR | O_CLOEXEC);
2202+
if (timer_fd < 0) {
2203+
snd_utimer_free(utimer);
2204+
return timer_fd;
2205+
}
2206+
2207+
utimer_info->fd = timer_fd;
2208+
2209+
err = copy_to_user(_utimer_info, utimer_info, sizeof(*utimer_info));
2210+
if (err) {
2211+
/*
2212+
* "Leak" the fd, as there is nothing we can do about it.
2213+
* It might have been closed already since anon_inode_getfd
2214+
* makes it available for userspace.
2215+
*
2216+
* We have to rely on the process exit path to do any
2217+
* necessary cleanup (e.g. releasing the file).
2218+
*/
2219+
return -EFAULT;
2220+
}
2221+
2222+
return 0;
2223+
}
2224+
2225+
#else
2226+
2227+
static int snd_utimer_ioctl_create(struct file *file,
2228+
struct snd_timer_uinfo __user *_utimer_info)
2229+
{
2230+
return -ENOTTY;
2231+
}
2232+
2233+
#endif
2234+
20122235
static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
20132236
unsigned long arg, bool compat)
20142237
{
@@ -2053,6 +2276,8 @@ static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
20532276
case SNDRV_TIMER_IOCTL_PAUSE:
20542277
case SNDRV_TIMER_IOCTL_PAUSE_OLD:
20552278
return snd_timer_user_pause(file);
2279+
case SNDRV_TIMER_IOCTL_CREATE:
2280+
return snd_utimer_ioctl_create(file, argp);
20562281
}
20572282
return -ENOTTY;
20582283
}

0 commit comments

Comments
 (0)