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-shark.c (11506B)


      1/*
      2 * Linux V4L2 radio driver for the Griffin radioSHARK USB radio receiver
      3 *
      4 * Note the radioSHARK offers the audio through a regular USB audio device,
      5 * this driver only handles the tuning.
      6 *
      7 * The info necessary to drive the shark was taken from the small userspace
      8 * shark.c program by Michael Rolig, which he kindly placed in the Public
      9 * Domain.
     10 *
     11 * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
     12 *
     13 * This program is free software; you can redistribute it and/or modify
     14 * it under the terms of the GNU General Public License as published by
     15 * the Free Software Foundation; either version 2 of the License, or
     16 * (at your option) any later version.
     17 *
     18 * This program is distributed in the hope that it will be useful,
     19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     21 * GNU General Public License for more details.
     22*/
     23
     24#include <linux/init.h>
     25#include <linux/kernel.h>
     26#include <linux/leds.h>
     27#include <linux/module.h>
     28#include <linux/slab.h>
     29#include <linux/usb.h>
     30#include <linux/workqueue.h>
     31#include <media/v4l2-device.h>
     32#include <media/drv-intf/tea575x.h>
     33
     34#if defined(CONFIG_LEDS_CLASS) || \
     35    (defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK_MODULE))
     36#define SHARK_USE_LEDS 1
     37#endif
     38
     39/*
     40 * Version Information
     41 */
     42MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
     43MODULE_DESCRIPTION("Griffin radioSHARK, USB radio receiver driver");
     44MODULE_LICENSE("GPL");
     45
     46#define SHARK_IN_EP		0x83
     47#define SHARK_OUT_EP		0x05
     48
     49#define TEA575X_BIT_MONO	(1<<22)		/* 0 = stereo, 1 = mono */
     50#define TEA575X_BIT_BAND_MASK	(3<<20)
     51#define TEA575X_BIT_BAND_FM	(0<<20)
     52
     53#define TB_LEN 6
     54#define DRV_NAME "radioshark"
     55
     56#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
     57
     58/* Note BLUE_IS_PULSE comes after NO_LEDS as it is a status bit, not a LED */
     59enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS, BLUE_IS_PULSE };
     60
     61struct shark_device {
     62	struct usb_device *usbdev;
     63	struct v4l2_device v4l2_dev;
     64	struct snd_tea575x tea;
     65
     66#ifdef SHARK_USE_LEDS
     67	struct work_struct led_work;
     68	struct led_classdev leds[NO_LEDS];
     69	char led_names[NO_LEDS][32];
     70	atomic_t brightness[NO_LEDS];
     71	unsigned long brightness_new;
     72#endif
     73
     74	u8 *transfer_buffer;
     75	u32 last_val;
     76};
     77
     78static atomic_t shark_instance = ATOMIC_INIT(0);
     79
     80static void shark_write_val(struct snd_tea575x *tea, u32 val)
     81{
     82	struct shark_device *shark = tea->private_data;
     83	int i, res, actual_len;
     84
     85	/* Avoid unnecessary (slow) USB transfers */
     86	if (shark->last_val == val)
     87		return;
     88
     89	memset(shark->transfer_buffer, 0, TB_LEN);
     90	shark->transfer_buffer[0] = 0xc0; /* Write shift register command */
     91	for (i = 0; i < 4; i++)
     92		shark->transfer_buffer[i] |= (val >> (24 - i * 8)) & 0xff;
     93
     94	res = usb_interrupt_msg(shark->usbdev,
     95				usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
     96				shark->transfer_buffer, TB_LEN,
     97				&actual_len, 1000);
     98	if (res >= 0)
     99		shark->last_val = val;
    100	else
    101		v4l2_err(&shark->v4l2_dev, "set-freq error: %d\n", res);
    102}
    103
    104static u32 shark_read_val(struct snd_tea575x *tea)
    105{
    106	struct shark_device *shark = tea->private_data;
    107	int i, res, actual_len;
    108	u32 val = 0;
    109
    110	memset(shark->transfer_buffer, 0, TB_LEN);
    111	shark->transfer_buffer[0] = 0x80;
    112	res = usb_interrupt_msg(shark->usbdev,
    113				usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
    114				shark->transfer_buffer, TB_LEN,
    115				&actual_len, 1000);
    116	if (res < 0) {
    117		v4l2_err(&shark->v4l2_dev, "request-status error: %d\n", res);
    118		return shark->last_val;
    119	}
    120
    121	res = usb_interrupt_msg(shark->usbdev,
    122				usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
    123				shark->transfer_buffer, TB_LEN,
    124				&actual_len, 1000);
    125	if (res < 0) {
    126		v4l2_err(&shark->v4l2_dev, "get-status error: %d\n", res);
    127		return shark->last_val;
    128	}
    129
    130	for (i = 0; i < 4; i++)
    131		val |= shark->transfer_buffer[i] << (24 - i * 8);
    132
    133	shark->last_val = val;
    134
    135	/*
    136	 * The shark does not allow actually reading the stereo / mono pin :(
    137	 * So assume that when we're tuned to an FM station and mono has not
    138	 * been requested, that we're receiving stereo.
    139	 */
    140	if (((val & TEA575X_BIT_BAND_MASK) == TEA575X_BIT_BAND_FM) &&
    141	    !(val & TEA575X_BIT_MONO))
    142		shark->tea.stereo = true;
    143	else
    144		shark->tea.stereo = false;
    145
    146	return val;
    147}
    148
    149static const struct snd_tea575x_ops shark_tea_ops = {
    150	.write_val = shark_write_val,
    151	.read_val  = shark_read_val,
    152};
    153
    154#ifdef SHARK_USE_LEDS
    155static void shark_led_work(struct work_struct *work)
    156{
    157	struct shark_device *shark =
    158		container_of(work, struct shark_device, led_work);
    159	int i, res, brightness, actual_len;
    160
    161	for (i = 0; i < 3; i++) {
    162		if (!test_and_clear_bit(i, &shark->brightness_new))
    163			continue;
    164
    165		brightness = atomic_read(&shark->brightness[i]);
    166		memset(shark->transfer_buffer, 0, TB_LEN);
    167		if (i != RED_LED) {
    168			shark->transfer_buffer[0] = 0xA0 + i;
    169			shark->transfer_buffer[1] = brightness;
    170		} else
    171			shark->transfer_buffer[0] = brightness ? 0xA9 : 0xA8;
    172		res = usb_interrupt_msg(shark->usbdev,
    173					usb_sndintpipe(shark->usbdev, 0x05),
    174					shark->transfer_buffer, TB_LEN,
    175					&actual_len, 1000);
    176		if (res < 0)
    177			v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
    178				 shark->led_names[i], res);
    179	}
    180}
    181
    182static void shark_led_set_blue(struct led_classdev *led_cdev,
    183			       enum led_brightness value)
    184{
    185	struct shark_device *shark =
    186		container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
    187
    188	atomic_set(&shark->brightness[BLUE_LED], value);
    189	set_bit(BLUE_LED, &shark->brightness_new);
    190	clear_bit(BLUE_IS_PULSE, &shark->brightness_new);
    191	schedule_work(&shark->led_work);
    192}
    193
    194static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
    195				     enum led_brightness value)
    196{
    197	struct shark_device *shark = container_of(led_cdev,
    198				struct shark_device, leds[BLUE_PULSE_LED]);
    199
    200	atomic_set(&shark->brightness[BLUE_PULSE_LED], 256 - value);
    201	set_bit(BLUE_PULSE_LED, &shark->brightness_new);
    202	set_bit(BLUE_IS_PULSE, &shark->brightness_new);
    203	schedule_work(&shark->led_work);
    204}
    205
    206static void shark_led_set_red(struct led_classdev *led_cdev,
    207			      enum led_brightness value)
    208{
    209	struct shark_device *shark =
    210		container_of(led_cdev, struct shark_device, leds[RED_LED]);
    211
    212	atomic_set(&shark->brightness[RED_LED], value);
    213	set_bit(RED_LED, &shark->brightness_new);
    214	schedule_work(&shark->led_work);
    215}
    216
    217static const struct led_classdev shark_led_templates[NO_LEDS] = {
    218	[BLUE_LED] = {
    219		.name		= "%s:blue:",
    220		.brightness	= LED_OFF,
    221		.max_brightness = 127,
    222		.brightness_set = shark_led_set_blue,
    223	},
    224	[BLUE_PULSE_LED] = {
    225		.name		= "%s:blue-pulse:",
    226		.brightness	= LED_OFF,
    227		.max_brightness = 255,
    228		.brightness_set = shark_led_set_blue_pulse,
    229	},
    230	[RED_LED] = {
    231		.name		= "%s:red:",
    232		.brightness	= LED_OFF,
    233		.max_brightness = 1,
    234		.brightness_set = shark_led_set_red,
    235	},
    236};
    237
    238static int shark_register_leds(struct shark_device *shark, struct device *dev)
    239{
    240	int i, retval;
    241
    242	atomic_set(&shark->brightness[BLUE_LED], 127);
    243	INIT_WORK(&shark->led_work, shark_led_work);
    244	for (i = 0; i < NO_LEDS; i++) {
    245		shark->leds[i] = shark_led_templates[i];
    246		snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
    247			 shark->leds[i].name, shark->v4l2_dev.name);
    248		shark->leds[i].name = shark->led_names[i];
    249		retval = led_classdev_register(dev, &shark->leds[i]);
    250		if (retval) {
    251			v4l2_err(&shark->v4l2_dev,
    252				 "couldn't register led: %s\n",
    253				 shark->led_names[i]);
    254			return retval;
    255		}
    256	}
    257	return 0;
    258}
    259
    260static void shark_unregister_leds(struct shark_device *shark)
    261{
    262	int i;
    263
    264	for (i = 0; i < NO_LEDS; i++)
    265		led_classdev_unregister(&shark->leds[i]);
    266
    267	cancel_work_sync(&shark->led_work);
    268}
    269
    270static inline void shark_resume_leds(struct shark_device *shark)
    271{
    272	if (test_bit(BLUE_IS_PULSE, &shark->brightness_new))
    273		set_bit(BLUE_PULSE_LED, &shark->brightness_new);
    274	else
    275		set_bit(BLUE_LED, &shark->brightness_new);
    276	set_bit(RED_LED, &shark->brightness_new);
    277	schedule_work(&shark->led_work);
    278}
    279#else
    280static int shark_register_leds(struct shark_device *shark, struct device *dev)
    281{
    282	v4l2_warn(&shark->v4l2_dev,
    283		  "CONFIG_LEDS_CLASS not enabled, LED support disabled\n");
    284	return 0;
    285}
    286static inline void shark_unregister_leds(struct shark_device *shark) { }
    287static inline void shark_resume_leds(struct shark_device *shark) { }
    288#endif
    289
    290static void usb_shark_disconnect(struct usb_interface *intf)
    291{
    292	struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
    293	struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
    294
    295	mutex_lock(&shark->tea.mutex);
    296	v4l2_device_disconnect(&shark->v4l2_dev);
    297	snd_tea575x_exit(&shark->tea);
    298	mutex_unlock(&shark->tea.mutex);
    299
    300	shark_unregister_leds(shark);
    301
    302	v4l2_device_put(&shark->v4l2_dev);
    303}
    304
    305static void usb_shark_release(struct v4l2_device *v4l2_dev)
    306{
    307	struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
    308
    309	v4l2_device_unregister(&shark->v4l2_dev);
    310	kfree(shark->transfer_buffer);
    311	kfree(shark);
    312}
    313
    314static int usb_shark_probe(struct usb_interface *intf,
    315			   const struct usb_device_id *id)
    316{
    317	struct shark_device *shark;
    318	int retval = -ENOMEM;
    319
    320	shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
    321	if (!shark)
    322		return retval;
    323
    324	shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
    325	if (!shark->transfer_buffer)
    326		goto err_alloc_buffer;
    327
    328	v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
    329
    330	retval = shark_register_leds(shark, &intf->dev);
    331	if (retval)
    332		goto err_reg_leds;
    333
    334	shark->v4l2_dev.release = usb_shark_release;
    335	retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
    336	if (retval) {
    337		v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
    338		goto err_reg_dev;
    339	}
    340
    341	shark->usbdev = interface_to_usbdev(intf);
    342	shark->tea.v4l2_dev = &shark->v4l2_dev;
    343	shark->tea.private_data = shark;
    344	shark->tea.radio_nr = -1;
    345	shark->tea.ops = &shark_tea_ops;
    346	shark->tea.cannot_mute = true;
    347	shark->tea.has_am = true;
    348	strscpy(shark->tea.card, "Griffin radioSHARK",
    349		sizeof(shark->tea.card));
    350	usb_make_path(shark->usbdev, shark->tea.bus_info,
    351		sizeof(shark->tea.bus_info));
    352
    353	retval = snd_tea575x_init(&shark->tea, THIS_MODULE);
    354	if (retval) {
    355		v4l2_err(&shark->v4l2_dev, "couldn't init tea5757\n");
    356		goto err_init_tea;
    357	}
    358
    359	return 0;
    360
    361err_init_tea:
    362	v4l2_device_unregister(&shark->v4l2_dev);
    363err_reg_dev:
    364	shark_unregister_leds(shark);
    365err_reg_leds:
    366	kfree(shark->transfer_buffer);
    367err_alloc_buffer:
    368	kfree(shark);
    369
    370	return retval;
    371}
    372
    373#ifdef CONFIG_PM
    374static int usb_shark_suspend(struct usb_interface *intf, pm_message_t message)
    375{
    376	return 0;
    377}
    378
    379static int usb_shark_resume(struct usb_interface *intf)
    380{
    381	struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
    382	struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
    383
    384	mutex_lock(&shark->tea.mutex);
    385	snd_tea575x_set_freq(&shark->tea);
    386	mutex_unlock(&shark->tea.mutex);
    387
    388	shark_resume_leds(shark);
    389
    390	return 0;
    391}
    392#endif
    393
    394/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
    395static const struct usb_device_id usb_shark_device_table[] = {
    396	{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
    397			 USB_DEVICE_ID_MATCH_INT_CLASS,
    398	  .idVendor     = 0x077d,
    399	  .idProduct    = 0x627a,
    400	  .bcdDevice_lo = 0x0001,
    401	  .bcdDevice_hi = 0x0001,
    402	  .bInterfaceClass = 3,
    403	},
    404	{ }
    405};
    406MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
    407
    408static struct usb_driver usb_shark_driver = {
    409	.name			= DRV_NAME,
    410	.probe			= usb_shark_probe,
    411	.disconnect		= usb_shark_disconnect,
    412	.id_table		= usb_shark_device_table,
    413#ifdef CONFIG_PM
    414	.suspend		= usb_shark_suspend,
    415	.resume			= usb_shark_resume,
    416	.reset_resume		= usb_shark_resume,
    417#endif
    418};
    419module_usb_driver(usb_shark_driver);