GNU Linux-libre 6.1.91-gnu
[releases.git] / sound / core / seq / oss / seq_oss_readq.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * OSS compatible sequencer driver
4  *
5  * seq_oss_readq.c - MIDI input queue
6  *
7  * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
8  */
9
10 #include "seq_oss_readq.h"
11 #include "seq_oss_event.h"
12 #include <sound/seq_oss_legacy.h>
13 #include "../seq_lock.h"
14 #include <linux/wait.h>
15 #include <linux/slab.h>
16
17 /*
18  * constants
19  */
20 //#define SNDRV_SEQ_OSS_MAX_TIMEOUT     (unsigned long)(-1)
21 #define SNDRV_SEQ_OSS_MAX_TIMEOUT       (HZ * 3600)
22
23
24 /*
25  * prototypes
26  */
27
28
29 /*
30  * create a read queue
31  */
32 struct seq_oss_readq *
33 snd_seq_oss_readq_new(struct seq_oss_devinfo *dp, int maxlen)
34 {
35         struct seq_oss_readq *q;
36
37         q = kzalloc(sizeof(*q), GFP_KERNEL);
38         if (!q)
39                 return NULL;
40
41         q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL);
42         if (!q->q) {
43                 kfree(q);
44                 return NULL;
45         }
46
47         q->maxlen = maxlen;
48         q->qlen = 0;
49         q->head = q->tail = 0;
50         init_waitqueue_head(&q->midi_sleep);
51         spin_lock_init(&q->lock);
52         q->pre_event_timeout = SNDRV_SEQ_OSS_MAX_TIMEOUT;
53         q->input_time = (unsigned long)-1;
54
55         return q;
56 }
57
58 /*
59  * delete the read queue
60  */
61 void
62 snd_seq_oss_readq_delete(struct seq_oss_readq *q)
63 {
64         if (q) {
65                 kfree(q->q);
66                 kfree(q);
67         }
68 }
69
70 /*
71  * reset the read queue
72  */
73 void
74 snd_seq_oss_readq_clear(struct seq_oss_readq *q)
75 {
76         if (q->qlen) {
77                 q->qlen = 0;
78                 q->head = q->tail = 0;
79         }
80         /* if someone sleeping, wake'em up */
81         wake_up(&q->midi_sleep);
82         q->input_time = (unsigned long)-1;
83 }
84
85 /*
86  * put a midi byte
87  */
88 int
89 snd_seq_oss_readq_puts(struct seq_oss_readq *q, int dev, unsigned char *data, int len)
90 {
91         union evrec rec;
92         int result;
93
94         memset(&rec, 0, sizeof(rec));
95         rec.c[0] = SEQ_MIDIPUTC;
96         rec.c[2] = dev;
97
98         while (len-- > 0) {
99                 rec.c[1] = *data++;
100                 result = snd_seq_oss_readq_put_event(q, &rec);
101                 if (result < 0)
102                         return result;
103         }
104         return 0;
105 }
106
107 /*
108  * put MIDI sysex bytes; the event buffer may be chained, thus it has
109  * to be expanded via snd_seq_dump_var_event().
110  */
111 struct readq_sysex_ctx {
112         struct seq_oss_readq *readq;
113         int dev;
114 };
115
116 static int readq_dump_sysex(void *ptr, void *buf, int count)
117 {
118         struct readq_sysex_ctx *ctx = ptr;
119
120         return snd_seq_oss_readq_puts(ctx->readq, ctx->dev, buf, count);
121 }
122
123 int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev,
124                             struct snd_seq_event *ev)
125 {
126         struct readq_sysex_ctx ctx = {
127                 .readq = q,
128                 .dev = dev
129         };
130
131         if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
132                 return 0;
133         return snd_seq_dump_var_event(ev, readq_dump_sysex, &ctx);
134 }
135
136 /*
137  * copy an event to input queue:
138  * return zero if enqueued
139  */
140 int
141 snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
142 {
143         unsigned long flags;
144
145         spin_lock_irqsave(&q->lock, flags);
146         if (q->qlen >= q->maxlen - 1) {
147                 spin_unlock_irqrestore(&q->lock, flags);
148                 return -ENOMEM;
149         }
150
151         memcpy(&q->q[q->tail], ev, sizeof(*ev));
152         q->tail = (q->tail + 1) % q->maxlen;
153         q->qlen++;
154
155         /* wake up sleeper */
156         wake_up(&q->midi_sleep);
157
158         spin_unlock_irqrestore(&q->lock, flags);
159
160         return 0;
161 }
162
163
164 /*
165  * pop queue
166  * caller must hold lock
167  */
168 int
169 snd_seq_oss_readq_pick(struct seq_oss_readq *q, union evrec *rec)
170 {
171         if (q->qlen == 0)
172                 return -EAGAIN;
173         memcpy(rec, &q->q[q->head], sizeof(*rec));
174         return 0;
175 }
176
177 /*
178  * sleep until ready
179  */
180 void
181 snd_seq_oss_readq_wait(struct seq_oss_readq *q)
182 {
183         wait_event_interruptible_timeout(q->midi_sleep,
184                                          (q->qlen > 0 || q->head == q->tail),
185                                          q->pre_event_timeout);
186 }
187
188 /*
189  * drain one record
190  * caller must hold lock
191  */
192 void
193 snd_seq_oss_readq_free(struct seq_oss_readq *q)
194 {
195         if (q->qlen > 0) {
196                 q->head = (q->head + 1) % q->maxlen;
197                 q->qlen--;
198         }
199 }
200
201 /*
202  * polling/select:
203  * return non-zero if readq is not empty.
204  */
205 unsigned int
206 snd_seq_oss_readq_poll(struct seq_oss_readq *q, struct file *file, poll_table *wait)
207 {
208         poll_wait(file, &q->midi_sleep, wait);
209         return q->qlen;
210 }
211
212 /*
213  * put a timestamp
214  */
215 int
216 snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *q, unsigned long curt, int seq_mode)
217 {
218         if (curt != q->input_time) {
219                 union evrec rec;
220                 memset(&rec, 0, sizeof(rec));
221                 switch (seq_mode) {
222                 case SNDRV_SEQ_OSS_MODE_SYNTH:
223                         rec.echo = (curt << 8) | SEQ_WAIT;
224                         snd_seq_oss_readq_put_event(q, &rec);
225                         break;
226                 case SNDRV_SEQ_OSS_MODE_MUSIC:
227                         rec.t.code = EV_TIMING;
228                         rec.t.cmd = TMR_WAIT_ABS;
229                         rec.t.time = curt;
230                         snd_seq_oss_readq_put_event(q, &rec);
231                         break;
232                 }
233                 q->input_time = curt;
234         }
235         return 0;
236 }
237
238
239 #ifdef CONFIG_SND_PROC_FS
240 /*
241  * proc interface
242  */
243 void
244 snd_seq_oss_readq_info_read(struct seq_oss_readq *q, struct snd_info_buffer *buf)
245 {
246         snd_iprintf(buf, "  read queue [%s] length = %d : tick = %ld\n",
247                     (waitqueue_active(&q->midi_sleep) ? "sleeping":"running"),
248                     q->qlen, q->input_time);
249 }
250 #endif /* CONFIG_SND_PROC_FS */