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

radio-maxiradio.c (5568B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux
      4 * (C) 2001 Dimitromanolakis Apostolos <apdim@grecian.net>
      5 *
      6 * Based in the radio Maestro PCI driver. Actually it uses the same chip
      7 * for radio but different pci controller.
      8 *
      9 * I didn't have any specs I reversed engineered the protocol from
     10 * the windows driver (radio.dll).
     11 *
     12 * The card uses the TEA5757 chip that includes a search function but it
     13 * is useless as I haven't found any way to read back the frequency. If
     14 * anybody does please mail me.
     15 *
     16 * For the pdf file see:
     17 * http://www.nxp.com/acrobat_download2/expired_datasheets/TEA5757_5759_3.pdf
     18 *
     19 *
     20 * CHANGES:
     21 *   0.75b
     22 *     - better pci interface thanks to Francois Romieu <romieu@cogenit.fr>
     23 *
     24 *   0.75      Sun Feb  4 22:51:27 EET 2001
     25 *     - tiding up
     26 *     - removed support for multiple devices as it didn't work anyway
     27 *
     28 * BUGS:
     29 *   - card unmutes if you change frequency
     30 *
     31 * (c) 2006, 2007 by Mauro Carvalho Chehab <mchehab@kernel.org>:
     32 *	- Conversion to V4L2 API
     33 *      - Uses video_ioctl2 for parsing and to add debug support
     34 */
     35
     36
     37#include <linux/module.h>
     38#include <linux/init.h>
     39#include <linux/ioport.h>
     40#include <linux/delay.h>
     41#include <linux/mutex.h>
     42#include <linux/pci.h>
     43#include <linux/videodev2.h>
     44#include <linux/io.h>
     45#include <linux/slab.h>
     46#include <media/drv-intf/tea575x.h>
     47#include <media/v4l2-device.h>
     48#include <media/v4l2-ioctl.h>
     49#include <media/v4l2-fh.h>
     50#include <media/v4l2-ctrls.h>
     51#include <media/v4l2-event.h>
     52
     53MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
     54MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000.");
     55MODULE_LICENSE("GPL");
     56MODULE_VERSION("1.0.0");
     57
     58static int radio_nr = -1;
     59module_param(radio_nr, int, 0644);
     60MODULE_PARM_DESC(radio_nr, "Radio device number");
     61
     62/* TEA5757 pin mappings */
     63static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16;
     64
     65static atomic_t maxiradio_instance = ATOMIC_INIT(0);
     66
     67#define PCI_VENDOR_ID_GUILLEMOT 0x5046
     68#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
     69
     70struct maxiradio
     71{
     72	struct snd_tea575x tea;
     73	struct v4l2_device v4l2_dev;
     74	struct pci_dev *pdev;
     75
     76	u16	io;	/* base of radio io */
     77};
     78
     79static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev)
     80{
     81	return container_of(v4l2_dev, struct maxiradio, v4l2_dev);
     82}
     83
     84static void maxiradio_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
     85{
     86	struct maxiradio *dev = tea->private_data;
     87	u8 bits = 0;
     88
     89	bits |= (pins & TEA575X_DATA) ? data : 0;
     90	bits |= (pins & TEA575X_CLK)  ? clk  : 0;
     91	bits |= (pins & TEA575X_WREN) ? wren : 0;
     92	bits |= power;
     93
     94	outb(bits, dev->io);
     95}
     96
     97/* Note: this card cannot read out the data of the shift registers,
     98   only the mono/stereo pin works. */
     99static u8 maxiradio_tea575x_get_pins(struct snd_tea575x *tea)
    100{
    101	struct maxiradio *dev = tea->private_data;
    102	u8 bits = inb(dev->io);
    103
    104	return  ((bits & data) ? TEA575X_DATA : 0) |
    105		((bits & mo_st) ? TEA575X_MOST : 0);
    106}
    107
    108static void maxiradio_tea575x_set_direction(struct snd_tea575x *tea, bool output)
    109{
    110}
    111
    112static const struct snd_tea575x_ops maxiradio_tea_ops = {
    113	.set_pins = maxiradio_tea575x_set_pins,
    114	.get_pins = maxiradio_tea575x_get_pins,
    115	.set_direction = maxiradio_tea575x_set_direction,
    116};
    117
    118static int maxiradio_probe(struct pci_dev *pdev,
    119			   const struct pci_device_id *ent)
    120{
    121	struct maxiradio *dev;
    122	struct v4l2_device *v4l2_dev;
    123	int retval = -ENOMEM;
    124
    125	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    126	if (dev == NULL) {
    127		dev_err(&pdev->dev, "not enough memory\n");
    128		return -ENOMEM;
    129	}
    130
    131	v4l2_dev = &dev->v4l2_dev;
    132	v4l2_device_set_name(v4l2_dev, "maxiradio", &maxiradio_instance);
    133
    134	retval = v4l2_device_register(&pdev->dev, v4l2_dev);
    135	if (retval < 0) {
    136		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
    137		goto errfr;
    138	}
    139	dev->tea.private_data = dev;
    140	dev->tea.ops = &maxiradio_tea_ops;
    141	/* The data pin cannot be read. This may be a hardware limitation, or
    142	   we just don't know how to read it. */
    143	dev->tea.cannot_read_data = true;
    144	dev->tea.v4l2_dev = v4l2_dev;
    145	dev->tea.radio_nr = radio_nr;
    146	strscpy(dev->tea.card, "Maxi Radio FM2000", sizeof(dev->tea.card));
    147
    148	retval = -ENODEV;
    149
    150	if (!request_region(pci_resource_start(pdev, 0),
    151			   pci_resource_len(pdev, 0), v4l2_dev->name)) {
    152		dev_err(&pdev->dev, "can't reserve I/O ports\n");
    153		goto err_hdl;
    154	}
    155
    156	if (pci_enable_device(pdev))
    157		goto err_out_free_region;
    158
    159	dev->io = pci_resource_start(pdev, 0);
    160	if (snd_tea575x_init(&dev->tea, THIS_MODULE)) {
    161		printk(KERN_ERR "radio-maxiradio: Unable to detect TEA575x tuner\n");
    162		goto err_out_free_region;
    163	}
    164	return 0;
    165
    166err_out_free_region:
    167	release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
    168err_hdl:
    169	v4l2_device_unregister(v4l2_dev);
    170errfr:
    171	kfree(dev);
    172	return retval;
    173}
    174
    175static void maxiradio_remove(struct pci_dev *pdev)
    176{
    177	struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
    178	struct maxiradio *dev = to_maxiradio(v4l2_dev);
    179
    180	snd_tea575x_exit(&dev->tea);
    181	/* Turn off power */
    182	outb(0, dev->io);
    183	v4l2_device_unregister(v4l2_dev);
    184	release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
    185	kfree(dev);
    186}
    187
    188static const struct pci_device_id maxiradio_pci_tbl[] = {
    189	{ PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO,
    190		PCI_ANY_ID, PCI_ANY_ID, },
    191	{ 0 }
    192};
    193
    194MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl);
    195
    196static struct pci_driver maxiradio_driver = {
    197	.name		= "radio-maxiradio",
    198	.id_table	= maxiradio_pci_tbl,
    199	.probe		= maxiradio_probe,
    200	.remove		= maxiradio_remove,
    201};
    202
    203module_pci_driver(maxiradio_driver);