cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

seq_oss_timer.c (5228B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * OSS compatible sequencer driver
      4 *
      5 * Timer control routines
      6 *
      7 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
      8 */
      9
     10#include "seq_oss_timer.h"
     11#include "seq_oss_event.h"
     12#include <sound/seq_oss_legacy.h>
     13#include <linux/slab.h>
     14
     15/*
     16 */
     17#define MIN_OSS_TEMPO		8
     18#define MAX_OSS_TEMPO		360
     19#define MIN_OSS_TIMEBASE	1
     20#define MAX_OSS_TIMEBASE	1000
     21
     22/*
     23 */
     24static void calc_alsa_tempo(struct seq_oss_timer *timer);
     25static int send_timer_event(struct seq_oss_devinfo *dp, int type, int value);
     26
     27
     28/*
     29 * create and register a new timer.
     30 * if queue is not started yet, start it.
     31 */
     32struct seq_oss_timer *
     33snd_seq_oss_timer_new(struct seq_oss_devinfo *dp)
     34{
     35	struct seq_oss_timer *rec;
     36
     37	rec = kzalloc(sizeof(*rec), GFP_KERNEL);
     38	if (rec == NULL)
     39		return NULL;
     40
     41	rec->dp = dp;
     42	rec->cur_tick = 0;
     43	rec->realtime = 0;
     44	rec->running = 0;
     45	rec->oss_tempo = 60;
     46	rec->oss_timebase = 100;
     47	calc_alsa_tempo(rec);
     48
     49	return rec;
     50}
     51
     52
     53/*
     54 * delete timer.
     55 * if no more timer exists, stop the queue.
     56 */
     57void
     58snd_seq_oss_timer_delete(struct seq_oss_timer *rec)
     59{
     60	if (rec) {
     61		snd_seq_oss_timer_stop(rec);
     62		kfree(rec);
     63	}
     64}
     65
     66
     67/*
     68 * process one timing event
     69 * return 1 : event proceseed -- skip this event
     70 *        0 : not a timer event -- enqueue this event
     71 */
     72int
     73snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *ev)
     74{
     75	abstime_t parm = ev->t.time;
     76
     77	if (ev->t.code == EV_TIMING) {
     78		switch (ev->t.cmd) {
     79		case TMR_WAIT_REL:
     80			parm += rec->cur_tick;
     81			rec->realtime = 0;
     82			fallthrough;
     83		case TMR_WAIT_ABS:
     84			if (parm == 0) {
     85				rec->realtime = 1;
     86			} else if (parm >= rec->cur_tick) {
     87				rec->realtime = 0;
     88				rec->cur_tick = parm;
     89			}
     90			return 1;	/* skip this event */
     91			
     92		case TMR_START:
     93			snd_seq_oss_timer_start(rec);
     94			return 1;
     95
     96		}
     97	} else if (ev->s.code == SEQ_WAIT) {
     98		/* time = from 1 to 3 bytes */
     99		parm = (ev->echo >> 8) & 0xffffff;
    100		if (parm > rec->cur_tick) {
    101			/* set next event time */
    102			rec->cur_tick = parm;
    103			rec->realtime = 0;
    104		}
    105		return 1;
    106	}
    107
    108	return 0;
    109}
    110
    111
    112/*
    113 * convert tempo units
    114 */
    115static void
    116calc_alsa_tempo(struct seq_oss_timer *timer)
    117{
    118	timer->tempo = (60 * 1000000) / timer->oss_tempo;
    119	timer->ppq = timer->oss_timebase;
    120}
    121
    122
    123/*
    124 * dispatch a timer event
    125 */
    126static int
    127send_timer_event(struct seq_oss_devinfo *dp, int type, int value)
    128{
    129	struct snd_seq_event ev;
    130
    131	memset(&ev, 0, sizeof(ev));
    132	ev.type = type;
    133	ev.source.client = dp->cseq;
    134	ev.source.port = 0;
    135	ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM;
    136	ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
    137	ev.queue = dp->queue;
    138	ev.data.queue.queue = dp->queue;
    139	ev.data.queue.param.value = value;
    140	return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 1, 0);
    141}
    142
    143/*
    144 * set queue tempo and start queue
    145 */
    146int
    147snd_seq_oss_timer_start(struct seq_oss_timer *timer)
    148{
    149	struct seq_oss_devinfo *dp = timer->dp;
    150	struct snd_seq_queue_tempo tmprec;
    151
    152	if (timer->running)
    153		snd_seq_oss_timer_stop(timer);
    154
    155	memset(&tmprec, 0, sizeof(tmprec));
    156	tmprec.queue = dp->queue;
    157	tmprec.ppq = timer->ppq;
    158	tmprec.tempo = timer->tempo;
    159	snd_seq_set_queue_tempo(dp->cseq, &tmprec);
    160
    161	send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0);
    162	timer->running = 1;
    163	timer->cur_tick = 0;
    164	return 0;
    165}
    166
    167
    168/*
    169 * stop queue
    170 */
    171int
    172snd_seq_oss_timer_stop(struct seq_oss_timer *timer)
    173{
    174	if (! timer->running)
    175		return 0;
    176	send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0);
    177	timer->running = 0;
    178	return 0;
    179}
    180
    181
    182/*
    183 * continue queue
    184 */
    185int
    186snd_seq_oss_timer_continue(struct seq_oss_timer *timer)
    187{
    188	if (timer->running)
    189		return 0;
    190	send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0);
    191	timer->running = 1;
    192	return 0;
    193}
    194
    195
    196/*
    197 * change queue tempo
    198 */
    199int
    200snd_seq_oss_timer_tempo(struct seq_oss_timer *timer, int value)
    201{
    202	if (value < MIN_OSS_TEMPO)
    203		value = MIN_OSS_TEMPO;
    204	else if (value > MAX_OSS_TEMPO)
    205		value = MAX_OSS_TEMPO;
    206	timer->oss_tempo = value;
    207	calc_alsa_tempo(timer);
    208	if (timer->running)
    209		send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo);
    210	return 0;
    211}
    212
    213
    214/*
    215 * ioctls
    216 */
    217int
    218snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __user *arg)
    219{
    220	int value;
    221
    222	if (cmd == SNDCTL_SEQ_CTRLRATE) {
    223		/* if *arg == 0, just return the current rate */
    224		if (get_user(value, arg))
    225			return -EFAULT;
    226		if (value)
    227			return -EINVAL;
    228		value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60;
    229		return put_user(value, arg) ? -EFAULT : 0;
    230	}
    231
    232	if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
    233		return 0;
    234
    235	switch (cmd) {
    236	case SNDCTL_TMR_START:
    237		return snd_seq_oss_timer_start(timer);
    238	case SNDCTL_TMR_STOP:
    239		return snd_seq_oss_timer_stop(timer);
    240	case SNDCTL_TMR_CONTINUE:
    241		return snd_seq_oss_timer_continue(timer);
    242	case SNDCTL_TMR_TEMPO:
    243		if (get_user(value, arg))
    244			return -EFAULT;
    245		return snd_seq_oss_timer_tempo(timer, value);
    246	case SNDCTL_TMR_TIMEBASE:
    247		if (get_user(value, arg))
    248			return -EFAULT;
    249		if (value < MIN_OSS_TIMEBASE)
    250			value = MIN_OSS_TIMEBASE;
    251		else if (value > MAX_OSS_TIMEBASE)
    252			value = MAX_OSS_TIMEBASE;
    253		timer->oss_timebase = value;
    254		calc_alsa_tempo(timer);
    255		return 0;
    256
    257	case SNDCTL_TMR_METRONOME:
    258	case SNDCTL_TMR_SELECT:
    259	case SNDCTL_TMR_SOURCE:
    260		/* not supported */
    261		return 0;
    262	}
    263	return 0;
    264}