1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2002 Steve Schmidtke
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/fs.h>
7*4882a593Smuzhiyun #include <linux/module.h>
8*4882a593Smuzhiyun #include <linux/slab.h>
9*4882a593Smuzhiyun #include <linux/sound.h>
10*4882a593Smuzhiyun #include <linux/soundcard.h>
11*4882a593Smuzhiyun #include <linux/mutex.h>
12*4882a593Smuzhiyun #include <linux/uaccess.h>
13*4882a593Smuzhiyun #include <init.h>
14*4882a593Smuzhiyun #include <os.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun struct hostaudio_state {
17*4882a593Smuzhiyun int fd;
18*4882a593Smuzhiyun };
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun struct hostmixer_state {
21*4882a593Smuzhiyun int fd;
22*4882a593Smuzhiyun };
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #define HOSTAUDIO_DEV_DSP "/dev/sound/dsp"
25*4882a593Smuzhiyun #define HOSTAUDIO_DEV_MIXER "/dev/sound/mixer"
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun /*
28*4882a593Smuzhiyun * Changed either at boot time or module load time. At boot, this is
29*4882a593Smuzhiyun * single-threaded; at module load, multiple modules would each have
30*4882a593Smuzhiyun * their own copy of these variables.
31*4882a593Smuzhiyun */
32*4882a593Smuzhiyun static char *dsp = HOSTAUDIO_DEV_DSP;
33*4882a593Smuzhiyun static char *mixer = HOSTAUDIO_DEV_MIXER;
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun #define DSP_HELP \
36*4882a593Smuzhiyun " This is used to specify the host dsp device to the hostaudio driver.\n" \
37*4882a593Smuzhiyun " The default is \"" HOSTAUDIO_DEV_DSP "\".\n\n"
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun #define MIXER_HELP \
40*4882a593Smuzhiyun " This is used to specify the host mixer device to the hostaudio driver.\n"\
41*4882a593Smuzhiyun " The default is \"" HOSTAUDIO_DEV_MIXER "\".\n\n"
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun module_param(dsp, charp, 0644);
44*4882a593Smuzhiyun MODULE_PARM_DESC(dsp, DSP_HELP);
45*4882a593Smuzhiyun module_param(mixer, charp, 0644);
46*4882a593Smuzhiyun MODULE_PARM_DESC(mixer, MIXER_HELP);
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun #ifndef MODULE
set_dsp(char * name,int * add)49*4882a593Smuzhiyun static int set_dsp(char *name, int *add)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun dsp = name;
52*4882a593Smuzhiyun return 0;
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun __uml_setup("dsp=", set_dsp, "dsp=<dsp device>\n" DSP_HELP);
56*4882a593Smuzhiyun
set_mixer(char * name,int * add)57*4882a593Smuzhiyun static int set_mixer(char *name, int *add)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun mixer = name;
60*4882a593Smuzhiyun return 0;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun __uml_setup("mixer=", set_mixer, "mixer=<mixer device>\n" MIXER_HELP);
64*4882a593Smuzhiyun #endif
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun static DEFINE_MUTEX(hostaudio_mutex);
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun /* /dev/dsp file operations */
69*4882a593Smuzhiyun
hostaudio_read(struct file * file,char __user * buffer,size_t count,loff_t * ppos)70*4882a593Smuzhiyun static ssize_t hostaudio_read(struct file *file, char __user *buffer,
71*4882a593Smuzhiyun size_t count, loff_t *ppos)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun struct hostaudio_state *state = file->private_data;
74*4882a593Smuzhiyun void *kbuf;
75*4882a593Smuzhiyun int err;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun #ifdef DEBUG
78*4882a593Smuzhiyun printk(KERN_DEBUG "hostaudio: read called, count = %d\n", count);
79*4882a593Smuzhiyun #endif
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun kbuf = kmalloc(count, GFP_KERNEL);
82*4882a593Smuzhiyun if (kbuf == NULL)
83*4882a593Smuzhiyun return -ENOMEM;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun err = os_read_file(state->fd, kbuf, count);
86*4882a593Smuzhiyun if (err < 0)
87*4882a593Smuzhiyun goto out;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun if (copy_to_user(buffer, kbuf, err))
90*4882a593Smuzhiyun err = -EFAULT;
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun out:
93*4882a593Smuzhiyun kfree(kbuf);
94*4882a593Smuzhiyun return err;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun
hostaudio_write(struct file * file,const char __user * buffer,size_t count,loff_t * ppos)97*4882a593Smuzhiyun static ssize_t hostaudio_write(struct file *file, const char __user *buffer,
98*4882a593Smuzhiyun size_t count, loff_t *ppos)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun struct hostaudio_state *state = file->private_data;
101*4882a593Smuzhiyun void *kbuf;
102*4882a593Smuzhiyun int err;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun #ifdef DEBUG
105*4882a593Smuzhiyun printk(KERN_DEBUG "hostaudio: write called, count = %d\n", count);
106*4882a593Smuzhiyun #endif
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun kbuf = memdup_user(buffer, count);
109*4882a593Smuzhiyun if (IS_ERR(kbuf))
110*4882a593Smuzhiyun return PTR_ERR(kbuf);
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun err = os_write_file(state->fd, kbuf, count);
113*4882a593Smuzhiyun if (err < 0)
114*4882a593Smuzhiyun goto out;
115*4882a593Smuzhiyun *ppos += err;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun out:
118*4882a593Smuzhiyun kfree(kbuf);
119*4882a593Smuzhiyun return err;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
hostaudio_poll(struct file * file,struct poll_table_struct * wait)122*4882a593Smuzhiyun static __poll_t hostaudio_poll(struct file *file,
123*4882a593Smuzhiyun struct poll_table_struct *wait)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun __poll_t mask = 0;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun #ifdef DEBUG
128*4882a593Smuzhiyun printk(KERN_DEBUG "hostaudio: poll called (unimplemented)\n");
129*4882a593Smuzhiyun #endif
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun return mask;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
hostaudio_ioctl(struct file * file,unsigned int cmd,unsigned long arg)134*4882a593Smuzhiyun static long hostaudio_ioctl(struct file *file,
135*4882a593Smuzhiyun unsigned int cmd, unsigned long arg)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun struct hostaudio_state *state = file->private_data;
138*4882a593Smuzhiyun unsigned long data = 0;
139*4882a593Smuzhiyun int err;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun #ifdef DEBUG
142*4882a593Smuzhiyun printk(KERN_DEBUG "hostaudio: ioctl called, cmd = %u\n", cmd);
143*4882a593Smuzhiyun #endif
144*4882a593Smuzhiyun switch(cmd){
145*4882a593Smuzhiyun case SNDCTL_DSP_SPEED:
146*4882a593Smuzhiyun case SNDCTL_DSP_STEREO:
147*4882a593Smuzhiyun case SNDCTL_DSP_GETBLKSIZE:
148*4882a593Smuzhiyun case SNDCTL_DSP_CHANNELS:
149*4882a593Smuzhiyun case SNDCTL_DSP_SUBDIVIDE:
150*4882a593Smuzhiyun case SNDCTL_DSP_SETFRAGMENT:
151*4882a593Smuzhiyun if (get_user(data, (int __user *) arg))
152*4882a593Smuzhiyun return -EFAULT;
153*4882a593Smuzhiyun break;
154*4882a593Smuzhiyun default:
155*4882a593Smuzhiyun break;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun err = os_ioctl_generic(state->fd, cmd, (unsigned long) &data);
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun switch(cmd){
161*4882a593Smuzhiyun case SNDCTL_DSP_SPEED:
162*4882a593Smuzhiyun case SNDCTL_DSP_STEREO:
163*4882a593Smuzhiyun case SNDCTL_DSP_GETBLKSIZE:
164*4882a593Smuzhiyun case SNDCTL_DSP_CHANNELS:
165*4882a593Smuzhiyun case SNDCTL_DSP_SUBDIVIDE:
166*4882a593Smuzhiyun case SNDCTL_DSP_SETFRAGMENT:
167*4882a593Smuzhiyun if (put_user(data, (int __user *) arg))
168*4882a593Smuzhiyun return -EFAULT;
169*4882a593Smuzhiyun break;
170*4882a593Smuzhiyun default:
171*4882a593Smuzhiyun break;
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun return err;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun
hostaudio_open(struct inode * inode,struct file * file)177*4882a593Smuzhiyun static int hostaudio_open(struct inode *inode, struct file *file)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun struct hostaudio_state *state;
180*4882a593Smuzhiyun int r = 0, w = 0;
181*4882a593Smuzhiyun int ret;
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun #ifdef DEBUG
184*4882a593Smuzhiyun kernel_param_lock(THIS_MODULE);
185*4882a593Smuzhiyun printk(KERN_DEBUG "hostaudio: open called (host: %s)\n", dsp);
186*4882a593Smuzhiyun kernel_param_unlock(THIS_MODULE);
187*4882a593Smuzhiyun #endif
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun state = kmalloc(sizeof(struct hostaudio_state), GFP_KERNEL);
190*4882a593Smuzhiyun if (state == NULL)
191*4882a593Smuzhiyun return -ENOMEM;
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun if (file->f_mode & FMODE_READ)
194*4882a593Smuzhiyun r = 1;
195*4882a593Smuzhiyun if (file->f_mode & FMODE_WRITE)
196*4882a593Smuzhiyun w = 1;
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun kernel_param_lock(THIS_MODULE);
199*4882a593Smuzhiyun mutex_lock(&hostaudio_mutex);
200*4882a593Smuzhiyun ret = os_open_file(dsp, of_set_rw(OPENFLAGS(), r, w), 0);
201*4882a593Smuzhiyun mutex_unlock(&hostaudio_mutex);
202*4882a593Smuzhiyun kernel_param_unlock(THIS_MODULE);
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun if (ret < 0) {
205*4882a593Smuzhiyun kfree(state);
206*4882a593Smuzhiyun return ret;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun state->fd = ret;
209*4882a593Smuzhiyun file->private_data = state;
210*4882a593Smuzhiyun return 0;
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun
hostaudio_release(struct inode * inode,struct file * file)213*4882a593Smuzhiyun static int hostaudio_release(struct inode *inode, struct file *file)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun struct hostaudio_state *state = file->private_data;
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun #ifdef DEBUG
218*4882a593Smuzhiyun printk(KERN_DEBUG "hostaudio: release called\n");
219*4882a593Smuzhiyun #endif
220*4882a593Smuzhiyun os_close_file(state->fd);
221*4882a593Smuzhiyun kfree(state);
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun return 0;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun /* /dev/mixer file operations */
227*4882a593Smuzhiyun
hostmixer_ioctl_mixdev(struct file * file,unsigned int cmd,unsigned long arg)228*4882a593Smuzhiyun static long hostmixer_ioctl_mixdev(struct file *file,
229*4882a593Smuzhiyun unsigned int cmd, unsigned long arg)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun struct hostmixer_state *state = file->private_data;
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun #ifdef DEBUG
234*4882a593Smuzhiyun printk(KERN_DEBUG "hostmixer: ioctl called\n");
235*4882a593Smuzhiyun #endif
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun return os_ioctl_generic(state->fd, cmd, arg);
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
hostmixer_open_mixdev(struct inode * inode,struct file * file)240*4882a593Smuzhiyun static int hostmixer_open_mixdev(struct inode *inode, struct file *file)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun struct hostmixer_state *state;
243*4882a593Smuzhiyun int r = 0, w = 0;
244*4882a593Smuzhiyun int ret;
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun #ifdef DEBUG
247*4882a593Smuzhiyun printk(KERN_DEBUG "hostmixer: open called (host: %s)\n", mixer);
248*4882a593Smuzhiyun #endif
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun state = kmalloc(sizeof(struct hostmixer_state), GFP_KERNEL);
251*4882a593Smuzhiyun if (state == NULL)
252*4882a593Smuzhiyun return -ENOMEM;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun if (file->f_mode & FMODE_READ)
255*4882a593Smuzhiyun r = 1;
256*4882a593Smuzhiyun if (file->f_mode & FMODE_WRITE)
257*4882a593Smuzhiyun w = 1;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun kernel_param_lock(THIS_MODULE);
260*4882a593Smuzhiyun mutex_lock(&hostaudio_mutex);
261*4882a593Smuzhiyun ret = os_open_file(mixer, of_set_rw(OPENFLAGS(), r, w), 0);
262*4882a593Smuzhiyun mutex_unlock(&hostaudio_mutex);
263*4882a593Smuzhiyun kernel_param_unlock(THIS_MODULE);
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun if (ret < 0) {
266*4882a593Smuzhiyun kernel_param_lock(THIS_MODULE);
267*4882a593Smuzhiyun printk(KERN_ERR "hostaudio_open_mixdev failed to open '%s', "
268*4882a593Smuzhiyun "err = %d\n", dsp, -ret);
269*4882a593Smuzhiyun kernel_param_unlock(THIS_MODULE);
270*4882a593Smuzhiyun kfree(state);
271*4882a593Smuzhiyun return ret;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun file->private_data = state;
275*4882a593Smuzhiyun return 0;
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun
hostmixer_release(struct inode * inode,struct file * file)278*4882a593Smuzhiyun static int hostmixer_release(struct inode *inode, struct file *file)
279*4882a593Smuzhiyun {
280*4882a593Smuzhiyun struct hostmixer_state *state = file->private_data;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun #ifdef DEBUG
283*4882a593Smuzhiyun printk(KERN_DEBUG "hostmixer: release called\n");
284*4882a593Smuzhiyun #endif
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun os_close_file(state->fd);
287*4882a593Smuzhiyun kfree(state);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun return 0;
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun /* kernel module operations */
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun static const struct file_operations hostaudio_fops = {
295*4882a593Smuzhiyun .owner = THIS_MODULE,
296*4882a593Smuzhiyun .llseek = no_llseek,
297*4882a593Smuzhiyun .read = hostaudio_read,
298*4882a593Smuzhiyun .write = hostaudio_write,
299*4882a593Smuzhiyun .poll = hostaudio_poll,
300*4882a593Smuzhiyun .unlocked_ioctl = hostaudio_ioctl,
301*4882a593Smuzhiyun .compat_ioctl = compat_ptr_ioctl,
302*4882a593Smuzhiyun .mmap = NULL,
303*4882a593Smuzhiyun .open = hostaudio_open,
304*4882a593Smuzhiyun .release = hostaudio_release,
305*4882a593Smuzhiyun };
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun static const struct file_operations hostmixer_fops = {
308*4882a593Smuzhiyun .owner = THIS_MODULE,
309*4882a593Smuzhiyun .llseek = no_llseek,
310*4882a593Smuzhiyun .unlocked_ioctl = hostmixer_ioctl_mixdev,
311*4882a593Smuzhiyun .open = hostmixer_open_mixdev,
312*4882a593Smuzhiyun .release = hostmixer_release,
313*4882a593Smuzhiyun };
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun struct {
316*4882a593Smuzhiyun int dev_audio;
317*4882a593Smuzhiyun int dev_mixer;
318*4882a593Smuzhiyun } module_data;
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun MODULE_AUTHOR("Steve Schmidtke");
321*4882a593Smuzhiyun MODULE_DESCRIPTION("UML Audio Relay");
322*4882a593Smuzhiyun MODULE_LICENSE("GPL");
323*4882a593Smuzhiyun
hostaudio_init_module(void)324*4882a593Smuzhiyun static int __init hostaudio_init_module(void)
325*4882a593Smuzhiyun {
326*4882a593Smuzhiyun kernel_param_lock(THIS_MODULE);
327*4882a593Smuzhiyun printk(KERN_INFO "UML Audio Relay (host dsp = %s, host mixer = %s)\n",
328*4882a593Smuzhiyun dsp, mixer);
329*4882a593Smuzhiyun kernel_param_unlock(THIS_MODULE);
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1);
332*4882a593Smuzhiyun if (module_data.dev_audio < 0) {
333*4882a593Smuzhiyun printk(KERN_ERR "hostaudio: couldn't register DSP device!\n");
334*4882a593Smuzhiyun return -ENODEV;
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun module_data.dev_mixer = register_sound_mixer(&hostmixer_fops, -1);
338*4882a593Smuzhiyun if (module_data.dev_mixer < 0) {
339*4882a593Smuzhiyun printk(KERN_ERR "hostmixer: couldn't register mixer "
340*4882a593Smuzhiyun "device!\n");
341*4882a593Smuzhiyun unregister_sound_dsp(module_data.dev_audio);
342*4882a593Smuzhiyun return -ENODEV;
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun return 0;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun
hostaudio_cleanup_module(void)348*4882a593Smuzhiyun static void __exit hostaudio_cleanup_module (void)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun unregister_sound_mixer(module_data.dev_mixer);
351*4882a593Smuzhiyun unregister_sound_dsp(module_data.dev_audio);
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun module_init(hostaudio_init_module);
355*4882a593Smuzhiyun module_exit(hostaudio_cleanup_module);
356