/* * Purpose: MIDI message queue management */ /* * * This file is part of Open Sound System. * * Copyright (C) 4Front Technologies 1996-2008. * * This this source file is released under GPL v2 license (no other versions). * See the COPYING file included in the main directory of this source * distribution for the license terms and conditions. * */ #include "oss_config.h" #include "midi_core.h" #define MDB(x) typedef struct { int len; midi_packet_header_t hdr; int next; int size; unsigned char *data; unsigned char buf[1]; /* Do _NOT_ add any fields after data[] */ /* Do _NOT_ add any fields after data[] */ /* Do _NOT_ add any fields after data[] */ /* Do _NOT_ add any fields after data[] */ /* Do _NOT_ add any fields after data[] */ } midi_buf_t; #define BUF_PREFIX_SIZE (sizeof(midi_buf_t)-1) #define MAX_QUEUE_DEPTH 31 #define QUEUE_BYTES ((MAX_QUEUE_DEPTH+1)*(BUF_PREFIX_SIZE+MIDI_PAYLOAD_SIZE)) struct midi_queue_t { oss_mutex_t mutex; char name[16]; /* For debugging purposes */ /* * Memeory management variables. */ unsigned char buffer[QUEUE_BYTES]; int buf_head, buf_tail; int debugging; int q_head, q_tail; int avail; int writecount, readcount; /* * Message queue */ midi_buf_t *buffers[MAX_QUEUE_DEPTH]; int dummy; }; midi_queue_t * midi_queue_alloc (oss_device_t * osdev, const char *name) { midi_queue_t *q; if ((q = KERNEL_MALLOC (sizeof (*q))) == NULL) return NULL; memset (q, 0, sizeof (*q)); MUTEX_INIT (osdev, q->mutex, MH_FRAMEW + 2); if (name != NULL) strcpy (q->name, name); q->avail = QUEUE_BYTES; return q; } void midi_queue_free (midi_queue_t * queue) { if (queue != NULL) { MUTEX_CLEANUP (queue->mutex); KERNEL_FREE (queue); } } static int queue_concat (midi_queue_t * queue, unsigned char *data, int len, midi_packet_header_t * hdr) { /* * Check if the MIDI event can be appended to the previous event (to improve * buffering performance). * * This is possible if: * * 1) The queue is not empty. * 2) The previous event has the same time stamp. * 3) The previous event is not too close to the end of the allocated buffer * area. */ midi_buf_t *buf; int p, i; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (queue->mutex, flags); if (queue->buf_tail == queue->buf_head) /* Nothing in the queue */ { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return 0; } if (queue->avail < len) { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return 0; } if (queue->q_tail == queue->q_head) /* No events in the queue */ { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return 0; } p = queue->q_head - 1; if (p < 0) p = MAX_QUEUE_DEPTH - 1; buf = queue->buffers[p]; if (hdr == NULL || buf->hdr.time != hdr->time) { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return 0; } if (buf->len + len > MIDI_PAYLOAD_SIZE) /* Event would become too long */ { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return 0; } if (buf->next + len >= QUEUE_BYTES - 1) /* No space to grow */ { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return 0; } /* Check that we are not too close to the buffer tail */ if (buf->next < queue->buf_tail && buf->next + len >= queue->buf_tail) { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return 0; } /* * Ok. All checks passed. Now append the data. */ queue->avail -= len; queue->writecount += len; buf->next += len; buf->size += len; for (i = 0; i < len; i++) buf->data[buf->len + i] = data[i]; buf->len += len; queue->buf_head = buf->next; MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return 1; } int midi_queue_alloc_record (midi_queue_t * queue, unsigned char **data, int len, midi_packet_header_t * hdr) { int p; int avail = 0, n, next; void *ptr; midi_buf_t *buf; oss_native_word flags; if (queue == NULL) { cmn_err (CE_WARN, "midi_queue_alloc_record: Queue==NULL\n"); return OSS_EIO; } if (len < 1 && hdr == NULL) /* Nothing was given */ { cmn_err (CE_WARN, "midi_queue_alloc_record: No data\n"); return OSS_EIO; } if (len > MIDI_PAYLOAD_SIZE) { cmn_err (CE_WARN, "Too long MIDI block\n"); len = MIDI_PAYLOAD_SIZE; } MUTEX_ENTER_IRQDISABLE (queue->mutex, flags); next = (queue->q_head + 1) % MAX_QUEUE_DEPTH; if (next == queue->q_tail) { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return OSS_ENOSPC; } if (queue->avail < BUF_PREFIX_SIZE + len) { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return OSS_ENOSPC; } n = queue->q_head - queue->q_tail; if (n <= 0) n += MAX_QUEUE_DEPTH; if (n < 1) { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return OSS_ENOSPC; } if (queue->buf_tail > queue->buf_head) { avail = queue->buf_tail - queue->buf_head; if (avail < BUF_PREFIX_SIZE + len) { queue->buf_head = 0; } } if (queue->buf_tail == queue->buf_head) { avail = QUEUE_BYTES; queue->buf_tail = queue->buf_head = 0; queue->avail = QUEUE_BYTES; } else if (queue->buf_tail < queue->buf_head) avail = QUEUE_BYTES - queue->buf_head; if (avail < BUF_PREFIX_SIZE + len) { #if 1 len = avail - BUF_PREFIX_SIZE; if (len <= 0) #endif { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return OSS_ENOSPC; } } if (queue->buf_head >= QUEUE_BYTES) { cmn_err (CE_CONT, "MIDI queue damaged (%d/%d/alloc)\n", queue->buf_head, QUEUE_BYTES); MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return OSS_EIO; } if (queue->buf_head + BUF_PREFIX_SIZE + len > QUEUE_BYTES) { cmn_err (CE_CONT, "Potential MIDI queue overflow detected\n"); cmn_err (CE_CONT, "Head=%d (%d)\n", queue->buf_head, QUEUE_BYTES); cmn_err (CE_CONT, "Tail=%d (%d)\n", queue->buf_tail, QUEUE_BYTES); cmn_err (CE_CONT, "Avail=%d\n", avail); cmn_err (CE_CONT, "Required=%d\n", len + BUF_PREFIX_SIZE); MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return OSS_EIO; } ptr = queue->buffer + queue->buf_head; p = queue->buf_head; queue->buf_head = (queue->buf_head + BUF_PREFIX_SIZE + len) % QUEUE_BYTES; buf = ptr; buf->size = BUF_PREFIX_SIZE + len; queue->avail -= buf->size; buf->data = buf->buf; buf->next = queue->buf_head; if (hdr == NULL) memset (&buf->hdr, 0, sizeof (midi_packet_header_t)); else memcpy (&buf->hdr, hdr, sizeof (midi_packet_header_t)); buf->len = len; *data = buf->data; queue->writecount += len; MDB (cmn_err (CE_CONT, "%s: alloc %d bytes\n", queue->name, len)); queue->buffers[queue->q_head] = buf; queue->q_head = (queue->q_head + 1) % MAX_QUEUE_DEPTH; MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return len; } int midi_queue_put (midi_queue_t * queue, unsigned char *data, int len, midi_packet_header_t * hdr) { int ret; unsigned char *targetbuf; if (queue == NULL) { return OSS_EIO; } #if 0 { char tmp[1024]; memcpy (tmp, data, len); tmp[len] = 0; cmn_err (CE_CONT, "Q Put='%s', %d\n", tmp, len); } #endif if (len < 1) return 0; if (len > MIDI_PAYLOAD_SIZE) return OSS_E2BIG; #if 1 if (len == 1) if (queue_concat (queue, data, len, hdr)) return len; #endif queue->debugging = 1; if ((ret = midi_queue_alloc_record (queue, &targetbuf, len, hdr)) <= 0) return ret; queue->debugging = 0; if (ret < len) /* All data didn't fit */ len = ret; memcpy (targetbuf, data, len); if (queue->buf_head >= QUEUE_BYTES || queue->buf_tail >= QUEUE_BYTES) { cmn_err (CE_CONT, "MIDI queue damaged (%d/%d/%d/put)\n", queue->buf_head, queue->buf_tail, QUEUE_BYTES); return OSS_EIO; } return len; } int midi_queue_get (midi_queue_t * queue, unsigned char **data, int max_len, midi_packet_header_t ** hdr) { midi_buf_t *buf; oss_native_word flags; *hdr = NULL; *data = NULL; if (queue == NULL) return OSS_EIO; if (queue->buf_head >= QUEUE_BYTES || queue->buf_tail >= QUEUE_BYTES) { cmn_err (CE_CONT, "MIDI queue damaged (%d/%d/%d/get)\n", queue->buf_head, queue->buf_tail, QUEUE_BYTES); return OSS_EIO; } MUTEX_ENTER_IRQDISABLE (queue->mutex, flags); if (queue->q_head == queue->q_tail) { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return 0; } buf = queue->buffers[queue->q_tail]; queue->q_tail = (queue->q_tail + 1) % MAX_QUEUE_DEPTH; queue->buf_tail = buf->next; queue->avail += buf->size; queue->readcount += buf->size; *data = buf->data; *hdr = &buf->hdr; #if 0 if (queue->buf_tail == queue->buf_head) /* Buffer empty */ queue->buf_tail = queue->buf_head = 0; /* Rewind */ if (queue->q_tail == queue->q_head) queue->q_tail = queue->q_head = 0; #endif MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return buf->len; } int midi_queue_find_buffer (midi_queue_t * queue, unsigned char **data, midi_packet_header_t ** hdr) { midi_buf_t *buf; oss_native_word flags; *hdr = NULL; *data = NULL; if (queue == NULL) return OSS_EIO; MUTEX_ENTER_IRQDISABLE (queue->mutex, flags); if (queue->q_head == queue->q_tail) { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return 0; } buf = queue->buffers[queue->q_tail]; *data = buf->data; *hdr = &buf->hdr; MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return buf->len; } void midi_queue_remove_chars (midi_queue_t * queue, int len) { midi_buf_t *buf; oss_native_word flags; if (queue == NULL) return; MUTEX_ENTER_IRQDISABLE (queue->mutex, flags); if (queue->q_head == queue->q_tail) { MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); MDB (cmn_err (CE_CONT, "%s: Q Nothing to remove\n", queue->name)); return; } buf = queue->buffers[queue->q_tail]; if (len < buf->len) { /* Buffer not completely used. Just remove len characters from the beginning */ /* unsigned char *data = buf->data; */ MDB (cmn_err (CE_CONT, "%s: Q Remove chars %d (%02x)\n", queue->name, len, buf->data[len])); /* memcpy(data, data+len, buf->len-len); */ buf->data += len; buf->len -= len; queue->readcount += len; MDB (cmn_err (CE_CONT, "%s: Q left %d\n", queue->name, buf->len)); MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return; } /* * Remove the whole buffer */ MDB (cmn_err (CE_CONT, "%s: Q Remove all\n", queue->name)); queue->q_tail = (queue->q_tail + 1) % MAX_QUEUE_DEPTH; queue->buf_tail = buf->next; queue->avail += buf->size; queue->readcount += len; if (queue->buf_tail == queue->buf_head) /* Buffer empty */ queue->buf_tail = queue->buf_head = 0; /* Rewind */ if (queue->q_tail == queue->q_head) queue->q_tail = queue->q_head = 0; MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); } void midi_queue_removeall (midi_queue_t * queue) { /* * Make the queue completely empty */ oss_native_word flags; if (queue == NULL) return; MUTEX_ENTER_IRQDISABLE (queue->mutex, flags); queue->buf_tail = queue->buf_head = 0; /* Rewind */ queue->q_tail = queue->q_head = 0; queue->avail = QUEUE_BYTES; queue->readcount = 0; queue->writecount = 0; MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); } int midi_queue_isempty (midi_queue_t * queue) { int is_empty; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (queue->mutex, flags); is_empty = (queue->q_tail == queue->q_head); MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return is_empty; } int midi_queue_spaceleft (midi_queue_t * queue) { oss_native_word flags; int space = 1; int next; MUTEX_ENTER_IRQDISABLE (queue->mutex, flags); next = (queue->q_head + 1) % MAX_QUEUE_DEPTH; if (next == queue->q_tail) space = 0; else { if (queue->avail < BUF_PREFIX_SIZE + 1) space = 0; else { space = queue->avail; if (space > MIDI_PAYLOAD_SIZE) space = MIDI_PAYLOAD_SIZE; } } MUTEX_EXIT_IRQRESTORE (queue->mutex, flags); return space; } void midi_queue_debugging (midi_queue_t * queue) { queue->debugging = 1; } void midi_queue_trace (midi_queue_t * queue) { MDB (cmn_err (CE_CONT, "Write %d, read %d\n", queue->writecount, queue->readcount)); }