linux/sound/core/seq/oss/seq_oss_writeq.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * OSS compatible sequencer driver
   4 *
   5 * seq_oss_writeq.c - write queue and sync
   6 *
   7 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
   8 */
   9
  10#include "seq_oss_writeq.h"
  11#include "seq_oss_event.h"
  12#include "seq_oss_timer.h"
  13#include <sound/seq_oss_legacy.h>
  14#include "../seq_lock.h"
  15#include "../seq_clientmgr.h"
  16#include <linux/wait.h>
  17#include <linux/slab.h>
  18#include <linux/sched/signal.h>
  19
  20
  21/*
  22 * create a write queue record
  23 */
  24struct seq_oss_writeq *
  25snd_seq_oss_writeq_new(struct seq_oss_devinfo *dp, int maxlen)
  26{
  27        struct seq_oss_writeq *q;
  28        struct snd_seq_client_pool pool;
  29
  30        q = kzalloc(sizeof(*q), GFP_KERNEL);
  31        if (!q)
  32                return NULL;
  33        q->dp = dp;
  34        q->maxlen = maxlen;
  35        spin_lock_init(&q->sync_lock);
  36        q->sync_event_put = 0;
  37        q->sync_time = 0;
  38        init_waitqueue_head(&q->sync_sleep);
  39
  40        memset(&pool, 0, sizeof(pool));
  41        pool.client = dp->cseq;
  42        pool.output_pool = maxlen;
  43        pool.output_room = maxlen / 2;
  44
  45        snd_seq_oss_control(dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
  46
  47        return q;
  48}
  49
  50/*
  51 * delete the write queue
  52 */
  53void
  54snd_seq_oss_writeq_delete(struct seq_oss_writeq *q)
  55{
  56        if (q) {
  57                snd_seq_oss_writeq_clear(q);    /* to be sure */
  58                kfree(q);
  59        }
  60}
  61
  62
  63/*
  64 * reset the write queue
  65 */
  66void
  67snd_seq_oss_writeq_clear(struct seq_oss_writeq *q)
  68{
  69        struct snd_seq_remove_events reset;
  70
  71        memset(&reset, 0, sizeof(reset));
  72        reset.remove_mode = SNDRV_SEQ_REMOVE_OUTPUT; /* remove all */
  73        snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, &reset);
  74
  75        /* wake up sleepers if any */
  76        snd_seq_oss_writeq_wakeup(q, 0);
  77}
  78
  79/*
  80 * wait until the write buffer has enough room
  81 */
  82int
  83snd_seq_oss_writeq_sync(struct seq_oss_writeq *q)
  84{
  85        struct seq_oss_devinfo *dp = q->dp;
  86        abstime_t time;
  87
  88        time = snd_seq_oss_timer_cur_tick(dp->timer);
  89        if (q->sync_time >= time)
  90                return 0; /* already finished */
  91
  92        if (! q->sync_event_put) {
  93                struct snd_seq_event ev;
  94                union evrec *rec;
  95
  96                /* put echoback event */
  97                memset(&ev, 0, sizeof(ev));
  98                ev.flags = 0;
  99                ev.type = SNDRV_SEQ_EVENT_ECHO;
 100                ev.time.tick = time;
 101                /* echo back to itself */
 102                snd_seq_oss_fill_addr(dp, &ev, dp->addr.client, dp->addr.port);
 103                rec = (union evrec *)&ev.data;
 104                rec->t.code = SEQ_SYNCTIMER;
 105                rec->t.time = time;
 106                q->sync_event_put = 1;
 107                snd_seq_kernel_client_enqueue(dp->cseq, &ev, NULL, true);
 108        }
 109
 110        wait_event_interruptible_timeout(q->sync_sleep, ! q->sync_event_put, HZ);
 111        if (signal_pending(current))
 112                /* interrupted - return 0 to finish sync */
 113                q->sync_event_put = 0;
 114        if (! q->sync_event_put || q->sync_time >= time)
 115                return 0;
 116        return 1;
 117}
 118
 119/*
 120 * wake up sync - echo event was catched
 121 */
 122void
 123snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
 124{
 125        unsigned long flags;
 126
 127        spin_lock_irqsave(&q->sync_lock, flags);
 128        q->sync_time = time;
 129        q->sync_event_put = 0;
 130        wake_up(&q->sync_sleep);
 131        spin_unlock_irqrestore(&q->sync_lock, flags);
 132}
 133
 134
 135/*
 136 * return the unused pool size
 137 */
 138int
 139snd_seq_oss_writeq_get_free_size(struct seq_oss_writeq *q)
 140{
 141        struct snd_seq_client_pool pool;
 142        pool.client = q->dp->cseq;
 143        snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
 144        return pool.output_free;
 145}
 146
 147
 148/*
 149 * set output threshold size from ioctl
 150 */
 151void
 152snd_seq_oss_writeq_set_output(struct seq_oss_writeq *q, int val)
 153{
 154        struct snd_seq_client_pool pool;
 155        pool.client = q->dp->cseq;
 156        snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
 157        pool.output_room = val;
 158        snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
 159}
 160
 161