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; |
