ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / sound / oss / emu10k1 / cardmo.c
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
43 int 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
66 int 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
106 int 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 = (struct midi_queue *) 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
152 void 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
217 int 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 }