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        if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL)
  31                return NULL;
  32        q->dp = dp;
  33        q->maxlen = maxlen;
  34        spin_lock_init(&q->sync_lock);
  35        q->sync_event_put = 0;
  36        q->sync_time = 0;
  37        init_waitqueue_head(&q->sync_sleep);
  38
  39        memset(&pool, 0, sizeof(pool));
  40        pool.client = dp->cseq;
  41        pool.output_pool = maxlen;
  42        pool.output_room = maxlen / 2;
  43
  44        snd_seq_oss_control(dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
  45
  46        return q;
  47}
  48
  49/*
  50 * delete the write queue
  51 */
  52void
  53snd_seq_oss_writeq_delete(struct seq_oss_writeq *q)
  54{
  55        if (q) {
  56                snd_seq_oss_writeq_clear(q);    /* to be sure */
  57                kfree(q);
  58        }
  59}
  60
  61
  62/*
  63 * reset the write queue
  64 */
  65void
  66snd_seq_oss_writeq_clear(struct seq_oss_writeq *q)
  67{
  68        struct snd_seq_remove_events reset;
  69
  70        memset(&reset, 0, sizeof(reset));
  71        reset.remove_mode = SNDRV_SEQ_REMOVE_OUTPUT; /* remove all */
  72        snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, &reset);
  73
  74        /* wake up sleepers if any */
  75        snd_seq_oss_writeq_wakeup(q, 0);
  76}
  77
  78/*
  79 * wait until the write buffer has enough room
  80 */
  81int
  82snd_seq_oss_writeq_sync(struct seq_oss_writeq *q)
  83{
  84        struct seq_oss_devinfo *dp = q->dp;
  85        abstime_t time;
  86
  87        time = snd_seq_oss_timer_cur_tick(dp->timer);
  88        if (q->sync_time >= time)
  89                return 0; /* already finished */
  90
  91        if (! q->sync_event_put) {
  92                struct snd_seq_event ev;
  93                union evrec *rec;
  94
  95                /* put echoback event */
  96                memset(&ev, 0, sizeof(ev));
  97                ev.flags = 0;
  98                ev.type = SNDRV_SEQ_EVENT_ECHO;
  99                ev.time.tick = time;
 100                /* echo back to itself */
 101                snd_seq_oss_fill_addr(dp, &ev, dp->addr.client, dp->addr.port);
 102                rec = (union evrec *)&ev.data;
 103                rec->t.code = SEQ_SYNCTIMER;
 104                rec->t.time = time;
 105                q->sync_event_put = 1;
 106                snd_seq_kernel_client_enqueue(dp->cseq, &ev, NULL, true);
 107        }
 108
 109        wait_event_interruptible_timeout(q->sync_sleep, ! q->sync_event_put, HZ);
 110        if (signal_pending(current))
 111                /* interrupted - return 0 to finish sync */
 112                q->sync_event_put = 0;
 113        if (! q->sync_event_put || q->sync_time >= time)
 114                return 0;
 115        return 1;
 116}
 117
 118/*
 119 * wake up sync - echo event was catched
 120 */
 121void
 122snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
 123{
 124        unsigned long flags;
 125
 126        spin_lock_irqsave(&q->sync_lock, flags);
 127        q->sync_time = time;
 128        q->sync_event_put = 0;
 129        wake_up(&q->sync_sleep);
 130        spin_unlock_irqrestore(&q->sync_lock, flags);
 131}
 132
 133
 134/*
 135 * return the unused pool size
 136 */
 137int
 138snd_seq_oss_writeq_get_free_size(struct seq_oss_writeq *q)
 139{
 140        struct snd_seq_client_pool pool;
 141        pool.client = q->dp->cseq;
 142        snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
 143        return pool.output_free;
 144}
 145
 146
 147/*
 148 * set output threshold size from ioctl
 149 */
 150void
 151snd_seq_oss_writeq_set_output(struct seq_oss_writeq *q, int val)
 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        pool.output_room = val;
 157        snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
 158}
 159
 160