Commit | Line | Data |
---|---|---|
fc55bcb0 AK |
1 | /* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21. |
2 | The device plugs into both the USB and an analog audio input, so this thing | |
1da177e4 LT |
3 | only deals with initialisation and frequency setting, the |
4 | audio data has to be handled by a sound driver. | |
5 | ||
6 | Major issue: I can't find out where the device reports the signal | |
7 | strength, and indeed the windows software appearantly just looks | |
8 | at the stereo indicator as well. So, scanning will only find | |
9 | stereo stations. Sad, but I can't help it. | |
10 | ||
11 | Also, the windows program sends oodles of messages over to the | |
12 | device, and I couldn't figure out their meaning. My suspicion | |
13 | is that they don't have any:-) | |
14 | ||
15 | You might find some interesting stuff about this module at | |
16 | http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr | |
17 | ||
18 | Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.de> | |
19 | ||
20 | This program is free software; you can redistribute it and/or modify | |
21 | it under the terms of the GNU General Public License as published by | |
22 | the Free Software Foundation; either version 2 of the License, or | |
23 | (at your option) any later version. | |
24 | ||
25 | This program is distributed in the hope that it will be useful, | |
26 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | GNU General Public License for more details. | |
29 | ||
30 | You should have received a copy of the GNU General Public License | |
31 | along with this program; if not, write to the Free Software | |
32 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
33 | ||
34 | History: | |
35 | ||
f1ca0adf AK |
36 | Version 0.46: |
37 | Removed usb_dsbr100_open/close calls and radio->users counter. Also, | |
38 | radio->muted changed to radio->status and suspend/resume calls updated. | |
39 | ||
406827cc AK |
40 | Version 0.45: |
41 | Converted to v4l2_device. | |
42 | ||
6076dbf4 AK |
43 | Version 0.44: |
44 | Add suspend/resume functions, fix unplug of device, | |
45 | a lot of cleanups and fixes by Alexey Klimov <klimov.linux@gmail.com> | |
46 | ||
863c86dd ON |
47 | Version 0.43: |
48 | Oliver Neukum: avoided DMA coherency issue | |
49 | ||
7002a4f3 DL |
50 | Version 0.42: |
51 | Converted dsbr100 to use video_ioctl2 | |
52 | by Douglas Landgraf <dougsland@gmail.com> | |
53 | ||
5aff308c AC |
54 | Version 0.41-ac1: |
55 | Alan Cox: Some cleanups and fixes | |
56 | ||
57 | Version 0.41: | |
58 | Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> | |
59 | ||
1da177e4 | 60 | Version 0.40: |
5aff308c | 61 | Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing |
1da177e4 LT |
62 | |
63 | Version 0.30: | |
d56410e0 | 64 | Markus: Updates for 2.5.x kernel and more ISO compliant source |
1da177e4 LT |
65 | |
66 | Version 0.25: | |
d56410e0 | 67 | PSL and Markus: Cleanup, radio now doesn't stop on device close |
1da177e4 LT |
68 | |
69 | Version 0.24: | |
d56410e0 | 70 | Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally |
1da177e4 LT |
71 | right. Some minor cleanup, improved standalone compilation |
72 | ||
73 | Version 0.23: | |
d56410e0 | 74 | Markus: Sign extension bug fixed by declaring transfer_buffer unsigned |
1da177e4 LT |
75 | |
76 | Version 0.22: | |
d56410e0 | 77 | Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, |
1da177e4 LT |
78 | thanks to Mike Cox for pointing the problem out. |
79 | ||
80 | Version 0.21: | |
d56410e0 | 81 | Markus: Minor cleanup, warnings if something goes wrong, lame attempt |
1da177e4 LT |
82 | to adhere to Documentation/CodingStyle |
83 | ||
d56410e0 MCC |
84 | Version 0.2: |
85 | Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module | |
1da177e4 LT |
86 | Markus: Copyright clarification |
87 | ||
88 | Version 0.01: Markus: initial release | |
89 | ||
90 | */ | |
91 | ||
1da177e4 LT |
92 | #include <linux/kernel.h> |
93 | #include <linux/module.h> | |
94 | #include <linux/init.h> | |
95 | #include <linux/slab.h> | |
96 | #include <linux/input.h> | |
5aff308c | 97 | #include <linux/videodev2.h> |
406827cc | 98 | #include <media/v4l2-device.h> |
35ea11ff | 99 | #include <media/v4l2-ioctl.h> |
1da177e4 | 100 | #include <linux/usb.h> |
1da177e4 LT |
101 | |
102 | /* | |
103 | * Version Information | |
104 | */ | |
5aff308c AC |
105 | #include <linux/version.h> /* for KERNEL_VERSION MACRO */ |
106 | ||
f1ca0adf AK |
107 | #define DRIVER_VERSION "v0.46" |
108 | #define RADIO_VERSION KERNEL_VERSION(0, 4, 6) | |
5aff308c | 109 | |
1da177e4 LT |
110 | #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>" |
111 | #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" | |
112 | ||
113 | #define DSB100_VENDOR 0x04b4 | |
114 | #define DSB100_PRODUCT 0x1002 | |
115 | ||
116 | /* Commands the device appears to understand */ | |
117 | #define DSB100_TUNE 1 | |
118 | #define DSB100_ONOFF 2 | |
119 | ||
120 | #define TB_LEN 16 | |
121 | ||
122 | /* Frequency limits in MHz -- these are European values. For Japanese | |
123 | devices, that would be 76 and 91. */ | |
124 | #define FREQ_MIN 87.5 | |
125 | #define FREQ_MAX 108.0 | |
126 | #define FREQ_MUL 16000 | |
127 | ||
f1ca0adf AK |
128 | /* defines for radio->status */ |
129 | #define STARTED 0 | |
130 | #define STOPPED 1 | |
131 | ||
3a0efc32 | 132 | #define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev) |
1da177e4 LT |
133 | |
134 | static int usb_dsbr100_probe(struct usb_interface *intf, | |
135 | const struct usb_device_id *id); | |
136 | static void usb_dsbr100_disconnect(struct usb_interface *intf); | |
04e0ffbb AK |
137 | static int usb_dsbr100_suspend(struct usb_interface *intf, |
138 | pm_message_t message); | |
139 | static int usb_dsbr100_resume(struct usb_interface *intf); | |
1da177e4 LT |
140 | |
141 | static int radio_nr = -1; | |
142 | module_param(radio_nr, int, 0); | |
143 | ||
144 | /* Data for one (physical) device */ | |
5aff308c | 145 | struct dsbr100_device { |
1da177e4 | 146 | struct usb_device *usbdev; |
3a0efc32 | 147 | struct video_device videodev; |
406827cc AK |
148 | struct v4l2_device v4l2_dev; |
149 | ||
863c86dd | 150 | u8 *transfer_buffer; |
3a0efc32 | 151 | struct mutex lock; /* buffer locking */ |
1da177e4 LT |
152 | int curfreq; |
153 | int stereo; | |
1da177e4 | 154 | int removed; |
f1ca0adf | 155 | int status; |
5aff308c | 156 | }; |
1da177e4 | 157 | |
1da177e4 LT |
158 | static struct usb_device_id usb_dsbr100_device_table [] = { |
159 | { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, | |
160 | { } /* Terminating entry */ | |
161 | }; | |
162 | ||
163 | MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); | |
164 | ||
165 | /* USB subsystem interface */ | |
166 | static struct usb_driver usb_dsbr100_driver = { | |
04e0ffbb AK |
167 | .name = "dsbr100", |
168 | .probe = usb_dsbr100_probe, | |
169 | .disconnect = usb_dsbr100_disconnect, | |
170 | .id_table = usb_dsbr100_device_table, | |
171 | .suspend = usb_dsbr100_suspend, | |
172 | .resume = usb_dsbr100_resume, | |
173 | .reset_resume = usb_dsbr100_resume, | |
174 | .supports_autosuspend = 0, | |
1da177e4 LT |
175 | }; |
176 | ||
177 | /* Low-level device interface begins here */ | |
178 | ||
179 | /* switch on radio */ | |
5aff308c | 180 | static int dsbr100_start(struct dsbr100_device *radio) |
1da177e4 | 181 | { |
417b7953 AK |
182 | int retval; |
183 | int request; | |
184 | ||
3a0efc32 | 185 | mutex_lock(&radio->lock); |
417b7953 AK |
186 | |
187 | retval = usb_control_msg(radio->usbdev, | |
188 | usb_rcvctrlpipe(radio->usbdev, 0), | |
189 | USB_REQ_GET_STATUS, | |
190 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | |
191 | 0x00, 0xC7, radio->transfer_buffer, 8, 300); | |
192 | ||
193 | if (retval < 0) { | |
194 | request = USB_REQ_GET_STATUS; | |
195 | goto usb_control_msg_failed; | |
3a0efc32 AK |
196 | } |
197 | ||
417b7953 AK |
198 | retval = usb_control_msg(radio->usbdev, |
199 | usb_rcvctrlpipe(radio->usbdev, 0), | |
200 | DSB100_ONOFF, | |
201 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | |
202 | 0x01, 0x00, radio->transfer_buffer, 8, 300); | |
203 | ||
204 | if (retval < 0) { | |
205 | request = DSB100_ONOFF; | |
206 | goto usb_control_msg_failed; | |
207 | } | |
208 | ||
f1ca0adf | 209 | radio->status = STARTED; |
3a0efc32 | 210 | mutex_unlock(&radio->lock); |
1da177e4 | 211 | return (radio->transfer_buffer)[0]; |
417b7953 AK |
212 | |
213 | usb_control_msg_failed: | |
214 | mutex_unlock(&radio->lock); | |
215 | dev_err(&radio->usbdev->dev, | |
216 | "%s - usb_control_msg returned %i, request %i\n", | |
217 | __func__, retval, request); | |
d25cb646 | 218 | return retval; |
417b7953 | 219 | |
1da177e4 LT |
220 | } |
221 | ||
1da177e4 | 222 | /* switch off radio */ |
5aff308c | 223 | static int dsbr100_stop(struct dsbr100_device *radio) |
1da177e4 | 224 | { |
417b7953 AK |
225 | int retval; |
226 | int request; | |
227 | ||
3a0efc32 | 228 | mutex_lock(&radio->lock); |
417b7953 AK |
229 | |
230 | retval = usb_control_msg(radio->usbdev, | |
231 | usb_rcvctrlpipe(radio->usbdev, 0), | |
232 | USB_REQ_GET_STATUS, | |
233 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | |
234 | 0x16, 0x1C, radio->transfer_buffer, 8, 300); | |
235 | ||
236 | if (retval < 0) { | |
237 | request = USB_REQ_GET_STATUS; | |
238 | goto usb_control_msg_failed; | |
239 | } | |
240 | ||
241 | retval = usb_control_msg(radio->usbdev, | |
242 | usb_rcvctrlpipe(radio->usbdev, 0), | |
243 | DSB100_ONOFF, | |
244 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | |
245 | 0x00, 0x00, radio->transfer_buffer, 8, 300); | |
246 | ||
247 | if (retval < 0) { | |
248 | request = DSB100_ONOFF; | |
249 | goto usb_control_msg_failed; | |
3a0efc32 AK |
250 | } |
251 | ||
f1ca0adf | 252 | radio->status = STOPPED; |
3a0efc32 | 253 | mutex_unlock(&radio->lock); |
1da177e4 | 254 | return (radio->transfer_buffer)[0]; |
417b7953 AK |
255 | |
256 | usb_control_msg_failed: | |
257 | mutex_unlock(&radio->lock); | |
258 | dev_err(&radio->usbdev->dev, | |
259 | "%s - usb_control_msg returned %i, request %i\n", | |
260 | __func__, retval, request); | |
d25cb646 | 261 | return retval; |
417b7953 | 262 | |
1da177e4 LT |
263 | } |
264 | ||
265 | /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ | |
917fab4f | 266 | static int dsbr100_setfreq(struct dsbr100_device *radio) |
1da177e4 | 267 | { |
417b7953 AK |
268 | int retval; |
269 | int request; | |
917fab4f | 270 | int freq = (radio->curfreq / 16 * 80) / 1000 + 856; |
417b7953 | 271 | |
3a0efc32 | 272 | mutex_lock(&radio->lock); |
417b7953 AK |
273 | |
274 | retval = usb_control_msg(radio->usbdev, | |
275 | usb_rcvctrlpipe(radio->usbdev, 0), | |
276 | DSB100_TUNE, | |
277 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | |
278 | (freq >> 8) & 0x00ff, freq & 0xff, | |
279 | radio->transfer_buffer, 8, 300); | |
280 | ||
281 | if (retval < 0) { | |
282 | request = DSB100_TUNE; | |
283 | goto usb_control_msg_failed; | |
284 | } | |
285 | ||
286 | retval = usb_control_msg(radio->usbdev, | |
287 | usb_rcvctrlpipe(radio->usbdev, 0), | |
288 | USB_REQ_GET_STATUS, | |
289 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | |
290 | 0x96, 0xB7, radio->transfer_buffer, 8, 300); | |
291 | ||
292 | if (retval < 0) { | |
293 | request = USB_REQ_GET_STATUS; | |
294 | goto usb_control_msg_failed; | |
295 | } | |
296 | ||
297 | retval = usb_control_msg(radio->usbdev, | |
298 | usb_rcvctrlpipe(radio->usbdev, 0), | |
299 | USB_REQ_GET_STATUS, | |
300 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | |
301 | 0x00, 0x24, radio->transfer_buffer, 8, 300); | |
302 | ||
303 | if (retval < 0) { | |
304 | request = USB_REQ_GET_STATUS; | |
305 | goto usb_control_msg_failed; | |
1da177e4 | 306 | } |
3a0efc32 | 307 | |
223377e7 | 308 | radio->stereo = !((radio->transfer_buffer)[0] & 0x01); |
3a0efc32 | 309 | mutex_unlock(&radio->lock); |
1da177e4 | 310 | return (radio->transfer_buffer)[0]; |
417b7953 AK |
311 | |
312 | usb_control_msg_failed: | |
313 | radio->stereo = -1; | |
314 | mutex_unlock(&radio->lock); | |
315 | dev_err(&radio->usbdev->dev, | |
316 | "%s - usb_control_msg returned %i, request %i\n", | |
317 | __func__, retval, request); | |
d25cb646 | 318 | return retval; |
1da177e4 LT |
319 | } |
320 | ||
321 | /* return the device status. This is, in effect, just whether it | |
322 | sees a stereo signal or not. Pity. */ | |
5aff308c | 323 | static void dsbr100_getstat(struct dsbr100_device *radio) |
1da177e4 | 324 | { |
417b7953 AK |
325 | int retval; |
326 | ||
3a0efc32 | 327 | mutex_lock(&radio->lock); |
417b7953 AK |
328 | |
329 | retval = usb_control_msg(radio->usbdev, | |
330 | usb_rcvctrlpipe(radio->usbdev, 0), | |
d56410e0 | 331 | USB_REQ_GET_STATUS, |
1da177e4 | 332 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
417b7953 AK |
333 | 0x00 , 0x24, radio->transfer_buffer, 8, 300); |
334 | ||
335 | if (retval < 0) { | |
1da177e4 | 336 | radio->stereo = -1; |
417b7953 AK |
337 | dev_err(&radio->usbdev->dev, |
338 | "%s - usb_control_msg returned %i, request %i\n", | |
339 | __func__, retval, USB_REQ_GET_STATUS); | |
340 | } else { | |
223377e7 | 341 | radio->stereo = !(radio->transfer_buffer[0] & 0x01); |
417b7953 AK |
342 | } |
343 | ||
3a0efc32 | 344 | mutex_unlock(&radio->lock); |
1da177e4 LT |
345 | } |
346 | ||
1da177e4 LT |
347 | /* USB subsystem interface begins here */ |
348 | ||
fc55bcb0 AK |
349 | /* |
350 | * Handle unplugging of the device. | |
351 | * We call video_unregister_device in any case. | |
352 | * The last function called in this procedure is | |
353 | * usb_dsbr100_video_device_release | |
354 | */ | |
1da177e4 LT |
355 | static void usb_dsbr100_disconnect(struct usb_interface *intf) |
356 | { | |
5aff308c | 357 | struct dsbr100_device *radio = usb_get_intfdata(intf); |
1da177e4 LT |
358 | |
359 | usb_set_intfdata (intf, NULL); | |
3a0efc32 AK |
360 | |
361 | mutex_lock(&radio->lock); | |
362 | radio->removed = 1; | |
363 | mutex_unlock(&radio->lock); | |
364 | ||
365 | video_unregister_device(&radio->videodev); | |
406827cc | 366 | v4l2_device_disconnect(&radio->v4l2_dev); |
1da177e4 LT |
367 | } |
368 | ||
369 | ||
7002a4f3 DL |
370 | static int vidioc_querycap(struct file *file, void *priv, |
371 | struct v4l2_capability *v) | |
372 | { | |
c7181cfa AK |
373 | struct dsbr100_device *radio = video_drvdata(file); |
374 | ||
7002a4f3 DL |
375 | strlcpy(v->driver, "dsbr100", sizeof(v->driver)); |
376 | strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card)); | |
c7181cfa | 377 | usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); |
7002a4f3 DL |
378 | v->version = RADIO_VERSION; |
379 | v->capabilities = V4L2_CAP_TUNER; | |
380 | return 0; | |
381 | } | |
1da177e4 | 382 | |
7002a4f3 DL |
383 | static int vidioc_g_tuner(struct file *file, void *priv, |
384 | struct v4l2_tuner *v) | |
1da177e4 | 385 | { |
c170ecf4 | 386 | struct dsbr100_device *radio = video_drvdata(file); |
7002a4f3 | 387 | |
3a0efc32 AK |
388 | /* safety check */ |
389 | if (radio->removed) | |
390 | return -EIO; | |
391 | ||
7002a4f3 DL |
392 | if (v->index > 0) |
393 | return -EINVAL; | |
394 | ||
395 | dsbr100_getstat(radio); | |
396 | strcpy(v->name, "FM"); | |
397 | v->type = V4L2_TUNER_RADIO; | |
223377e7 AK |
398 | v->rangelow = FREQ_MIN * FREQ_MUL; |
399 | v->rangehigh = FREQ_MAX * FREQ_MUL; | |
400 | v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; | |
7002a4f3 DL |
401 | v->capability = V4L2_TUNER_CAP_LOW; |
402 | if(radio->stereo) | |
403 | v->audmode = V4L2_TUNER_MODE_STEREO; | |
404 | else | |
405 | v->audmode = V4L2_TUNER_MODE_MONO; | |
406 | v->signal = 0xffff; /* We can't get the signal strength */ | |
407 | return 0; | |
408 | } | |
1da177e4 | 409 | |
7002a4f3 DL |
410 | static int vidioc_s_tuner(struct file *file, void *priv, |
411 | struct v4l2_tuner *v) | |
412 | { | |
3a0efc32 AK |
413 | struct dsbr100_device *radio = video_drvdata(file); |
414 | ||
415 | /* safety check */ | |
416 | if (radio->removed) | |
417 | return -EIO; | |
418 | ||
7002a4f3 DL |
419 | if (v->index > 0) |
420 | return -EINVAL; | |
1da177e4 | 421 | |
7002a4f3 DL |
422 | return 0; |
423 | } | |
5aff308c | 424 | |
7002a4f3 DL |
425 | static int vidioc_s_frequency(struct file *file, void *priv, |
426 | struct v4l2_frequency *f) | |
427 | { | |
c170ecf4 | 428 | struct dsbr100_device *radio = video_drvdata(file); |
417b7953 | 429 | int retval; |
1da177e4 | 430 | |
3a0efc32 AK |
431 | /* safety check */ |
432 | if (radio->removed) | |
433 | return -EIO; | |
434 | ||
fdf9c997 | 435 | mutex_lock(&radio->lock); |
7002a4f3 | 436 | radio->curfreq = f->frequency; |
fdf9c997 AK |
437 | mutex_unlock(&radio->lock); |
438 | ||
917fab4f | 439 | retval = dsbr100_setfreq(radio); |
d25cb646 | 440 | if (retval < 0) |
aa82661b | 441 | dev_warn(&radio->usbdev->dev, "Set frequency failed\n"); |
7002a4f3 DL |
442 | return 0; |
443 | } | |
5aff308c | 444 | |
7002a4f3 DL |
445 | static int vidioc_g_frequency(struct file *file, void *priv, |
446 | struct v4l2_frequency *f) | |
447 | { | |
c170ecf4 | 448 | struct dsbr100_device *radio = video_drvdata(file); |
5aff308c | 449 | |
3a0efc32 AK |
450 | /* safety check */ |
451 | if (radio->removed) | |
452 | return -EIO; | |
453 | ||
7002a4f3 DL |
454 | f->type = V4L2_TUNER_RADIO; |
455 | f->frequency = radio->curfreq; | |
456 | return 0; | |
457 | } | |
5aff308c | 458 | |
7002a4f3 DL |
459 | static int vidioc_queryctrl(struct file *file, void *priv, |
460 | struct v4l2_queryctrl *qc) | |
461 | { | |
406827cc AK |
462 | switch (qc->id) { |
463 | case V4L2_CID_AUDIO_MUTE: | |
464 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | |
7002a4f3 | 465 | } |
406827cc | 466 | |
7002a4f3 DL |
467 | return -EINVAL; |
468 | } | |
5aff308c | 469 | |
7002a4f3 DL |
470 | static int vidioc_g_ctrl(struct file *file, void *priv, |
471 | struct v4l2_control *ctrl) | |
472 | { | |
c170ecf4 | 473 | struct dsbr100_device *radio = video_drvdata(file); |
1da177e4 | 474 | |
3a0efc32 AK |
475 | /* safety check */ |
476 | if (radio->removed) | |
477 | return -EIO; | |
478 | ||
7002a4f3 DL |
479 | switch (ctrl->id) { |
480 | case V4L2_CID_AUDIO_MUTE: | |
f1ca0adf | 481 | ctrl->value = radio->status; |
7002a4f3 DL |
482 | return 0; |
483 | } | |
484 | return -EINVAL; | |
485 | } | |
5aff308c | 486 | |
7002a4f3 DL |
487 | static int vidioc_s_ctrl(struct file *file, void *priv, |
488 | struct v4l2_control *ctrl) | |
489 | { | |
c170ecf4 | 490 | struct dsbr100_device *radio = video_drvdata(file); |
417b7953 | 491 | int retval; |
5aff308c | 492 | |
3a0efc32 AK |
493 | /* safety check */ |
494 | if (radio->removed) | |
495 | return -EIO; | |
496 | ||
7002a4f3 DL |
497 | switch (ctrl->id) { |
498 | case V4L2_CID_AUDIO_MUTE: | |
499 | if (ctrl->value) { | |
417b7953 | 500 | retval = dsbr100_stop(radio); |
d25cb646 | 501 | if (retval < 0) { |
aa82661b GKH |
502 | dev_warn(&radio->usbdev->dev, |
503 | "Radio did not respond properly\n"); | |
90b698dd AK |
504 | return -EBUSY; |
505 | } | |
7002a4f3 | 506 | } else { |
417b7953 | 507 | retval = dsbr100_start(radio); |
d25cb646 | 508 | if (retval < 0) { |
aa82661b GKH |
509 | dev_warn(&radio->usbdev->dev, |
510 | "Radio did not respond properly\n"); | |
90b698dd AK |
511 | return -EBUSY; |
512 | } | |
1da177e4 | 513 | } |
7002a4f3 | 514 | return 0; |
1da177e4 | 515 | } |
7002a4f3 | 516 | return -EINVAL; |
1da177e4 LT |
517 | } |
518 | ||
7002a4f3 DL |
519 | static int vidioc_g_audio(struct file *file, void *priv, |
520 | struct v4l2_audio *a) | |
1da177e4 | 521 | { |
7002a4f3 DL |
522 | if (a->index > 1) |
523 | return -EINVAL; | |
524 | ||
525 | strcpy(a->name, "Radio"); | |
526 | a->capability = V4L2_AUDCAP_STEREO; | |
527 | return 0; | |
528 | } | |
529 | ||
530 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | |
531 | { | |
532 | *i = 0; | |
533 | return 0; | |
534 | } | |
535 | ||
536 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | |
537 | { | |
538 | if (i != 0) | |
539 | return -EINVAL; | |
540 | return 0; | |
541 | } | |
542 | ||
543 | static int vidioc_s_audio(struct file *file, void *priv, | |
544 | struct v4l2_audio *a) | |
545 | { | |
546 | if (a->index != 0) | |
547 | return -EINVAL; | |
548 | return 0; | |
1da177e4 LT |
549 | } |
550 | ||
04e0ffbb AK |
551 | /* Suspend device - stop device. */ |
552 | static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) | |
553 | { | |
554 | struct dsbr100_device *radio = usb_get_intfdata(intf); | |
555 | int retval; | |
556 | ||
f1ca0adf AK |
557 | if (radio->status == STARTED) { |
558 | retval = dsbr100_stop(radio); | |
559 | if (retval < 0) | |
560 | dev_warn(&intf->dev, "dsbr100_stop failed\n"); | |
561 | ||
562 | /* After dsbr100_stop() status set to STOPPED. | |
563 | * If we want driver to start radio on resume | |
564 | * we set status equal to STARTED. | |
565 | * On resume we will check status and run radio if needed. | |
566 | */ | |
567 | ||
568 | mutex_lock(&radio->lock); | |
569 | radio->status = STARTED; | |
570 | mutex_unlock(&radio->lock); | |
571 | } | |
04e0ffbb AK |
572 | |
573 | dev_info(&intf->dev, "going into suspend..\n"); | |
574 | ||
575 | return 0; | |
576 | } | |
577 | ||
578 | /* Resume device - start device. */ | |
579 | static int usb_dsbr100_resume(struct usb_interface *intf) | |
580 | { | |
581 | struct dsbr100_device *radio = usb_get_intfdata(intf); | |
582 | int retval; | |
583 | ||
f1ca0adf AK |
584 | if (radio->status == STARTED) { |
585 | retval = dsbr100_start(radio); | |
586 | if (retval < 0) | |
587 | dev_warn(&intf->dev, "dsbr100_start failed\n"); | |
588 | } | |
04e0ffbb AK |
589 | |
590 | dev_info(&intf->dev, "coming out of suspend..\n"); | |
591 | ||
592 | return 0; | |
593 | } | |
594 | ||
fc55bcb0 | 595 | /* free data structures */ |
3a0efc32 AK |
596 | static void usb_dsbr100_video_device_release(struct video_device *videodev) |
597 | { | |
598 | struct dsbr100_device *radio = videodev_to_radio(videodev); | |
599 | ||
406827cc | 600 | v4l2_device_unregister(&radio->v4l2_dev); |
3a0efc32 AK |
601 | kfree(radio->transfer_buffer); |
602 | kfree(radio); | |
603 | } | |
604 | ||
7002a4f3 | 605 | /* File system interface */ |
bec43661 | 606 | static const struct v4l2_file_operations usb_dsbr100_fops = { |
7002a4f3 | 607 | .owner = THIS_MODULE, |
7002a4f3 | 608 | .ioctl = video_ioctl2, |
7002a4f3 DL |
609 | }; |
610 | ||
a399810c | 611 | static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { |
7002a4f3 DL |
612 | .vidioc_querycap = vidioc_querycap, |
613 | .vidioc_g_tuner = vidioc_g_tuner, | |
614 | .vidioc_s_tuner = vidioc_s_tuner, | |
615 | .vidioc_g_frequency = vidioc_g_frequency, | |
616 | .vidioc_s_frequency = vidioc_s_frequency, | |
617 | .vidioc_queryctrl = vidioc_queryctrl, | |
618 | .vidioc_g_ctrl = vidioc_g_ctrl, | |
619 | .vidioc_s_ctrl = vidioc_s_ctrl, | |
620 | .vidioc_g_audio = vidioc_g_audio, | |
621 | .vidioc_s_audio = vidioc_s_audio, | |
622 | .vidioc_g_input = vidioc_g_input, | |
623 | .vidioc_s_input = vidioc_s_input, | |
624 | }; | |
625 | ||
fc55bcb0 | 626 | /* check if the device is present and register with v4l and usb if it is */ |
7002a4f3 DL |
627 | static int usb_dsbr100_probe(struct usb_interface *intf, |
628 | const struct usb_device_id *id) | |
629 | { | |
630 | struct dsbr100_device *radio; | |
406827cc | 631 | struct v4l2_device *v4l2_dev; |
417b7953 | 632 | int retval; |
7002a4f3 | 633 | |
406827cc | 634 | radio = kzalloc(sizeof(struct dsbr100_device), GFP_KERNEL); |
223377e7 AK |
635 | |
636 | if (!radio) | |
7002a4f3 | 637 | return -ENOMEM; |
223377e7 AK |
638 | |
639 | radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL); | |
640 | ||
641 | if (!(radio->transfer_buffer)) { | |
863c86dd ON |
642 | kfree(radio); |
643 | return -ENOMEM; | |
644 | } | |
223377e7 | 645 | |
406827cc AK |
646 | v4l2_dev = &radio->v4l2_dev; |
647 | ||
648 | retval = v4l2_device_register(&intf->dev, v4l2_dev); | |
649 | if (retval < 0) { | |
650 | v4l2_err(v4l2_dev, "couldn't register v4l2_device\n"); | |
651 | kfree(radio->transfer_buffer); | |
652 | kfree(radio); | |
653 | return retval; | |
654 | } | |
655 | ||
656 | strlcpy(radio->videodev.name, v4l2_dev->name, sizeof(radio->videodev.name)); | |
657 | radio->videodev.v4l2_dev = v4l2_dev; | |
658 | radio->videodev.fops = &usb_dsbr100_fops; | |
659 | radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops; | |
660 | radio->videodev.release = usb_dsbr100_video_device_release; | |
661 | ||
3a0efc32 | 662 | mutex_init(&radio->lock); |
3a0efc32 | 663 | |
7002a4f3 | 664 | radio->removed = 0; |
7002a4f3 | 665 | radio->usbdev = interface_to_usbdev(intf); |
223377e7 | 666 | radio->curfreq = FREQ_MIN * FREQ_MUL; |
f1ca0adf | 667 | radio->status = STOPPED; |
406827cc | 668 | |
3a0efc32 | 669 | video_set_drvdata(&radio->videodev, radio); |
406827cc | 670 | |
417b7953 AK |
671 | retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); |
672 | if (retval < 0) { | |
406827cc AK |
673 | v4l2_err(v4l2_dev, "couldn't register video device\n"); |
674 | v4l2_device_unregister(v4l2_dev); | |
863c86dd | 675 | kfree(radio->transfer_buffer); |
7002a4f3 DL |
676 | kfree(radio); |
677 | return -EIO; | |
678 | } | |
679 | usb_set_intfdata(intf, radio); | |
680 | return 0; | |
681 | } | |
682 | ||
1da177e4 LT |
683 | static int __init dsbr100_init(void) |
684 | { | |
685 | int retval = usb_register(&usb_dsbr100_driver); | |
a482f327 GKH |
686 | printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" |
687 | DRIVER_DESC "\n"); | |
1da177e4 LT |
688 | return retval; |
689 | } | |
690 | ||
691 | static void __exit dsbr100_exit(void) | |
692 | { | |
693 | usb_deregister(&usb_dsbr100_driver); | |
694 | } | |
695 | ||
696 | module_init (dsbr100_init); | |
697 | module_exit (dsbr100_exit); | |
698 | ||
699 | MODULE_AUTHOR( DRIVER_AUTHOR ); | |
700 | MODULE_DESCRIPTION( DRIVER_DESC ); | |
701 | MODULE_LICENSE("GPL"); |