1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * OSS compatible sequencer driver |
4 | * |
5 | * OSS compatible i/o control |
6 | * |
7 | * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de> |
8 | */ |
9 | |
10 | #include "seq_oss_device.h" |
11 | #include "seq_oss_readq.h" |
12 | #include "seq_oss_writeq.h" |
13 | #include "seq_oss_timer.h" |
14 | #include "seq_oss_synth.h" |
15 | #include "seq_oss_midi.h" |
16 | #include "seq_oss_event.h" |
17 | |
18 | static int snd_seq_oss_synth_info_user(struct seq_oss_devinfo *dp, void __user *arg) |
19 | { |
20 | struct synth_info info; |
21 | |
22 | if (copy_from_user(to: &info, from: arg, n: sizeof(info))) |
23 | return -EFAULT; |
24 | if (snd_seq_oss_synth_make_info(dp, dev: info.device, inf: &info) < 0) |
25 | return -EINVAL; |
26 | if (copy_to_user(to: arg, from: &info, n: sizeof(info))) |
27 | return -EFAULT; |
28 | return 0; |
29 | } |
30 | |
31 | static int snd_seq_oss_midi_info_user(struct seq_oss_devinfo *dp, void __user *arg) |
32 | { |
33 | struct midi_info info; |
34 | |
35 | if (copy_from_user(to: &info, from: arg, n: sizeof(info))) |
36 | return -EFAULT; |
37 | if (snd_seq_oss_midi_make_info(dp, dev: info.device, inf: &info) < 0) |
38 | return -EINVAL; |
39 | if (copy_to_user(to: arg, from: &info, n: sizeof(info))) |
40 | return -EFAULT; |
41 | return 0; |
42 | } |
43 | |
44 | static int snd_seq_oss_oob_user(struct seq_oss_devinfo *dp, void __user *arg) |
45 | { |
46 | unsigned char ev[8]; |
47 | struct snd_seq_event tmpev; |
48 | |
49 | if (copy_from_user(to: ev, from: arg, n: 8)) |
50 | return -EFAULT; |
51 | memset(&tmpev, 0, sizeof(tmpev)); |
52 | snd_seq_oss_fill_addr(dp, ev: &tmpev, dest_client: dp->addr.client, dest_port: dp->addr.port); |
53 | tmpev.time.tick = 0; |
54 | if (! snd_seq_oss_process_event(dp, q: (union evrec *)ev, ev: &tmpev)) { |
55 | snd_seq_oss_dispatch(dp, ev: &tmpev, atomic: 0, hop: 0); |
56 | } |
57 | return 0; |
58 | } |
59 | |
60 | int |
61 | snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long carg) |
62 | { |
63 | int dev, val; |
64 | void __user *arg = (void __user *)carg; |
65 | int __user *p = arg; |
66 | |
67 | switch (cmd) { |
68 | case SNDCTL_TMR_TIMEBASE: |
69 | case SNDCTL_TMR_TEMPO: |
70 | case SNDCTL_TMR_START: |
71 | case SNDCTL_TMR_STOP: |
72 | case SNDCTL_TMR_CONTINUE: |
73 | case SNDCTL_TMR_METRONOME: |
74 | case SNDCTL_TMR_SOURCE: |
75 | case SNDCTL_TMR_SELECT: |
76 | case SNDCTL_SEQ_CTRLRATE: |
77 | return snd_seq_oss_timer_ioctl(timer: dp->timer, cmd, arg); |
78 | |
79 | case SNDCTL_SEQ_PANIC: |
80 | snd_seq_oss_reset(dp); |
81 | return -EINVAL; |
82 | |
83 | case SNDCTL_SEQ_SYNC: |
84 | if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) |
85 | return 0; |
86 | while (snd_seq_oss_writeq_sync(q: dp->writeq)) |
87 | ; |
88 | if (signal_pending(current)) |
89 | return -ERESTARTSYS; |
90 | return 0; |
91 | |
92 | case SNDCTL_SEQ_RESET: |
93 | snd_seq_oss_reset(dp); |
94 | return 0; |
95 | |
96 | case SNDCTL_SEQ_TESTMIDI: |
97 | if (get_user(dev, p)) |
98 | return -EFAULT; |
99 | return snd_seq_oss_midi_open(dp, dev, file_mode: dp->file_mode); |
100 | |
101 | case SNDCTL_SEQ_GETINCOUNT: |
102 | if (dp->readq == NULL || ! is_read_mode(dp->file_mode)) |
103 | return 0; |
104 | return put_user(dp->readq->qlen, p) ? -EFAULT : 0; |
105 | |
106 | case SNDCTL_SEQ_GETOUTCOUNT: |
107 | if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) |
108 | return 0; |
109 | return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), p) ? -EFAULT : 0; |
110 | |
111 | case SNDCTL_SEQ_GETTIME: |
112 | return put_user(snd_seq_oss_timer_cur_tick(dp->timer), p) ? -EFAULT : 0; |
113 | |
114 | case SNDCTL_SEQ_RESETSAMPLES: |
115 | if (get_user(dev, p)) |
116 | return -EFAULT; |
117 | return snd_seq_oss_synth_ioctl(dp, dev, cmd, addr: carg); |
118 | |
119 | case SNDCTL_SEQ_NRSYNTHS: |
120 | return put_user(dp->max_synthdev, p) ? -EFAULT : 0; |
121 | |
122 | case SNDCTL_SEQ_NRMIDIS: |
123 | return put_user(dp->max_mididev, p) ? -EFAULT : 0; |
124 | |
125 | case SNDCTL_SYNTH_MEMAVL: |
126 | if (get_user(dev, p)) |
127 | return -EFAULT; |
128 | val = snd_seq_oss_synth_ioctl(dp, dev, cmd, addr: carg); |
129 | return put_user(val, p) ? -EFAULT : 0; |
130 | |
131 | case SNDCTL_FM_4OP_ENABLE: |
132 | if (get_user(dev, p)) |
133 | return -EFAULT; |
134 | snd_seq_oss_synth_ioctl(dp, dev, cmd, addr: carg); |
135 | return 0; |
136 | |
137 | case SNDCTL_SYNTH_INFO: |
138 | case SNDCTL_SYNTH_ID: |
139 | return snd_seq_oss_synth_info_user(dp, arg); |
140 | |
141 | case SNDCTL_SEQ_OUTOFBAND: |
142 | return snd_seq_oss_oob_user(dp, arg); |
143 | |
144 | case SNDCTL_MIDI_INFO: |
145 | return snd_seq_oss_midi_info_user(dp, arg); |
146 | |
147 | case SNDCTL_SEQ_THRESHOLD: |
148 | if (! is_write_mode(dp->file_mode)) |
149 | return 0; |
150 | if (get_user(val, p)) |
151 | return -EFAULT; |
152 | if (val < 1) |
153 | val = 1; |
154 | if (val >= dp->writeq->maxlen) |
155 | val = dp->writeq->maxlen - 1; |
156 | snd_seq_oss_writeq_set_output(q: dp->writeq, size: val); |
157 | return 0; |
158 | |
159 | case SNDCTL_MIDI_PRETIME: |
160 | if (dp->readq == NULL || !is_read_mode(dp->file_mode)) |
161 | return 0; |
162 | if (get_user(val, p)) |
163 | return -EFAULT; |
164 | if (val <= 0) |
165 | val = -1; |
166 | else |
167 | val = (HZ * val) / 10; |
168 | dp->readq->pre_event_timeout = val; |
169 | return put_user(val, p) ? -EFAULT : 0; |
170 | |
171 | default: |
172 | if (! is_write_mode(dp->file_mode)) |
173 | return -EIO; |
174 | return snd_seq_oss_synth_ioctl(dp, dev: 0, cmd, addr: carg); |
175 | } |
176 | return 0; |
177 | } |
178 | |
179 | |