]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* A driver for the D-Link DSB-R100 USB radio. The R100 plugs |
2 | into both the USB and an analog audio input, so this thing | |
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 | ||
863c86dd ON |
36 | Version 0.43: |
37 | Oliver Neukum: avoided DMA coherency issue | |
38 | ||
7002a4f3 DL |
39 | Version 0.42: |
40 | Converted dsbr100 to use video_ioctl2 | |
41 | by Douglas Landgraf <dougsland@gmail.com> | |
42 | ||
5aff308c AC |
43 | Version 0.41-ac1: |
44 | Alan Cox: Some cleanups and fixes | |
45 | ||
46 | Version 0.41: | |
47 | Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> | |
48 | ||
1da177e4 | 49 | Version 0.40: |
5aff308c | 50 | Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing |
1da177e4 LT |
51 | |
52 | Version 0.30: | |
d56410e0 | 53 | Markus: Updates for 2.5.x kernel and more ISO compliant source |
1da177e4 LT |
54 | |
55 | Version 0.25: | |
d56410e0 | 56 | PSL and Markus: Cleanup, radio now doesn't stop on device close |
1da177e4 LT |
57 | |
58 | Version 0.24: | |
d56410e0 | 59 | Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally |
1da177e4 LT |
60 | right. Some minor cleanup, improved standalone compilation |
61 | ||
62 | Version 0.23: | |
d56410e0 | 63 | Markus: Sign extension bug fixed by declaring transfer_buffer unsigned |
1da177e4 LT |
64 | |
65 | Version 0.22: | |
d56410e0 | 66 | Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, |
1da177e4 LT |
67 | thanks to Mike Cox for pointing the problem out. |
68 | ||
69 | Version 0.21: | |
d56410e0 | 70 | Markus: Minor cleanup, warnings if something goes wrong, lame attempt |
1da177e4 LT |
71 | to adhere to Documentation/CodingStyle |
72 | ||
d56410e0 MCC |
73 | Version 0.2: |
74 | Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module | |
1da177e4 LT |
75 | Markus: Copyright clarification |
76 | ||
77 | Version 0.01: Markus: initial release | |
78 | ||
79 | */ | |
80 | ||
1da177e4 LT |
81 | #include <linux/kernel.h> |
82 | #include <linux/module.h> | |
83 | #include <linux/init.h> | |
84 | #include <linux/slab.h> | |
85 | #include <linux/input.h> | |
5aff308c | 86 | #include <linux/videodev2.h> |
5e87efa3 | 87 | #include <media/v4l2-common.h> |
35ea11ff | 88 | #include <media/v4l2-ioctl.h> |
1da177e4 | 89 | #include <linux/usb.h> |
1da177e4 LT |
90 | |
91 | /* | |
92 | * Version Information | |
93 | */ | |
5aff308c AC |
94 | #include <linux/version.h> /* for KERNEL_VERSION MACRO */ |
95 | ||
96 | #define DRIVER_VERSION "v0.41" | |
97 | #define RADIO_VERSION KERNEL_VERSION(0,4,1) | |
98 | ||
99 | static struct v4l2_queryctrl radio_qctrl[] = { | |
100 | { | |
101 | .id = V4L2_CID_AUDIO_MUTE, | |
102 | .name = "Mute", | |
103 | .minimum = 0, | |
104 | .maximum = 1, | |
105 | .default_value = 1, | |
106 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
107 | } | |
108 | }; | |
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 | ||
128 | ||
129 | static int usb_dsbr100_probe(struct usb_interface *intf, | |
130 | const struct usb_device_id *id); | |
131 | static void usb_dsbr100_disconnect(struct usb_interface *intf); | |
1da177e4 LT |
132 | static int usb_dsbr100_open(struct inode *inode, struct file *file); |
133 | static int usb_dsbr100_close(struct inode *inode, struct file *file); | |
134 | ||
135 | static int radio_nr = -1; | |
136 | module_param(radio_nr, int, 0); | |
137 | ||
138 | /* Data for one (physical) device */ | |
5aff308c | 139 | struct dsbr100_device { |
1da177e4 LT |
140 | struct usb_device *usbdev; |
141 | struct video_device *videodev; | |
863c86dd | 142 | u8 *transfer_buffer; |
1da177e4 LT |
143 | int curfreq; |
144 | int stereo; | |
145 | int users; | |
146 | int removed; | |
5aff308c AC |
147 | int muted; |
148 | }; | |
1da177e4 LT |
149 | |
150 | ||
1da177e4 LT |
151 | static struct usb_device_id usb_dsbr100_device_table [] = { |
152 | { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, | |
153 | { } /* Terminating entry */ | |
154 | }; | |
155 | ||
156 | MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); | |
157 | ||
158 | /* USB subsystem interface */ | |
159 | static struct usb_driver usb_dsbr100_driver = { | |
1da177e4 LT |
160 | .name = "dsbr100", |
161 | .probe = usb_dsbr100_probe, | |
162 | .disconnect = usb_dsbr100_disconnect, | |
163 | .id_table = usb_dsbr100_device_table, | |
164 | }; | |
165 | ||
166 | /* Low-level device interface begins here */ | |
167 | ||
168 | /* switch on radio */ | |
5aff308c | 169 | static int dsbr100_start(struct dsbr100_device *radio) |
1da177e4 LT |
170 | { |
171 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | |
d56410e0 | 172 | USB_REQ_GET_STATUS, |
1da177e4 LT |
173 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
174 | 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 || | |
175 | usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | |
d56410e0 | 176 | DSB100_ONOFF, |
1da177e4 LT |
177 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
178 | 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) | |
179 | return -1; | |
5aff308c | 180 | radio->muted=0; |
1da177e4 LT |
181 | return (radio->transfer_buffer)[0]; |
182 | } | |
183 | ||
184 | ||
185 | /* switch off radio */ | |
5aff308c | 186 | static int dsbr100_stop(struct dsbr100_device *radio) |
1da177e4 LT |
187 | { |
188 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | |
d56410e0 | 189 | USB_REQ_GET_STATUS, |
1da177e4 LT |
190 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
191 | 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 || | |
192 | usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | |
d56410e0 | 193 | DSB100_ONOFF, |
1da177e4 LT |
194 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
195 | 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) | |
196 | return -1; | |
5aff308c | 197 | radio->muted=1; |
1da177e4 LT |
198 | return (radio->transfer_buffer)[0]; |
199 | } | |
200 | ||
201 | /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ | |
5aff308c | 202 | static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) |
1da177e4 LT |
203 | { |
204 | freq = (freq/16*80)/1000+856; | |
205 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | |
d56410e0 | 206 | DSB100_TUNE, |
1da177e4 | 207 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
d56410e0 | 208 | (freq>>8)&0x00ff, freq&0xff, |
1da177e4 LT |
209 | radio->transfer_buffer, 8, 300)<0 || |
210 | usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | |
d56410e0 | 211 | USB_REQ_GET_STATUS, |
1da177e4 LT |
212 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
213 | 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 || | |
214 | usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | |
d56410e0 | 215 | USB_REQ_GET_STATUS, |
1da177e4 LT |
216 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
217 | 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) { | |
218 | radio->stereo = -1; | |
219 | return -1; | |
220 | } | |
221 | radio->stereo = ! ((radio->transfer_buffer)[0]&0x01); | |
222 | return (radio->transfer_buffer)[0]; | |
223 | } | |
224 | ||
225 | /* return the device status. This is, in effect, just whether it | |
226 | sees a stereo signal or not. Pity. */ | |
5aff308c | 227 | static void dsbr100_getstat(struct dsbr100_device *radio) |
1da177e4 LT |
228 | { |
229 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | |
d56410e0 | 230 | USB_REQ_GET_STATUS, |
1da177e4 LT |
231 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
232 | 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0) | |
233 | radio->stereo = -1; | |
234 | else | |
235 | radio->stereo = ! (radio->transfer_buffer[0]&0x01); | |
236 | } | |
237 | ||
238 | ||
239 | /* USB subsystem interface begins here */ | |
240 | ||
1da177e4 LT |
241 | /* handle unplugging of the device, release data structures |
242 | if nothing keeps us from doing it. If something is still | |
243 | keeping us busy, the release callback of v4l will take care | |
863c86dd | 244 | of releasing it. */ |
1da177e4 LT |
245 | static void usb_dsbr100_disconnect(struct usb_interface *intf) |
246 | { | |
5aff308c | 247 | struct dsbr100_device *radio = usb_get_intfdata(intf); |
1da177e4 LT |
248 | |
249 | usb_set_intfdata (intf, NULL); | |
250 | if (radio) { | |
251 | video_unregister_device(radio->videodev); | |
252 | radio->videodev = NULL; | |
253 | if (radio->users) { | |
863c86dd | 254 | kfree(radio->transfer_buffer); |
1da177e4 LT |
255 | kfree(radio); |
256 | } else { | |
257 | radio->removed = 1; | |
258 | } | |
259 | } | |
260 | } | |
261 | ||
262 | ||
7002a4f3 DL |
263 | static int vidioc_querycap(struct file *file, void *priv, |
264 | struct v4l2_capability *v) | |
265 | { | |
266 | strlcpy(v->driver, "dsbr100", sizeof(v->driver)); | |
267 | strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card)); | |
268 | sprintf(v->bus_info, "ISA"); | |
269 | v->version = RADIO_VERSION; | |
270 | v->capabilities = V4L2_CAP_TUNER; | |
271 | return 0; | |
272 | } | |
1da177e4 | 273 | |
7002a4f3 DL |
274 | static int vidioc_g_tuner(struct file *file, void *priv, |
275 | struct v4l2_tuner *v) | |
1da177e4 | 276 | { |
7002a4f3 DL |
277 | struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); |
278 | ||
279 | if (v->index > 0) | |
280 | return -EINVAL; | |
281 | ||
282 | dsbr100_getstat(radio); | |
283 | strcpy(v->name, "FM"); | |
284 | v->type = V4L2_TUNER_RADIO; | |
285 | v->rangelow = FREQ_MIN*FREQ_MUL; | |
286 | v->rangehigh = FREQ_MAX*FREQ_MUL; | |
287 | v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; | |
288 | v->capability = V4L2_TUNER_CAP_LOW; | |
289 | if(radio->stereo) | |
290 | v->audmode = V4L2_TUNER_MODE_STEREO; | |
291 | else | |
292 | v->audmode = V4L2_TUNER_MODE_MONO; | |
293 | v->signal = 0xffff; /* We can't get the signal strength */ | |
294 | return 0; | |
295 | } | |
1da177e4 | 296 | |
7002a4f3 DL |
297 | static int vidioc_s_tuner(struct file *file, void *priv, |
298 | struct v4l2_tuner *v) | |
299 | { | |
300 | if (v->index > 0) | |
301 | return -EINVAL; | |
1da177e4 | 302 | |
7002a4f3 DL |
303 | return 0; |
304 | } | |
5aff308c | 305 | |
7002a4f3 DL |
306 | static int vidioc_s_frequency(struct file *file, void *priv, |
307 | struct v4l2_frequency *f) | |
308 | { | |
309 | struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); | |
1da177e4 | 310 | |
7002a4f3 DL |
311 | radio->curfreq = f->frequency; |
312 | if (dsbr100_setfreq(radio, radio->curfreq)==-1) | |
313 | warn("Set frequency failed"); | |
314 | return 0; | |
315 | } | |
5aff308c | 316 | |
7002a4f3 DL |
317 | static int vidioc_g_frequency(struct file *file, void *priv, |
318 | struct v4l2_frequency *f) | |
319 | { | |
320 | struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); | |
5aff308c | 321 | |
7002a4f3 DL |
322 | f->type = V4L2_TUNER_RADIO; |
323 | f->frequency = radio->curfreq; | |
324 | return 0; | |
325 | } | |
5aff308c | 326 | |
7002a4f3 DL |
327 | static int vidioc_queryctrl(struct file *file, void *priv, |
328 | struct v4l2_queryctrl *qc) | |
329 | { | |
330 | int i; | |
1da177e4 | 331 | |
7002a4f3 DL |
332 | for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { |
333 | if (qc->id && qc->id == radio_qctrl[i].id) { | |
334 | memcpy(qc, &(radio_qctrl[i]), | |
335 | sizeof(*qc)); | |
1da177e4 LT |
336 | return 0; |
337 | } | |
7002a4f3 DL |
338 | } |
339 | return -EINVAL; | |
340 | } | |
5aff308c | 341 | |
7002a4f3 DL |
342 | static int vidioc_g_ctrl(struct file *file, void *priv, |
343 | struct v4l2_control *ctrl) | |
344 | { | |
345 | struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); | |
1da177e4 | 346 | |
7002a4f3 DL |
347 | switch (ctrl->id) { |
348 | case V4L2_CID_AUDIO_MUTE: | |
349 | ctrl->value = radio->muted; | |
350 | return 0; | |
351 | } | |
352 | return -EINVAL; | |
353 | } | |
5aff308c | 354 | |
7002a4f3 DL |
355 | static int vidioc_s_ctrl(struct file *file, void *priv, |
356 | struct v4l2_control *ctrl) | |
357 | { | |
358 | struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); | |
5aff308c | 359 | |
7002a4f3 DL |
360 | switch (ctrl->id) { |
361 | case V4L2_CID_AUDIO_MUTE: | |
362 | if (ctrl->value) { | |
363 | if (dsbr100_stop(radio)==-1) | |
364 | warn("Radio did not respond properly"); | |
365 | } else { | |
366 | if (dsbr100_start(radio)==-1) | |
367 | warn("Radio did not respond properly"); | |
1da177e4 | 368 | } |
7002a4f3 | 369 | return 0; |
1da177e4 | 370 | } |
7002a4f3 | 371 | return -EINVAL; |
1da177e4 LT |
372 | } |
373 | ||
7002a4f3 DL |
374 | static int vidioc_g_audio(struct file *file, void *priv, |
375 | struct v4l2_audio *a) | |
1da177e4 | 376 | { |
7002a4f3 DL |
377 | if (a->index > 1) |
378 | return -EINVAL; | |
379 | ||
380 | strcpy(a->name, "Radio"); | |
381 | a->capability = V4L2_AUDCAP_STEREO; | |
382 | return 0; | |
383 | } | |
384 | ||
385 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | |
386 | { | |
387 | *i = 0; | |
388 | return 0; | |
389 | } | |
390 | ||
391 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | |
392 | { | |
393 | if (i != 0) | |
394 | return -EINVAL; | |
395 | return 0; | |
396 | } | |
397 | ||
398 | static int vidioc_s_audio(struct file *file, void *priv, | |
399 | struct v4l2_audio *a) | |
400 | { | |
401 | if (a->index != 0) | |
402 | return -EINVAL; | |
403 | return 0; | |
1da177e4 LT |
404 | } |
405 | ||
406 | static int usb_dsbr100_open(struct inode *inode, struct file *file) | |
407 | { | |
5aff308c | 408 | struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); |
1da177e4 LT |
409 | |
410 | radio->users = 1; | |
5aff308c AC |
411 | radio->muted = 1; |
412 | ||
1da177e4 LT |
413 | if (dsbr100_start(radio)<0) { |
414 | warn("Radio did not start up properly"); | |
415 | radio->users = 0; | |
416 | return -EIO; | |
417 | } | |
418 | dsbr100_setfreq(radio, radio->curfreq); | |
419 | return 0; | |
420 | } | |
421 | ||
422 | static int usb_dsbr100_close(struct inode *inode, struct file *file) | |
423 | { | |
5aff308c | 424 | struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); |
1da177e4 LT |
425 | |
426 | if (!radio) | |
427 | return -ENODEV; | |
428 | radio->users = 0; | |
429 | if (radio->removed) { | |
863c86dd | 430 | kfree(radio->transfer_buffer); |
1da177e4 LT |
431 | kfree(radio); |
432 | } | |
433 | return 0; | |
434 | } | |
435 | ||
7002a4f3 DL |
436 | /* File system interface */ |
437 | static const struct file_operations usb_dsbr100_fops = { | |
438 | .owner = THIS_MODULE, | |
439 | .open = usb_dsbr100_open, | |
440 | .release = usb_dsbr100_close, | |
441 | .ioctl = video_ioctl2, | |
078ff795 | 442 | #ifdef CONFIG_COMPAT |
7002a4f3 | 443 | .compat_ioctl = v4l_compat_ioctl32, |
078ff795 | 444 | #endif |
7002a4f3 DL |
445 | .llseek = no_llseek, |
446 | }; | |
447 | ||
a399810c | 448 | static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { |
7002a4f3 DL |
449 | .vidioc_querycap = vidioc_querycap, |
450 | .vidioc_g_tuner = vidioc_g_tuner, | |
451 | .vidioc_s_tuner = vidioc_s_tuner, | |
452 | .vidioc_g_frequency = vidioc_g_frequency, | |
453 | .vidioc_s_frequency = vidioc_s_frequency, | |
454 | .vidioc_queryctrl = vidioc_queryctrl, | |
455 | .vidioc_g_ctrl = vidioc_g_ctrl, | |
456 | .vidioc_s_ctrl = vidioc_s_ctrl, | |
457 | .vidioc_g_audio = vidioc_g_audio, | |
458 | .vidioc_s_audio = vidioc_s_audio, | |
459 | .vidioc_g_input = vidioc_g_input, | |
460 | .vidioc_s_input = vidioc_s_input, | |
461 | }; | |
462 | ||
a399810c HV |
463 | /* V4L2 interface */ |
464 | static struct video_device dsbr100_videodev_template = { | |
a399810c | 465 | .name = "D-Link DSB-R 100", |
a399810c HV |
466 | .fops = &usb_dsbr100_fops, |
467 | .ioctl_ops = &usb_dsbr100_ioctl_ops, | |
468 | .release = video_device_release, | |
469 | }; | |
470 | ||
7002a4f3 DL |
471 | /* check if the device is present and register with v4l and |
472 | usb if it is */ | |
473 | static int usb_dsbr100_probe(struct usb_interface *intf, | |
474 | const struct usb_device_id *id) | |
475 | { | |
476 | struct dsbr100_device *radio; | |
477 | ||
478 | if (!(radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL))) | |
479 | return -ENOMEM; | |
863c86dd ON |
480 | if (!(radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL))) { |
481 | kfree(radio); | |
482 | return -ENOMEM; | |
483 | } | |
7002a4f3 | 484 | if (!(radio->videodev = video_device_alloc())) { |
863c86dd | 485 | kfree(radio->transfer_buffer); |
7002a4f3 DL |
486 | kfree(radio); |
487 | return -ENOMEM; | |
488 | } | |
489 | memcpy(radio->videodev, &dsbr100_videodev_template, | |
490 | sizeof(dsbr100_videodev_template)); | |
491 | radio->removed = 0; | |
492 | radio->users = 0; | |
493 | radio->usbdev = interface_to_usbdev(intf); | |
494 | radio->curfreq = FREQ_MIN*FREQ_MUL; | |
495 | video_set_drvdata(radio->videodev, radio); | |
cba99ae8 | 496 | if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr) < 0) { |
7002a4f3 DL |
497 | warn("Could not register video device"); |
498 | video_device_release(radio->videodev); | |
863c86dd | 499 | kfree(radio->transfer_buffer); |
7002a4f3 DL |
500 | kfree(radio); |
501 | return -EIO; | |
502 | } | |
503 | usb_set_intfdata(intf, radio); | |
504 | return 0; | |
505 | } | |
506 | ||
1da177e4 LT |
507 | static int __init dsbr100_init(void) |
508 | { | |
509 | int retval = usb_register(&usb_dsbr100_driver); | |
510 | info(DRIVER_VERSION ":" DRIVER_DESC); | |
511 | return retval; | |
512 | } | |
513 | ||
514 | static void __exit dsbr100_exit(void) | |
515 | { | |
516 | usb_deregister(&usb_dsbr100_driver); | |
517 | } | |
518 | ||
519 | module_init (dsbr100_init); | |
520 | module_exit (dsbr100_exit); | |
521 | ||
522 | MODULE_AUTHOR( DRIVER_AUTHOR ); | |
523 | MODULE_DESCRIPTION( DRIVER_DESC ); | |
524 | MODULE_LICENSE("GPL"); |