mirror of
https://github.com/torvalds/linux.git
synced 2024-11-17 09:31:50 +00:00
9c2e08c592
Many struct file_operations in the kernel can be "const". Marking them const moves these to the .rodata section, which avoids false sharing with potential dirty data. In addition it'll catch accidental writes at compile time to these shared resources. Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
615 lines
14 KiB
C
615 lines
14 KiB
C
/*
|
|
**********************************************************************
|
|
* midi.c - /dev/midi interface for emu10k1 driver
|
|
* Copyright 1999, 2000 Creative Labs, Inc.
|
|
*
|
|
**********************************************************************
|
|
*
|
|
* Date Author Summary of changes
|
|
* ---- ------ ------------------
|
|
* October 20, 1999 Bertrand Lee base code release
|
|
*
|
|
**********************************************************************
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program; if not, write to the Free
|
|
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
|
|
* USA.
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/smp_lock.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
#include "hwaccess.h"
|
|
#include "cardmo.h"
|
|
#include "cardmi.h"
|
|
#include "midi.h"
|
|
|
|
#ifdef EMU10K1_SEQUENCER
|
|
#include "../sound_config.h"
|
|
#endif
|
|
|
|
static DEFINE_SPINLOCK(midi_spinlock);
|
|
|
|
static void init_midi_hdr(struct midi_hdr *midihdr)
|
|
{
|
|
midihdr->bufferlength = MIDIIN_BUFLEN;
|
|
midihdr->bytesrecorded = 0;
|
|
midihdr->flags = 0;
|
|
}
|
|
|
|
static int midiin_add_buffer(struct emu10k1_mididevice *midi_dev, struct midi_hdr **midihdrptr)
|
|
{
|
|
struct midi_hdr *midihdr;
|
|
|
|
if ((midihdr = kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL) {
|
|
ERROR();
|
|
return -EINVAL;
|
|
}
|
|
|
|
init_midi_hdr(midihdr);
|
|
|
|
midihdr->data = kmalloc(MIDIIN_BUFLEN, GFP_KERNEL);
|
|
if (!midihdr->data) {
|
|
ERROR();
|
|
kfree(midihdr);
|
|
return -1;
|
|
}
|
|
|
|
if (emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr) < 0) {
|
|
ERROR();
|
|
kfree(midihdr->data);
|
|
kfree(midihdr);
|
|
return -1;
|
|
}
|
|
|
|
*midihdrptr = midihdr;
|
|
list_add_tail(&midihdr->list, &midi_dev->mid_hdrs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int emu10k1_midi_open(struct inode *inode, struct file *file)
|
|
{
|
|
int minor = iminor(inode);
|
|
struct emu10k1_card *card = NULL;
|
|
struct emu10k1_mididevice *midi_dev;
|
|
struct list_head *entry;
|
|
|
|
DPF(2, "emu10k1_midi_open()\n");
|
|
|
|
/* Check for correct device to open */
|
|
list_for_each(entry, &emu10k1_devs) {
|
|
card = list_entry(entry, struct emu10k1_card, list);
|
|
|
|
if (card->midi_dev == minor)
|
|
goto match;
|
|
}
|
|
|
|
return -ENODEV;
|
|
|
|
match:
|
|
#ifdef EMU10K1_SEQUENCER
|
|
if (card->seq_mididev) /* card is opened by sequencer */
|
|
return -EBUSY;
|
|
#endif
|
|
|
|
/* Wait for device to become free */
|
|
mutex_lock(&card->open_sem);
|
|
while (card->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
|
|
if (file->f_flags & O_NONBLOCK) {
|
|
mutex_unlock(&card->open_sem);
|
|
return -EBUSY;
|
|
}
|
|
|
|
mutex_unlock(&card->open_sem);
|
|
interruptible_sleep_on(&card->open_wait);
|
|
|
|
if (signal_pending(current)) {
|
|
return -ERESTARTSYS;
|
|
}
|
|
|
|
mutex_lock(&card->open_sem);
|
|
}
|
|
|
|
if ((midi_dev = kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL)
|
|
return -EINVAL;
|
|
|
|
midi_dev->card = card;
|
|
midi_dev->mistate = MIDIIN_STATE_STOPPED;
|
|
init_waitqueue_head(&midi_dev->oWait);
|
|
init_waitqueue_head(&midi_dev->iWait);
|
|
midi_dev->ird = 0;
|
|
midi_dev->iwr = 0;
|
|
midi_dev->icnt = 0;
|
|
INIT_LIST_HEAD(&midi_dev->mid_hdrs);
|
|
|
|
if (file->f_mode & FMODE_READ) {
|
|
struct midi_openinfo dsCardMidiOpenInfo;
|
|
struct midi_hdr *midihdr1;
|
|
struct midi_hdr *midihdr2;
|
|
|
|
dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev;
|
|
|
|
if (emu10k1_mpuin_open(card, &dsCardMidiOpenInfo) < 0) {
|
|
ERROR();
|
|
kfree(midi_dev);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Add two buffers to receive sysex buffer */
|
|
if (midiin_add_buffer(midi_dev, &midihdr1) < 0) {
|
|
kfree(midi_dev);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (midiin_add_buffer(midi_dev, &midihdr2) < 0) {
|
|
list_del(&midihdr1->list);
|
|
kfree(midihdr1->data);
|
|
kfree(midihdr1);
|
|
kfree(midi_dev);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
if (file->f_mode & FMODE_WRITE) {
|
|
struct midi_openinfo dsCardMidiOpenInfo;
|
|
|
|
dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev;
|
|
|
|
if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) {
|
|
ERROR();
|
|
kfree(midi_dev);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
file->private_data = (void *) midi_dev;
|
|
|
|
card->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
|
|
|
|
mutex_unlock(&card->open_sem);
|
|
|
|
return nonseekable_open(inode, file);
|
|
}
|
|
|
|
static int emu10k1_midi_release(struct inode *inode, struct file *file)
|
|
{
|
|
struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
|
|
struct emu10k1_card *card;
|
|
|
|
lock_kernel();
|
|
|
|
card = midi_dev->card;
|
|
DPF(2, "emu10k1_midi_release()\n");
|
|
|
|
if (file->f_mode & FMODE_WRITE) {
|
|
if (!(file->f_flags & O_NONBLOCK)) {
|
|
|
|
while (!signal_pending(current) && (card->mpuout->firstmidiq != NULL)) {
|
|
DPF(4, "Cannot close - buffers not empty\n");
|
|
|
|
interruptible_sleep_on(&midi_dev->oWait);
|
|
|
|
}
|
|
}
|
|
|
|
emu10k1_mpuout_close(card);
|
|
}
|
|
|
|
if (file->f_mode & FMODE_READ) {
|
|
struct midi_hdr *midihdr;
|
|
|
|
if (midi_dev->mistate == MIDIIN_STATE_STARTED) {
|
|
emu10k1_mpuin_stop(card);
|
|
midi_dev->mistate = MIDIIN_STATE_STOPPED;
|
|
}
|
|
|
|
emu10k1_mpuin_reset(card);
|
|
emu10k1_mpuin_close(card);
|
|
|
|
while (!list_empty(&midi_dev->mid_hdrs)) {
|
|
midihdr = list_entry(midi_dev->mid_hdrs.next, struct midi_hdr, list);
|
|
|
|
list_del(midi_dev->mid_hdrs.next);
|
|
kfree(midihdr->data);
|
|
kfree(midihdr);
|
|
}
|
|
}
|
|
|
|
kfree(midi_dev);
|
|
|
|
mutex_lock(&card->open_sem);
|
|
card->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE));
|
|
mutex_unlock(&card->open_sem);
|
|
wake_up_interruptible(&card->open_wait);
|
|
|
|
unlock_kernel();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t emu10k1_midi_read(struct file *file, char __user *buffer, size_t count, loff_t * pos)
|
|
{
|
|
struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
|
|
ssize_t ret = 0;
|
|
u16 cnt;
|
|
unsigned long flags;
|
|
|
|
DPD(4, "emu10k1_midi_read(), count %#x\n", (u32) count);
|
|
|
|
if (!access_ok(VERIFY_WRITE, buffer, count))
|
|
return -EFAULT;
|
|
|
|
if (midi_dev->mistate == MIDIIN_STATE_STOPPED) {
|
|
if (emu10k1_mpuin_start(midi_dev->card) < 0) {
|
|
ERROR();
|
|
return -EINVAL;
|
|
}
|
|
|
|
midi_dev->mistate = MIDIIN_STATE_STARTED;
|
|
}
|
|
|
|
while (count > 0) {
|
|
cnt = MIDIIN_BUFLEN - midi_dev->ird;
|
|
|
|
spin_lock_irqsave(&midi_spinlock, flags);
|
|
|
|
if (midi_dev->icnt < cnt)
|
|
cnt = midi_dev->icnt;
|
|
|
|
spin_unlock_irqrestore(&midi_spinlock, flags);
|
|
|
|
if (cnt > count)
|
|
cnt = count;
|
|
|
|
if (cnt <= 0) {
|
|
if (file->f_flags & O_NONBLOCK)
|
|
return ret ? ret : -EAGAIN;
|
|
DPF(2, " Go to sleep...\n");
|
|
|
|
interruptible_sleep_on(&midi_dev->iWait);
|
|
|
|
if (signal_pending(current))
|
|
return ret ? ret : -ERESTARTSYS;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (copy_to_user(buffer, midi_dev->iBuf + midi_dev->ird, cnt)) {
|
|
ERROR();
|
|
return ret ? ret : -EFAULT;
|
|
}
|
|
|
|
midi_dev->ird += cnt;
|
|
midi_dev->ird %= MIDIIN_BUFLEN;
|
|
|
|
spin_lock_irqsave(&midi_spinlock, flags);
|
|
|
|
midi_dev->icnt -= cnt;
|
|
|
|
spin_unlock_irqrestore(&midi_spinlock, flags);
|
|
|
|
count -= cnt;
|
|
buffer += cnt;
|
|
ret += cnt;
|
|
|
|
if (midi_dev->icnt == 0)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t emu10k1_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t * pos)
|
|
{
|
|
struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
|
|
struct midi_hdr *midihdr;
|
|
unsigned long flags;
|
|
|
|
DPD(4, "emu10k1_midi_write(), count=%#x\n", (u32) count);
|
|
|
|
if (!access_ok(VERIFY_READ, buffer, count))
|
|
return -EFAULT;
|
|
|
|
if ((midihdr = kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL)
|
|
return -EINVAL;
|
|
|
|
midihdr->bufferlength = count;
|
|
midihdr->bytesrecorded = 0;
|
|
midihdr->flags = 0;
|
|
|
|
midihdr->data = kmalloc(count, GFP_KERNEL);
|
|
if (!midihdr->data) {
|
|
ERROR();
|
|
kfree(midihdr);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (copy_from_user(midihdr->data, buffer, count)) {
|
|
kfree(midihdr->data);
|
|
kfree(midihdr);
|
|
return -EFAULT;
|
|
}
|
|
|
|
spin_lock_irqsave(&midi_spinlock, flags);
|
|
|
|
if (emu10k1_mpuout_add_buffer(midi_dev->card, midihdr) < 0) {
|
|
ERROR();
|
|
kfree(midihdr->data);
|
|
kfree(midihdr);
|
|
spin_unlock_irqrestore(&midi_spinlock, flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&midi_spinlock, flags);
|
|
|
|
return count;
|
|
}
|
|
|
|
static unsigned int emu10k1_midi_poll(struct file *file, struct poll_table_struct *wait)
|
|
{
|
|
struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
|
|
unsigned long flags;
|
|
unsigned int mask = 0;
|
|
|
|
DPF(4, "emu10k1_midi_poll() called\n");
|
|
|
|
if (file->f_mode & FMODE_WRITE)
|
|
poll_wait(file, &midi_dev->oWait, wait);
|
|
|
|
if (file->f_mode & FMODE_READ)
|
|
poll_wait(file, &midi_dev->iWait, wait);
|
|
|
|
spin_lock_irqsave(&midi_spinlock, flags);
|
|
|
|
if (file->f_mode & FMODE_WRITE)
|
|
mask |= POLLOUT | POLLWRNORM;
|
|
|
|
if (file->f_mode & FMODE_READ) {
|
|
if (midi_dev->mistate == MIDIIN_STATE_STARTED)
|
|
if (midi_dev->icnt > 0)
|
|
mask |= POLLIN | POLLRDNORM;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&midi_spinlock, flags);
|
|
|
|
return mask;
|
|
}
|
|
|
|
int emu10k1_midi_callback(unsigned long msg, unsigned long refdata, unsigned long *pmsg)
|
|
{
|
|
struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) refdata;
|
|
struct midi_hdr *midihdr = NULL;
|
|
unsigned long flags;
|
|
int i;
|
|
|
|
DPF(4, "emu10k1_midi_callback()\n");
|
|
|
|
spin_lock_irqsave(&midi_spinlock, flags);
|
|
|
|
switch (msg) {
|
|
case ICARDMIDI_OUTLONGDATA:
|
|
midihdr = (struct midi_hdr *) pmsg[2];
|
|
|
|
kfree(midihdr->data);
|
|
kfree(midihdr);
|
|
wake_up_interruptible(&midi_dev->oWait);
|
|
|
|
break;
|
|
|
|
case ICARDMIDI_INLONGDATA:
|
|
midihdr = (struct midi_hdr *) pmsg[2];
|
|
|
|
for (i = 0; i < midihdr->bytesrecorded; i++) {
|
|
midi_dev->iBuf[midi_dev->iwr++] = midihdr->data[i];
|
|
midi_dev->iwr %= MIDIIN_BUFLEN;
|
|
}
|
|
|
|
midi_dev->icnt += midihdr->bytesrecorded;
|
|
|
|
if (midi_dev->mistate == MIDIIN_STATE_STARTED) {
|
|
init_midi_hdr(midihdr);
|
|
emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr);
|
|
wake_up_interruptible(&midi_dev->iWait);
|
|
}
|
|
break;
|
|
|
|
case ICARDMIDI_INDATA:
|
|
{
|
|
u8 *pBuf = (u8 *) & pmsg[1];
|
|
u16 bytesvalid = pmsg[2];
|
|
|
|
for (i = 0; i < bytesvalid; i++) {
|
|
midi_dev->iBuf[midi_dev->iwr++] = pBuf[i];
|
|
midi_dev->iwr %= MIDIIN_BUFLEN;
|
|
}
|
|
|
|
midi_dev->icnt += bytesvalid;
|
|
}
|
|
|
|
wake_up_interruptible(&midi_dev->iWait);
|
|
break;
|
|
|
|
default: /* Unknown message */
|
|
spin_unlock_irqrestore(&midi_spinlock, flags);
|
|
return -1;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&midi_spinlock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* MIDI file operations */
|
|
const struct file_operations emu10k1_midi_fops = {
|
|
.owner = THIS_MODULE,
|
|
.read = emu10k1_midi_read,
|
|
.write = emu10k1_midi_write,
|
|
.poll = emu10k1_midi_poll,
|
|
.open = emu10k1_midi_open,
|
|
.release = emu10k1_midi_release,
|
|
};
|
|
|
|
|
|
#ifdef EMU10K1_SEQUENCER
|
|
|
|
/* functions used for sequencer access */
|
|
|
|
int emu10k1_seq_midi_open(int dev, int mode,
|
|
void (*input) (int dev, unsigned char data),
|
|
void (*output) (int dev))
|
|
{
|
|
struct emu10k1_card *card;
|
|
struct midi_openinfo dsCardMidiOpenInfo;
|
|
struct emu10k1_mididevice *midi_dev;
|
|
|
|
if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
|
|
return -EINVAL;
|
|
|
|
card = midi_devs[dev]->devc;
|
|
|
|
if (card->open_mode) /* card is opened native */
|
|
return -EBUSY;
|
|
|
|
DPF(2, "emu10k1_seq_midi_open()\n");
|
|
|
|
if ((midi_dev = kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL)
|
|
return -EINVAL;
|
|
|
|
midi_dev->card = card;
|
|
midi_dev->mistate = MIDIIN_STATE_STOPPED;
|
|
init_waitqueue_head(&midi_dev->oWait);
|
|
init_waitqueue_head(&midi_dev->iWait);
|
|
midi_dev->ird = 0;
|
|
midi_dev->iwr = 0;
|
|
midi_dev->icnt = 0;
|
|
INIT_LIST_HEAD(&midi_dev->mid_hdrs);
|
|
|
|
dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev;
|
|
|
|
if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) {
|
|
ERROR();
|
|
return -ENODEV;
|
|
}
|
|
|
|
card->seq_mididev = midi_dev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void emu10k1_seq_midi_close(int dev)
|
|
{
|
|
struct emu10k1_card *card;
|
|
|
|
DPF(2, "emu10k1_seq_midi_close()\n");
|
|
if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
|
|
return;
|
|
|
|
card = midi_devs[dev]->devc;
|
|
emu10k1_mpuout_close(card);
|
|
|
|
kfree(card->seq_mididev);
|
|
card->seq_mididev = NULL;
|
|
}
|
|
|
|
int emu10k1_seq_midi_out(int dev, unsigned char midi_byte)
|
|
{
|
|
struct emu10k1_card *card;
|
|
struct midi_hdr *midihdr;
|
|
unsigned long flags;
|
|
|
|
if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
|
|
return -EINVAL;
|
|
|
|
card = midi_devs[dev]->devc;
|
|
|
|
if ((midihdr = kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL)
|
|
return -EINVAL;
|
|
|
|
midihdr->bufferlength = 1;
|
|
midihdr->bytesrecorded = 0;
|
|
midihdr->flags = 0;
|
|
|
|
midihdr->data = kmalloc(1, GFP_KERNEL);
|
|
if (!midihdr->data) {
|
|
ERROR();
|
|
kfree(midihdr);
|
|
return -EINVAL;
|
|
}
|
|
|
|
*(midihdr->data) = midi_byte;
|
|
|
|
spin_lock_irqsave(&midi_spinlock, flags);
|
|
|
|
if (emu10k1_mpuout_add_buffer(card, midihdr) < 0) {
|
|
ERROR();
|
|
kfree(midihdr->data);
|
|
kfree(midihdr);
|
|
spin_unlock_irqrestore(&midi_spinlock, flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&midi_spinlock, flags);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int emu10k1_seq_midi_start_read(int dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int emu10k1_seq_midi_end_read(int dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void emu10k1_seq_midi_kick(int dev)
|
|
{
|
|
}
|
|
|
|
int emu10k1_seq_midi_buffer_status(int dev)
|
|
{
|
|
int count;
|
|
struct midi_queue *queue;
|
|
struct emu10k1_card *card;
|
|
|
|
if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
|
|
return -EINVAL;
|
|
|
|
count = 0;
|
|
|
|
card = midi_devs[dev]->devc;
|
|
queue = card->mpuout->firstmidiq;
|
|
|
|
while (queue != NULL) {
|
|
count++;
|
|
if (queue == card->mpuout->lastmidiq)
|
|
break;
|
|
|
|
queue = queue->next;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
#endif
|
|
|