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
32static status_t
33create_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
65static status_t
66create_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----- */
105static status_t
106delete_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
124static status_t
125midi_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----- */
224static status_t
225midi_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----- */
249static status_t
250midi_free(void * cookie)
251{
252	LOG(("midi_free()\n"));
253	return B_OK;
254}
255
256
257/* ----------
258	midi_control - handle control() calls
259----- */
260static status_t
261midi_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----- */
274static status_t
275midi_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----- */
330static status_t
331midi_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----- */
365static bool
366interrupt_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
402uchar
403Read_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
443status_t
444Write_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
485static status_t
486std_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
512static 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
531static 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
556spinlock locked = B_SPINLOCK_INITIALIZER;
557cpu_status
558lock(void)
559{
560	cpu_status status = disable_interrupts();
561	acquire_spinlock(&locked);
562	return status;
563}
564
565void
566unlock(cpu_status status)
567{
568	 release_spinlock(&locked);
569	 restore_interrupts(status);
570}
571
572