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

dmasound_q40.c (14397B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  linux/sound/oss/dmasound/dmasound_q40.c
      4 *
      5 *  Q40 DMA Sound Driver
      6 *
      7 *  See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
      8 *  prior to 28/01/2001
      9 *
     10 *  28/01/2001 [0.1] Iain Sandoe
     11 *		     - added versioning
     12 *		     - put in and populated the hardware_afmts field.
     13 *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
     14 *	       [0.3] - put in default hard/soft settings.
     15 */
     16
     17
     18#include <linux/module.h>
     19#include <linux/init.h>
     20#include <linux/slab.h>
     21#include <linux/soundcard.h>
     22#include <linux/interrupt.h>
     23
     24#include <linux/uaccess.h>
     25#include <asm/q40ints.h>
     26#include <asm/q40_master.h>
     27
     28#include "dmasound.h"
     29
     30#define DMASOUND_Q40_REVISION 0
     31#define DMASOUND_Q40_EDITION 3
     32
     33static int expand_bal;	/* Balance factor for expanding (not volume!) */
     34static int expand_data;	/* Data for expanding */
     35
     36
     37/*** Low level stuff *********************************************************/
     38
     39
     40static void *Q40Alloc(unsigned int size, gfp_t flags);
     41static void Q40Free(void *, unsigned int);
     42static int Q40IrqInit(void);
     43#ifdef MODULE
     44static void Q40IrqCleanUp(void);
     45#endif
     46static void Q40Silence(void);
     47static void Q40Init(void);
     48static int Q40SetFormat(int format);
     49static int Q40SetVolume(int volume);
     50static void Q40PlayNextFrame(int index);
     51static void Q40Play(void);
     52static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
     53static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
     54static void Q40Interrupt(void);
     55
     56
     57/*** Mid level stuff *********************************************************/
     58
     59
     60
     61/* userCount, frameUsed, frameLeft == byte counts */
     62static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,
     63			   u_char frame[], ssize_t *frameUsed,
     64			   ssize_t frameLeft)
     65{
     66	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
     67	ssize_t count, used;
     68	u_char *p = (u_char *) &frame[*frameUsed];
     69
     70	used = count = min_t(size_t, userCount, frameLeft);
     71	if (copy_from_user(p,userPtr,count))
     72	  return -EFAULT;
     73	while (count > 0) {
     74		*p = table[*p]+128;
     75		p++;
     76		count--;
     77	}
     78	*frameUsed += used ;
     79	return used;
     80}
     81
     82
     83static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
     84			  u_char frame[], ssize_t *frameUsed,
     85			  ssize_t frameLeft)
     86{
     87	ssize_t count, used;
     88	u_char *p = (u_char *) &frame[*frameUsed];
     89
     90	used = count = min_t(size_t, userCount, frameLeft);
     91	if (copy_from_user(p,userPtr,count))
     92	  return -EFAULT;
     93	while (count > 0) {
     94		*p = *p + 128;
     95		p++;
     96		count--;
     97	}
     98	*frameUsed += used;
     99	return used;
    100}
    101
    102static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
    103			  u_char frame[], ssize_t *frameUsed,
    104			  ssize_t frameLeft)
    105{
    106	ssize_t count, used;
    107	u_char *p = (u_char *) &frame[*frameUsed];
    108
    109	used = count = min_t(size_t, userCount, frameLeft);
    110	if (copy_from_user(p,userPtr,count))
    111	  return -EFAULT;
    112	*frameUsed += used;
    113	return used;
    114}
    115
    116
    117/* a bit too complicated to optimise right now ..*/
    118static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
    119			    u_char frame[], ssize_t *frameUsed,
    120			    ssize_t frameLeft)
    121{
    122	unsigned char *table = (unsigned char *)
    123		(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
    124	unsigned int data = expand_data;
    125	u_char *p = (u_char *) &frame[*frameUsed];
    126	int bal = expand_bal;
    127	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
    128	int utotal, ftotal;
    129
    130	ftotal = frameLeft;
    131	utotal = userCount;
    132	while (frameLeft) {
    133		u_char c;
    134		if (bal < 0) {
    135			if (userCount == 0)
    136				break;
    137			if (get_user(c, userPtr++))
    138				return -EFAULT;
    139			data = table[c];
    140			data += 0x80;
    141			userCount--;
    142			bal += hSpeed;
    143		}
    144		*p++ = data;
    145		frameLeft--;
    146		bal -= sSpeed;
    147	}
    148	expand_bal = bal;
    149	expand_data = data;
    150	*frameUsed += (ftotal - frameLeft);
    151	utotal -= userCount;
    152	return utotal;
    153}
    154
    155
    156static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
    157			   u_char frame[], ssize_t *frameUsed,
    158			   ssize_t frameLeft)
    159{
    160	u_char *p = (u_char *) &frame[*frameUsed];
    161	unsigned int data = expand_data;
    162	int bal = expand_bal;
    163	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
    164	int utotal, ftotal;
    165
    166
    167	ftotal = frameLeft;
    168	utotal = userCount;
    169	while (frameLeft) {
    170		u_char c;
    171		if (bal < 0) {
    172			if (userCount == 0)
    173				break;
    174			if (get_user(c, userPtr++))
    175				return -EFAULT;
    176			data = c ;
    177			data += 0x80;
    178			userCount--;
    179			bal += hSpeed;
    180		}
    181		*p++ = data;
    182		frameLeft--;
    183		bal -= sSpeed;
    184	}
    185	expand_bal = bal;
    186	expand_data = data;
    187	*frameUsed += (ftotal - frameLeft);
    188	utotal -= userCount;
    189	return utotal;
    190}
    191
    192
    193static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
    194			   u_char frame[], ssize_t *frameUsed,
    195			   ssize_t frameLeft)
    196{
    197	u_char *p = (u_char *) &frame[*frameUsed];
    198	unsigned int data = expand_data;
    199	int bal = expand_bal;
    200	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
    201	int utotal, ftotal;
    202
    203	ftotal = frameLeft;
    204	utotal = userCount;
    205	while (frameLeft) {
    206		u_char c;
    207		if (bal < 0) {
    208			if (userCount == 0)
    209				break;
    210			if (get_user(c, userPtr++))
    211				return -EFAULT;
    212			data = c ;
    213			userCount--;
    214			bal += hSpeed;
    215		}
    216		*p++ = data;
    217		frameLeft--;
    218		bal -= sSpeed;
    219	}
    220	expand_bal = bal;
    221	expand_data = data;
    222	*frameUsed += (ftotal - frameLeft) ;
    223	utotal -= userCount;
    224	return utotal;
    225}
    226
    227/* compressing versions */
    228static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
    229			    u_char frame[], ssize_t *frameUsed,
    230			    ssize_t frameLeft)
    231{
    232	unsigned char *table = (unsigned char *)
    233		(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
    234	unsigned int data = expand_data;
    235	u_char *p = (u_char *) &frame[*frameUsed];
    236	int bal = expand_bal;
    237	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
    238	int utotal, ftotal;
    239 
    240	ftotal = frameLeft;
    241	utotal = userCount;
    242	while (frameLeft) {
    243		u_char c;
    244		while(bal<0) {
    245			if (userCount == 0)
    246				goto lout;
    247			if (!(bal<(-hSpeed))) {
    248				if (get_user(c, userPtr))
    249					return -EFAULT;
    250				data = 0x80 + table[c];
    251			}
    252			userPtr++;
    253			userCount--;
    254			bal += hSpeed;
    255		}
    256		*p++ = data;
    257		frameLeft--;
    258		bal -= sSpeed;
    259	}
    260 lout:
    261	expand_bal = bal;
    262	expand_data = data;
    263	*frameUsed += (ftotal - frameLeft);
    264	utotal -= userCount;
    265	return utotal;
    266}
    267
    268
    269static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
    270			   u_char frame[], ssize_t *frameUsed,
    271			   ssize_t frameLeft)
    272{
    273	u_char *p = (u_char *) &frame[*frameUsed];
    274	unsigned int data = expand_data;
    275	int bal = expand_bal;
    276	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
    277	int utotal, ftotal;
    278
    279	ftotal = frameLeft;
    280	utotal = userCount;
    281	while (frameLeft) {
    282		u_char c;
    283		while (bal < 0) {
    284			if (userCount == 0)
    285				goto lout;
    286			if (!(bal<(-hSpeed))) {
    287				if (get_user(c, userPtr))
    288					return -EFAULT;
    289				data = c + 0x80;
    290			}
    291			userPtr++;
    292			userCount--;
    293			bal += hSpeed;
    294		}
    295		*p++ = data;
    296		frameLeft--;
    297		bal -= sSpeed;
    298	}
    299 lout:
    300	expand_bal = bal;
    301	expand_data = data;
    302	*frameUsed += (ftotal - frameLeft);
    303	utotal -= userCount;
    304	return utotal;
    305}
    306
    307
    308static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
    309			   u_char frame[], ssize_t *frameUsed,
    310			   ssize_t frameLeft)
    311{
    312	u_char *p = (u_char *) &frame[*frameUsed];
    313	unsigned int data = expand_data;
    314	int bal = expand_bal;
    315	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
    316	int utotal, ftotal;
    317
    318	ftotal = frameLeft;
    319	utotal = userCount;
    320	while (frameLeft) {
    321		u_char c;
    322		while (bal < 0) {
    323			if (userCount == 0)
    324				goto lout;
    325			if (!(bal<(-hSpeed))) {
    326				if (get_user(c, userPtr))
    327					return -EFAULT;
    328				data = c ;
    329			}
    330			userPtr++;
    331			userCount--;
    332			bal += hSpeed;
    333		}
    334		*p++ = data;
    335		frameLeft--;
    336		bal -= sSpeed;
    337	}
    338 lout:
    339	expand_bal = bal;
    340	expand_data = data;
    341	*frameUsed += (ftotal - frameLeft) ;
    342	utotal -= userCount;
    343	return utotal;
    344}
    345
    346
    347static TRANS transQ40Normal = {
    348	q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
    349};
    350
    351static TRANS transQ40Expanding = {
    352	q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
    353};
    354
    355static TRANS transQ40Compressing = {
    356	q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
    357};
    358
    359
    360/*** Low level stuff *********************************************************/
    361
    362static void *Q40Alloc(unsigned int size, gfp_t flags)
    363{
    364         return kmalloc(size, flags); /* change to vmalloc */
    365}
    366
    367static void Q40Free(void *ptr, unsigned int size)
    368{
    369	kfree(ptr);
    370}
    371
    372static int __init Q40IrqInit(void)
    373{
    374	/* Register interrupt handler. */
    375	if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
    376		    "DMA sound", Q40Interrupt))
    377		return 0;
    378
    379	return(1);
    380}
    381
    382
    383#ifdef MODULE
    384static void Q40IrqCleanUp(void)
    385{
    386        master_outb(0,SAMPLE_ENABLE_REG);
    387	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
    388}
    389#endif /* MODULE */
    390
    391
    392static void Q40Silence(void)
    393{
    394        master_outb(0,SAMPLE_ENABLE_REG);
    395	*DAC_LEFT=*DAC_RIGHT=127;
    396}
    397
    398static char *q40_pp;
    399static unsigned int q40_sc;
    400
    401static void Q40PlayNextFrame(int index)
    402{
    403	u_char *start;
    404	u_long size;
    405	u_char speed;
    406	int error;
    407
    408	/* used by Q40Play() if all doubts whether there really is something
    409	 * to be played are already wiped out.
    410	 */
    411	start = write_sq.buffers[write_sq.front];
    412	size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
    413
    414	q40_pp=start;
    415	q40_sc=size;
    416
    417	write_sq.front = (write_sq.front+1) % write_sq.max_count;
    418	write_sq.active++;
    419
    420	speed=(dmasound.hard.speed==10000 ? 0 : 1);
    421
    422	master_outb( 0,SAMPLE_ENABLE_REG);
    423	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
    424	if (dmasound.soft.stereo)
    425		error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
    426				    "Q40 sound", Q40Interrupt);
    427	  else
    428		error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
    429				    "Q40 sound", Q40Interrupt);
    430	if (error && printk_ratelimit())
    431		pr_err("Couldn't register sound interrupt\n");
    432
    433	master_outb( speed, SAMPLE_RATE_REG);
    434	master_outb( 1,SAMPLE_CLEAR_REG);
    435	master_outb( 1,SAMPLE_ENABLE_REG);
    436}
    437
    438static void Q40Play(void)
    439{
    440        unsigned long flags;
    441
    442	if (write_sq.active || write_sq.count<=0 ) {
    443		/* There's already a frame loaded */
    444		return;
    445	}
    446
    447	/* nothing in the queue */
    448	if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
    449	         /* hmmm, the only existing frame is not
    450		  * yet filled and we're not syncing?
    451		  */
    452	         return;
    453	}
    454	spin_lock_irqsave(&dmasound.lock, flags);
    455	Q40PlayNextFrame(1);
    456	spin_unlock_irqrestore(&dmasound.lock, flags);
    457}
    458
    459static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
    460{
    461	spin_lock(&dmasound.lock);
    462        if (q40_sc>1){
    463            *DAC_LEFT=*q40_pp++;
    464	    *DAC_RIGHT=*q40_pp++;
    465	    q40_sc -=2;
    466	    master_outb(1,SAMPLE_CLEAR_REG);
    467	}else Q40Interrupt();
    468	spin_unlock(&dmasound.lock);
    469	return IRQ_HANDLED;
    470}
    471static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
    472{
    473	spin_lock(&dmasound.lock);
    474        if (q40_sc>0){
    475            *DAC_LEFT=*q40_pp;
    476	    *DAC_RIGHT=*q40_pp++;
    477	    q40_sc --;
    478	    master_outb(1,SAMPLE_CLEAR_REG);
    479	}else Q40Interrupt();
    480	spin_unlock(&dmasound.lock);
    481	return IRQ_HANDLED;
    482}
    483static void Q40Interrupt(void)
    484{
    485	if (!write_sq.active) {
    486	          /* playing was interrupted and sq_reset() has already cleared
    487		   * the sq variables, so better don't do anything here.
    488		   */
    489	           WAKE_UP(write_sq.sync_queue);
    490		   master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
    491		   goto exit;
    492	} else write_sq.active=0;
    493	write_sq.count--;
    494	Q40Play();
    495
    496	if (q40_sc<2)
    497	      { /* there was nothing to play, disable irq */
    498		master_outb(0,SAMPLE_ENABLE_REG);
    499		*DAC_LEFT=*DAC_RIGHT=127;
    500	      }
    501	WAKE_UP(write_sq.action_queue);
    502
    503 exit:
    504	master_outb(1,SAMPLE_CLEAR_REG);
    505}
    506
    507
    508static void Q40Init(void)
    509{
    510	int i, idx;
    511	const int freq[] = {10000, 20000};
    512
    513	/* search a frequency that fits into the allowed error range */
    514
    515	idx = -1;
    516	for (i = 0; i < 2; i++)
    517		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
    518			idx = i;
    519
    520	dmasound.hard = dmasound.soft;
    521	/*sound.hard.stereo=1;*/ /* no longer true */
    522	dmasound.hard.size=8;
    523
    524	if (idx > -1) {
    525		dmasound.soft.speed = freq[idx];
    526		dmasound.trans_write = &transQ40Normal;
    527	} else
    528		dmasound.trans_write = &transQ40Expanding;
    529
    530	Q40Silence();
    531
    532	if (dmasound.hard.speed > 20200) {
    533		/* squeeze the sound, we do that */
    534		dmasound.hard.speed = 20000;
    535		dmasound.trans_write = &transQ40Compressing;
    536	} else if (dmasound.hard.speed > 10000) {
    537		dmasound.hard.speed = 20000;
    538	} else {
    539		dmasound.hard.speed = 10000;
    540	}
    541	expand_bal = -dmasound.soft.speed;
    542}
    543
    544
    545static int Q40SetFormat(int format)
    546{
    547	/* Q40 sound supports only 8bit modes */
    548
    549	switch (format) {
    550	case AFMT_QUERY:
    551		return(dmasound.soft.format);
    552	case AFMT_MU_LAW:
    553	case AFMT_A_LAW:
    554	case AFMT_S8:
    555	case AFMT_U8:
    556		break;
    557	default:
    558		format = AFMT_S8;
    559	}
    560
    561	dmasound.soft.format = format;
    562	dmasound.soft.size = 8;
    563	if (dmasound.minDev == SND_DEV_DSP) {
    564		dmasound.dsp.format = format;
    565		dmasound.dsp.size = 8;
    566	}
    567	Q40Init();
    568
    569	return(format);
    570}
    571
    572static int Q40SetVolume(int volume)
    573{
    574    return 0;
    575}
    576
    577
    578/*** Machine definitions *****************************************************/
    579
    580static SETTINGS def_hard = {
    581	.format	= AFMT_U8,
    582	.stereo	= 0,
    583	.size	= 8,
    584	.speed	= 10000
    585} ;
    586
    587static SETTINGS def_soft = {
    588	.format	= AFMT_U8,
    589	.stereo	= 0,
    590	.size	= 8,
    591	.speed	= 8000
    592} ;
    593
    594static MACHINE machQ40 = {
    595	.name		= "Q40",
    596	.name2		= "Q40",
    597	.owner		= THIS_MODULE,
    598	.dma_alloc	= Q40Alloc,
    599	.dma_free	= Q40Free,
    600	.irqinit	= Q40IrqInit,
    601#ifdef MODULE
    602	.irqcleanup	= Q40IrqCleanUp,
    603#endif /* MODULE */
    604	.init		= Q40Init,
    605	.silence	= Q40Silence,
    606	.setFormat	= Q40SetFormat,
    607	.setVolume	= Q40SetVolume,
    608	.play		= Q40Play,
    609 	.min_dsp_speed	= 10000,
    610	.version	= ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
    611	.hardware_afmts	= AFMT_U8, /* h'ware-supported formats *only* here */
    612	.capabilities	= DSP_CAP_BATCH  /* As per SNDCTL_DSP_GETCAPS */
    613};
    614
    615
    616/*** Config & Setup **********************************************************/
    617
    618
    619static int __init dmasound_q40_init(void)
    620{
    621	if (MACH_IS_Q40) {
    622	    dmasound.mach = machQ40;
    623	    dmasound.mach.default_hard = def_hard ;
    624	    dmasound.mach.default_soft = def_soft ;
    625	    return dmasound_init();
    626	} else
    627	    return -ENODEV;
    628}
    629
    630static void __exit dmasound_q40_cleanup(void)
    631{
    632	dmasound_deinit();
    633}
    634
    635module_init(dmasound_q40_init);
    636module_exit(dmasound_q40_cleanup);
    637
    638MODULE_DESCRIPTION("Q40/Q60 sound driver");
    639MODULE_LICENSE("GPL");