GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/sndiod/midi.c Lines: 0 235 0.0 %
Date: 2016-12-06 Branches: 0 154 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: midi.c,v 1.15 2016/01/08 16:17:31 ratchov Exp $	*/
2
/*
3
 * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
#include <stdio.h>
18
#include <stdlib.h>
19
#include <string.h>
20
21
#include "abuf.h"
22
#include "defs.h"
23
#include "dev.h"
24
#include "file.h"
25
#include "midi.h"
26
#include "miofile.h"
27
#include "sysex.h"
28
#include "utils.h"
29
30
int  port_open(struct port *);
31
void port_imsg(void *, unsigned char *, int);
32
void port_omsg(void *, unsigned char *, int);
33
void port_fill(void *, int);
34
void port_exit(void *);
35
36
struct midiops port_midiops = {
37
	port_imsg,
38
	port_omsg,
39
	port_fill,
40
	port_exit
41
};
42
43
#define MIDI_NEP 32
44
struct midi midi_ep[MIDI_NEP];
45
struct port *port_list = NULL;
46
unsigned int midi_portnum = 0;
47
48
struct midithru {
49
	unsigned int txmask, rxmask;
50
#define MIDITHRU_NMAX 32
51
} midithru[MIDITHRU_NMAX];
52
53
/*
54
 * length of voice and common messages (status byte included)
55
 */
56
unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
57
unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
58
59
void
60
midi_log(struct midi *ep)
61
{
62
	log_puts("midi");
63
	log_putu(ep - midi_ep);
64
}
65
66
void
67
midi_init(void)
68
{
69
}
70
71
void
72
midi_done(void)
73
{
74
}
75
76
struct midi *
77
midi_new(struct midiops *ops, void *arg, int mode)
78
{
79
	int i;
80
	struct midi *ep;
81
82
	for (i = 0, ep = midi_ep;; i++, ep++) {
83
		if (i == MIDI_NEP)
84
			return NULL;
85
		if (ep->ops == NULL)
86
			break;
87
	}
88
	ep->ops = ops;
89
	ep->arg = arg;
90
	ep->used = 0;
91
	ep->len = 0;
92
	ep->idx = 0;
93
	ep->st = 0;
94
	ep->txmask = 0;
95
	ep->self = 1 << i;
96
	ep->tickets = 0;
97
	ep->mode = mode;
98
99
	/*
100
	 * the output buffer is the client intput
101
	 */
102
	if (ep->mode & MODE_MIDIIN)
103
		abuf_init(&ep->obuf, MIDI_BUFSZ);
104
	midi_tickets(ep);
105
	return ep;
106
}
107
108
void
109
midi_del(struct midi *ep)
110
{
111
	int i;
112
	struct midi *peer;
113
114
	ep->txmask = 0;
115
	for (i = 0; i < MIDI_NEP; i++) {
116
		peer = midi_ep + i;
117
		if (peer->txmask & ep->self) {
118
			peer->txmask &= ~ep->self;
119
			midi_tickets(peer);
120
		}
121
	}
122
	for (i = 0; i < MIDITHRU_NMAX; i++) {
123
		midithru[i].txmask &= ~ep->self;
124
		midithru[i].rxmask &= ~ep->self;
125
	}
126
	ep->ops = NULL;
127
	if (ep->mode & MODE_MIDIIN) {
128
		abuf_done(&ep->obuf);
129
	}
130
}
131
132
/*
133
 * connect two midi endpoints
134
 */
135
void
136
midi_link(struct midi *ep, struct midi *peer)
137
{
138
	if (ep->mode & MODE_MIDIOUT) {
139
		ep->txmask |= peer->self;
140
		midi_tickets(ep);
141
	}
142
	if (ep->mode & MODE_MIDIIN) {
143
#ifdef DEBUG
144
		if (ep->obuf.used > 0) {
145
			midi_log(ep);
146
			log_puts(": linked with non-empty buffer\n");
147
			panic();
148
		}
149
#endif
150
		/* ep has empry buffer, so no need to call midi_tickets() */
151
		peer->txmask |= ep->self;
152
	}
153
}
154
155
/*
156
 * add the midi endpoint in the ``tag'' midi thru box
157
 */
158
void
159
midi_tag(struct midi *ep, unsigned int tag)
160
{
161
	struct midi *peer;
162
	struct midithru *t = midithru + tag;
163
	int i;
164
165
	if (ep->mode & MODE_MIDIOUT) {
166
		ep->txmask |= t->txmask;
167
		midi_tickets(ep);
168
	}
169
	if (ep->mode & MODE_MIDIIN) {
170
#ifdef DEBUG
171
		if (ep->obuf.used > 0) {
172
			midi_log(ep);
173
			log_puts(": tagged with non-empty buffer\n");
174
			panic();
175
		}
176
#endif
177
		for (i = 0; i < MIDI_NEP; i++) {
178
			if (!(t->rxmask & (1 << i)))
179
				continue;
180
			peer = midi_ep + i;
181
			peer->txmask |= ep->self;
182
		}
183
	}
184
	if (ep->mode & MODE_MIDIOUT)
185
		t->rxmask |= ep->self;
186
	if (ep->mode & MODE_MIDIIN)
187
		t->txmask |= ep->self;
188
}
189
190
/*
191
 * broadcast the given message to other endpoints
192
 */
193
void
194
midi_send(struct midi *iep, unsigned char *msg, int size)
195
{
196
	struct midi *oep;
197
	int i;
198
199
#ifdef DEBUG
200
	if (log_level >= 4) {
201
		midi_log(iep);
202
		log_puts(": sending:");
203
		for (i = 0; i < size; i++) {
204
			log_puts(" ");
205
			log_putx(msg[i]);
206
		}
207
		log_puts("\n");
208
	}
209
#endif
210
	for (i = 0; i < MIDI_NEP ; i++) {
211
		if ((iep->txmask & (1 << i)) == 0)
212
			continue;
213
		oep = midi_ep + i;
214
		if (msg[0] <= 0x7f) {
215
			if (oep->owner != iep)
216
				continue;
217
		} else if (msg[0] <= 0xf7)
218
			oep->owner = iep;
219
#ifdef DEBUG
220
		if (log_level >= 4) {
221
			midi_log(iep);
222
			log_puts(" -> ");
223
			midi_log(oep);
224
			log_puts("\n");
225
		}
226
#endif
227
		oep->ops->omsg(oep->arg, msg, size);
228
	}
229
}
230
231
/*
232
 * determine if we have gained more input tickets, and if so call the
233
 * fill() call-back to notify the i/o layer that it can send more data
234
 */
235
void
236
midi_tickets(struct midi *iep)
237
{
238
	int i, tickets, avail, maxavail;
239
	struct midi *oep;
240
241
	maxavail = MIDI_BUFSZ;
242
	for (i = 0; i < MIDI_NEP ; i++) {
243
		if ((iep->txmask & (1 << i)) == 0)
244
			continue;
245
		oep = midi_ep + i;
246
		avail = oep->obuf.len - oep->obuf.used;
247
		if (maxavail > avail)
248
			maxavail = avail;
249
	}
250
251
	/*
252
	 * in the worst case output message is twice the
253
	 * input message (2-byte messages with running status)
254
	 */
255
	tickets = maxavail / 2 - iep->tickets;
256
	if (tickets > 0) {
257
		iep->tickets += tickets;
258
		iep->ops->fill(iep->arg, tickets);
259
	}
260
}
261
262
/*
263
 * recalculate tickets of endpoints sending data to this one
264
 */
265
void
266
midi_fill(struct midi *oep)
267
{
268
	int i;
269
	struct midi *iep;
270
271
	for (i = 0; i < MIDI_NEP; i++) {
272
		iep = midi_ep + i;
273
		if (iep->txmask & oep->self)
274
			midi_tickets(iep);
275
	}
276
}
277
278
/*
279
 * parse then give data chunk, and calling imsg() for each message
280
 */
281
void
282
midi_in(struct midi *iep, unsigned char *idata, int icount)
283
{
284
	int i;
285
	unsigned char c;
286
287
	for (i = 0; i < icount; i++) {
288
		c = *idata++;
289
		if (c >= 0xf8) {
290
			if (c != MIDI_ACK)
291
				iep->ops->imsg(iep->arg, &c, 1);
292
		} else if (c == SYSEX_END) {
293
			if (iep->st == SYSEX_START) {
294
				iep->msg[iep->idx++] = c;
295
				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
296
			}
297
			iep->st = 0;
298
			iep->idx = 0;
299
		} else if (c >= 0xf0) {
300
			iep->msg[0] = c;
301
			iep->len = common_len[c & 7];
302
			iep->st = c;
303
			iep->idx = 1;
304
		} else if (c >= 0x80) {
305
			iep->msg[0] = c;
306
			iep->len = voice_len[(c >> 4) & 7];
307
			iep->st = c;
308
			iep->idx = 1;
309
		} else if (iep->st) {
310
			if (iep->idx == 0 && iep->st != SYSEX_START)
311
				iep->msg[iep->idx++] = iep->st;
312
			iep->msg[iep->idx++] = c;
313
			if (iep->idx == iep->len) {
314
				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
315
				if (iep->st >= 0xf0)
316
					iep->st = 0;
317
				iep->idx = 0;
318
			} else if (iep->idx == MIDI_MSGMAX) {
319
				/* sysex continued */
320
				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
321
				iep->idx = 0;
322
			}
323
		}
324
	}
325
	iep->tickets -= icount;
326
	if (iep->tickets < 0)
327
		iep->tickets = 0;
328
	midi_tickets(iep);
329
}
330
331
/*
332
 * store the given message in the output buffer
333
 */
334
void
335
midi_out(struct midi *oep, unsigned char *idata, int icount)
336
{
337
	unsigned char *odata;
338
	int ocount;
339
#ifdef DEBUG
340
	int i;
341
#endif
342
343
	while (icount > 0) {
344
		if (oep->obuf.used == oep->obuf.len) {
345
#ifdef DEBUG
346
			if (log_level >= 2) {
347
				midi_log(oep);
348
				log_puts(": too slow, discarding ");
349
				log_putu(oep->obuf.used);
350
				log_puts(" bytes\n");
351
			}
352
#endif
353
			abuf_rdiscard(&oep->obuf, oep->obuf.used);
354
			oep->owner = NULL;
355
			return;
356
		}
357
		odata = abuf_wgetblk(&oep->obuf, &ocount);
358
		if (ocount > icount)
359
			ocount = icount;
360
		memcpy(odata, idata, ocount);
361
#ifdef DEBUG
362
		if (log_level >= 4) {
363
			midi_log(oep);
364
			log_puts(": out: ");
365
			for (i = 0; i < ocount; i++) {
366
				log_puts(" ");
367
				log_putx(odata[i]);
368
			}
369
			log_puts("\n");
370
		}
371
#endif
372
		abuf_wcommit(&oep->obuf, ocount);
373
		icount -= ocount;
374
		idata += ocount;
375
	}
376
}
377
378
void
379
port_log(struct port *p)
380
{
381
	midi_log(p->midi);
382
}
383
384
void
385
port_imsg(void *arg, unsigned char *msg, int size)
386
{
387
	struct port *p = arg;
388
389
	midi_send(p->midi, msg, size);
390
}
391
392
393
void
394
port_omsg(void *arg, unsigned char *msg, int size)
395
{
396
	struct port *p = arg;
397
398
	midi_out(p->midi, msg, size);
399
}
400
401
void
402
port_fill(void *arg, int count)
403
{
404
	/* no flow control */
405
}
406
407
void
408
port_exit(void *arg)
409
{
410
#ifdef DEBUG
411
	struct port *p = arg;
412
413
	if (log_level >= 3) {
414
		port_log(p);
415
		log_puts(": port exit\n");
416
		panic();
417
	}
418
#endif
419
}
420
421
/*
422
 * create a new midi port
423
 */
424
struct port *
425
port_new(char *path, unsigned int mode, int hold)
426
{
427
	struct port *c;
428
429
	c = xmalloc(sizeof(struct port));
430
	c->path = xstrdup(path);
431
	c->state = PORT_CFG;
432
	c->hold = hold;
433
	c->midi = midi_new(&port_midiops, c, mode);
434
	c->num = midi_portnum++;
435
	c->next = port_list;
436
	port_list = c;
437
	return c;
438
}
439
440
/*
441
 * destroy the given midi port
442
 */
443
void
444
port_del(struct port *c)
445
{
446
	struct port **p;
447
448
	if (c->state != PORT_CFG)
449
		port_close(c);
450
	midi_del(c->midi);
451
	for (p = &port_list; *p != c; p = &(*p)->next) {
452
#ifdef DEBUG
453
		if (*p == NULL) {
454
			log_puts("port to delete not on list\n");
455
			panic();
456
		}
457
#endif
458
	}
459
	*p = c->next;
460
	xfree(c->path);
461
	xfree(c);
462
}
463
464
int
465
port_ref(struct port *c)
466
{
467
#ifdef DEBUG
468
	if (log_level >= 3) {
469
		port_log(c);
470
		log_puts(": port requested\n");
471
	}
472
#endif
473
	if (c->state == PORT_CFG && !port_open(c))
474
		return 0;
475
	return 1;
476
}
477
478
void
479
port_unref(struct port *c)
480
{
481
	int i, rxmask;
482
483
#ifdef DEBUG
484
	if (log_level >= 3) {
485
		port_log(c);
486
		log_puts(": port released\n");
487
	}
488
#endif
489
	for (rxmask = 0, i = 0; i < MIDI_NEP; i++)
490
		rxmask |= midi_ep[i].txmask;
491
	if ((rxmask & c->midi->self) == 0 && c->midi->txmask == 0 &&
492
	    c->state == PORT_INIT && !c->hold)
493
		port_drain(c);
494
}
495
496
struct port *
497
port_bynum(int num)
498
{
499
	struct port *p;
500
501
	for (p = port_list; p != NULL; p = p->next) {
502
		if (p->num == num)
503
			return p;
504
	}
505
	return NULL;
506
}
507
508
int
509
port_open(struct port *c)
510
{
511
	if (!port_mio_open(c)) {
512
		if (log_level >= 1) {
513
			log_puts(c->path);
514
			log_puts(": failed to open midi port\n");
515
		}
516
		return 0;
517
	}
518
	c->state = PORT_INIT;
519
	return 1;
520
}
521
522
int
523
port_close(struct port *c)
524
{
525
	int i;
526
	struct midi *ep;
527
#ifdef DEBUG
528
	if (c->state == PORT_CFG) {
529
		port_log(c);
530
		log_puts(": can't close port (not opened)\n");
531
		panic();
532
	}
533
#endif
534
	c->state = PORT_CFG;
535
	port_mio_close(c);
536
537
	for (i = 0; i < MIDI_NEP; i++) {
538
		ep = midi_ep + i;
539
		if ((ep->txmask & c->midi->self) ||
540
		    (c->midi->txmask & ep->self))
541
			ep->ops->exit(ep->arg);
542
	}
543
	return 1;
544
}
545
546
void
547
port_drain(struct port *c)
548
{
549
	struct midi *ep = c->midi;
550
551
	if (!(ep->mode & MODE_MIDIOUT) || ep->obuf.used == 0)
552
		port_close(c);
553
	else {
554
		c->state = PORT_DRAIN;
555
#ifdef DEBUG
556
		if (log_level >= 3) {
557
			port_log(c);
558
			log_puts(": draining\n");
559
		}
560
#endif
561
	}
562
}
563
564
int
565
port_init(struct port *c)
566
{
567
	if (c->hold)
568
		return port_open(c);
569
	return 1;
570
}
571
572
void
573
port_done(struct port *c)
574
{
575
	if (c->state == PORT_INIT)
576
		port_drain(c);
577
}