xref: /OK3568_Linux_fs/kernel/arch/um/drivers/hostaudio_kern.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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