dsbr100.c 18.0 KB
Newer Older
1 2
/* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21.
 The device plugs into both the USB and an analog audio input, so this thing
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 only deals with initialisation and frequency setting, the
 audio data has to be handled by a sound driver.

 Major issue: I can't find out where the device reports the signal
 strength, and indeed the windows software appearantly just looks
 at the stereo indicator as well.  So, scanning will only find
 stereo stations.  Sad, but I can't help it.

 Also, the windows program sends oodles of messages over to the
 device, and I couldn't figure out their meaning.  My suspicion
 is that they don't have any:-)

 You might find some interesting stuff about this module at
 http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr

 Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.de>

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

 History:

36 37 38 39
 Version 0.44:
	Add suspend/resume functions, fix unplug of device,
	a lot of cleanups and fixes by Alexey Klimov <klimov.linux@gmail.com>

40 41 42
 Version 0.43:
	Oliver Neukum: avoided DMA coherency issue

43 44 45 46
 Version 0.42:
	Converted dsbr100 to use video_ioctl2
	by Douglas Landgraf <dougsland@gmail.com>

47 48 49 50 51 52
 Version 0.41-ac1:
	Alan Cox: Some cleanups and fixes

 Version 0.41:
	Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>

L
Linus Torvalds 已提交
53
 Version 0.40:
54
	Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing
L
Linus Torvalds 已提交
55 56

 Version 0.30:
57
	Markus: Updates for 2.5.x kernel and more ISO compliant source
L
Linus Torvalds 已提交
58 59

 Version 0.25:
60
	PSL and Markus: Cleanup, radio now doesn't stop on device close
L
Linus Torvalds 已提交
61 62

 Version 0.24:
63
	Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally
L
Linus Torvalds 已提交
64 65 66
	right.  Some minor cleanup, improved standalone compilation

 Version 0.23:
67
	Markus: Sign extension bug fixed by declaring transfer_buffer unsigned
L
Linus Torvalds 已提交
68 69

 Version 0.22:
70
	Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns,
L
Linus Torvalds 已提交
71 72 73
	thanks to Mike Cox for pointing the problem out.

 Version 0.21:
74
	Markus: Minor cleanup, warnings if something goes wrong, lame attempt
L
Linus Torvalds 已提交
75 76
	to adhere to Documentation/CodingStyle

77 78
 Version 0.2:
	Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
L
Linus Torvalds 已提交
79 80 81 82 83 84 85 86 87 88 89
	Markus: Copyright clarification

 Version 0.01: Markus: initial release

*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/input.h>
90
#include <linux/videodev2.h>
91
#include <media/v4l2-common.h>
92
#include <media/v4l2-ioctl.h>
L
Linus Torvalds 已提交
93 94 95 96 97
#include <linux/usb.h>

/*
 * Version Information
 */
98 99
#include <linux/version.h>	/* for KERNEL_VERSION MACRO	*/

100 101
#define DRIVER_VERSION "v0.44"
#define RADIO_VERSION KERNEL_VERSION(0, 4, 4)
102 103 104 105 106 107 108 109 110

static struct v4l2_queryctrl radio_qctrl[] = {
	{
		.id            = V4L2_CID_AUDIO_MUTE,
		.name          = "Mute",
		.minimum       = 0,
		.maximum       = 1,
		.default_value = 1,
		.type          = V4L2_CTRL_TYPE_BOOLEAN,
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
	},
/* HINT: the disabled controls are only here to satify kradio and such apps */
	{       .id             = V4L2_CID_AUDIO_VOLUME,
		.flags          = V4L2_CTRL_FLAG_DISABLED,
	},
	{
		.id             = V4L2_CID_AUDIO_BALANCE,
		.flags          = V4L2_CTRL_FLAG_DISABLED,
	},
	{
		.id             = V4L2_CID_AUDIO_BASS,
		.flags          = V4L2_CTRL_FLAG_DISABLED,
	},
	{
		.id             = V4L2_CID_AUDIO_TREBLE,
		.flags          = V4L2_CTRL_FLAG_DISABLED,
	},
	{
		.id             = V4L2_CID_AUDIO_LOUDNESS,
		.flags          = V4L2_CTRL_FLAG_DISABLED,
	},
132 133
};

L
Linus Torvalds 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
#define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"

#define DSB100_VENDOR 0x04b4
#define DSB100_PRODUCT 0x1002

/* Commands the device appears to understand */
#define DSB100_TUNE 1
#define DSB100_ONOFF 2

#define TB_LEN 16

/* Frequency limits in MHz -- these are European values.  For Japanese
devices, that would be 76 and 91.  */
#define FREQ_MIN  87.5
#define FREQ_MAX 108.0
#define FREQ_MUL 16000

152
#define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev)
L
Linus Torvalds 已提交
153 154 155 156 157 158

static int usb_dsbr100_probe(struct usb_interface *intf,
			     const struct usb_device_id *id);
static void usb_dsbr100_disconnect(struct usb_interface *intf);
static int usb_dsbr100_open(struct inode *inode, struct file *file);
static int usb_dsbr100_close(struct inode *inode, struct file *file);
159 160 161
static int usb_dsbr100_suspend(struct usb_interface *intf,
						pm_message_t message);
static int usb_dsbr100_resume(struct usb_interface *intf);
L
Linus Torvalds 已提交
162 163 164 165 166

static int radio_nr = -1;
module_param(radio_nr, int, 0);

/* Data for one (physical) device */
167
struct dsbr100_device {
L
Linus Torvalds 已提交
168
	struct usb_device *usbdev;
169
	struct video_device videodev;
170
	u8 *transfer_buffer;
171
	struct mutex lock;	/* buffer locking */
L
Linus Torvalds 已提交
172 173 174 175
	int curfreq;
	int stereo;
	int users;
	int removed;
176 177
	int muted;
};
L
Linus Torvalds 已提交
178 179 180 181 182 183 184 185 186 187

static struct usb_device_id usb_dsbr100_device_table [] = {
	{ USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
	{ }						/* Terminating entry */
};

MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table);

/* USB subsystem interface */
static struct usb_driver usb_dsbr100_driver = {
188 189 190 191 192 193 194 195
	.name			= "dsbr100",
	.probe			= usb_dsbr100_probe,
	.disconnect		= usb_dsbr100_disconnect,
	.id_table		= usb_dsbr100_device_table,
	.suspend		= usb_dsbr100_suspend,
	.resume			= usb_dsbr100_resume,
	.reset_resume		= usb_dsbr100_resume,
	.supports_autosuspend	= 0,
L
Linus Torvalds 已提交
196 197 198 199 200
};

/* Low-level device interface begins here */

/* switch on radio */
201
static int dsbr100_start(struct dsbr100_device *radio)
L
Linus Torvalds 已提交
202
{
203 204 205
	int retval;
	int request;

206
	mutex_lock(&radio->lock);
207 208 209 210 211 212 213 214 215 216

	retval = usb_control_msg(radio->usbdev,
		usb_rcvctrlpipe(radio->usbdev, 0),
		USB_REQ_GET_STATUS,
		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
		0x00, 0xC7, radio->transfer_buffer, 8, 300);

	if (retval < 0) {
		request = USB_REQ_GET_STATUS;
		goto usb_control_msg_failed;
217 218
	}

219 220 221 222 223 224 225 226 227 228 229 230
	retval = usb_control_msg(radio->usbdev,
		usb_rcvctrlpipe(radio->usbdev, 0),
		DSB100_ONOFF,
		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
		0x01, 0x00, radio->transfer_buffer, 8, 300);

	if (retval < 0) {
		request = DSB100_ONOFF;
		goto usb_control_msg_failed;
	}

	radio->muted = 0;
231
	mutex_unlock(&radio->lock);
L
Linus Torvalds 已提交
232
	return (radio->transfer_buffer)[0];
233 234 235 236 237 238 239 240

usb_control_msg_failed:
	mutex_unlock(&radio->lock);
	dev_err(&radio->usbdev->dev,
		"%s - usb_control_msg returned %i, request %i\n",
			__func__, retval, request);
	return -1;

L
Linus Torvalds 已提交
241 242 243
}

/* switch off radio */
244
static int dsbr100_stop(struct dsbr100_device *radio)
L
Linus Torvalds 已提交
245
{
246 247 248
	int retval;
	int request;

249
	mutex_lock(&radio->lock);
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

	retval = usb_control_msg(radio->usbdev,
		usb_rcvctrlpipe(radio->usbdev, 0),
		USB_REQ_GET_STATUS,
		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
		0x16, 0x1C, radio->transfer_buffer, 8, 300);

	if (retval < 0) {
		request = USB_REQ_GET_STATUS;
		goto usb_control_msg_failed;
	}

	retval = usb_control_msg(radio->usbdev,
		usb_rcvctrlpipe(radio->usbdev, 0),
		DSB100_ONOFF,
		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
		0x00, 0x00, radio->transfer_buffer, 8, 300);

	if (retval < 0) {
		request = DSB100_ONOFF;
		goto usb_control_msg_failed;
271 272
	}

273
	radio->muted = 1;
274
	mutex_unlock(&radio->lock);
L
Linus Torvalds 已提交
275
	return (radio->transfer_buffer)[0];
276 277 278 279 280 281 282 283

usb_control_msg_failed:
	mutex_unlock(&radio->lock);
	dev_err(&radio->usbdev->dev,
		"%s - usb_control_msg returned %i, request %i\n",
			__func__, retval, request);
	return -1;

L
Linus Torvalds 已提交
284 285 286
}

/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
287
static int dsbr100_setfreq(struct dsbr100_device *radio, int freq)
L
Linus Torvalds 已提交
288
{
289 290 291
	int retval;
	int request;

292
	freq = (freq / 16 * 80) / 1000 + 856;
293
	mutex_lock(&radio->lock);
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326

	retval = usb_control_msg(radio->usbdev,
		usb_rcvctrlpipe(radio->usbdev, 0),
		DSB100_TUNE,
		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
		(freq >> 8) & 0x00ff, freq & 0xff,
		radio->transfer_buffer, 8, 300);

	if (retval < 0) {
		request = DSB100_TUNE;
		goto usb_control_msg_failed;
	}

	retval = usb_control_msg(radio->usbdev,
		usb_rcvctrlpipe(radio->usbdev, 0),
		USB_REQ_GET_STATUS,
		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
		0x96, 0xB7, radio->transfer_buffer, 8, 300);

	if (retval < 0) {
		request = USB_REQ_GET_STATUS;
		goto usb_control_msg_failed;
	}

	retval = usb_control_msg(radio->usbdev,
		usb_rcvctrlpipe(radio->usbdev, 0),
		USB_REQ_GET_STATUS,
		USB_TYPE_VENDOR | USB_RECIP_DEVICE |  USB_DIR_IN,
		0x00, 0x24, radio->transfer_buffer, 8, 300);

	if (retval < 0) {
		request = USB_REQ_GET_STATUS;
		goto usb_control_msg_failed;
L
Linus Torvalds 已提交
327
	}
328

329
	radio->stereo = !((radio->transfer_buffer)[0] & 0x01);
330
	mutex_unlock(&radio->lock);
L
Linus Torvalds 已提交
331
	return (radio->transfer_buffer)[0];
332 333 334 335 336 337 338 339

usb_control_msg_failed:
	radio->stereo = -1;
	mutex_unlock(&radio->lock);
	dev_err(&radio->usbdev->dev,
		"%s - usb_control_msg returned %i, request %i\n",
			__func__, retval, request);
	return -1;
L
Linus Torvalds 已提交
340 341 342 343
}

/* return the device status.  This is, in effect, just whether it
sees a stereo signal or not.  Pity. */
344
static void dsbr100_getstat(struct dsbr100_device *radio)
L
Linus Torvalds 已提交
345
{
346 347
	int retval;

348
	mutex_lock(&radio->lock);
349 350 351

	retval = usb_control_msg(radio->usbdev,
		usb_rcvctrlpipe(radio->usbdev, 0),
352
		USB_REQ_GET_STATUS,
L
Linus Torvalds 已提交
353
		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
354 355 356
		0x00 , 0x24, radio->transfer_buffer, 8, 300);

	if (retval < 0) {
L
Linus Torvalds 已提交
357
		radio->stereo = -1;
358 359 360 361
		dev_err(&radio->usbdev->dev,
			"%s - usb_control_msg returned %i, request %i\n",
				__func__, retval, USB_REQ_GET_STATUS);
	} else {
362
		radio->stereo = !(radio->transfer_buffer[0] & 0x01);
363 364
	}

365
	mutex_unlock(&radio->lock);
L
Linus Torvalds 已提交
366 367 368 369
}

/* USB subsystem interface begins here */

370 371 372 373 374 375
/*
 * Handle unplugging of the device.
 * We call video_unregister_device in any case.
 * The last function called in this procedure is
 * usb_dsbr100_video_device_release
 */
L
Linus Torvalds 已提交
376 377
static void usb_dsbr100_disconnect(struct usb_interface *intf)
{
378
	struct dsbr100_device *radio = usb_get_intfdata(intf);
L
Linus Torvalds 已提交
379 380

	usb_set_intfdata (intf, NULL);
381 382 383 384 385 386

	mutex_lock(&radio->lock);
	radio->removed = 1;
	mutex_unlock(&radio->lock);

	video_unregister_device(&radio->videodev);
L
Linus Torvalds 已提交
387 388 389
}


390 391 392 393 394
static int vidioc_querycap(struct file *file, void *priv,
					struct v4l2_capability *v)
{
	strlcpy(v->driver, "dsbr100", sizeof(v->driver));
	strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card));
395
	sprintf(v->bus_info, "USB");
396 397 398 399
	v->version = RADIO_VERSION;
	v->capabilities = V4L2_CAP_TUNER;
	return 0;
}
L
Linus Torvalds 已提交
400

401 402
static int vidioc_g_tuner(struct file *file, void *priv,
				struct v4l2_tuner *v)
L
Linus Torvalds 已提交
403
{
404
	struct dsbr100_device *radio = video_drvdata(file);
405

406 407 408 409
	/* safety check */
	if (radio->removed)
		return -EIO;

410 411 412 413 414 415
	if (v->index > 0)
		return -EINVAL;

	dsbr100_getstat(radio);
	strcpy(v->name, "FM");
	v->type = V4L2_TUNER_RADIO;
416 417 418
	v->rangelow = FREQ_MIN * FREQ_MUL;
	v->rangehigh = FREQ_MAX * FREQ_MUL;
	v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
419 420 421 422 423 424 425 426
	v->capability = V4L2_TUNER_CAP_LOW;
	if(radio->stereo)
		v->audmode = V4L2_TUNER_MODE_STEREO;
	else
		v->audmode = V4L2_TUNER_MODE_MONO;
	v->signal = 0xffff;     /* We can't get the signal strength */
	return 0;
}
L
Linus Torvalds 已提交
427

428 429 430
static int vidioc_s_tuner(struct file *file, void *priv,
				struct v4l2_tuner *v)
{
431 432 433 434 435 436
	struct dsbr100_device *radio = video_drvdata(file);

	/* safety check */
	if (radio->removed)
		return -EIO;

437 438
	if (v->index > 0)
		return -EINVAL;
L
Linus Torvalds 已提交
439

440 441
	return 0;
}
442

443 444 445
static int vidioc_s_frequency(struct file *file, void *priv,
				struct v4l2_frequency *f)
{
446
	struct dsbr100_device *radio = video_drvdata(file);
447
	int retval;
L
Linus Torvalds 已提交
448

449 450 451 452
	/* safety check */
	if (radio->removed)
		return -EIO;

453
	radio->curfreq = f->frequency;
454 455
	retval = dsbr100_setfreq(radio, radio->curfreq);
	if (retval == -1)
456
		dev_warn(&radio->usbdev->dev, "Set frequency failed\n");
457 458
	return 0;
}
459

460 461 462
static int vidioc_g_frequency(struct file *file, void *priv,
				struct v4l2_frequency *f)
{
463
	struct dsbr100_device *radio = video_drvdata(file);
464

465 466 467 468
	/* safety check */
	if (radio->removed)
		return -EIO;

469 470 471 472
	f->type = V4L2_TUNER_RADIO;
	f->frequency = radio->curfreq;
	return 0;
}
473

474 475 476 477
static int vidioc_queryctrl(struct file *file, void *priv,
				struct v4l2_queryctrl *qc)
{
	int i;
L
Linus Torvalds 已提交
478

479 480
	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
		if (qc->id && qc->id == radio_qctrl[i].id) {
481
			memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
L
Linus Torvalds 已提交
482 483
			return 0;
		}
484 485 486
	}
	return -EINVAL;
}
487

488 489 490
static int vidioc_g_ctrl(struct file *file, void *priv,
				struct v4l2_control *ctrl)
{
491
	struct dsbr100_device *radio = video_drvdata(file);
L
Linus Torvalds 已提交
492

493 494 495 496
	/* safety check */
	if (radio->removed)
		return -EIO;

497 498 499 500 501 502 503
	switch (ctrl->id) {
	case V4L2_CID_AUDIO_MUTE:
		ctrl->value = radio->muted;
		return 0;
	}
	return -EINVAL;
}
504

505 506 507
static int vidioc_s_ctrl(struct file *file, void *priv,
				struct v4l2_control *ctrl)
{
508
	struct dsbr100_device *radio = video_drvdata(file);
509
	int retval;
510

511 512 513 514
	/* safety check */
	if (radio->removed)
		return -EIO;

515 516 517
	switch (ctrl->id) {
	case V4L2_CID_AUDIO_MUTE:
		if (ctrl->value) {
518 519
			retval = dsbr100_stop(radio);
			if (retval == -1) {
520 521
				dev_warn(&radio->usbdev->dev,
					 "Radio did not respond properly\n");
522 523
				return -EBUSY;
			}
524
		} else {
525 526
			retval = dsbr100_start(radio);
			if (retval == -1) {
527 528
				dev_warn(&radio->usbdev->dev,
					 "Radio did not respond properly\n");
529 530
				return -EBUSY;
			}
L
Linus Torvalds 已提交
531
		}
532
		return 0;
L
Linus Torvalds 已提交
533
	}
534
	return -EINVAL;
L
Linus Torvalds 已提交
535 536
}

537 538
static int vidioc_g_audio(struct file *file, void *priv,
				struct v4l2_audio *a)
L
Linus Torvalds 已提交
539
{
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
	if (a->index > 1)
		return -EINVAL;

	strcpy(a->name, "Radio");
	a->capability = V4L2_AUDCAP_STEREO;
	return 0;
}

static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
{
	*i = 0;
	return 0;
}

static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
{
	if (i != 0)
		return -EINVAL;
	return 0;
}

static int vidioc_s_audio(struct file *file, void *priv,
					struct v4l2_audio *a)
{
	if (a->index != 0)
		return -EINVAL;
	return 0;
L
Linus Torvalds 已提交
567 568 569 570
}

static int usb_dsbr100_open(struct inode *inode, struct file *file)
{
571
	struct dsbr100_device *radio = video_drvdata(file);
572
	int retval;
L
Linus Torvalds 已提交
573

574
	lock_kernel();
L
Linus Torvalds 已提交
575
	radio->users = 1;
576 577
	radio->muted = 1;

578 579
	retval = dsbr100_start(radio);
	if (retval < 0) {
580 581
		dev_warn(&radio->usbdev->dev,
			 "Radio did not start up properly\n");
L
Linus Torvalds 已提交
582
		radio->users = 0;
583
		unlock_kernel();
L
Linus Torvalds 已提交
584 585
		return -EIO;
	}
586 587 588

	retval = dsbr100_setfreq(radio, radio->curfreq);
	if (retval == -1)
589 590
		dev_warn(&radio->usbdev->dev,
			"set frequency failed\n");
591

592
	unlock_kernel();
L
Linus Torvalds 已提交
593 594 595 596 597
	return 0;
}

static int usb_dsbr100_close(struct inode *inode, struct file *file)
{
598
	struct dsbr100_device *radio = video_drvdata(file);
599
	int retval;
L
Linus Torvalds 已提交
600 601 602

	if (!radio)
		return -ENODEV;
603

L
Linus Torvalds 已提交
604
	radio->users = 0;
605 606 607 608 609 610 611
	if (!radio->removed) {
		retval = dsbr100_stop(radio);
		if (retval == -1) {
			dev_warn(&radio->usbdev->dev,
				"dsbr100_stop failed\n");
		}

L
Linus Torvalds 已提交
612 613 614 615
	}
	return 0;
}

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
/* Suspend device - stop device. */
static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct dsbr100_device *radio = usb_get_intfdata(intf);
	int retval;

	retval = dsbr100_stop(radio);
	if (retval == -1)
		dev_warn(&intf->dev, "dsbr100_stop failed\n");

	dev_info(&intf->dev, "going into suspend..\n");

	return 0;
}

/* Resume device - start device. */
static int usb_dsbr100_resume(struct usb_interface *intf)
{
	struct dsbr100_device *radio = usb_get_intfdata(intf);
	int retval;

	retval = dsbr100_start(radio);
	if (retval == -1)
		dev_warn(&intf->dev, "dsbr100_start failed\n");

	dev_info(&intf->dev, "coming out of suspend..\n");

	return 0;
}

646
/* free data structures */
647 648 649 650 651 652 653 654
static void usb_dsbr100_video_device_release(struct video_device *videodev)
{
	struct dsbr100_device *radio = videodev_to_radio(videodev);

	kfree(radio->transfer_buffer);
	kfree(radio);
}

655 656 657 658 659 660
/* File system interface */
static const struct file_operations usb_dsbr100_fops = {
	.owner		= THIS_MODULE,
	.open		= usb_dsbr100_open,
	.release	= usb_dsbr100_close,
	.ioctl		= video_ioctl2,
661
#ifdef CONFIG_COMPAT
662
	.compat_ioctl	= v4l_compat_ioctl32,
663
#endif
664 665 666
	.llseek		= no_llseek,
};

667
static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
668 669 670 671 672 673 674 675 676 677 678 679 680 681
	.vidioc_querycap    = vidioc_querycap,
	.vidioc_g_tuner     = vidioc_g_tuner,
	.vidioc_s_tuner     = vidioc_s_tuner,
	.vidioc_g_frequency = vidioc_g_frequency,
	.vidioc_s_frequency = vidioc_s_frequency,
	.vidioc_queryctrl   = vidioc_queryctrl,
	.vidioc_g_ctrl      = vidioc_g_ctrl,
	.vidioc_s_ctrl      = vidioc_s_ctrl,
	.vidioc_g_audio     = vidioc_g_audio,
	.vidioc_s_audio     = vidioc_s_audio,
	.vidioc_g_input     = vidioc_g_input,
	.vidioc_s_input     = vidioc_s_input,
};

682
/* V4L2 interface */
683
static struct video_device dsbr100_videodev_data = {
684 685 686
	.name		= "D-Link DSB-R 100",
	.fops		= &usb_dsbr100_fops,
	.ioctl_ops 	= &usb_dsbr100_ioctl_ops,
687
	.release	= usb_dsbr100_video_device_release,
688 689
};

690
/* check if the device is present and register with v4l and usb if it is */
691 692 693 694
static int usb_dsbr100_probe(struct usb_interface *intf,
				const struct usb_device_id *id)
{
	struct dsbr100_device *radio;
695
	int retval;
696

697 698 699
	radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL);

	if (!radio)
700
		return -ENOMEM;
701 702 703 704

	radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);

	if (!(radio->transfer_buffer)) {
705 706 707
		kfree(radio);
		return -ENOMEM;
	}
708

709 710 711
	mutex_init(&radio->lock);
	radio->videodev = dsbr100_videodev_data;

712 713 714
	radio->removed = 0;
	radio->users = 0;
	radio->usbdev = interface_to_usbdev(intf);
715
	radio->curfreq = FREQ_MIN * FREQ_MUL;
716
	video_set_drvdata(&radio->videodev, radio);
717 718
	retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr);
	if (retval < 0) {
719
		dev_err(&intf->dev, "couldn't register video device\n");
720
		kfree(radio->transfer_buffer);
721 722 723 724 725 726 727
		kfree(radio);
		return -EIO;
	}
	usb_set_intfdata(intf, radio);
	return 0;
}

L
Linus Torvalds 已提交
728 729 730
static int __init dsbr100_init(void)
{
	int retval = usb_register(&usb_dsbr100_driver);
731 732
	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
	       DRIVER_DESC "\n");
L
Linus Torvalds 已提交
733 734 735 736 737 738 739 740 741 742 743 744 745 746
	return retval;
}

static void __exit dsbr100_exit(void)
{
	usb_deregister(&usb_dsbr100_driver);
}

module_init (dsbr100_init);
module_exit (dsbr100_exit);

MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");