mpu401.c revision 193640
1/*-
2 * Copyright (c) 2003 Mathew Kanner
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/sound/midi/mpu401.c 193640 2009-06-07 19:12:08Z ariff $");
29
30#include <sys/param.h>
31#include <sys/types.h>
32#include <sys/param.h>
33#include <sys/queue.h>
34#include <sys/kernel.h>
35#include <sys/lock.h>
36#include <sys/mutex.h>
37#include <sys/proc.h>
38#include <sys/systm.h>
39#include <sys/kobj.h>
40#ifdef SND_DEBUG
41#undef KOBJMETHOD
42#define KOBJMETHOD(NAME, FUNC)						\
43	{								\
44		&NAME##_desc,						\
45		(kobjop_t) ((FUNC != (NAME##_t *)NULL) ? FUNC : NULL)	\
46	}
47#endif
48#ifndef KOBJMETHOD_END
49#define KOBJMETHOD_END	{ NULL, NULL }
50#endif
51#include <sys/malloc.h>
52#include <sys/bus.h>			/* to get driver_intr_t */
53
54#ifdef HAVE_KERNEL_OPTION_HEADERS
55#include "opt_snd.h"
56#endif
57
58#include <dev/sound/midi/mpu401.h>
59#include <dev/sound/midi/midi.h>
60
61#include "mpu_if.h"
62#include "mpufoi_if.h"
63
64#define MPU_DATAPORT   0
65#define MPU_CMDPORT    1
66#define MPU_STATPORT   1
67#define MPU_RESET      0xff
68#define MPU_UART       0x3f
69#define MPU_ACK        0xfe
70#define MPU_STATMASK   0xc0
71#define MPU_OUTPUTBUSY 0x40
72#define MPU_INPUTBUSY  0x80
73#define MPU_TRYDATA 50
74#define MPU_DELAY   2500
75
76#define CMD(m,d)	MPUFOI_WRITE(m, m->cookie, MPU_CMDPORT,d)
77#define STATUS(m)	MPUFOI_READ(m, m->cookie, MPU_STATPORT)
78#define READ(m)		MPUFOI_READ(m, m->cookie, MPU_DATAPORT)
79#define WRITE(m,d)	MPUFOI_WRITE(m, m->cookie, MPU_DATAPORT,d)
80
81struct mpu401 {
82	KOBJ_FIELDS;
83	struct snd_midi *mid;
84	int	flags;
85	driver_intr_t *si;
86	void   *cookie;
87	struct callout timer;
88};
89
90static void mpu401_timeout(void *m);
91static mpu401_intr_t mpu401_intr;
92
93static int mpu401_minit(struct snd_midi *, void *);
94static int mpu401_muninit(struct snd_midi *, void *);
95static int mpu401_minqsize(struct snd_midi *, void *);
96static int mpu401_moutqsize(struct snd_midi *, void *);
97static void mpu401_mcallback(struct snd_midi *, void *, int);
98static void mpu401_mcallbackp(struct snd_midi *, void *, int);
99static const char *mpu401_mdescr(struct snd_midi *, void *, int);
100static const char *mpu401_mprovider(struct snd_midi *, void *);
101
102static kobj_method_t mpu401_methods[] = {
103	KOBJMETHOD(mpu_init, mpu401_minit),
104	KOBJMETHOD(mpu_uninit, mpu401_muninit),
105	KOBJMETHOD(mpu_inqsize, mpu401_minqsize),
106	KOBJMETHOD(mpu_outqsize, mpu401_moutqsize),
107	KOBJMETHOD(mpu_callback, mpu401_mcallback),
108	KOBJMETHOD(mpu_callbackp, mpu401_mcallbackp),
109	KOBJMETHOD(mpu_descr, mpu401_mdescr),
110	KOBJMETHOD(mpu_provider, mpu401_mprovider),
111	KOBJMETHOD_END
112};
113
114DEFINE_CLASS(mpu401, mpu401_methods, 0);
115
116void
117mpu401_timeout(void *a)
118{
119	struct mpu401 *m = (struct mpu401 *)a;
120
121	if (m->si)
122		(m->si)(m->cookie);
123
124}
125static int
126mpu401_intr(struct mpu401 *m)
127{
128#define MPU_INTR_BUF	16
129	MIDI_TYPE b[MPU_INTR_BUF];
130	int i;
131	int s;
132
133/*
134	printf("mpu401_intr\n");
135*/
136#define RXRDY(m) ( (STATUS(m) & MPU_INPUTBUSY) == 0)
137#define TXRDY(m) ( (STATUS(m) & MPU_OUTPUTBUSY) == 0)
138#if 0
139#define D(x,l) printf("mpu401_intr %d %x %s %s\n",l, x, x&MPU_INPUTBUSY?"RX":"", x&MPU_OUTPUTBUSY?"TX":"")
140#else
141#define D(x,l)
142#endif
143	i = 0;
144	s = STATUS(m);
145	D(s, 1);
146	while ((s & MPU_INPUTBUSY) == 0 && i < MPU_INTR_BUF) {
147		b[i] = READ(m);
148/*
149		printf("mpu401_intr in i %d d %d\n", i, b[i]);
150*/
151		i++;
152		s = STATUS(m);
153	}
154	if (i)
155		midi_in(m->mid, b, i);
156	i = 0;
157	while (!(s & MPU_OUTPUTBUSY) && i < MPU_INTR_BUF) {
158		if (midi_out(m->mid, b, 1)) {
159/*
160			printf("mpu401_intr out i %d d %d\n", i, b[0]);
161*/
162
163			WRITE(m, *b);
164		} else {
165/*
166			printf("mpu401_intr write: no output\n");
167*/
168			return 0;
169		}
170		i++;
171		/* DELAY(100); */
172		s = STATUS(m);
173	}
174
175	if ((m->flags & M_TXEN) && (m->si)) {
176		callout_reset(&m->timer, 1, mpu401_timeout, m);
177	}
178	return (m->flags & M_TXEN) == M_TXEN;
179}
180
181struct mpu401 *
182mpu401_init(kobj_class_t cls, void *cookie, driver_intr_t softintr,
183    mpu401_intr_t ** cb)
184{
185	struct mpu401 *m;
186
187	*cb = NULL;
188	m = malloc(sizeof(*m), M_MIDI, M_NOWAIT | M_ZERO);
189
190	if (!m)
191		return NULL;
192
193	kobj_init((kobj_t)m, cls);
194
195	callout_init(&m->timer, CALLOUT_MPSAFE);
196
197	m->si = softintr;
198	m->cookie = cookie;
199	m->flags = 0;
200
201	m->mid = midi_init(&mpu401_class, 0, 0, m);
202	if (!m->mid)
203		goto err;
204	*cb = mpu401_intr;
205	return m;
206err:
207	printf("mpu401_init error\n");
208	free(m, M_MIDI);
209	return NULL;
210}
211
212int
213mpu401_uninit(struct mpu401 *m)
214{
215	int retval;
216
217	CMD(m, MPU_RESET);
218	retval = midi_uninit(m->mid);
219	if (retval)
220		return retval;
221	free(m, M_MIDI);
222	return 0;
223}
224
225static int
226mpu401_minit(struct snd_midi *sm, void *arg)
227{
228	struct mpu401 *m = arg;
229	int i;
230
231	CMD(m, MPU_RESET);
232	CMD(m, MPU_UART);
233	return 0;
234	i = 0;
235	while (++i < 2000) {
236		if (RXRDY(m))
237			if (READ(m) == MPU_ACK)
238				break;
239	}
240
241	if (i < 2000) {
242		CMD(m, MPU_UART);
243		return 0;
244	}
245	printf("mpu401_minit failed active sensing\n");
246	return 1;
247}
248
249
250int
251mpu401_muninit(struct snd_midi *sm, void *arg)
252{
253	struct mpu401 *m = arg;
254
255	return MPUFOI_UNINIT(m, m->cookie);
256}
257
258int
259mpu401_minqsize(struct snd_midi *sm, void *arg)
260{
261	return 128;
262}
263
264int
265mpu401_moutqsize(struct snd_midi *sm, void *arg)
266{
267	return 128;
268}
269
270static void
271mpu401_mcallback(struct snd_midi *sm, void *arg, int flags)
272{
273	struct mpu401 *m = arg;
274#if 0
275	printf("mpu401_callback %s %s %s %s\n",
276	    flags & M_RX ? "M_RX" : "",
277	    flags & M_TX ? "M_TX" : "",
278	    flags & M_RXEN ? "M_RXEN" : "",
279	    flags & M_TXEN ? "M_TXEN" : "");
280#endif
281	if (flags & M_TXEN && m->si) {
282		callout_reset(&m->timer, 1, mpu401_timeout, m);
283	}
284	m->flags = flags;
285}
286
287static void
288mpu401_mcallbackp(struct snd_midi *sm, void *arg, int flags)
289{
290/*	printf("mpu401_callbackp\n"); */
291	mpu401_mcallback(sm, arg, flags);
292}
293
294static const char *
295mpu401_mdescr(struct snd_midi *sm, void *arg, int verbosity)
296{
297
298	return "descr mpu401";
299}
300
301static const char *
302mpu401_mprovider(struct snd_midi *m, void *arg)
303{
304	return "provider mpu401";
305}
306