linux-bk/sound/isa/wavefront/wavefront_midi.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) by Paul Barton-Davis 1998-1999
   3 *
   4 * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
   5 * Version 2 (June 1991). See the "COPYING" file distributed with this
   6 * software for more info.  
   7 */
   8
   9/* The low level driver for the WaveFront ICS2115 MIDI interface(s)
  10 *
  11 * Note that there is also an MPU-401 emulation (actually, a UART-401
  12 * emulation) on the CS4232 on the Tropez and Tropez Plus. This code
  13 * has nothing to do with that interface at all.
  14 *
  15 * The interface is essentially just a UART-401, but is has the
  16 * interesting property of supporting what Turtle Beach called
  17 * "Virtual MIDI" mode. In this mode, there are effectively *two*
  18 * MIDI buses accessible via the interface, one that is routed
  19 * solely to/from the external WaveFront synthesizer and the other
  20 * corresponding to the pin/socket connector used to link external
  21 * MIDI devices to the board.
  22 *
  23 * This driver fully supports this mode, allowing two distinct MIDI
  24 * busses to be used completely independently, giving 32 channels of
  25 * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI
  26 * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1,
  27 * where `n' is the card number. Note that the device numbers may be
  28 * something other than 0 and 1 if the CS4232 UART/MPU-401 interface
  29 * is enabled.
  30 *
  31 * Switching between the two is accomplished externally by the driver
  32 * using the two otherwise unused MIDI bytes. See the code for more details.
  33 *
  34 * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c)
  35 *
  36 * The main reason to turn off Virtual MIDI mode is when you want to
  37 * tightly couple the WaveFront synth with an external MIDI
  38 * device. You won't be able to distinguish the source of any MIDI
  39 * data except via SysEx ID, but thats probably OK, since for the most
  40 * part, the WaveFront won't be sending any MIDI data at all.
  41 *  
  42 * The main reason to turn on Virtual MIDI Mode is to provide two
  43 * completely independent 16-channel MIDI buses, one to the
  44 * WaveFront and one to any external MIDI devices. Given the 32
  45 * voice nature of the WaveFront, its pretty easy to find a use
  46 * for all 16 channels driving just that synth.
  47 *  
  48 */
  49
  50#include <sound/driver.h>
  51#include <asm/io.h>
  52#include <linux/init.h>
  53#include <linux/time.h>
  54#include <linux/wait.h>
  55#include <sound/core.h>
  56#include <sound/snd_wavefront.h>
  57
  58static inline int 
  59wf_mpu_status (snd_wavefront_midi_t *midi)
  60
  61{
  62        return inb (midi->mpu_status_port);
  63}
  64
  65static inline int 
  66input_avail (snd_wavefront_midi_t *midi)
  67
  68{
  69        return !(wf_mpu_status(midi) & INPUT_AVAIL);
  70}
  71
  72static inline int
  73output_ready (snd_wavefront_midi_t *midi)
  74
  75{
  76        return !(wf_mpu_status(midi) & OUTPUT_READY);
  77}
  78
  79static inline int 
  80read_data (snd_wavefront_midi_t *midi)
  81
  82{
  83        return inb (midi->mpu_data_port);
  84}
  85
  86static inline void 
  87write_data (snd_wavefront_midi_t *midi, unsigned char byte)
  88
  89{
  90        outb (byte, midi->mpu_data_port);
  91}
  92
  93static snd_wavefront_midi_t *
  94get_wavefront_midi (snd_rawmidi_substream_t *substream)
  95
  96{
  97        snd_card_t *card;
  98        snd_wavefront_card_t *acard;
  99
 100        if (substream == NULL || substream->rmidi == NULL) 
 101                return NULL;
 102
 103        card = substream->rmidi->card;
 104
 105        if (card == NULL) 
 106                return NULL;
 107
 108        if (card->private_data == NULL) 
 109                return NULL;
 110
 111        acard = card->private_data;
 112
 113        return &acard->wavefront.midi;
 114}
 115
 116static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
 117{
 118        snd_wavefront_midi_t *midi = &card->wavefront.midi;
 119        snd_wavefront_mpu_id  mpu;
 120        unsigned long flags;
 121        unsigned char midi_byte;
 122        int max = 256, mask = 1;
 123        int timeout;
 124
 125        /* Its not OK to try to change the status of "virtuality" of
 126           the MIDI interface while we're outputting stuff.  See
 127           snd_wavefront_midi_{enable,disable}_virtual () for the
 128           other half of this.  
 129
 130           The first loop attempts to flush any data from the
 131           current output device, and then the second 
 132           emits the switch byte (if necessary), and starts
 133           outputting data for the output device currently in use.
 134        */
 135
 136        if (midi->substream_output[midi->output_mpu] == NULL) {
 137                goto __second;
 138        }
 139
 140        while (max > 0) {
 141
 142                /* XXX fix me - no hard timing loops allowed! */
 143
 144                for (timeout = 30000; timeout > 0; timeout--) {
 145                        if (output_ready (midi))
 146                                break;
 147                }
 148        
 149                spin_lock_irqsave (&midi->virtual, flags);
 150                if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) {
 151                        spin_unlock_irqrestore (&midi->virtual, flags);
 152                        goto __second;
 153                }
 154                if (output_ready (midi)) {
 155                        if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) {
 156                                if (!midi->isvirtual ||
 157                                        (midi_byte != WF_INTERNAL_SWITCH &&
 158                                         midi_byte != WF_EXTERNAL_SWITCH))
 159                                        write_data(midi, midi_byte);
 160                                max--;
 161                        } else {
 162                                if (midi->istimer) {
 163                                        if (--midi->istimer <= 0)
 164                                                del_timer(&midi->timer);
 165                                }
 166                                midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
 167                                spin_unlock_irqrestore (&midi->virtual, flags);
 168                                goto __second;
 169                        }
 170                } else {
 171                        spin_unlock_irqrestore (&midi->virtual, flags);
 172                        return;
 173                }
 174                spin_unlock_irqrestore (&midi->virtual, flags);
 175        }
 176
 177      __second:
 178
 179        if (midi->substream_output[!midi->output_mpu] == NULL) {
 180                return;
 181        }
 182
 183        while (max > 0) {
 184
 185                /* XXX fix me - no hard timing loops allowed! */
 186
 187                for (timeout = 30000; timeout > 0; timeout--) {
 188                        if (output_ready (midi))
 189                                break;
 190                }
 191        
 192                spin_lock_irqsave (&midi->virtual, flags);
 193                if (!midi->isvirtual)
 194                        mask = 0;
 195                mpu = midi->output_mpu ^ mask;
 196                mask = 0;       /* don't invert the value from now */
 197                if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) {
 198                        spin_unlock_irqrestore (&midi->virtual, flags);
 199                        return;
 200                }
 201                if (snd_rawmidi_transmit_empty(midi->substream_output[mpu]))
 202                        goto __timer;
 203                if (output_ready (midi)) {
 204                        if (mpu != midi->output_mpu) {
 205                                write_data(midi, mpu == internal_mpu ?
 206                                                        WF_INTERNAL_SWITCH :
 207                                                        WF_EXTERNAL_SWITCH);
 208                                midi->output_mpu = mpu;
 209                        } else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) {
 210                                if (!midi->isvirtual ||
 211                                        (midi_byte != WF_INTERNAL_SWITCH &&
 212                                         midi_byte != WF_EXTERNAL_SWITCH))
 213                                        write_data(midi, midi_byte);
 214                                max--;
 215                        } else {
 216                              __timer:
 217                                if (midi->istimer) {
 218                                        if (--midi->istimer <= 0)
 219                                                del_timer(&midi->timer);
 220                                }
 221                                midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
 222                                spin_unlock_irqrestore (&midi->virtual, flags);
 223                                return;
 224                        }
 225                } else {
 226                        spin_unlock_irqrestore (&midi->virtual, flags);
 227                        return;
 228                }
 229                spin_unlock_irqrestore (&midi->virtual, flags);
 230        }
 231}
 232
 233static int snd_wavefront_midi_input_open(snd_rawmidi_substream_t * substream)
 234{
 235        unsigned long flags;
 236        snd_wavefront_midi_t *midi;
 237        snd_wavefront_mpu_id mpu;
 238
 239        snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
 240        snd_assert(substream->rmidi->private_data != NULL, return -EIO);
 241
 242        mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
 243
 244        if ((midi = get_wavefront_midi (substream)) == NULL)
 245                return -EIO;
 246
 247        spin_lock_irqsave (&midi->open, flags);
 248        midi->mode[mpu] |= MPU401_MODE_INPUT;
 249        midi->substream_input[mpu] = substream;
 250        spin_unlock_irqrestore (&midi->open, flags);
 251
 252        return 0;
 253}
 254
 255static int snd_wavefront_midi_output_open(snd_rawmidi_substream_t * substream)
 256{
 257        unsigned long flags;
 258        snd_wavefront_midi_t *midi;
 259        snd_wavefront_mpu_id mpu;
 260
 261        snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
 262        snd_assert(substream->rmidi->private_data != NULL, return -EIO);
 263
 264        mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
 265
 266        if ((midi = get_wavefront_midi (substream)) == NULL)
 267                return -EIO;
 268
 269        spin_lock_irqsave (&midi->open, flags);
 270        midi->mode[mpu] |= MPU401_MODE_OUTPUT;
 271        midi->substream_output[mpu] = substream;
 272        spin_unlock_irqrestore (&midi->open, flags);
 273
 274        return 0;
 275}
 276
 277static int snd_wavefront_midi_input_close(snd_rawmidi_substream_t * substream)
 278{
 279        unsigned long flags;
 280        snd_wavefront_midi_t *midi;
 281        snd_wavefront_mpu_id mpu;
 282
 283        snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
 284        snd_assert(substream->rmidi->private_data != NULL, return -EIO);
 285
 286        mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
 287
 288        if ((midi = get_wavefront_midi (substream)) == NULL)
 289                return -EIO;
 290
 291        spin_lock_irqsave (&midi->open, flags);
 292        midi->mode[mpu] &= ~MPU401_MODE_INPUT;
 293        spin_unlock_irqrestore (&midi->open, flags);
 294
 295        return 0;
 296}
 297
 298static int snd_wavefront_midi_output_close(snd_rawmidi_substream_t * substream)
 299{
 300        unsigned long flags;
 301        snd_wavefront_midi_t *midi;
 302        snd_wavefront_mpu_id mpu;
 303
 304        snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
 305        snd_assert(substream->rmidi->private_data != NULL, return -EIO);
 306
 307        mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
 308
 309        if ((midi = get_wavefront_midi (substream)) == NULL)
 310                return -EIO;
 311
 312        spin_lock_irqsave (&midi->open, flags);
 313        midi->mode[mpu] &= ~MPU401_MODE_OUTPUT;
 314        spin_unlock_irqrestore (&midi->open, flags);
 315        return 0;
 316}
 317
 318static void snd_wavefront_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
 319{
 320        unsigned long flags;
 321        snd_wavefront_midi_t *midi;
 322        snd_wavefront_mpu_id mpu;
 323
 324        if (substream == NULL || substream->rmidi == NULL) 
 325                return;
 326
 327        if (substream->rmidi->private_data == NULL)
 328                return;
 329
 330        mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
 331
 332        if ((midi = get_wavefront_midi (substream)) == NULL) {
 333                return;
 334        }
 335
 336        spin_lock_irqsave (&midi->virtual, flags);
 337        if (up) {
 338                midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER;
 339        } else {
 340                midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER;
 341        }
 342        spin_unlock_irqrestore (&midi->virtual, flags);
 343}
 344
 345static void snd_wavefront_midi_output_timer(unsigned long data)
 346{
 347        snd_wavefront_card_t *card = (snd_wavefront_card_t *)data;
 348        snd_wavefront_midi_t *midi = &card->wavefront.midi;
 349        unsigned long flags;
 350        
 351        spin_lock_irqsave (&midi->virtual, flags);
 352        midi->timer.expires = 1 + jiffies;
 353        add_timer(&midi->timer);
 354        spin_unlock_irqrestore (&midi->virtual, flags);
 355        snd_wavefront_midi_output_write(card);
 356}
 357
 358static void snd_wavefront_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
 359{
 360        unsigned long flags;
 361        snd_wavefront_midi_t *midi;
 362        snd_wavefront_mpu_id mpu;
 363
 364        if (substream == NULL || substream->rmidi == NULL) 
 365                return;
 366
 367        if (substream->rmidi->private_data == NULL)
 368                return;
 369
 370        mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
 371
 372        if ((midi = get_wavefront_midi (substream)) == NULL) {
 373                return;
 374        }
 375
 376        spin_lock_irqsave (&midi->virtual, flags);
 377        if (up) {
 378                if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) {
 379                        if (!midi->istimer) {
 380                                init_timer(&midi->timer);
 381                                midi->timer.function = snd_wavefront_midi_output_timer;
 382                                midi->timer.data = (unsigned long) substream->rmidi->card->private_data;
 383                                midi->timer.expires = 1 + jiffies;
 384                                add_timer(&midi->timer);
 385                        }
 386                        midi->istimer++;
 387                        midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;
 388                }
 389        } else {
 390                midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
 391        }
 392        spin_unlock_irqrestore (&midi->virtual, flags);
 393
 394        if (up)
 395                snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data);
 396}
 397
 398void
 399snd_wavefront_midi_interrupt (snd_wavefront_card_t *card)
 400
 401{
 402        unsigned long flags;
 403        snd_wavefront_midi_t *midi;
 404        static snd_rawmidi_substream_t *substream = NULL;
 405        static int mpu = external_mpu; 
 406        int max = 128;
 407        unsigned char byte;
 408
 409        midi = &card->wavefront.midi;
 410
 411        if (!input_avail (midi)) { /* not for us */
 412                snd_wavefront_midi_output_write(card);
 413                return;
 414        }
 415
 416        while (--max) {
 417                spin_lock_irqsave (&midi->virtual, flags);
 418
 419                if (input_avail (midi)) {
 420                        byte = read_data (midi);
 421
 422                        if (midi->isvirtual) {                          
 423                                if (byte == WF_EXTERNAL_SWITCH) {
 424                                        substream = midi->substream_input[external_mpu];
 425                                        mpu = external_mpu;
 426                                } else if (byte == WF_INTERNAL_SWITCH) { 
 427                                        substream = midi->substream_output[internal_mpu];
 428                                        mpu = internal_mpu;
 429                                } /* else just leave it as it is */
 430                        } else {
 431                                substream = midi->substream_input[internal_mpu];
 432                                mpu = internal_mpu;
 433                        }
 434
 435                        if (substream == NULL) {
 436                                spin_unlock_irqrestore (&midi->virtual, flags);
 437                                continue;
 438                        }
 439
 440                        if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {
 441                                spin_unlock_irqrestore (&midi->virtual, flags);
 442                                snd_rawmidi_receive(substream, &byte, 1);
 443                        } else {
 444                                spin_unlock_irqrestore (&midi->virtual, flags);
 445                        }
 446                } else {
 447                        spin_unlock_irqrestore (&midi->virtual, flags);
 448                        break;
 449                }
 450        } 
 451
 452        snd_wavefront_midi_output_write(card);
 453}
 454
 455void
 456snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card)
 457
 458{
 459        unsigned long flags;
 460
 461        spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
 462        card->wavefront.midi.isvirtual = 1;
 463        card->wavefront.midi.output_mpu = internal_mpu;
 464        card->wavefront.midi.input_mpu = internal_mpu;
 465        spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
 466}
 467
 468void
 469snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)
 470
 471{
 472        unsigned long flags;
 473
 474        spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
 475        // snd_wavefront_midi_input_close (card->ics2115_external_rmidi);
 476        // snd_wavefront_midi_output_close (card->ics2115_external_rmidi);
 477        card->wavefront.midi.isvirtual = 0;
 478        spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
 479}
 480
 481int __init
 482snd_wavefront_midi_start (snd_wavefront_card_t *card)
 483
 484{
 485        int ok, i;
 486        unsigned char rbuf[4], wbuf[4];
 487        snd_wavefront_t *dev;
 488        snd_wavefront_midi_t *midi;
 489
 490        dev = &card->wavefront;
 491        midi = &dev->midi;
 492
 493        /* The ICS2115 MPU-401 interface doesn't do anything
 494           until its set into UART mode.
 495        */
 496
 497        /* XXX fix me - no hard timing loops allowed! */
 498
 499        for (i = 0; i < 30000 && !output_ready (midi); i++);
 500
 501        if (!output_ready (midi)) {
 502                snd_printk ("MIDI interface not ready for command\n");
 503                return -1;
 504        }
 505
 506        /* Any interrupts received from now on
 507           are owned by the MIDI side of things.
 508        */
 509
 510        dev->interrupts_are_midi = 1;
 511        
 512        outb (UART_MODE_ON, midi->mpu_command_port);
 513
 514        for (ok = 0, i = 50000; i > 0 && !ok; i--) {
 515                if (input_avail (midi)) {
 516                        if (read_data (midi) == MPU_ACK) {
 517                                ok = 1;
 518                                break;
 519                        }
 520                }
 521        }
 522
 523        if (!ok) {
 524                snd_printk ("cannot set UART mode for MIDI interface");
 525                dev->interrupts_are_midi = 0;
 526                return -1;
 527        }
 528
 529        /* Route external MIDI to WaveFront synth (by default) */
 530    
 531        if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) {
 532                snd_printk ("can't enable MIDI-IN-2-synth routing.\n");
 533                /* XXX error ? */
 534        }
 535
 536        /* Turn on Virtual MIDI, but first *always* turn it off,
 537           since otherwise consectutive reloads of the driver will
 538           never cause the hardware to generate the initial "internal" or 
 539           "external" source bytes in the MIDI data stream. This
 540           is pretty important, since the internal hardware generally will
 541           be used to generate none or very little MIDI output, and
 542           thus the only source of MIDI data is actually external. Without
 543           the switch bytes, the driver will think it all comes from
 544           the internal interface. Duh.
 545        */
 546
 547        if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) { 
 548                snd_printk ("virtual MIDI mode not disabled\n");
 549                return 0; /* We're OK, but missing the external MIDI dev */
 550        }
 551
 552        snd_wavefront_midi_enable_virtual (card);
 553
 554        if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) {
 555                snd_printk ("cannot enable virtual MIDI mode.\n");
 556                snd_wavefront_midi_disable_virtual (card);
 557        } 
 558        return 0;
 559}
 560
 561snd_rawmidi_ops_t snd_wavefront_midi_output =
 562{
 563        .open =         snd_wavefront_midi_output_open,
 564        .close =        snd_wavefront_midi_output_close,
 565        .trigger =      snd_wavefront_midi_output_trigger,
 566};
 567
 568snd_rawmidi_ops_t snd_wavefront_midi_input =
 569{
 570        .open =         snd_wavefront_midi_input_open,
 571        .close =        snd_wavefront_midi_input_close,
 572        .trigger =      snd_wavefront_midi_input_trigger,
 573};
 574
 575
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.