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

wavefront_fx.c (5624B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Copyright (c) 1998-2002 by Paul Davis <pbd@op.net>
      4 */
      5
      6#include <linux/io.h>
      7#include <linux/init.h>
      8#include <linux/time.h>
      9#include <linux/wait.h>
     10#include <linux/slab.h>
     11#include <linux/module.h>
     12#include <linux/firmware.h>
     13#include <sound/core.h>
     14#include <sound/snd_wavefront.h>
     15#include <sound/initval.h>
     16
     17/* Control bits for the Load Control Register
     18 */
     19
     20#define FX_LSB_TRANSFER 0x01    /* transfer after DSP LSB byte written */
     21#define FX_MSB_TRANSFER 0x02    /* transfer after DSP MSB byte written */
     22#define FX_AUTO_INCR    0x04    /* auto-increment DSP address after transfer */
     23
     24#define WAIT_IDLE	0xff
     25
     26static int
     27wavefront_fx_idle (snd_wavefront_t *dev)
     28
     29{
     30	int i;
     31	unsigned int x = 0x80;
     32
     33	for (i = 0; i < 1000; i++) {
     34		x = inb (dev->fx_status);
     35		if ((x & 0x80) == 0) {
     36			break;
     37		}
     38	}
     39
     40	if (x & 0x80) {
     41		snd_printk ("FX device never idle.\n");
     42		return 0;
     43	}
     44
     45	return (1);
     46}
     47
     48static void
     49wavefront_fx_mute (snd_wavefront_t *dev, int onoff)
     50
     51{
     52	if (!wavefront_fx_idle(dev)) {
     53		return;
     54	}
     55
     56	outb (onoff ? 0x02 : 0x00, dev->fx_op);
     57}
     58
     59static int
     60wavefront_fx_memset (snd_wavefront_t *dev,
     61		     int page,
     62		     int addr,
     63		     int cnt,
     64		     unsigned short *data)
     65{
     66	if (page < 0 || page > 7) {
     67		snd_printk ("FX memset: "
     68			"page must be >= 0 and <= 7\n");
     69		return -EINVAL;
     70	}
     71
     72	if (addr < 0 || addr > 0x7f) {
     73		snd_printk ("FX memset: "
     74			"addr must be >= 0 and <= 7f\n");
     75		return -EINVAL;
     76	}
     77
     78	if (cnt == 1) {
     79
     80		outb (FX_LSB_TRANSFER, dev->fx_lcr);
     81		outb (page, dev->fx_dsp_page);
     82		outb (addr, dev->fx_dsp_addr);
     83		outb ((data[0] >> 8), dev->fx_dsp_msb);
     84		outb ((data[0] & 0xff), dev->fx_dsp_lsb);
     85
     86		snd_printk ("FX: addr %d:%x set to 0x%x\n",
     87			page, addr, data[0]);
     88
     89	} else {
     90		int i;
     91
     92		outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
     93		outb (page, dev->fx_dsp_page);
     94		outb (addr, dev->fx_dsp_addr);
     95
     96		for (i = 0; i < cnt; i++) {
     97			outb ((data[i] >> 8), dev->fx_dsp_msb);
     98			outb ((data[i] & 0xff), dev->fx_dsp_lsb);
     99			if (!wavefront_fx_idle (dev)) {
    100				break;
    101			}
    102		}
    103
    104		if (i != cnt) {
    105			snd_printk ("FX memset "
    106				    "(0x%x, 0x%x, 0x%lx, %d) incomplete\n",
    107				    page, addr, (unsigned long) data, cnt);
    108			return -EIO;
    109		}
    110	}
    111
    112	return 0;
    113}
    114
    115int
    116snd_wavefront_fx_detect (snd_wavefront_t *dev)
    117
    118{
    119	/* This is a crude check, but its the best one I have for now.
    120	   Certainly on the Maui and the Tropez, wavefront_fx_idle() will
    121	   report "never idle", which suggests that this test should
    122	   work OK.
    123	*/
    124
    125	if (inb (dev->fx_status) & 0x80) {
    126		snd_printk ("Hmm, probably a Maui or Tropez.\n");
    127		return -1;
    128	}
    129
    130	return 0;
    131}
    132
    133int
    134snd_wavefront_fx_open (struct snd_hwdep *hw, struct file *file)
    135
    136{
    137	if (!try_module_get(hw->card->module))
    138		return -EFAULT;
    139	file->private_data = hw;
    140	return 0;
    141}
    142
    143int 
    144snd_wavefront_fx_release (struct snd_hwdep *hw, struct file *file)
    145
    146{
    147	module_put(hw->card->module);
    148	return 0;
    149}
    150
    151int
    152snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
    153			unsigned int cmd, unsigned long arg)
    154
    155{
    156	struct snd_card *card;
    157	snd_wavefront_card_t *acard;
    158	snd_wavefront_t *dev;
    159	wavefront_fx_info r;
    160	unsigned short *page_data = NULL;
    161	unsigned short *pd;
    162	int err = 0;
    163
    164	card = sdev->card;
    165	if (snd_BUG_ON(!card))
    166		return -ENODEV;
    167	if (snd_BUG_ON(!card->private_data))
    168		return -ENODEV;
    169
    170	acard = card->private_data;
    171	dev = &acard->wavefront;
    172
    173	if (copy_from_user (&r, (void __user *)arg, sizeof (wavefront_fx_info)))
    174		return -EFAULT;
    175
    176	switch (r.request) {
    177	case WFFX_MUTE:
    178		wavefront_fx_mute (dev, r.data[0]);
    179		return -EIO;
    180
    181	case WFFX_MEMSET:
    182		if (r.data[2] <= 0) {
    183			snd_printk ("cannot write "
    184				"<= 0 bytes to FX\n");
    185			return -EIO;
    186		} else if (r.data[2] == 1) {
    187			pd = (unsigned short *) &r.data[3];
    188		} else {
    189			if (r.data[2] > 256) {
    190				snd_printk ("cannot write "
    191					    "> 512 bytes to FX\n");
    192				return -EIO;
    193			}
    194			page_data = memdup_user((unsigned char __user *)
    195						r.data[3],
    196						r.data[2] * sizeof(short));
    197			if (IS_ERR(page_data))
    198				return PTR_ERR(page_data);
    199			pd = page_data;
    200		}
    201
    202		err = wavefront_fx_memset (dev,
    203			     r.data[0], /* page */
    204			     r.data[1], /* addr */
    205			     r.data[2], /* cnt */
    206			     pd);
    207		kfree(page_data);
    208		break;
    209
    210	default:
    211		snd_printk ("FX: ioctl %d not yet supported\n",
    212			    r.request);
    213		return -ENOTTY;
    214	}
    215	return err;
    216}
    217
    218/* YSS225 initialization.
    219
    220   This code was developed using DOSEMU. The Turtle Beach SETUPSND
    221   utility was run with I/O tracing in DOSEMU enabled, and a reconstruction
    222   of the port I/O done, using the Yamaha faxback document as a guide
    223   to add more logic to the code. Its really pretty weird.
    224
    225   This is the approach of just dumping the whole I/O
    226   sequence as a series of port/value pairs and a simple loop
    227   that outputs it.
    228*/
    229
    230int
    231snd_wavefront_fx_start (snd_wavefront_t *dev)
    232{
    233	unsigned int i;
    234	int err;
    235	const struct firmware *firmware = NULL;
    236
    237	if (dev->fx_initialized)
    238		return 0;
    239
    240	err = request_firmware(&firmware, "yamaha/yss225_registers.bin",
    241			       dev->card->dev);
    242	if (err < 0) {
    243		err = -1;
    244		goto out;
    245	}
    246
    247	for (i = 0; i + 1 < firmware->size; i += 2) {
    248		if (firmware->data[i] >= 8 && firmware->data[i] < 16) {
    249			outb(firmware->data[i + 1],
    250			     dev->base + firmware->data[i]);
    251		} else if (firmware->data[i] == WAIT_IDLE) {
    252			if (!wavefront_fx_idle(dev)) {
    253				err = -1;
    254				goto out;
    255			}
    256		} else {
    257			snd_printk(KERN_ERR "invalid address"
    258				   " in register data\n");
    259			err = -1;
    260			goto out;
    261		}
    262	}
    263
    264	dev->fx_initialized = 1;
    265	err = 0;
    266
    267out:
    268	release_firmware(firmware);
    269	return err;
    270}
    271
    272MODULE_FIRMWARE("yamaha/yss225_registers.bin");