/*
* sound/oss/midibuf.c
*
* Device file manager for /dev/midi#
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
/*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
*/
#include <linux/stddef.h>
#include <linux/kmod.h>
#include <linux/spinlock.h>
#define MIDIBUF_C
#include "sound_config.h"
/*
* Don't make MAX_QUEUE_SIZE larger than 4000
*/
#define MAX_QUEUE_SIZE 4000
static wait_queue_head_t midi_sleeper[MAX_MIDI_DEV];
static wait_queue_head_t input_sleeper[MAX_MIDI_DEV];
struct midi_buf
{
int len, head, tail;
unsigned char queue[MAX_QUEUE_SIZE];
};
struct midi_parms
{
long prech_timeout; /*
* Timeout before the first ch
*/
};
static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL};
static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL};
static struct midi_parms parms[MAX_MIDI_DEV];
static void midi_poll(unsigned long dummy);
static DEFINE_TIMER(poll_timer, midi_poll, 0, 0);
static volatile int open_devs;
static DEFINE_SPINLOCK(lock);
#define DATA_AVAIL(q) (q->len)
#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len)
#define QUEUE_BYTE(q, data) \
if (SPACE_AVAIL(q)) \
{ \
unsigned long flags; \
spin_lock_irqsave(&lock, flags); \
q->queue[q->tail] = (data); \
q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \
spin_unlock_irqrestore(&lock, flags); \
}
#define REMOVE_BYTE(q, data) \
if (DATA_AVAIL(q)) \
{ \
unsigned long flags; \
spin_lock_irqsave(&lock, flags); \
data = q->queue[q->head]; \
q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \
spin_unlock_irqrestore(&lock, flags); \
}
static void drain_midi_queue(int dev)
{
/*
* Give the Midi driver time to drain its output queues
*/
if (midi_devs[dev]->buffer_status != NULL)
while (!signal_pending(current) && midi_devs[dev]->buffer_status(dev))
interruptible_sleep_on_timeout(&midi_sleeper[dev],
HZ/10);
}
static void midi_input_intr(int dev, unsigned char data)
{
if (midi_in_buf[dev] == NULL)
return;
if (data == 0xfe) /*
* Active sensing
*/
return; /*
* Ignore
*/
if (SPACE_AVAIL(midi_in_buf[dev])) {
QUEUE_BYTE(midi_in_buf[dev], data);
wake_up(&input_sleeper[dev]);
}
}
static void midi_output_intr(int dev)
{
/*
* Currently NOP
*/
}
static void midi_poll(unsigned long dummy)
{
unsigned long flags;
int dev;
spin_lock_irqsave(&lock, flags);
if (open_devs)
{
for (dev = 0; dev < num_midis; dev++)
if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
{
while (DATA_AVAIL(midi_out_buf[dev]))
{
int ok;
int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
spin_unlock_irqrestore(&lock,flags);/* Give some time to others */
ok = midi_devs[dev]->outputc(dev, c);
spin_lock_irqsave(&lock, flags);
if (!ok)
break;
midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
midi_out_buf[dev]->len--;
}
if (DATA_AVAIL(midi_out_buf[dev]) < 100)
wake_up(&midi_sleeper[dev]);
}
poll_timer.expires = (1) + jiffies;
add_timer(&poll_timer);
/*
* Come back later
*/
}
spin_unlock_irqrestore(&lock, flags);
}
int MIDIbuf_open(int dev, struct file *file)
{
int mode, err;
dev = dev >> 4;
mode = translate_mode(file);
if (num_midis > MAX_MIDI_DEV)
{
printk(KERN_ERR "midi: Too many midi interfaces\n");
num_midis = MAX_MIDI_DEV;
}
if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
return -ENXIO;
/*
* Interrupts disabled. Be careful
*/
module_put(midi_devs[dev]->owner);
if ((err = midi_devs[dev]->open(dev, mode,
midi_input_intr, midi_output_intr)) < 0)
return err;
parms[dev].prech_timeout = MAX_SCHEDULE_TIMEOUT;
midi_in_buf[dev] = vmalloc(sizeof(struct midi_buf));
if (midi_in_buf[dev] == NULL)
{
printk(KERN_WARNING "midi: Can't allocate buffer\n");
midi_devs[dev]->close(dev);
return -EIO;
}
midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;