1/*
2 **********************************************************************
3 *     cardmo.c - MIDI UART output HAL for emu10k1 driver
4 *     Copyright 1999, 2000 Creative Labs, Inc.
5 *
6 **********************************************************************
7 *
8 *     Date                 Author          Summary of changes
9 *     ----                 ------          ------------------
10 *     October 20, 1999     Bertrand Lee    base code release
11 *     November 2, 1999     Alan Cox        cleaned up
12 *
13 **********************************************************************
14 *
15 *     This program is free software; you can redistribute it and/or
16 *     modify it under the terms of the GNU General Public License as
17 *     published by the Free Software Foundation; either version 2 of
18 *     the License, or (at your option) any later version.
19 *
20 *     This program is distributed in the hope that it will be useful,
21 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
22 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 *     GNU General Public License for more details.
24 *
25 *     You should have received a copy of the GNU General Public
26 *     License along with this program; if not, write to the Free
27 *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
28 *     USA.
29 *
30 **********************************************************************
31 */
32
33#include <linux/slab.h>
34
35#include "hwaccess.h"
36#include "8010.h"
37#include "cardmo.h"
38#include "irqmgr.h"
39
40/* Installs the IRQ handler for the MPU out port               *
41 * and initialize parameters                                    */
42
43int emu10k1_mpuout_open(struct emu10k1_card *card, struct midi_openinfo *openinfo)
44{
45	struct emu10k1_mpuout *card_mpuout = card->mpuout;
46
47	DPF(2, "emu10k1_mpuout_open()\n");
48
49	if (!(card_mpuout->status & FLAGS_AVAILABLE))
50		return -1;
51
52	/* Copy open info and mark channel as in use */
53	card_mpuout->intr = 0;
54	card_mpuout->openinfo = *openinfo;
55	card_mpuout->status &= ~FLAGS_AVAILABLE;
56	card_mpuout->laststatus = 0x80;
57	card_mpuout->firstmidiq = NULL;
58	card_mpuout->lastmidiq = NULL;
59
60	emu10k1_mpu_reset(card);
61	emu10k1_mpu_acquire(card);
62
63	return 0;
64}
65
66int emu10k1_mpuout_close(struct emu10k1_card *card)
67{
68	struct emu10k1_mpuout *card_mpuout = card->mpuout;
69	struct midi_queue *midiq;
70	struct midi_hdr *midihdr;
71	unsigned long flags;
72
73	DPF(2, "emu10k1_mpuout_close()\n");
74
75	emu10k1_irq_disable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE);
76
77	spin_lock_irqsave(&card_mpuout->lock, flags);
78
79	while (card_mpuout->firstmidiq != NULL) {
80		midiq = card_mpuout->firstmidiq;
81		midihdr = (struct midi_hdr *) midiq->refdata;
82
83		card_mpuout->firstmidiq = midiq->next;
84
85		kfree(midihdr->data);
86		kfree(midihdr);
87		kfree(midiq);
88	}
89
90	card_mpuout->lastmidiq = NULL;
91
92	emu10k1_mpu_release(card);
93
94	card_mpuout->status |= FLAGS_AVAILABLE;
95
96	spin_unlock_irqrestore(&card_mpuout->lock, flags);
97
98	return 0;
99}
100
101/* If there isn't enough buffer space, reject Midi Buffer.     *
102* Otherwise, disable TX, create object to hold Midi            *
103*  uffer, update buffer flags and other parameters             *
104* before enabling TX again.                                    */
105
106int emu10k1_mpuout_add_buffer(struct emu10k1_card *card, struct midi_hdr *midihdr)
107{
108	struct emu10k1_mpuout *card_mpuout = card->mpuout;
109	struct midi_queue *midiq;
110	unsigned long flags;
111
112	DPF(2, "emu10k1_mpuout_add_buffer()\n");
113
114	if (card_mpuout->state == CARDMIDIOUT_STATE_SUSPEND)
115		return 0;
116
117	midihdr->flags |= MIDIBUF_INQUEUE;
118	midihdr->flags &= ~MIDIBUF_DONE;
119
120	if ((midiq = kmalloc(sizeof(struct midi_queue), GFP_KERNEL)) == NULL) {
121		/* Message lost */
122		return -1;
123	}
124
125	midiq->next = NULL;
126	midiq->qtype = 1;
127	midiq->length = midihdr->bufferlength;
128	midiq->sizeLeft = midihdr->bufferlength;
129	midiq->midibyte = midihdr->data;
130
131	midiq->refdata = (unsigned long) midihdr;
132
133	spin_lock_irqsave(&card_mpuout->lock, flags);
134
135	if (card_mpuout->firstmidiq == NULL) {
136		card_mpuout->firstmidiq = midiq;
137		card_mpuout->lastmidiq = midiq;
138	} else {
139		(card_mpuout->lastmidiq)->next = midiq;
140		card_mpuout->lastmidiq = midiq;
141	}
142
143	card_mpuout->intr = 0;
144
145	emu10k1_irq_enable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE);
146
147	spin_unlock_irqrestore(&card_mpuout->lock, flags);
148
149	return 0;
150}
151
152void emu10k1_mpuout_bh(unsigned long refdata)
153{
154	struct emu10k1_card *card = (struct emu10k1_card *) refdata;
155	struct emu10k1_mpuout *card_mpuout = card->mpuout;
156	int cByteSent = 0;
157	struct midi_queue *midiq;
158	struct midi_queue *doneq = NULL;
159	unsigned long flags;
160
161	spin_lock_irqsave(&card_mpuout->lock, flags);
162
163	while (card_mpuout->firstmidiq != NULL) {
164		midiq = card_mpuout->firstmidiq;
165
166		while (cByteSent < 4 && midiq->sizeLeft) {
167			if (emu10k1_mpu_write_data(card, *midiq->midibyte) < 0) {
168				DPF(2, "emu10k1_mpuoutDpcCallback error!!\n");
169			} else {
170				++cByteSent;
171				--midiq->sizeLeft;
172				++midiq->midibyte;
173			}
174		}
175
176		if (midiq->sizeLeft == 0) {
177			if (doneq == NULL)
178				doneq = midiq;
179			card_mpuout->firstmidiq = midiq->next;
180		} else
181			break;
182	}
183
184	if (card_mpuout->firstmidiq == NULL)
185		card_mpuout->lastmidiq = NULL;
186
187	if (doneq != NULL) {
188		while (doneq != card_mpuout->firstmidiq) {
189			unsigned long callback_msg[3];
190
191			midiq = doneq;
192			doneq = midiq->next;
193
194			if (midiq->qtype) {
195				callback_msg[0] = 0;
196				callback_msg[1] = midiq->length;
197				callback_msg[2] = midiq->refdata;
198
199				emu10k1_midi_callback(ICARDMIDI_OUTLONGDATA, card_mpuout->openinfo.refdata, callback_msg);
200			} else if (((u8) midiq->refdata) < 0xF0 && ((u8) midiq->refdata) > 0x7F)
201				card_mpuout->laststatus = (u8) midiq->refdata;
202
203			kfree(midiq);
204		}
205	}
206
207	if ((card_mpuout->firstmidiq != NULL) || cByteSent) {
208		card_mpuout->intr = 0;
209		emu10k1_irq_enable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE);
210	}
211
212	spin_unlock_irqrestore(&card_mpuout->lock, flags);
213
214	return;
215}
216
217int emu10k1_mpuout_irqhandler(struct emu10k1_card *card)
218{
219	struct emu10k1_mpuout *card_mpuout = card->mpuout;
220
221	DPF(4, "emu10k1_mpuout_irqhandler\n");
222
223	card_mpuout->intr = 1;
224	emu10k1_irq_disable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE);
225
226	tasklet_hi_schedule(&card_mpuout->tasklet);
227
228	return 0;
229}
230