|  | /* | 
|  | ********************************************************************** | 
|  | *     cardmo.c - MIDI UART output HAL for emu10k1 driver | 
|  | *     Copyright 1999, 2000 Creative Labs, Inc. | 
|  | * | 
|  | ********************************************************************** | 
|  | * | 
|  | *     Date                 Author          Summary of changes | 
|  | *     ----                 ------          ------------------ | 
|  | *     October 20, 1999     Bertrand Lee    base code release | 
|  | *     November 2, 1999     Alan Cox        cleaned up | 
|  | * | 
|  | ********************************************************************** | 
|  | * | 
|  | *     This program is free software; you can redistribute it and/or | 
|  | *     modify it under the terms of the GNU General Public License as | 
|  | *     published by the Free Software Foundation; either version 2 of | 
|  | *     the License, or (at your option) any later version. | 
|  | * | 
|  | *     This program is distributed in the hope that it will be useful, | 
|  | *     but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | *     GNU General Public License for more details. | 
|  | * | 
|  | *     You should have received a copy of the GNU General Public | 
|  | *     License along with this program; if not, write to the Free | 
|  | *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, | 
|  | *     USA. | 
|  | * | 
|  | ********************************************************************** | 
|  | */ | 
|  |  | 
|  | #include <linux/slab.h> | 
|  |  | 
|  | #include "hwaccess.h" | 
|  | #include "8010.h" | 
|  | #include "cardmo.h" | 
|  | #include "irqmgr.h" | 
|  |  | 
|  | /* Installs the IRQ handler for the MPU out port               * | 
|  | * and initialize parameters                                    */ | 
|  |  | 
|  | int emu10k1_mpuout_open(struct emu10k1_card *card, struct midi_openinfo *openinfo) | 
|  | { | 
|  | struct emu10k1_mpuout *card_mpuout = card->mpuout; | 
|  |  | 
|  | DPF(2, "emu10k1_mpuout_open()\n"); | 
|  |  | 
|  | if (!(card_mpuout->status & FLAGS_AVAILABLE)) | 
|  | return -1; | 
|  |  | 
|  | /* Copy open info and mark channel as in use */ | 
|  | card_mpuout->intr = 0; | 
|  | card_mpuout->openinfo = *openinfo; | 
|  | card_mpuout->status &= ~FLAGS_AVAILABLE; | 
|  | card_mpuout->laststatus = 0x80; | 
|  | card_mpuout->firstmidiq = NULL; | 
|  | card_mpuout->lastmidiq = NULL; | 
|  |  | 
|  | emu10k1_mpu_reset(card); | 
|  | emu10k1_mpu_acquire(card); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int emu10k1_mpuout_close(struct emu10k1_card *card) | 
|  | { | 
|  | struct emu10k1_mpuout *card_mpuout = card->mpuout; | 
|  | struct midi_queue *midiq; | 
|  | struct midi_hdr *midihdr; | 
|  | unsigned long flags; | 
|  |  | 
|  | DPF(2, "emu10k1_mpuout_close()\n"); | 
|  |  | 
|  | emu10k1_irq_disable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE); | 
|  |  | 
|  | spin_lock_irqsave(&card_mpuout->lock, flags); | 
|  |  | 
|  | while (card_mpuout->firstmidiq != NULL) { | 
|  | midiq = card_mpuout->firstmidiq; | 
|  | midihdr = (struct midi_hdr *) midiq->refdata; | 
|  |  | 
|  | card_mpuout->firstmidiq = midiq->next; | 
|  |  | 
|  | kfree(midihdr->data); | 
|  | kfree(midihdr); | 
|  | kfree(midiq); | 
|  | } | 
|  |  | 
|  | card_mpuout->lastmidiq = NULL; | 
|  |  | 
|  | emu10k1_mpu_release(card); | 
|  |  | 
|  | card_mpuout->status |= FLAGS_AVAILABLE; | 
|  |  | 
|  | spin_unlock_irqrestore(&card_mpuout->lock, flags); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* If there isn't enough buffer space, reject Midi Buffer.     * | 
|  | * Otherwise, disable TX, create object to hold Midi            * | 
|  | *  uffer, update buffer flags and other parameters             * | 
|  | * before enabling TX again.                                    */ | 
|  |  | 
|  | int emu10k1_mpuout_add_buffer(struct emu10k1_card *card, struct midi_hdr *midihdr) | 
|  | { | 
|  | struct emu10k1_mpuout *card_mpuout = card->mpuout; | 
|  | struct midi_queue *midiq; | 
|  | unsigned long flags; | 
|  |  | 
|  | DPF(2, "emu10k1_mpuout_add_buffer()\n"); | 
|  |  | 
|  | if (card_mpuout->state == CARDMIDIOUT_STATE_SUSPEND) | 
|  | return 0; | 
|  |  | 
|  | midihdr->flags |= MIDIBUF_INQUEUE; | 
|  | midihdr->flags &= ~MIDIBUF_DONE; | 
|  |  | 
|  | if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_KERNEL)) == NULL) { | 
|  | /* Message lost */ | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | midiq->next = NULL; | 
|  | midiq->qtype = 1; | 
|  | midiq->length = midihdr->bufferlength; | 
|  | midiq->sizeLeft = midihdr->bufferlength; | 
|  | midiq->midibyte = midihdr->data; | 
|  |  | 
|  | midiq->refdata = (unsigned long) midihdr; | 
|  |  | 
|  | spin_lock_irqsave(&card_mpuout->lock, flags); | 
|  |  | 
|  | if (card_mpuout->firstmidiq == NULL) { | 
|  | card_mpuout->firstmidiq = midiq; | 
|  | card_mpuout->lastmidiq = midiq; | 
|  | } else { | 
|  | (card_mpuout->lastmidiq)->next = midiq; | 
|  | card_mpuout->lastmidiq = midiq; | 
|  | } | 
|  |  | 
|  | card_mpuout->intr = 0; | 
|  |  | 
|  | emu10k1_irq_enable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE); | 
|  |  | 
|  | spin_unlock_irqrestore(&card_mpuout->lock, flags); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void emu10k1_mpuout_bh(unsigned long refdata) | 
|  | { | 
|  | struct emu10k1_card *card = (struct emu10k1_card *) refdata; | 
|  | struct emu10k1_mpuout *card_mpuout = card->mpuout; | 
|  | int cByteSent = 0; | 
|  | struct midi_queue *midiq; | 
|  | struct midi_queue *doneq = NULL; | 
|  | unsigned long flags; | 
|  |  | 
|  | spin_lock_irqsave(&card_mpuout->lock, flags); | 
|  |  | 
|  | while (card_mpuout->firstmidiq != NULL) { | 
|  | midiq = card_mpuout->firstmidiq; | 
|  |  | 
|  | while (cByteSent < 4 && midiq->sizeLeft) { | 
|  | if (emu10k1_mpu_write_data(card, *midiq->midibyte) < 0) { | 
|  | DPF(2, "emu10k1_mpuoutDpcCallback error!!\n"); | 
|  | } else { | 
|  | ++cByteSent; | 
|  | --midiq->sizeLeft; | 
|  | ++midiq->midibyte; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (midiq->sizeLeft == 0) { | 
|  | if (doneq == NULL) | 
|  | doneq = midiq; | 
|  | card_mpuout->firstmidiq = midiq->next; | 
|  | } else | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (card_mpuout->firstmidiq == NULL) | 
|  | card_mpuout->lastmidiq = NULL; | 
|  |  | 
|  | if (doneq != NULL) { | 
|  | while (doneq != card_mpuout->firstmidiq) { | 
|  | unsigned long callback_msg[3]; | 
|  |  | 
|  | midiq = doneq; | 
|  | doneq = midiq->next; | 
|  |  | 
|  | if (midiq->qtype) { | 
|  | callback_msg[0] = 0; | 
|  | callback_msg[1] = midiq->length; | 
|  | callback_msg[2] = midiq->refdata; | 
|  |  | 
|  | emu10k1_midi_callback(ICARDMIDI_OUTLONGDATA, card_mpuout->openinfo.refdata, callback_msg); | 
|  | } else if (((u8) midiq->refdata) < 0xF0 && ((u8) midiq->refdata) > 0x7F) | 
|  | card_mpuout->laststatus = (u8) midiq->refdata; | 
|  |  | 
|  | kfree(midiq); | 
|  | } | 
|  | } | 
|  |  | 
|  | if ((card_mpuout->firstmidiq != NULL) || cByteSent) { | 
|  | card_mpuout->intr = 0; | 
|  | emu10k1_irq_enable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE); | 
|  | } | 
|  |  | 
|  | spin_unlock_irqrestore(&card_mpuout->lock, flags); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | int emu10k1_mpuout_irqhandler(struct emu10k1_card *card) | 
|  | { | 
|  | struct emu10k1_mpuout *card_mpuout = card->mpuout; | 
|  |  | 
|  | DPF(4, "emu10k1_mpuout_irqhandler\n"); | 
|  |  | 
|  | card_mpuout->intr = 1; | 
|  | emu10k1_irq_disable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE); | 
|  |  | 
|  | tasklet_hi_schedule(&card_mpuout->tasklet); | 
|  |  | 
|  | return 0; | 
|  | } |