diff options
Diffstat (limited to 'drivers/media/radio/dsbr100.c')
-rw-r--r-- | drivers/media/radio/dsbr100.c | 198 |
1 files changed, 127 insertions, 71 deletions
diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index f7e33f9ee8e9..db865a0667e5 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c | |||
@@ -33,8 +33,14 @@ | |||
33 | 33 | ||
34 | History: | 34 | History: |
35 | 35 | ||
36 | Version 0.41-ac1: | ||
37 | Alan Cox: Some cleanups and fixes | ||
38 | |||
39 | Version 0.41: | ||
40 | Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> | ||
41 | |||
36 | Version 0.40: | 42 | Version 0.40: |
37 | Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing | 43 | Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing |
38 | 44 | ||
39 | Version 0.30: | 45 | Version 0.30: |
40 | Markus: Updates for 2.5.x kernel and more ISO compliant source | 46 | Markus: Updates for 2.5.x kernel and more ISO compliant source |
@@ -65,13 +71,12 @@ | |||
65 | 71 | ||
66 | */ | 72 | */ |
67 | 73 | ||
68 | |||
69 | #include <linux/kernel.h> | 74 | #include <linux/kernel.h> |
70 | #include <linux/module.h> | 75 | #include <linux/module.h> |
71 | #include <linux/init.h> | 76 | #include <linux/init.h> |
72 | #include <linux/slab.h> | 77 | #include <linux/slab.h> |
73 | #include <linux/input.h> | 78 | #include <linux/input.h> |
74 | #include <linux/videodev.h> | 79 | #include <linux/videodev2.h> |
75 | #include <media/v4l2-common.h> | 80 | #include <media/v4l2-common.h> |
76 | #include <linux/usb.h> | 81 | #include <linux/usb.h> |
77 | #include <linux/smp_lock.h> | 82 | #include <linux/smp_lock.h> |
@@ -79,7 +84,22 @@ | |||
79 | /* | 84 | /* |
80 | * Version Information | 85 | * Version Information |
81 | */ | 86 | */ |
82 | #define DRIVER_VERSION "v0.40" | 87 | #include <linux/version.h> /* for KERNEL_VERSION MACRO */ |
88 | |||
89 | #define DRIVER_VERSION "v0.41" | ||
90 | #define RADIO_VERSION KERNEL_VERSION(0,4,1) | ||
91 | |||
92 | static struct v4l2_queryctrl radio_qctrl[] = { | ||
93 | { | ||
94 | .id = V4L2_CID_AUDIO_MUTE, | ||
95 | .name = "Mute", | ||
96 | .minimum = 0, | ||
97 | .maximum = 1, | ||
98 | .default_value = 1, | ||
99 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
100 | } | ||
101 | }; | ||
102 | |||
83 | #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>" | 103 | #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>" |
84 | #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" | 104 | #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" |
85 | 105 | ||
@@ -111,7 +131,7 @@ static int radio_nr = -1; | |||
111 | module_param(radio_nr, int, 0); | 131 | module_param(radio_nr, int, 0); |
112 | 132 | ||
113 | /* Data for one (physical) device */ | 133 | /* Data for one (physical) device */ |
114 | typedef struct { | 134 | struct dsbr100_device { |
115 | struct usb_device *usbdev; | 135 | struct usb_device *usbdev; |
116 | struct video_device *videodev; | 136 | struct video_device *videodev; |
117 | unsigned char transfer_buffer[TB_LEN]; | 137 | unsigned char transfer_buffer[TB_LEN]; |
@@ -119,7 +139,8 @@ typedef struct { | |||
119 | int stereo; | 139 | int stereo; |
120 | int users; | 140 | int users; |
121 | int removed; | 141 | int removed; |
122 | } dsbr100_device; | 142 | int muted; |
143 | }; | ||
123 | 144 | ||
124 | 145 | ||
125 | /* File system interface */ | 146 | /* File system interface */ |
@@ -138,7 +159,6 @@ static struct video_device dsbr100_videodev_template= | |||
138 | .owner = THIS_MODULE, | 159 | .owner = THIS_MODULE, |
139 | .name = "D-Link DSB-R 100", | 160 | .name = "D-Link DSB-R 100", |
140 | .type = VID_TYPE_TUNER, | 161 | .type = VID_TYPE_TUNER, |
141 | .hardware = VID_HARDWARE_AZTECH, | ||
142 | .fops = &usb_dsbr100_fops, | 162 | .fops = &usb_dsbr100_fops, |
143 | .release = video_device_release, | 163 | .release = video_device_release, |
144 | }; | 164 | }; |
@@ -161,7 +181,7 @@ static struct usb_driver usb_dsbr100_driver = { | |||
161 | /* Low-level device interface begins here */ | 181 | /* Low-level device interface begins here */ |
162 | 182 | ||
163 | /* switch on radio */ | 183 | /* switch on radio */ |
164 | static int dsbr100_start(dsbr100_device *radio) | 184 | static int dsbr100_start(struct dsbr100_device *radio) |
165 | { | 185 | { |
166 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | 186 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), |
167 | USB_REQ_GET_STATUS, | 187 | USB_REQ_GET_STATUS, |
@@ -172,12 +192,13 @@ static int dsbr100_start(dsbr100_device *radio) | |||
172 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | 192 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
173 | 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) | 193 | 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) |
174 | return -1; | 194 | return -1; |
195 | radio->muted=0; | ||
175 | return (radio->transfer_buffer)[0]; | 196 | return (radio->transfer_buffer)[0]; |
176 | } | 197 | } |
177 | 198 | ||
178 | 199 | ||
179 | /* switch off radio */ | 200 | /* switch off radio */ |
180 | static int dsbr100_stop(dsbr100_device *radio) | 201 | static int dsbr100_stop(struct dsbr100_device *radio) |
181 | { | 202 | { |
182 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | 203 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), |
183 | USB_REQ_GET_STATUS, | 204 | USB_REQ_GET_STATUS, |
@@ -188,11 +209,12 @@ static int dsbr100_stop(dsbr100_device *radio) | |||
188 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | 209 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
189 | 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) | 210 | 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) |
190 | return -1; | 211 | return -1; |
212 | radio->muted=1; | ||
191 | return (radio->transfer_buffer)[0]; | 213 | return (radio->transfer_buffer)[0]; |
192 | } | 214 | } |
193 | 215 | ||
194 | /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ | 216 | /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ |
195 | static int dsbr100_setfreq(dsbr100_device *radio, int freq) | 217 | static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) |
196 | { | 218 | { |
197 | freq = (freq/16*80)/1000+856; | 219 | freq = (freq/16*80)/1000+856; |
198 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | 220 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), |
@@ -217,7 +239,7 @@ static int dsbr100_setfreq(dsbr100_device *radio, int freq) | |||
217 | 239 | ||
218 | /* return the device status. This is, in effect, just whether it | 240 | /* return the device status. This is, in effect, just whether it |
219 | sees a stereo signal or not. Pity. */ | 241 | sees a stereo signal or not. Pity. */ |
220 | static void dsbr100_getstat(dsbr100_device *radio) | 242 | static void dsbr100_getstat(struct dsbr100_device *radio) |
221 | { | 243 | { |
222 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | 244 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), |
223 | USB_REQ_GET_STATUS, | 245 | USB_REQ_GET_STATUS, |
@@ -236,9 +258,9 @@ usb if it is */ | |||
236 | static int usb_dsbr100_probe(struct usb_interface *intf, | 258 | static int usb_dsbr100_probe(struct usb_interface *intf, |
237 | const struct usb_device_id *id) | 259 | const struct usb_device_id *id) |
238 | { | 260 | { |
239 | dsbr100_device *radio; | 261 | struct dsbr100_device *radio; |
240 | 262 | ||
241 | if (!(radio = kmalloc(sizeof(dsbr100_device), GFP_KERNEL))) | 263 | if (!(radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL))) |
242 | return -ENOMEM; | 264 | return -ENOMEM; |
243 | if (!(radio->videodev = video_device_alloc())) { | 265 | if (!(radio->videodev = video_device_alloc())) { |
244 | kfree(radio); | 266 | kfree(radio); |
@@ -271,7 +293,7 @@ code I'd expect I better did that, but if there's a memory | |||
271 | leak here it's tiny (~50 bytes per disconnect) */ | 293 | leak here it's tiny (~50 bytes per disconnect) */ |
272 | static void usb_dsbr100_disconnect(struct usb_interface *intf) | 294 | static void usb_dsbr100_disconnect(struct usb_interface *intf) |
273 | { | 295 | { |
274 | dsbr100_device *radio = usb_get_intfdata(intf); | 296 | struct dsbr100_device *radio = usb_get_intfdata(intf); |
275 | 297 | ||
276 | usb_set_intfdata (intf, NULL); | 298 | usb_set_intfdata (intf, NULL); |
277 | if (radio) { | 299 | if (radio) { |
@@ -291,89 +313,121 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf) | |||
291 | static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file, | 313 | static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file, |
292 | unsigned int cmd, void *arg) | 314 | unsigned int cmd, void *arg) |
293 | { | 315 | { |
294 | dsbr100_device *radio=video_get_drvdata(video_devdata(file)); | 316 | struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); |
295 | 317 | ||
296 | if (!radio) | 318 | if (!radio) |
297 | return -EIO; | 319 | return -EIO; |
298 | 320 | ||
299 | switch(cmd) { | 321 | switch(cmd) { |
300 | case VIDIOCGCAP: { | 322 | case VIDIOC_QUERYCAP: |
301 | struct video_capability *v = arg; | 323 | { |
302 | 324 | struct v4l2_capability *v = arg; | |
303 | memset(v, 0, sizeof(*v)); | 325 | memset(v,0,sizeof(*v)); |
304 | v->type = VID_TYPE_TUNER; | 326 | strlcpy(v->driver, "dsbr100", sizeof (v->driver)); |
305 | v->channels = 1; | 327 | strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof (v->card)); |
306 | v->audios = 1; | 328 | sprintf(v->bus_info,"ISA"); |
307 | strcpy(v->name, "D-Link R-100 USB FM Radio"); | 329 | v->version = RADIO_VERSION; |
330 | v->capabilities = V4L2_CAP_TUNER; | ||
331 | |||
308 | return 0; | 332 | return 0; |
309 | } | 333 | } |
310 | case VIDIOCGTUNER: { | 334 | case VIDIOC_G_TUNER: |
311 | struct video_tuner *v = arg; | 335 | { |
336 | struct v4l2_tuner *v = arg; | ||
312 | 337 | ||
313 | dsbr100_getstat(radio); | 338 | if (v->index > 0) |
314 | if(v->tuner) /* Only 1 tuner */ | ||
315 | return -EINVAL; | 339 | return -EINVAL; |
340 | |||
341 | dsbr100_getstat(radio); | ||
342 | |||
343 | memset(v,0,sizeof(*v)); | ||
344 | strcpy(v->name, "FM"); | ||
345 | v->type = V4L2_TUNER_RADIO; | ||
346 | |||
316 | v->rangelow = FREQ_MIN*FREQ_MUL; | 347 | v->rangelow = FREQ_MIN*FREQ_MUL; |
317 | v->rangehigh = FREQ_MAX*FREQ_MUL; | 348 | v->rangehigh = FREQ_MAX*FREQ_MUL; |
318 | v->flags = VIDEO_TUNER_LOW; | 349 | v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; |
319 | v->mode = VIDEO_MODE_AUTO; | 350 | v->capability=V4L2_TUNER_CAP_LOW; |
320 | v->signal = radio->stereo*0x7000; | 351 | if(radio->stereo) |
321 | /* Don't know how to get signal strength */ | 352 | v->audmode = V4L2_TUNER_MODE_STEREO; |
322 | v->flags |= VIDEO_TUNER_STEREO_ON*radio->stereo; | 353 | else |
323 | strcpy(v->name, "DSB R-100"); | 354 | v->audmode = V4L2_TUNER_MODE_MONO; |
324 | return 0; | 355 | v->signal = 0xFFFF; /* We can't get the signal strength */ |
325 | } | ||
326 | case VIDIOCSTUNER: { | ||
327 | struct video_tuner *v = arg; | ||
328 | 356 | ||
329 | if(v->tuner!=0) | ||
330 | return -EINVAL; | ||
331 | /* Only 1 tuner so no setting needed ! */ | ||
332 | return 0; | 357 | return 0; |
333 | } | 358 | } |
334 | case VIDIOCGFREQ: { | 359 | case VIDIOC_S_TUNER: |
335 | int *freq = arg; | 360 | { |
361 | struct v4l2_tuner *v = arg; | ||
336 | 362 | ||
337 | if (radio->curfreq==-1) | 363 | if (v->index > 0) |
338 | return -EINVAL; | 364 | return -EINVAL; |
339 | *freq = radio->curfreq; | 365 | |
340 | return 0; | 366 | return 0; |
341 | } | 367 | } |
342 | case VIDIOCSFREQ: { | 368 | case VIDIOC_S_FREQUENCY: |
343 | int *freq = arg; | 369 | { |
370 | struct v4l2_frequency *f = arg; | ||
344 | 371 | ||
345 | radio->curfreq = *freq; | 372 | radio->curfreq = f->frequency; |
346 | if (dsbr100_setfreq(radio, radio->curfreq)==-1) | 373 | if (dsbr100_setfreq(radio, radio->curfreq)==-1) |
347 | warn("Set frequency failed"); | 374 | warn("Set frequency failed"); |
348 | return 0; | 375 | return 0; |
349 | } | 376 | } |
350 | case VIDIOCGAUDIO: { | 377 | case VIDIOC_G_FREQUENCY: |
351 | struct video_audio *v = arg; | 378 | { |
352 | 379 | struct v4l2_frequency *f = arg; | |
353 | memset(v, 0, sizeof(*v)); | 380 | |
354 | v->flags |= VIDEO_AUDIO_MUTABLE; | 381 | f->type = V4L2_TUNER_RADIO; |
355 | v->mode = VIDEO_SOUND_STEREO; | 382 | f->frequency = radio->curfreq; |
356 | v->volume = 1; | 383 | |
357 | v->step = 1; | ||
358 | strcpy(v->name, "Radio"); | ||
359 | return 0; | 384 | return 0; |
360 | } | 385 | } |
361 | case VIDIOCSAUDIO: { | 386 | case VIDIOC_QUERYCTRL: |
362 | struct video_audio *v = arg; | 387 | { |
363 | 388 | struct v4l2_queryctrl *qc = arg; | |
364 | if (v->audio) | 389 | int i; |
365 | return -EINVAL; | 390 | |
366 | if (v->flags&VIDEO_AUDIO_MUTE) { | 391 | for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { |
367 | if (dsbr100_stop(radio)==-1) | 392 | if (qc->id && qc->id == radio_qctrl[i].id) { |
368 | warn("Radio did not respond properly"); | 393 | memcpy(qc, &(radio_qctrl[i]), |
394 | sizeof(*qc)); | ||
395 | return 0; | ||
396 | } | ||
369 | } | 397 | } |
370 | else | 398 | return -EINVAL; |
371 | if (dsbr100_start(radio)==-1) | 399 | } |
372 | warn("Radio did not respond properly"); | 400 | case VIDIOC_G_CTRL: |
373 | return 0; | 401 | { |
402 | struct v4l2_control *ctrl= arg; | ||
403 | |||
404 | switch (ctrl->id) { | ||
405 | case V4L2_CID_AUDIO_MUTE: | ||
406 | ctrl->value=radio->muted; | ||
407 | return 0; | ||
408 | } | ||
409 | return -EINVAL; | ||
410 | } | ||
411 | case VIDIOC_S_CTRL: | ||
412 | { | ||
413 | struct v4l2_control *ctrl= arg; | ||
414 | |||
415 | switch (ctrl->id) { | ||
416 | case V4L2_CID_AUDIO_MUTE: | ||
417 | if (ctrl->value) { | ||
418 | if (dsbr100_stop(radio)==-1) | ||
419 | warn("Radio did not respond properly"); | ||
420 | } else { | ||
421 | if (dsbr100_start(radio)==-1) | ||
422 | warn("Radio did not respond properly"); | ||
423 | } | ||
424 | return 0; | ||
425 | } | ||
426 | return -EINVAL; | ||
374 | } | 427 | } |
375 | default: | 428 | default: |
376 | return -ENOIOCTLCMD; | 429 | return v4l_compat_translate_ioctl(inode,file,cmd,arg, |
430 | usb_dsbr100_do_ioctl); | ||
377 | } | 431 | } |
378 | } | 432 | } |
379 | 433 | ||
@@ -385,9 +439,11 @@ static int usb_dsbr100_ioctl(struct inode *inode, struct file *file, | |||
385 | 439 | ||
386 | static int usb_dsbr100_open(struct inode *inode, struct file *file) | 440 | static int usb_dsbr100_open(struct inode *inode, struct file *file) |
387 | { | 441 | { |
388 | dsbr100_device *radio=video_get_drvdata(video_devdata(file)); | 442 | struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); |
389 | 443 | ||
390 | radio->users = 1; | 444 | radio->users = 1; |
445 | radio->muted = 1; | ||
446 | |||
391 | if (dsbr100_start(radio)<0) { | 447 | if (dsbr100_start(radio)<0) { |
392 | warn("Radio did not start up properly"); | 448 | warn("Radio did not start up properly"); |
393 | radio->users = 0; | 449 | radio->users = 0; |
@@ -399,7 +455,7 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file) | |||
399 | 455 | ||
400 | static int usb_dsbr100_close(struct inode *inode, struct file *file) | 456 | static int usb_dsbr100_close(struct inode *inode, struct file *file) |
401 | { | 457 | { |
402 | dsbr100_device *radio=video_get_drvdata(video_devdata(file)); | 458 | struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); |
403 | 459 | ||
404 | if (!radio) | 460 | if (!radio) |
405 | return -ENODEV; | 461 | return -ENODEV; |