|
13 | 13 | #include <linux/module.h>
|
14 | 14 | #include <linux/string.h>
|
15 | 15 | #include <linux/sched/signal.h>
|
| 16 | +#include <linux/anon_inodes.h> |
| 17 | +#include <linux/idr.h> |
16 | 18 | #include <sound/core.h>
|
17 | 19 | #include <sound/timer.h>
|
18 | 20 | #include <sound/control.h>
|
@@ -109,6 +111,16 @@ struct snd_timer_status64 {
|
109 | 111 | unsigned char reserved[64]; /* reserved */
|
110 | 112 | };
|
111 | 113 |
|
| 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 | + |
112 | 124 | #define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64)
|
113 | 125 |
|
114 | 126 | /* list of timers */
|
@@ -2009,6 +2021,217 @@ enum {
|
2009 | 2021 | SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
|
2010 | 2022 | };
|
2011 | 2023 |
|
| 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 | + |
2012 | 2235 | static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
|
2013 | 2236 | unsigned long arg, bool compat)
|
2014 | 2237 | {
|
@@ -2053,6 +2276,8 @@ static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
|
2053 | 2276 | case SNDRV_TIMER_IOCTL_PAUSE:
|
2054 | 2277 | case SNDRV_TIMER_IOCTL_PAUSE_OLD:
|
2055 | 2278 | return snd_timer_user_pause(file);
|
| 2279 | + case SNDRV_TIMER_IOCTL_CREATE: |
| 2280 | + return snd_utimer_ioctl_create(file, argp); |
2056 | 2281 | }
|
2057 | 2282 | return -ENOTTY;
|
2058 | 2283 | }
|
|
0 commit comments