xref: /haiku/src/add-ons/kernel/drivers/midi/mpu401/mpu401.c (revision 344ded80d400028c8f561b4b876257b94c12db4a)
1 /*
2 * Copyright 2003-2006, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * A module driver for the generic mpu401 midi interface.
6 *
7 * Author:
8 *		Greg Crain (gsc70@comcast.net)
9 *
10 *  mpu401.c
11 */
12 
13 
14 #include <ISA.h>
15 #include <KernelExport.h>
16 #include <OS.h>
17 #include <midi_driver.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include "debug.h"
22 #include "mpu401_priv.h"
23 
24 
25 /* ----------
26 	midi_create_device -
27 ----- */
28 /*-----------------------------*/
29 /*  Version 1 of mpu401 module */
30 /*-----------------------------*/
31 
32 static status_t
33 create_device(int port, void ** out_storage, uint32 workarounds,
34 	void (*interrupt_op)(int32 op, void * card), void * card)
35 {
36 	mpu401device mpu_device;
37 	mpu401device *mpuptr;
38 
39 	/* fill the structure with specific info from caller */
40 	mpu_device.addrport = port;
41 	mpu_device.workarounds = workarounds;
42 	mpu_device.V2 = FALSE;
43 	mpu_device.count = 1;
44 	mpu_device.interrupt_op = interrupt_op;
45 	mpu_device.card = card;
46 
47 	LOG(("create_device count= %ld, addrport 0x%x, workarounds: %d",
48 		mpu_device.count, mpu_device.addrport, mpu_device.workarounds));
49 
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;
56 
57 	return B_OK;
58 }
59 
60 
61 /*-----------------------------*/
62 /*  Version 2 of mpu401 module */
63 /*-----------------------------*/
64 
65 static status_t
66 create_device_v2(int port, void ** out_storage, uint32 workarounds,
67 	void (*interrupt_op)(int32 op, void * card), void * card)
68 {
69 	mpu401device *mpuptr;
70 	mpu401device mpu_device;
71 
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.
80 
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.
84 
85  	mpu_device.addrport = port;
86 	mpu_device.workarounds = workarounds;
87 	mpu_device.V2 = TRUE;
88 	mpu_device.count = 1;
89 	mpu_device.card = card;
90 
91 	LOG(("create_device count= %ld, addrport 0x%x, workarounds: %d",
92 		mpu_device.count, mpu_device.addrport, mpu_device.workarounds));
93 
94 	mpuptr = (mpu401device*)malloc(sizeof(mpu401device));
95 	memcpy(mpuptr, &mpu_device, sizeof(mpu_device));
96 	*out_storage = (void *)mpuptr;
97 
98 	return B_OK;
99 }
100 
101 
102 /* ----------
103 	midi_delete_device
104 ----- */
105 static status_t
106 delete_device(void * storage)
107 {
108 	mpu401device * mpu_device = (mpu401device *)storage;
109 
110 	LOG(("device->addrport= 0x%x count= %ld\n",
111 		mpu_device->addrport, mpu_device->count));
112 	LOG(("delete_device: *storage:%p\n", storage));
113 
114 	free(mpu_device);   // free the memory allocated in create_device
115 
116 	return B_OK;
117 }
118 
119 
120 /* ----------
121 	midi_open - handle open() calls
122 ----- */
123 
124 static status_t
125 midi_open(void * storage, uint32 flags, void ** out_cookie)
126 {
127 	char semname[25];
128 	int ack_byte;
129 	mpu401device * mpu_device = (mpu401device *)storage;
130 
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));
135 
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)
140 		return B_ERROR;
141 
142 	switch (mpu_device->workarounds) {
143 		case 0x11020004: // Still required for Creative Audigy, Audigy2
144 		case 0x11020005:
145 		case 0:
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);
150 			snooze(30000);
151 			Write_MPU401(mpu_device->addrport, UARTCMD,
152 				mpu_device->workarounds, MPU401_RESET);
153 			snooze(30000);
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);
159 			snooze(30000);
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;
164 			break;
165 		case 0x14121712:
166 			PRINT(("reset MPU401\n"));
167 			Write_MPU401(mpu_device->addrport, UARTDATA,
168 				mpu_device->workarounds, 0x00);
169 			snooze(30000);
170 			Write_MPU401(mpu_device->addrport, UARTCMD,
171 				mpu_device->workarounds, MPU401_RESET);
172 			snooze(30000);
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);
178 			snooze(30000);
179 			Write_MPU401(mpu_device->addrport, UARTCMD,
180 				mpu_device->workarounds, MPU401_UART);
181 			snooze(30000);
182 			ack_byte = Read_MPU401(mpu_device->addrport, UARTDATA,
183 				mpu_device->workarounds);
184 			PRINT(("port cmd ack is 0x%x\n", ack_byte));
185 			break;
186 		case 1:
187 			//  Some devices are always in UART mode
188 			PRINT(("already in UART mode\n"));
189   			break;
190 		default:
191 			PRINT(("Unknown workaround: %d\n", mpu_device->workarounds));
192 			break;
193 	}  //end switch
194 
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);
198 
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);
202 
203 	// clear midi-in buffer
204 	mbuf_bytes = 0;
205 	mbuf_current = 0;
206 	mbuf_start = 0;
207 
208   	//Enable midi interrupts
209 	mpu_device->interrupt_op(B_MPU_401_ENABLE_CARD_INT, mpu_device->card);
210 
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));
215 		return B_OK;
216 	}
217 	return B_ERROR;
218 }
219 
220 
221 /* ----------
222 	midi_close - handle close() calls
223 ----- */
224 static status_t
225 midi_close(void * cookie)
226 {
227 	mpu401device * mpu_device = (mpu401device *)cookie;
228 
229 	if (mpu_device->count <= 0)
230  	   return B_ERROR;
231 
232 	//Disable soundcard midi interrupts
233 	mpu_device->interrupt_op(B_MPU_401_DISABLE_CARD_INT, mpu_device->card);
234 
235 	// Delete the semaphores
236 	delete_sem(mpu_device->readsemaphore);
237 	delete_sem(mpu_device->writesemaphore);
238 
239 	atomic_add(&mpu_device->count, -1);
240   	PRINT(("midi_close() done (count = %" B_PRId32 ")\n", mpu_device->count));
241 
242 	return B_OK;
243 }
244 
245 
246 /* ----------
247 	midi_free - free up allocated memory
248 ----- */
249 static status_t
250 midi_free(void * cookie)
251 {
252 	LOG(("midi_free()\n"));
253 	return B_OK;
254 }
255 
256 
257 /* ----------
258 	midi_control - handle control() calls
259 ----- */
260 static status_t
261 midi_control(void * cookie, uint32 op, void * data, size_t len)
262 {
263 	//mpu401device *mpu_device = (mpu401device *)cookie;
264 
265 	/* I don't think this is ever called ...*/
266 	LOG(("midi_control()\n"));
267 	return B_OK;
268 }
269 
270 
271 /* ----------
272 	midi_read - handle read() calls
273 ----- */
274 static status_t
275 midi_read(void *cookie, off_t pos, void *buffer, size_t *num_bytes)
276 {
277 	/* The actual midi data is read from the device in the interrupt handler;
278 	   this reads and returns the data from a buffer */
279 
280 	unsigned char *data;
281 	unsigned int i;
282 	cpu_status status __attribute__((unused));
283 	status_t bestat;
284 	mpu401device *mpu_device = (mpu401device *)cookie;
285 
286  	data = (unsigned char*)buffer;
287 
288 	i = 0;
289 	*num_bytes = 0;
290 	bestat = acquire_sem_etc(mpu_device->readsemaphore, 1,
291 		B_CAN_INTERRUPT, 0);
292 	if (bestat == B_INTERRUPTED) {
293 		//PRINT(("acquire_sem B_INTERRUPTED!\n"));
294 		return B_INTERRUPTED;
295 	}
296 	if (bestat != B_OK) {
297 		TRACE(("acquire_sem not B_OK %d\n",(int)bestat));
298 		*num_bytes = 1;
299 		return B_INTERRUPTED;
300 	} else {
301 #ifdef __HAIKU__
302 		if (user_memcpy(data+i, &(mpubuffer[mbuf_start]),
303 			sizeof(unsigned char)) == B_OK) {
304 #else
305 		status = lock();
306 		*(data+i) = mpubuffer[mbuf_start];
307 #endif
308 		i++;
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
313 		if (mbuf_bytes > 0)
314 			mbuf_bytes--; // bytes read from buffer, so decrement buffer count
315 #ifdef __HAIKU__
316 		}
317 #else
318    		unlock(status);
319 #endif
320    		//PRINT(("bytes in buffer: %d\n",mbuf_bytes));
321 	}
322 
323 	return B_OK;
324 }
325 
326 
327 /* ----------
328 	midi_write - handle write() calls
329 ----- */
330 static status_t
331 midi_write(void * cookie, off_t pos, const void * data, size_t * num_bytes)
332 {
333 	unsigned char *bufdata;
334 	uint32 i;
335 	size_t count;
336 
337 	mpu401device *mpu_device = (mpu401device *)cookie;
338 	bufdata = (unsigned char*)data;	/* Pointer to midi data buffer */
339 	count = *num_bytes;
340 
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));*/
344 
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));
350 
351 		Write_MPU401(mpu_device->addrport, UARTDATA,
352 			mpu_device->workarounds, *(bufdata+i));
353 	}
354 
355 	*num_bytes = 0;
356 	release_sem(mpu_device->writesemaphore);
357 
358 	return B_OK;
359 }
360 
361 
362 /* ----------
363 	interrupt_hook - handle interrupts for mpu401 data
364 ----- */
365 static bool
366 interrupt_hook(void * cookie)
367 {
368 	mpu401device *mpu_device = (mpu401device *)cookie;
369 
370 	/* Only for deep debugging..will slow things down */
371 	//PRINT(("irq! port: 0x%x\n",mpu_device->addrport));
372 
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. */
377 
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))
382 			mbuf_current = 0;
383 
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 */
389 
390 		release_sem_etc(mpu_device->readsemaphore, 1, B_DO_NOT_RESCHEDULE);
391 
392 		return TRUE; //B_INVOKE_SCHEDULER
393 	}
394 
395 	/* No midi data from this interrupt */
396 	return FALSE; //B_UNHANDLED_INTERRUPT
397 }
398 
399 
400 /*-----------------------------------------------------------------*/
401 
402 uchar
403 Read_MPU401(unsigned int addrport, const char cmdtype,
404 	unsigned int workarounds)
405 {
406 	uchar mpudatabyte;
407 	cpu_status status;
408 	unsigned int regptr;
409 
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);
415 			status = lock();
416 			gPCI->write_io_32(addrport + D_PTR, regptr);  /*DATA or CMD */
417 			mpudatabyte = gPCI->read_io_32(addrport + D_DATA);
418 			unlock(status);
419 			break;
420 
421 		case 0x11020005:  /* Creative Audigy LiveDrive */
422 			regptr = (((I_MPU2 + cmdtype) << 16) & PTR_ADDRESS_MASK);
423 			status = lock();
424 			gPCI->write_io_32(addrport + D_PTR, regptr);  /*DATA2 or CMD2 */
425 			mpudatabyte = gPCI->read_io_32(addrport + D_DATA);
426 			unlock(status);
427 			break;
428 
429 		case 0x14121712:
430 			status = lock();
431 			mpudatabyte = gPCI->read_io_8(addrport + cmdtype);
432 			unlock(status);
433 			break;
434 
435 		default:
436 			mpudatabyte = gISA->read_io_8(addrport + cmdtype);
437 			break;
438 	}
439 	return mpudatabyte;
440 }
441 
442 
443 status_t
444 Write_MPU401(unsigned int addrport, const char cmdtype,
445 	unsigned int workarounds, uchar mpudatabyte)
446 {
447 	cpu_status status;
448 	unsigned int regptr;
449 
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);
455 			status = lock();
456 			gPCI->write_io_32(addrport + D_PTR, regptr); /*DATA or CMD */
457 			gPCI->write_io_32(addrport + D_DATA, mpudatabyte);
458 			unlock(status);
459 			break;
460 
461 		case 0x11020005: /* Creative Audigy LiveDrive */
462 			regptr = (((I_MPU2 + cmdtype) << 16) & PTR_ADDRESS_MASK);
463 			status = lock();
464 			gPCI->write_io_32(addrport + D_PTR, regptr); /*DATA2 or CMD2 */
465 			gPCI->write_io_32(addrport + D_DATA, mpudatabyte);
466 			unlock(status);
467 			break;
468 
469 		case 0x14121712:
470 			status = lock();
471 			gPCI->write_io_8(addrport + cmdtype, mpudatabyte);
472 			unlock(status);
473 			break;
474 
475 		default:
476 			gISA->write_io_8(addrport + cmdtype, mpudatabyte);
477 			break;
478 	}
479 	return B_OK;
480 }
481 
482 
483 /*-----------------------------------------------------------------*/
484 
485 static status_t
486 std_ops(int32 op, ...)
487 {
488 	switch(op) {
489 
490 	case B_MODULE_INIT:
491 
492 		LOG_CREATE();
493 		PRINT(("B_MODULE_INIT\n"));
494 
495 		if (get_module(B_ISA_MODULE_NAME, (module_info **)&gISA) < B_OK)
496 			return B_ERROR;
497  		if (get_module(B_PCI_MODULE_NAME, (module_info **)&gPCI) < B_OK)
498 			return B_ERROR;
499 		return B_OK;
500 
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"));
505 		return B_OK;
506 
507 	default:
508 		return B_ERROR;
509 	}
510 }
511 
512 static generic_mpu401_module mpu401_module =
513 {
514 	{
515 		B_MPU_401_MODULE_NAME,
516 		B_KEEP_LOADED /*0*/ ,
517 		std_ops
518 	},
519 	create_device,
520 	delete_device,
521 	midi_open,
522 	midi_close,
523 	midi_free,
524 	midi_control,
525 	midi_read,
526 	midi_write,
527 	interrupt_hook
528 };
529 
530 // Module v2 seems to be undocumented
531 static generic_mpu401_module mpu401_module2 =
532 {
533 	{
534 		"generic/mpu401/v2",
535 		0,
536 		std_ops
537 	},
538 	create_device_v2,
539 	delete_device,
540 	midi_open,
541 	midi_close,
542 	midi_free,
543 	midi_control,
544 	midi_read,
545 	midi_write,
546 	interrupt_hook
547 };
548 
549 _EXPORT generic_mpu401_module *modules[] =
550 {
551 	&mpu401_module,
552 	&mpu401_module2,
553 	NULL
554 };
555 
556 spinlock locked = B_SPINLOCK_INITIALIZER;
557 cpu_status
558 lock(void)
559 {
560 	cpu_status status = disable_interrupts();
561 	acquire_spinlock(&locked);
562 	return status;
563 }
564 
565 void
566 unlock(cpu_status status)
567 {
568 	 release_spinlock(&locked);
569 	 restore_interrupts(status);
570 }
571 
572