* Copyright 2003-2006, Haiku.
* Distributed under the terms of the MIT License.
*
* A module driver for the generic mpu401 midi interface.
*
* Author:
* Greg Crain (gsc70@comcast.net)
*
* mpu401.c
*/
#include <ISA.h>
#include <KernelExport.h>
#include <OS.h>
#include <midi_driver.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "debug.h"
#include "mpu401_priv.h"
midi_create_device -
----- */
static status_t
create_device(int port, void ** out_storage, uint32 workarounds,
void (*interrupt_op)(int32 op, void * card), void * card)
{
mpu401device mpu_device;
mpu401device *mpuptr;
mpu_device.addrport = port;
mpu_device.workarounds = workarounds;
mpu_device.V2 = FALSE;
mpu_device.count = 1;
mpu_device.interrupt_op = interrupt_op;
mpu_device.card = card;
LOG(("create_device count= %ld, addrport 0x%x, workarounds: %d",
mpu_device.count, mpu_device.addrport, mpu_device.workarounds));
mpuptr = (mpu401device*)malloc (sizeof(mpu401device));
memcpy(mpuptr, &mpu_device, sizeof(mpu_device));
*out_storage = (void *)mpuptr;
return B_OK;
}
static status_t
create_device_v2(int port, void ** out_storage, uint32 workarounds,
void (*interrupt_op)(int32 op, void * card), void * card)
{
mpu401device *mpuptr;
mpu401device mpu_device;
mpu_device.addrport = port;
mpu_device.workarounds = workarounds;
mpu_device.V2 = TRUE;
mpu_device.count = 1;
mpu_device.card = card;
LOG(("create_device count= %ld, addrport 0x%x, workarounds: %d",
mpu_device.count, mpu_device.addrport, mpu_device.workarounds));
mpuptr = (mpu401device*)malloc(sizeof(mpu401device));
memcpy(mpuptr, &mpu_device, sizeof(mpu_device));
*out_storage = (void *)mpuptr;
return B_OK;
}
midi_delete_device
----- */
static status_t
delete_device(void * storage)
{
mpu401device * mpu_device = (mpu401device *)storage;
LOG(("device->addrport= 0x%x count= %ld\n",
mpu_device->addrport, mpu_device->count));
LOG(("delete_device: *storage:%p\n", storage));
free(mpu_device);
return B_OK;
}
midi_open - handle open() calls
----- */
static status_t
midi_open(void * storage, uint32 flags, void ** out_cookie)
{
char semname[25];
int ack_byte;
mpu401device * mpu_device = (mpu401device *)storage;
LOG(("open() flags: %ld, *storage: %p, **out_cookie: %p\n", flags,
storage, out_cookie));
LOG(("open: device->addrport 0x%x ,workarounds 0x%x\n",
mpu_device->addrport, mpu_device->workarounds));
if (mpu_device->V2 == TRUE)
return B_ERROR;
switch (mpu_device->workarounds) {
case 0x11020004:
case 0x11020005:
case 0:
PRINT(("reset MPU401\n"));
Write_MPU401(mpu_device->addrport, UARTCMD,
mpu_device->workarounds, MPU401_RESET);
snooze(30000);
Write_MPU401(mpu_device->addrport, UARTCMD,
mpu_device->workarounds, MPU401_RESET);
snooze(30000);
ack_byte = Read_MPU401(mpu_device->addrport, UARTDATA,
mpu_device->workarounds);
PRINT(("enable UART mode\n"));
Write_MPU401(mpu_device->addrport, UARTCMD,
mpu_device->workarounds, MPU401_UART);
snooze(30000);
ack_byte = Read_MPU401(mpu_device->addrport, UARTDATA,
mpu_device->workarounds);
PRINT(("port cmd ack is 0x%x\n", ack_byte));
*out_cookie = mpu_device;
break;
case 0x14121712:
PRINT(("reset MPU401\n"));
Write_MPU401(mpu_device->addrport, UARTDATA,
mpu_device->workarounds, 0x00);
snooze(30000);
Write_MPU401(mpu_device->addrport, UARTCMD,
mpu_device->workarounds, MPU401_RESET);
snooze(30000);
ack_byte = Read_MPU401(mpu_device->addrport, UARTDATA,
mpu_device->workarounds);
PRINT(("enable UART mode\n"));
Write_MPU401(mpu_device->addrport, UARTDATA,
mpu_device->workarounds, 0x00);
snooze(30000);
Write_MPU401(mpu_device->addrport, UARTCMD,
mpu_device->workarounds, MPU401_UART);
snooze(30000);
ack_byte = Read_MPU401(mpu_device->addrport, UARTDATA,
mpu_device->workarounds);
PRINT(("port cmd ack is 0x%x\n", ack_byte));
break;
case 1:
PRINT(("already in UART mode\n"));
break;
default:
PRINT(("Unknown workaround: %d\n", mpu_device->workarounds));
break;
}
sprintf(semname, "mpu401:%04x:read_sem", mpu_device->addrport);
mpu_device->readsemaphore = create_sem(0, semname);
sprintf(semname,"mpu401:%04x:write_sem", mpu_device->addrport);
mpu_device->writesemaphore = create_sem(1, semname);
mbuf_bytes = 0;
mbuf_current = 0;
mbuf_start = 0;
mpu_device->interrupt_op(B_MPU_401_ENABLE_CARD_INT, mpu_device->card);
if ((mpu_device->readsemaphore > B_OK)
&& (mpu_device->writesemaphore > B_OK)) {
atomic_add(&mpu_device->count, 1);
PRINT(("midi_open() done (count = %x)\n", open_count));
return B_OK;
}
return B_ERROR;
}
midi_close - handle close() calls
----- */
static status_t
midi_close(void * cookie)
{
mpu401device * mpu_device = (mpu401device *)cookie;
if (mpu_device->count <= 0)
return B_ERROR;
mpu_device->interrupt_op(B_MPU_401_DISABLE_CARD_INT, mpu_device->card);
delete_sem(mpu_device->readsemaphore);
delete_sem(mpu_device->writesemaphore);
atomic_add(&mpu_device->count, -1);
PRINT(("midi_close() done (count = %" B_PRId32 ")\n", mpu_device->count));
return B_OK;
}
midi_free - free up allocated memory
----- */
static status_t
midi_free(void * cookie)
{
LOG(("midi_free()\n"));
return B_OK;
}
midi_control - handle control() calls
----- */
static status_t
midi_control(void * cookie, uint32 op, void * data, size_t len)
{
LOG(("midi_control()\n"));
return B_OK;
}
midi_read - handle read() calls
----- */
static status_t
midi_read(void *cookie, off_t pos, void *buffer, size_t *num_bytes)
{
this reads and returns the data from a buffer */
unsigned char *data;
unsigned int i;
cpu_status status __attribute__((unused));
status_t bestat;
mpu401device *mpu_device = (mpu401device *)cookie;
data = (unsigned char*)buffer;
i = 0;
*num_bytes = 0;
bestat = acquire_sem_etc(mpu_device->readsemaphore, 1,
B_CAN_INTERRUPT, 0);
if (bestat == B_INTERRUPTED) {
return B_INTERRUPTED;
}
if (bestat != B_OK) {
TRACE(("acquire_sem not B_OK %d\n",(int)bestat));
*num_bytes = 1;
return B_INTERRUPTED;
} else {
#ifdef __HAIKU__
if (user_memcpy(data+i, &(mpubuffer[mbuf_start]),
sizeof(unsigned char)) == B_OK) {
#else
status = lock();
*(data+i) = mpubuffer[mbuf_start];
#endif
i++;
mbuf_start++;
if (mbuf_start >= (MBUF_ELEMENTS-1))
mbuf_start = 0;
*num_bytes = 1;
if (mbuf_bytes > 0)
mbuf_bytes--;
#ifdef __HAIKU__
}
#else
unlock(status);
#endif
}
return B_OK;
}
midi_write - handle write() calls
----- */
static status_t
midi_write(void * cookie, off_t pos, const void * data, size_t * num_bytes)
{
unsigned char *bufdata;
uint32 i;
size_t count;
mpu401device *mpu_device = (mpu401device *)cookie;
bufdata = (unsigned char*)data;
count = *num_bytes;
(int)count, mpu_device->addrport, mpu_device->workarounds));*/
acquire_sem(mpu_device->writesemaphore);
for (i = 0; i < count; i++) {
while ((Read_MPU401(mpu_device->addrport, UARTCMD,
mpu_device->workarounds) & MPU401_OK2WR));
Write_MPU401(mpu_device->addrport, UARTDATA,
mpu_device->workarounds, *(bufdata+i));
}
*num_bytes = 0;
release_sem(mpu_device->writesemaphore);
return B_OK;
}
interrupt_hook - handle interrupts for mpu401 data
----- */
static bool
interrupt_hook(void * cookie)
{
mpu401device *mpu_device = (mpu401device *)cookie;
Conversely, when bit 7 is is a one, no MIDI data is available.
Reading from the data port will often clear the interrupt signal
depending on the sound card. */
if ((Read_MPU401(mpu_device->addrport, UARTCMD,
mpu_device->workarounds) & MPU401_OK2RD) == 0) {
if (mbuf_current >= (MBUF_ELEMENTS-1))
mbuf_current = 0;
mpubuffer[mbuf_current] = Read_MPU401(mpu_device->addrport,
UARTDATA, mpu_device->workarounds);
mbuf_current++;
mbuf_bytes++;
release_sem_etc(mpu_device->readsemaphore, 1, B_DO_NOT_RESCHEDULE);
return TRUE;
}
return FALSE;
}
uchar
Read_MPU401(unsigned int addrport, const char cmdtype,
unsigned int workarounds)
{
uchar mpudatabyte;
cpu_status status;
unsigned int regptr;
switch (workarounds) {
case 0x11020004:
regptr = (((I_MPU1 + cmdtype) << 16) & PTR_ADDRESS_MASK);
status = lock();
gPCI->write_io_32(addrport + D_PTR, regptr);
mpudatabyte = gPCI->read_io_32(addrport + D_DATA);
unlock(status);
break;
case 0x11020005:
regptr = (((I_MPU2 + cmdtype) << 16) & PTR_ADDRESS_MASK);
status = lock();
gPCI->write_io_32(addrport + D_PTR, regptr);
mpudatabyte = gPCI->read_io_32(addrport + D_DATA);
unlock(status);
break;
case 0x14121712:
status = lock();
mpudatabyte = gPCI->read_io_8(addrport + cmdtype);
unlock(status);
break;
default:
mpudatabyte = gISA->read_io_8(addrport + cmdtype);
break;
}
return mpudatabyte;
}
status_t
Write_MPU401(unsigned int addrport, const char cmdtype,
unsigned int workarounds, uchar mpudatabyte)
{
cpu_status status;
unsigned int regptr;
switch (workarounds) {
case 0x11020004:
regptr = (((I_MPU1 + cmdtype) << 16) & PTR_ADDRESS_MASK);
status = lock();
gPCI->write_io_32(addrport + D_PTR, regptr);
gPCI->write_io_32(addrport + D_DATA, mpudatabyte);
unlock(status);
break;
case 0x11020005:
regptr = (((I_MPU2 + cmdtype) << 16) & PTR_ADDRESS_MASK);
status = lock();
gPCI->write_io_32(addrport + D_PTR, regptr);
gPCI->write_io_32(addrport + D_DATA, mpudatabyte);
unlock(status);
break;
case 0x14121712:
status = lock();
gPCI->write_io_8(addrport + cmdtype, mpudatabyte);
unlock(status);
break;
default:
gISA->write_io_8(addrport + cmdtype, mpudatabyte);
break;
}
return B_OK;
}
static status_t
std_ops(int32 op, ...)
{
switch(op) {
case B_MODULE_INIT:
LOG_CREATE();
PRINT(("B_MODULE_INIT\n"));
if (get_module(B_ISA_MODULE_NAME, (module_info **)&gISA) < B_OK)
return B_ERROR;
if (get_module(B_PCI_MODULE_NAME, (module_info **)&gPCI) < B_OK)
return B_ERROR;
return B_OK;
case B_MODULE_UNINIT:
put_module(B_ISA_MODULE_NAME);
put_module(B_PCI_MODULE_NAME);
PRINT(("B_MODULE_UNINIT\n"));
return B_OK;
default:
return B_ERROR;
}
}
static generic_mpu401_module mpu401_module =
{
{
B_MPU_401_MODULE_NAME,
B_KEEP_LOADED ,
std_ops
},
create_device,
delete_device,
midi_open,
midi_close,
midi_free,
midi_control,
midi_read,
midi_write,
interrupt_hook
};
static generic_mpu401_module mpu401_module2 =
{
{
"generic/mpu401/v2",
0,
std_ops
},
create_device_v2,
delete_device,
midi_open,
midi_close,
midi_free,
midi_control,
midi_read,
midi_write,
interrupt_hook
};
_EXPORT generic_mpu401_module *modules[] =
{
&mpu401_module,
&mpu401_module2,
NULL
};
spinlock locked = B_SPINLOCK_INITIALIZER;
cpu_status
lock(void)
{
cpu_status status = disable_interrupts();
acquire_spinlock(&locked);
return status;
}
void
unlock(cpu_status status)
{
release_spinlock(&locked);
restore_interrupts(status);
}