📄 mixart_core.c
字号:
/* * Driver for Digigram miXart soundcards * * low level interface with interrupt handling and mail box implementation * * Copyright (c) 2003 by Digigram <alsa@digigram.com> * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <sound/driver.h>#include <linux/interrupt.h>#include <asm/io.h>#include <sound/core.h>#include "mixart.h"#include "mixart_hwdep.h"#include "mixart_core.h"#define MSG_TIMEOUT_JIFFIES (400 * HZ) / 1000 /* 400 ms */#define MSG_DESCRIPTOR_SIZE 0x24#define MSG_HEADER_SIZE (MSG_DESCRIPTOR_SIZE + 4)#define MSG_DEFAULT_SIZE 512#define MSG_TYPE_MASK 0x00000003 /* mask for following types */#define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */#define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */#define MSG_TYPE_REQUEST 2 /* driver -> embedded (request will get an answer back) */#define MSG_TYPE_ANSWER 3 /* embedded -> driver */#define MSG_CANCEL_NOTIFY_MASK 0x80000000 /* this bit is set for a notification that has been canceled */static int retrieve_msg_frame(mixart_mgr_t *mgr, u32 *msg_frame){ /* read the message frame fifo */ u32 headptr, tailptr; tailptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL)); headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_HEAD)); if (tailptr == headptr) return 0; /* no message posted */ snd_assert( tailptr >= MSG_OUTBOUND_POST_STACK, return 0); /* error */ snd_assert( tailptr < (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE), return 0); /* error */ *msg_frame = readl_be(MIXART_MEM(mgr, tailptr)); /* increment the tail index */ tailptr += 4; if( tailptr >= (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) ) tailptr = MSG_OUTBOUND_POST_STACK; writel_be(tailptr, MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL)); return 1;}static int get_msg(mixart_mgr_t *mgr, mixart_msg_t *resp, u32 msg_frame_address ){ unsigned long flags; u32 headptr; u32 size; int err;#ifndef __BIG_ENDIAN unsigned int i;#endif spin_lock_irqsave(&mgr->msg_lock, flags); err = 0; /* copy message descriptor from miXart to driver */ size = readl_be(MIXART_MEM(mgr, msg_frame_address)); /* size of descriptor + response */ resp->message_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 4)); /* dwMessageID */ resp->uid.object_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 8)); /* uidDest */ resp->uid.desc = readl_be(MIXART_MEM(mgr, msg_frame_address + 12)); /* */ if( (size < MSG_DESCRIPTOR_SIZE) || (resp->size < (size - MSG_DESCRIPTOR_SIZE))) { err = -EINVAL; snd_printk(KERN_ERR "problem with response size = %d\n", size); goto _clean_exit; } size -= MSG_DESCRIPTOR_SIZE; memcpy_fromio(resp->data, MIXART_MEM(mgr, msg_frame_address + MSG_HEADER_SIZE ), size); resp->size = size; /* swap if necessary */#ifndef __BIG_ENDIAN size /= 4; /* u32 size */ for(i=0; i < size; i++) { ((u32*)resp->data)[i] = be32_to_cpu(((u32*)resp->data)[i]); }#endif /* * free message frame address */ headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD)); if( (headptr < MSG_OUTBOUND_FREE_STACK) || ( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) { err = -EINVAL; goto _clean_exit; } /* give address back to outbound fifo */ writel_be(msg_frame_address, MIXART_MEM(mgr, headptr)); /* increment the outbound free head */ headptr += 4; if( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) ) headptr = MSG_OUTBOUND_FREE_STACK; writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD)); _clean_exit: spin_unlock_irqrestore(&mgr->msg_lock, flags); return err;}/* * send a message to miXart. return: the msg_frame used for this message *//* call with mgr->msg_lock held! */static int send_msg( mixart_mgr_t *mgr, mixart_msg_t *msg, int max_answersize, int mark_pending, u32 *msg_event){ u32 headptr, tailptr; u32 msg_frame_address; int err, i; snd_assert(msg->size % 4 == 0, return -EINVAL); err = 0; /* get message frame address */ tailptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL)); headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_HEAD)); if (tailptr == headptr) { snd_printk(KERN_ERR "error: no message frame available\n"); return -EBUSY; } if( (tailptr < MSG_INBOUND_FREE_STACK) || (tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) { return -EINVAL; } msg_frame_address = readl_be(MIXART_MEM(mgr, tailptr)); writel(0, MIXART_MEM(mgr, tailptr)); /* set address to zero on this fifo position */ /* increment the inbound free tail */ tailptr += 4; if( tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) ) tailptr = MSG_INBOUND_FREE_STACK; writel_be(tailptr, MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL)); /* TODO : use memcpy_toio() with intermediate buffer to copy the message */ /* copy message descriptor to card memory */ writel_be( msg->size + MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address) ); /* size of descriptor + request */ writel_be( msg->message_id , MIXART_MEM(mgr, msg_frame_address + 4) ); /* dwMessageID */ writel_be( msg->uid.object_id, MIXART_MEM(mgr, msg_frame_address + 8) ); /* uidDest */ writel_be( msg->uid.desc, MIXART_MEM(mgr, msg_frame_address + 12) ); /* */ writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 16) ); /* SizeHeader */ writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 20) ); /* OffsetDLL_T16 */ writel_be( msg->size, MIXART_MEM(mgr, msg_frame_address + 24) ); /* SizeDLL_T16 */ writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 28) ); /* OffsetDLL_DRV */ writel_be( 0, MIXART_MEM(mgr, msg_frame_address + 32) ); /* SizeDLL_DRV */ writel_be( MSG_DESCRIPTOR_SIZE + max_answersize, MIXART_MEM(mgr, msg_frame_address + 36) ); /* dwExpectedAnswerSize */ /* copy message data to card memory */ for( i=0; i < msg->size; i+=4 ) { writel_be( *(u32*)(msg->data + i), MIXART_MEM(mgr, MSG_HEADER_SIZE + msg_frame_address + i) ); } if( mark_pending ) { if( *msg_event ) { /* the pending event is the notification we wait for ! */ mgr->pending_event = *msg_event; } else { /* the pending event is the answer we wait for (same address than the request)! */ mgr->pending_event = msg_frame_address; /* copy address back to caller */ *msg_event = msg_frame_address; } } /* mark the frame as a request (will have an answer) */ msg_frame_address |= MSG_TYPE_REQUEST; /* post the frame */ headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD)); if( (headptr < MSG_INBOUND_POST_STACK) || (headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE))) { return -EINVAL; } writel_be(msg_frame_address, MIXART_MEM(mgr, headptr)); /* increment the inbound post head */ headptr += 4; if( headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) ) headptr = MSG_INBOUND_POST_STACK; writel_be(headptr, MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD)); return 0;}int snd_mixart_send_msg(mixart_mgr_t *mgr, mixart_msg_t *request, int max_resp_size, void *resp_data){ mixart_msg_t resp; u32 msg_frame = 0; /* set to 0, so it's no notification to wait for, but the answer */ int err; wait_queue_t wait; long timeout; down(&mgr->msg_mutex); init_waitqueue_entry(&wait, current); spin_lock_irq(&mgr->msg_lock); /* send the message */ err = send_msg(mgr, request, max_resp_size, 1, &msg_frame); /* send and mark the answer pending */ if (err) { spin_unlock_irq(&mgr->msg_lock); up(&mgr->msg_mutex); return err; } set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&mgr->msg_sleep, &wait); spin_unlock_irq(&mgr->msg_lock); timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES); remove_wait_queue(&mgr->msg_sleep, &wait); if (! timeout) { /* error - no ack */ up(&mgr->msg_mutex); snd_printk(KERN_ERR "error: no reponse on msg %x\n", msg_frame); return -EIO; } /* retrieve the answer into the same mixart_msg_t */ resp.message_id = 0; resp.uid = (mixart_uid_t){0,0}; resp.data = resp_data; resp.size = max_resp_size; err = get_msg(mgr, &resp, msg_frame); if( request->message_id != resp.message_id ) snd_printk(KERN_ERR "REPONSE ERROR!\n"); up(&mgr->msg_mutex); return err;}int snd_mixart_send_msg_wait_notif(mixart_mgr_t *mgr, mixart_msg_t *request, u32 notif_event){ int err; wait_queue_t wait; long timeout; snd_assert(notif_event != 0, return -EINVAL); snd_assert((notif_event & MSG_TYPE_MASK) == MSG_TYPE_NOTIFY, return -EINVAL); snd_assert((notif_event & MSG_CANCEL_NOTIFY_MASK) == 0, return -EINVAL); down(&mgr->msg_mutex); init_waitqueue_entry(&wait, current);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -