From 1058def8e7827e56ce4a70afb4aeacb5dc44148f Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Fri, 3 May 2013 21:08:42 +0400 Subject: Imported Upstream version 4.2-build2006 --- kernel/framework/midi/oss_midi_queue.c | 567 +++++++++++++++++++++++++++++++++ 1 file changed, 567 insertions(+) create mode 100644 kernel/framework/midi/oss_midi_queue.c (limited to 'kernel/framework/midi/oss_midi_queue.c') diff --git a/kernel/framework/midi/oss_midi_queue.c b/kernel/framework/midi/oss_midi_queue.c new file mode 100644 index 0000000..23801ae --- /dev/null +++ b/kernel/framework/midi/oss_midi_queue.c @@ -0,0 +1,567 @@ +/* + * 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)); +} -- cgit v1.2.3