linux/sound/core/seq/oss/seq_oss_writeq.c
<<
>>
Prefs
   1/*
   2 * OSS compatible sequencer driver
   3 *
   4 * seq_oss_writeq.c - write queue and sync
   5 *
   6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  21 */
  22
  23#include "seq_oss_writeq.h"
  24#include "seq_oss_event.h"
  25#include "seq_oss_timer.h"
  26#include <sound/seq_oss_legacy.h>
  27#include "../seq_lock.h"
  28#include "../seq_clientmgr.h"
  29#include <linux/wait.h>
  30
  31
  32/*
  33 * create a write queue record
  34 */
  35struct seq_oss_writeq *
  36snd_seq_oss_writeq_new(struct seq_oss_devinfo *dp, int maxlen)
  37{
  38        struct seq_oss_writeq *q;
  39        struct snd_seq_client_pool pool;
  40
  41        if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL)
  42                return NULL;
  43        q->dp = dp;
  44        q->maxlen = maxlen;
  45        spin_lock_init(&q->sync_lock);
  46        q->sync_event_put = 0;
  47        q->sync_time = 0;
  48        init_waitqueue_head(&q->sync_sleep);
  49
  50        memset(&pool, 0, sizeof(pool));
  51        pool.client = dp->cseq;
  52        pool.output_pool = maxlen;
  53        pool.output_room = maxlen / 2;
  54
  55        snd_seq_oss_control(dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
  56
  57        return q;
  58}
  59
  60/*
  61 * delete the write queue
  62 */
  63void
  64snd_seq_oss_writeq_delete(struct seq_oss_writeq *q)
  65{
  66        if (q) {
  67                snd_seq_oss_writeq_clear(q);    /* to be sure */
  68                kfree(q);
  69        }
  70}
  71
  72
  73/*
  74 * reset the write queue
  75 */
  76void
  77snd_seq_oss_writeq_clear(struct seq_oss_writeq *q)
  78{
  79        struct snd_seq_remove_events reset;
  80
  81        memset(&reset, 0, sizeof(reset));
  82        reset.remove_mode = SNDRV_SEQ_REMOVE_OUTPUT; /* remove all */
  83        snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, &reset);
  84
  85        /* wake up sleepers if any */
  86        snd_seq_oss_writeq_wakeup(q, 0);
  87}
  88
  89/*
  90 * wait until the write buffer has enough room
  91 */
  92int
  93snd_seq_oss_writeq_sync(struct seq_oss_writeq *q)
  94{
  95        struct seq_oss_devinfo *dp = q->dp;
  96        abstime_t time;
  97
  98        time = snd_seq_oss_timer_cur_tick(dp->timer);
  99        if (q->sync_time >= time)
 100                return 0; /* already finished */
 101
 102        if (! q->sync_event_put) {
 103                struct snd_seq_event ev;
 104                union evrec *rec;
 105
 106                /* put echoback event */
 107                memset(&ev, 0, sizeof(ev));
 108                ev.flags = 0;
 109                ev.type = SNDRV_SEQ_EVENT_ECHO;
 110                ev.time.tick = time;
 111                /* echo back to itself */
 112                snd_seq_oss_fill_addr(dp, &ev, dp->addr.client, dp->addr.port);
 113                rec = (union evrec *)&ev.data;
 114                rec->t.code = SEQ_SYNCTIMER;
 115                rec->t.time = time;
 116                q->sync_event_put = 1;
 117                snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0);
 118        }
 119
 120        wait_event_interruptible_timeout(q->sync_sleep, ! q->sync_event_put, HZ);
 121        if (signal_pending(current))
 122                /* interrupted - return 0 to finish sync */
 123                q->sync_event_put = 0;
 124        if (! q->sync_event_put || q->sync_time >= time)
 125                return 0;
 126        return 1;
 127}
 128
 129/*
 130 * wake up sync - echo event was catched
 131 */
 132void
 133snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
 134{
 135        unsigned long flags;
 136
 137        spin_lock_irqsave(&q->sync_lock, flags);
 138        q->sync_time = time;
 139        q->sync_event_put = 0;
 140        if (waitqueue_active(&q->sync_sleep)) {
 141                wake_up(&q->sync_sleep);
 142        }
 143        spin_unlock_irqrestore(&q->sync_lock, flags);
 144}
 145
 146
 147/*
 148 * return the unused pool size
 149 */
 150int
 151snd_seq_oss_writeq_get_free_size(struct seq_oss_writeq *q)
 152{
 153        struct snd_seq_client_pool pool;
 154        pool.client = q->dp->cseq;
 155        snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
 156        return pool.output_free;
 157}
 158
 159
 160/*
 161 * set output threshold size from ioctl
 162 */
 163void
 164snd_seq_oss_writeq_set_output(struct seq_oss_writeq *q, int val)
 165{
 166        struct snd_seq_client_pool pool;
 167        pool.client = q->dp->cseq;
 168        snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
 169        pool.output_room = val;
 170        snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
 171}
 172
 173