Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * OSS compatible sequencer driver | |
4 | * | |
5 | * read/write/select interface to device file | |
6 | * | |
7 | * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de> | |
1da177e4 LT |
8 | */ |
9 | ||
10 | #include "seq_oss_device.h" | |
11 | #include "seq_oss_readq.h" | |
12 | #include "seq_oss_writeq.h" | |
13 | #include "seq_oss_synth.h" | |
14 | #include <sound/seq_oss_legacy.h> | |
15 | #include "seq_oss_event.h" | |
16 | #include "seq_oss_timer.h" | |
17 | #include "../seq_clientmgr.h" | |
18 | ||
19 | ||
20 | /* | |
21 | * protoypes | |
22 | */ | |
080dece3 | 23 | static int insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt); |
1da177e4 LT |
24 | |
25 | ||
26 | /* | |
27 | * read interface | |
28 | */ | |
29 | ||
30 | int | |
080dece3 | 31 | snd_seq_oss_read(struct seq_oss_devinfo *dp, char __user *buf, int count) |
1da177e4 | 32 | { |
080dece3 | 33 | struct seq_oss_readq *readq = dp->readq; |
1da177e4 LT |
34 | int result = 0, err = 0; |
35 | int ev_len; | |
080dece3 | 36 | union evrec rec; |
1da177e4 LT |
37 | unsigned long flags; |
38 | ||
39 | if (readq == NULL || ! is_read_mode(dp->file_mode)) | |
40 | return -ENXIO; | |
41 | ||
42 | while (count >= SHORT_EVENT_SIZE) { | |
43 | snd_seq_oss_readq_lock(readq, flags); | |
44 | err = snd_seq_oss_readq_pick(readq, &rec); | |
45 | if (err == -EAGAIN && | |
46 | !is_nonblock_mode(dp->file_mode) && result == 0) { | |
47 | snd_seq_oss_readq_unlock(readq, flags); | |
48 | snd_seq_oss_readq_wait(readq); | |
49 | snd_seq_oss_readq_lock(readq, flags); | |
50 | if (signal_pending(current)) | |
51 | err = -ERESTARTSYS; | |
52 | else | |
53 | err = snd_seq_oss_readq_pick(readq, &rec); | |
54 | } | |
55 | if (err < 0) { | |
56 | snd_seq_oss_readq_unlock(readq, flags); | |
57 | break; | |
58 | } | |
59 | ev_len = ev_length(&rec); | |
60 | if (ev_len < count) { | |
61 | snd_seq_oss_readq_unlock(readq, flags); | |
62 | break; | |
63 | } | |
64 | snd_seq_oss_readq_free(readq); | |
65 | snd_seq_oss_readq_unlock(readq, flags); | |
66 | if (copy_to_user(buf, &rec, ev_len)) { | |
67 | err = -EFAULT; | |
68 | break; | |
69 | } | |
70 | result += ev_len; | |
71 | buf += ev_len; | |
72 | count -= ev_len; | |
73 | } | |
74 | return result > 0 ? result : err; | |
75 | } | |
76 | ||
77 | ||
78 | /* | |
79 | * write interface | |
80 | */ | |
81 | ||
82 | int | |
080dece3 | 83 | snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int count, struct file *opt) |
1da177e4 LT |
84 | { |
85 | int result = 0, err = 0; | |
86 | int ev_size, fmt; | |
080dece3 | 87 | union evrec rec; |
1da177e4 LT |
88 | |
89 | if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) | |
90 | return -ENXIO; | |
91 | ||
92 | while (count >= SHORT_EVENT_SIZE) { | |
93 | if (copy_from_user(&rec, buf, SHORT_EVENT_SIZE)) { | |
94 | err = -EFAULT; | |
95 | break; | |
96 | } | |
97 | if (rec.s.code == SEQ_FULLSIZE) { | |
98 | /* load patch */ | |
99 | if (result > 0) { | |
100 | err = -EINVAL; | |
101 | break; | |
102 | } | |
103 | fmt = (*(unsigned short *)rec.c) & 0xffff; | |
104 | /* FIXME the return value isn't correct */ | |
105 | return snd_seq_oss_synth_load_patch(dp, rec.s.dev, | |
106 | fmt, buf, 0, count); | |
107 | } | |
108 | if (ev_is_long(&rec)) { | |
109 | /* extended code */ | |
110 | if (rec.s.code == SEQ_EXTENDED && | |
111 | dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { | |
112 | err = -EINVAL; | |
113 | break; | |
114 | } | |
115 | ev_size = LONG_EVENT_SIZE; | |
116 | if (count < ev_size) | |
117 | break; | |
118 | /* copy the reset 4 bytes */ | |
119 | if (copy_from_user(rec.c + SHORT_EVENT_SIZE, | |
120 | buf + SHORT_EVENT_SIZE, | |
121 | LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) { | |
122 | err = -EFAULT; | |
123 | break; | |
124 | } | |
125 | } else { | |
126 | /* old-type code */ | |
127 | if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { | |
128 | err = -EINVAL; | |
129 | break; | |
130 | } | |
131 | ev_size = SHORT_EVENT_SIZE; | |
132 | } | |
133 | ||
134 | /* insert queue */ | |
135 | if ((err = insert_queue(dp, &rec, opt)) < 0) | |
136 | break; | |
137 | ||
138 | result += ev_size; | |
139 | buf += ev_size; | |
140 | count -= ev_size; | |
141 | } | |
142 | return result > 0 ? result : err; | |
143 | } | |
144 | ||
145 | ||
146 | /* | |
147 | * insert event record to write queue | |
148 | * return: 0 = OK, non-zero = NG | |
149 | */ | |
150 | static int | |
080dece3 | 151 | insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt) |
1da177e4 LT |
152 | { |
153 | int rc = 0; | |
080dece3 | 154 | struct snd_seq_event event; |
1da177e4 LT |
155 | |
156 | /* if this is a timing event, process the current time */ | |
157 | if (snd_seq_oss_process_timer_event(dp->timer, rec)) | |
158 | return 0; /* no need to insert queue */ | |
159 | ||
160 | /* parse this event */ | |
161 | memset(&event, 0, sizeof(event)); | |
162 | /* set dummy -- to be sure */ | |
163 | event.type = SNDRV_SEQ_EVENT_NOTEOFF; | |
164 | snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client); | |
165 | ||
166 | if (snd_seq_oss_process_event(dp, rec, &event)) | |
167 | return 0; /* invalid event - no need to insert queue */ | |
168 | ||
169 | event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer); | |
6740ea67 | 170 | if (dp->timer->realtime || !dp->timer->running) |
1da177e4 | 171 | snd_seq_oss_dispatch(dp, &event, 0, 0); |
6740ea67 TI |
172 | else |
173 | rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, opt, | |
174 | !is_nonblock_mode(dp->file_mode)); | |
1da177e4 LT |
175 | return rc; |
176 | } | |
177 | ||
178 | ||
179 | /* | |
180 | * select / poll | |
181 | */ | |
182 | ||
680ef72a | 183 | __poll_t |
080dece3 | 184 | snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait) |
1da177e4 | 185 | { |
680ef72a | 186 | __poll_t mask = 0; |
1da177e4 LT |
187 | |
188 | /* input */ | |
189 | if (dp->readq && is_read_mode(dp->file_mode)) { | |
190 | if (snd_seq_oss_readq_poll(dp->readq, file, wait)) | |
a9a08845 | 191 | mask |= EPOLLIN | EPOLLRDNORM; |
1da177e4 LT |
192 | } |
193 | ||
194 | /* output */ | |
195 | if (dp->writeq && is_write_mode(dp->file_mode)) { | |
196 | if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait)) | |
a9a08845 | 197 | mask |= EPOLLOUT | EPOLLWRNORM; |
1da177e4 LT |
198 | } |
199 | return mask; | |
200 | } |