linux/drivers/staging/line6/midibuf.c
<<
>>
Prefs
   1/*
   2 * Line6 Linux USB driver - 0.9.1beta
   3 *
   4 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
   5 *
   6 *      This program is free software; you can redistribute it and/or
   7 *      modify it under the terms of the GNU General Public License as
   8 *      published by the Free Software Foundation, version 2.
   9 *
  10 */
  11
  12#include <linux/slab.h>
  13
  14#include "midibuf.h"
  15
  16static int midibuf_message_length(unsigned char code)
  17{
  18        if (code < 0x80)
  19                return -1;
  20        else if (code < 0xf0) {
  21                static const int length[] = { 3, 3, 3, 3, 2, 2, 3 };
  22                return length[(code >> 4) - 8];
  23        } else {
  24                /*
  25                   Note that according to the MIDI specification 0xf2 is
  26                   the "Song Position Pointer", but this is used by Line6
  27                   to send sysex messages to the host.
  28                 */
  29                static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1,
  30                        1, 1, 1, -1, 1, 1
  31                };
  32                return length[code & 0x0f];
  33        }
  34}
  35
  36static int midibuf_is_empty(struct midi_buffer *this)
  37{
  38        return (this->pos_read == this->pos_write) && !this->full;
  39}
  40
  41static int midibuf_is_full(struct midi_buffer *this)
  42{
  43        return this->full;
  44}
  45
  46void line6_midibuf_reset(struct midi_buffer *this)
  47{
  48        this->pos_read = this->pos_write = this->full = 0;
  49        this->command_prev = -1;
  50}
  51
  52int line6_midibuf_init(struct midi_buffer *this, int size, int split)
  53{
  54        this->buf = kmalloc(size, GFP_KERNEL);
  55
  56        if (this->buf == NULL)
  57                return -ENOMEM;
  58
  59        this->size = size;
  60        this->split = split;
  61        line6_midibuf_reset(this);
  62        return 0;
  63}
  64
  65void line6_midibuf_status(struct midi_buffer *this)
  66{
  67        pr_debug("midibuf size=%d split=%d pos_read=%d pos_write=%d full=%d command_prev=%02x\n",
  68                 this->size, this->split, this->pos_read, this->pos_write,
  69                 this->full, this->command_prev);
  70}
  71
  72int line6_midibuf_bytes_free(struct midi_buffer *this)
  73{
  74        return
  75            midibuf_is_full(this) ?
  76            0 :
  77            (this->pos_read - this->pos_write + this->size - 1) % this->size +
  78            1;
  79}
  80
  81int line6_midibuf_bytes_used(struct midi_buffer *this)
  82{
  83        return
  84            midibuf_is_empty(this) ?
  85            0 :
  86            (this->pos_write - this->pos_read + this->size - 1) % this->size +
  87            1;
  88}
  89
  90int line6_midibuf_write(struct midi_buffer *this, unsigned char *data,
  91                        int length)
  92{
  93        int bytes_free;
  94        int length1, length2;
  95        int skip_active_sense = 0;
  96
  97        if (midibuf_is_full(this) || (length <= 0))
  98                return 0;
  99
 100        /* skip trailing active sense */
 101        if (data[length - 1] == 0xfe) {
 102                --length;
 103                skip_active_sense = 1;
 104        }
 105
 106        bytes_free = line6_midibuf_bytes_free(this);
 107
 108        if (length > bytes_free)
 109                length = bytes_free;
 110
 111        if (length > 0) {
 112                length1 = this->size - this->pos_write;
 113
 114                if (length < length1) {
 115                        /* no buffer wraparound */
 116                        memcpy(this->buf + this->pos_write, data, length);
 117                        this->pos_write += length;
 118                } else {
 119                        /* buffer wraparound */
 120                        length2 = length - length1;
 121                        memcpy(this->buf + this->pos_write, data, length1);
 122                        memcpy(this->buf, data + length1, length2);
 123                        this->pos_write = length2;
 124                }
 125
 126                if (this->pos_write == this->pos_read)
 127                        this->full = 1;
 128        }
 129
 130        return length + skip_active_sense;
 131}
 132
 133int line6_midibuf_read(struct midi_buffer *this, unsigned char *data,
 134                       int length)
 135{
 136        int bytes_used;
 137        int length1, length2;
 138        int command;
 139        int midi_length;
 140        int repeat = 0;
 141        int i;
 142
 143        /* we need to be able to store at least a 3 byte MIDI message */
 144        if (length < 3)
 145                return -EINVAL;
 146
 147        if (midibuf_is_empty(this))
 148                return 0;
 149
 150        bytes_used = line6_midibuf_bytes_used(this);
 151
 152        if (length > bytes_used)
 153                length = bytes_used;
 154
 155        length1 = this->size - this->pos_read;
 156
 157        /* check MIDI command length */
 158        command = this->buf[this->pos_read];
 159
 160        if (command & 0x80) {
 161                midi_length = midibuf_message_length(command);
 162                this->command_prev = command;
 163        } else {
 164                if (this->command_prev > 0) {
 165                        int midi_length_prev =
 166                            midibuf_message_length(this->command_prev);
 167
 168                        if (midi_length_prev > 0) {
 169                                midi_length = midi_length_prev - 1;
 170                                repeat = 1;
 171                        } else
 172                                midi_length = -1;
 173                } else
 174                        midi_length = -1;
 175        }
 176
 177        if (midi_length < 0) {
 178                /* search for end of message */
 179                if (length < length1) {
 180                        /* no buffer wraparound */
 181                        for (i = 1; i < length; ++i)
 182                                if (this->buf[this->pos_read + i] & 0x80)
 183                                        break;
 184
 185                        midi_length = i;
 186                } else {
 187                        /* buffer wraparound */
 188                        length2 = length - length1;
 189
 190                        for (i = 1; i < length1; ++i)
 191                                if (this->buf[this->pos_read + i] & 0x80)
 192                                        break;
 193
 194                        if (i < length1)
 195                                midi_length = i;
 196                        else {
 197                                for (i = 0; i < length2; ++i)
 198                                        if (this->buf[i] & 0x80)
 199                                                break;
 200
 201                                midi_length = length1 + i;
 202                        }
 203                }
 204
 205                if (midi_length == length)
 206                        midi_length = -1;       /* end of message not found */
 207        }
 208
 209        if (midi_length < 0) {
 210                if (!this->split)
 211                        return 0;       /* command is not yet complete */
 212        } else {
 213                if (length < midi_length)
 214                        return 0;       /* command is not yet complete */
 215
 216                length = midi_length;
 217        }
 218
 219        if (length < length1) {
 220                /* no buffer wraparound */
 221                memcpy(data + repeat, this->buf + this->pos_read, length);
 222                this->pos_read += length;
 223        } else {
 224                /* buffer wraparound */
 225                length2 = length - length1;
 226                memcpy(data + repeat, this->buf + this->pos_read, length1);
 227                memcpy(data + repeat + length1, this->buf, length2);
 228                this->pos_read = length2;
 229        }
 230
 231        if (repeat)
 232                data[0] = this->command_prev;
 233
 234        this->full = 0;
 235        return length + repeat;
 236}
 237
 238int line6_midibuf_ignore(struct midi_buffer *this, int length)
 239{
 240        int bytes_used = line6_midibuf_bytes_used(this);
 241
 242        if (length > bytes_used)
 243                length = bytes_used;
 244
 245        this->pos_read = (this->pos_read + length) % this->size;
 246        this->full = 0;
 247        return length;
 248}
 249
 250int line6_midibuf_skip_message(struct midi_buffer *this, unsigned short mask)
 251{
 252        int cmd = this->command_prev;
 253
 254        if ((cmd >= 0x80) && (cmd < 0xf0))
 255                if ((mask & (1 << (cmd & 0x0f))) == 0)
 256                        return 1;
 257
 258        return 0;
 259}
 260
 261void line6_midibuf_destroy(struct midi_buffer *this)
 262{
 263        kfree(this->buf);
 264        this->buf = NULL;
 265}
 266