2 * Copyright 2003-2006, Haiku.
3 * Distributed under the terms of the MIT License.
5 * A module driver for the generic mpu401 midi interface.
8 * Greg Crain (gsc70@comcast.net)
15 #include <KernelExport.h>
17 #include <midi_driver.h>
22 #include "mpu401_priv.h"
28 /*-----------------------------*/
29 /* Version 1 of mpu401 module */
30 /*-----------------------------*/
33 create_device(int port
, void ** out_storage
, uint32 workarounds
,
34 void (*interrupt_op
)(int32 op
, void * card
), void * card
)
36 mpu401device mpu_device
;
39 /* fill the structure with specific info from caller */
40 mpu_device
.addrport
= port
;
41 mpu_device
.workarounds
= workarounds
;
42 mpu_device
.V2
= FALSE
;
44 mpu_device
.interrupt_op
= interrupt_op
;
45 mpu_device
.card
= card
;
47 LOG(("create_device count= %ld, addrport 0x%x, workarounds: %d",
48 mpu_device
.count
, mpu_device
.addrport
, mpu_device
.workarounds
));
50 // basically, each call to create device allocates memory for
51 //a structure with the specific device info. The pointer to this is
52 // returned back to calling driver
53 mpuptr
= (mpu401device
*)malloc (sizeof(mpu401device
));
54 memcpy(mpuptr
, &mpu_device
, sizeof(mpu_device
));
55 *out_storage
= (void *)mpuptr
;
61 /*-----------------------------*/
62 /* Version 2 of mpu401 module */
63 /*-----------------------------*/
66 create_device_v2(int port
, void ** out_storage
, uint32 workarounds
,
67 void (*interrupt_op
)(int32 op
, void * card
), void * card
)
70 mpu401device mpu_device
;
72 // not sure exactly how v2 of the module works. I think that two ports
73 // are created. One for midi in data, and another for midi out data.
74 // Instead of transfering data using a buffer and pointer, the midi
75 // data is transfered via the global ports.
76 // If the ports are created in the midi server, then the port id's
77 // should be known in this hook call.
78 // If the ports are created in this hook call, the the port id's
79 // should be returned to the midi server.
81 // One version of read/write hook functions are used for both v1, v2.
82 // Therefore, in those calls, it needs to be known whether the mididata
83 // is to be read/written to a buffer, or to the port.
85 mpu_device
.addrport
= port
;
86 mpu_device
.workarounds
= workarounds
;
89 mpu_device
.card
= card
;
91 LOG(("create_device count= %ld, addrport 0x%x, workarounds: %d",
92 mpu_device
.count
, mpu_device
.addrport
, mpu_device
.workarounds
));
94 mpuptr
= (mpu401device
*)malloc(sizeof(mpu401device
));
95 memcpy(mpuptr
, &mpu_device
, sizeof(mpu_device
));
96 *out_storage
= (void *)mpuptr
;
106 delete_device(void * storage
)
108 mpu401device
* mpu_device
= (mpu401device
*)storage
;
110 LOG(("device->addrport= 0x%x count= %ld\n",
111 mpu_device
->addrport
, mpu_device
->count
));
112 LOG(("delete_device: *storage:%p\n", storage
));
114 free(mpu_device
); // free the memory allocated in create_device
121 midi_open - handle open() calls
125 midi_open(void * storage
, uint32 flags
, void ** out_cookie
)
129 mpu401device
* mpu_device
= (mpu401device
*)storage
;
131 LOG(("open() flags: %ld, *storage: %p, **out_cookie: %p\n", flags
,
132 storage
, out_cookie
));
133 LOG(("open: device->addrport 0x%x ,workarounds 0x%x\n",
134 mpu_device
->addrport
, mpu_device
->workarounds
));
136 // the undocumented V2 module is not complete
137 // we will allow the device to be created since some drivers depend on it
138 // but will return an error if the actual midi device is opened:
139 if (mpu_device
->V2
== TRUE
)
142 switch (mpu_device
->workarounds
) {
143 case 0x11020004: // Still required for Creative Audigy, Audigy2
146 // don't know the current mpu state
147 PRINT(("reset MPU401\n"));
148 Write_MPU401(mpu_device
->addrport
, UARTCMD
,
149 mpu_device
->workarounds
, MPU401_RESET
);
151 Write_MPU401(mpu_device
->addrport
, UARTCMD
,
152 mpu_device
->workarounds
, MPU401_RESET
);
154 ack_byte
= Read_MPU401(mpu_device
->addrport
, UARTDATA
,
155 mpu_device
->workarounds
);
156 PRINT(("enable UART mode\n"));
157 Write_MPU401(mpu_device
->addrport
, UARTCMD
,
158 mpu_device
->workarounds
, MPU401_UART
);
160 ack_byte
= Read_MPU401(mpu_device
->addrport
, UARTDATA
,
161 mpu_device
->workarounds
);
162 PRINT(("port cmd ack is 0x%x\n", ack_byte
));
163 *out_cookie
= mpu_device
;
166 PRINT(("reset MPU401\n"));
167 Write_MPU401(mpu_device
->addrport
, UARTDATA
,
168 mpu_device
->workarounds
, 0x00);
170 Write_MPU401(mpu_device
->addrport
, UARTCMD
,
171 mpu_device
->workarounds
, MPU401_RESET
);
173 ack_byte
= Read_MPU401(mpu_device
->addrport
, UARTDATA
,
174 mpu_device
->workarounds
);
175 PRINT(("enable UART mode\n"));
176 Write_MPU401(mpu_device
->addrport
, UARTDATA
,
177 mpu_device
->workarounds
, 0x00);
179 Write_MPU401(mpu_device
->addrport
, UARTCMD
,
180 mpu_device
->workarounds
, MPU401_UART
);
182 ack_byte
= Read_MPU401(mpu_device
->addrport
, UARTDATA
,
183 mpu_device
->workarounds
);
184 PRINT(("port cmd ack is 0x%x\n", ack_byte
));
187 // Some devices are always in UART mode
188 PRINT(("already in UART mode\n"));
191 PRINT(("Unknown workaround: %d\n", mpu_device
->workarounds
));
195 // Create Read semaphore for midi-in data
196 sprintf(semname
, "mpu401:%04x:read_sem", mpu_device
->addrport
);
197 mpu_device
->readsemaphore
= create_sem(0, semname
);
199 // Create Write semaphore for midi-out data
200 sprintf(semname
,"mpu401:%04x:write_sem", mpu_device
->addrport
);
201 mpu_device
->writesemaphore
= create_sem(1, semname
);
203 // clear midi-in buffer
208 //Enable midi interrupts
209 mpu_device
->interrupt_op(B_MPU_401_ENABLE_CARD_INT
, mpu_device
->card
);
211 if ((mpu_device
->readsemaphore
> B_OK
)
212 && (mpu_device
->writesemaphore
> B_OK
)) {
213 atomic_add(&mpu_device
->count
, 1);
214 PRINT(("midi_open() done (count = %x)\n", open_count
));
222 midi_close - handle close() calls
225 midi_close(void * cookie
)
227 mpu401device
* mpu_device
= (mpu401device
*)cookie
;
229 if (mpu_device
->count
<= 0)
232 //Disable soundcard midi interrupts
233 mpu_device
->interrupt_op(B_MPU_401_DISABLE_CARD_INT
, mpu_device
->card
);
235 // Delete the semaphores
236 delete_sem(mpu_device
->readsemaphore
);
237 delete_sem(mpu_device
->writesemaphore
);
239 atomic_add(&mpu_device
->count
, -1);
240 PRINT(("midi_close() done (count = %" B_PRId32
")\n", mpu_device
->count
));
247 midi_free - free up allocated memory
250 midi_free(void * cookie
)
252 LOG(("midi_free()\n"));
258 midi_control - handle control() calls
261 midi_control(void * cookie
, uint32 op
, void * data
, size_t len
)
263 //mpu401device *mpu_device = (mpu401device *)cookie;
265 /* I don't think this is ever called ...*/
266 LOG(("midi_control()\n"));
272 midi_read - handle read() calls
275 midi_read(void *cookie
, off_t pos
, void *buffer
, size_t *num_bytes
)
277 /* The actual midi data is read from the device in the interrupt handler;
278 this reads and returns the data from a buffer */
282 cpu_status status
__attribute__((unused
));
284 mpu401device
*mpu_device
= (mpu401device
*)cookie
;
286 data
= (unsigned char*)buffer
;
290 bestat
= acquire_sem_etc(mpu_device
->readsemaphore
, 1,
292 if (bestat
== B_INTERRUPTED
) {
293 //PRINT(("acquire_sem B_INTERRUPTED!\n"));
294 return B_INTERRUPTED
;
296 if (bestat
!= B_OK
) {
297 TRACE(("acquire_sem not B_OK %d\n",(int)bestat
));
299 return B_INTERRUPTED
;
302 if (user_memcpy(data
+i
, &(mpubuffer
[mbuf_start
]),
303 sizeof(unsigned char)) == B_OK
) {
306 *(data
+i
) = mpubuffer
[mbuf_start
];
309 mbuf_start
++; // pointer to data in ringbuffer
310 if (mbuf_start
>= (MBUF_ELEMENTS
-1))
311 mbuf_start
= 0; //wraparound of ringbuffer
312 *num_bytes
= 1; // How many bytes are being returned in buffer
314 mbuf_bytes
--; // bytes read from buffer, so decrement buffer count
320 //PRINT(("bytes in buffer: %d\n",mbuf_bytes));
328 midi_write - handle write() calls
331 midi_write(void * cookie
, off_t pos
, const void * data
, size_t * num_bytes
)
333 unsigned char *bufdata
;
337 mpu401device
*mpu_device
= (mpu401device
*)cookie
;
338 bufdata
= (unsigned char*)data
; /* Pointer to midi data buffer */
341 /* Only for deep debugging..will slow things down */
342 /*PRINT(("write %d bytes, addrport 0x%x, workarounds 0x%x\n",
343 (int)count, mpu_device->addrport, mpu_device->workarounds));*/
345 acquire_sem(mpu_device
->writesemaphore
);
346 for (i
= 0; i
< count
; i
++) {
347 // wait until device is ready
348 while ((Read_MPU401(mpu_device
->addrport
, UARTCMD
,
349 mpu_device
->workarounds
) & MPU401_OK2WR
));
351 Write_MPU401(mpu_device
->addrport
, UARTDATA
,
352 mpu_device
->workarounds
, *(bufdata
+i
));
356 release_sem(mpu_device
->writesemaphore
);
363 interrupt_hook - handle interrupts for mpu401 data
366 interrupt_hook(void * cookie
)
368 mpu401device
*mpu_device
= (mpu401device
*)cookie
;
370 /* Only for deep debugging..will slow things down */
371 //PRINT(("irq! port: 0x%x\n",mpu_device->addrport));
373 /* Input data is available when bit 7 of the Status port is zero.
374 Conversely, when bit 7 is is a one, no MIDI data is available.
375 Reading from the data port will often clear the interrupt signal
376 depending on the sound card. */
378 if ((Read_MPU401(mpu_device
->addrport
, UARTCMD
,
379 mpu_device
->workarounds
) & MPU401_OK2RD
) == 0) {
380 /* Okay, midi data waiting to be read from device */
381 if (mbuf_current
>= (MBUF_ELEMENTS
-1))
384 /* store midi data byte into buffer */
385 mpubuffer
[mbuf_current
] = Read_MPU401(mpu_device
->addrport
,
386 UARTDATA
, mpu_device
->workarounds
);
387 mbuf_current
++; /* pointer to next blank byte */
388 mbuf_bytes
++; /* increment count of midi data bytes */
390 release_sem_etc(mpu_device
->readsemaphore
, 1, B_DO_NOT_RESCHEDULE
);
392 return TRUE
; //B_INVOKE_SCHEDULER
395 /* No midi data from this interrupt */
396 return FALSE
; //B_UNHANDLED_INTERRUPT
400 /*-----------------------------------------------------------------*/
403 Read_MPU401(unsigned int addrport
, const char cmdtype
,
404 unsigned int workarounds
)
410 /* Only for deep debugging..will slow things down */
411 //PRINT(("read workaround 0x%x\n",workarounds));
412 switch (workarounds
) {
413 case 0x11020004: /* Creative Audigy Gameport */
414 regptr
= (((I_MPU1
+ cmdtype
) << 16) & PTR_ADDRESS_MASK
);
416 gPCI
->write_io_32(addrport
+ D_PTR
, regptr
); /*DATA or CMD */
417 mpudatabyte
= gPCI
->read_io_32(addrport
+ D_DATA
);
421 case 0x11020005: /* Creative Audigy LiveDrive */
422 regptr
= (((I_MPU2
+ cmdtype
) << 16) & PTR_ADDRESS_MASK
);
424 gPCI
->write_io_32(addrport
+ D_PTR
, regptr
); /*DATA2 or CMD2 */
425 mpudatabyte
= gPCI
->read_io_32(addrport
+ D_DATA
);
431 mpudatabyte
= gPCI
->read_io_8(addrport
+ cmdtype
);
436 mpudatabyte
= gISA
->read_io_8(addrport
+ cmdtype
);
444 Write_MPU401(unsigned int addrport
, const char cmdtype
,
445 unsigned int workarounds
, uchar mpudatabyte
)
450 /* Only for deep debugging..will slow things down */
451 //PRINT(("write workaround 0x%x at addr: 0x%x\n",workarounds,addrport));
452 switch (workarounds
) {
453 case 0x11020004: /* Creative Audigy Gameport */
454 regptr
= (((I_MPU1
+ cmdtype
) << 16) & PTR_ADDRESS_MASK
);
456 gPCI
->write_io_32(addrport
+ D_PTR
, regptr
); /*DATA or CMD */
457 gPCI
->write_io_32(addrport
+ D_DATA
, mpudatabyte
);
461 case 0x11020005: /* Creative Audigy LiveDrive */
462 regptr
= (((I_MPU2
+ cmdtype
) << 16) & PTR_ADDRESS_MASK
);
464 gPCI
->write_io_32(addrport
+ D_PTR
, regptr
); /*DATA2 or CMD2 */
465 gPCI
->write_io_32(addrport
+ D_DATA
, mpudatabyte
);
471 gPCI
->write_io_8(addrport
+ cmdtype
, mpudatabyte
);
476 gISA
->write_io_8(addrport
+ cmdtype
, mpudatabyte
);
483 /*-----------------------------------------------------------------*/
486 std_ops(int32 op
, ...)
493 PRINT(("B_MODULE_INIT\n"));
495 if (get_module(B_ISA_MODULE_NAME
, (module_info
**)&gISA
) < B_OK
)
497 if (get_module(B_PCI_MODULE_NAME
, (module_info
**)&gPCI
) < B_OK
)
501 case B_MODULE_UNINIT
:
502 put_module(B_ISA_MODULE_NAME
);
503 put_module(B_PCI_MODULE_NAME
);
504 PRINT(("B_MODULE_UNINIT\n"));
512 static generic_mpu401_module mpu401_module
=
515 B_MPU_401_MODULE_NAME
,
516 B_KEEP_LOADED
/*0*/ ,
530 // Module v2 seems to be undocumented
531 static generic_mpu401_module mpu401_module2
=
549 _EXPORT generic_mpu401_module
*modules
[] =
556 spinlock locked
= B_SPINLOCK_INITIALIZER
;
560 cpu_status status
= disable_interrupts();
561 acquire_spinlock(&locked
);
566 unlock(cpu_status status
)
568 release_spinlock(&locked
);
569 restore_interrupts(status
);