diff options
author | Hans Verkuil <hverkuil@xs4all.nl> | 2009-03-06 11:52:34 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-03-30 11:43:13 -0400 |
commit | c41269fd9275cce88b90af644969c6a5e2067657 (patch) | |
tree | bdf76273785e8fec8dd3845cb1e5ac138e2e6dc2 | |
parent | 922c78e9f6900ade073da1a92d43c56d77cf790f (diff) |
V4L/DVB (10888): radio-sf16fmi: convert to v4l2_device.
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/radio/radio-sf16fmi.c | 284 |
1 files changed, 135 insertions, 149 deletions
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index d358e48c2422..4982d0cefb4f 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c | |||
@@ -22,113 +22,110 @@ | |||
22 | #include <linux/init.h> /* Initdata */ | 22 | #include <linux/init.h> /* Initdata */ |
23 | #include <linux/ioport.h> /* request_region */ | 23 | #include <linux/ioport.h> /* request_region */ |
24 | #include <linux/delay.h> /* udelay */ | 24 | #include <linux/delay.h> /* udelay */ |
25 | #include <linux/videodev2.h> /* kernel radio structs */ | ||
26 | #include <media/v4l2-common.h> | ||
27 | #include <media/v4l2-ioctl.h> | ||
28 | #include <linux/isapnp.h> | 25 | #include <linux/isapnp.h> |
29 | #include <asm/io.h> /* outb, outb_p */ | ||
30 | #include <asm/uaccess.h> /* copy to/from user */ | ||
31 | #include <linux/mutex.h> | 26 | #include <linux/mutex.h> |
27 | #include <linux/videodev2.h> /* kernel radio structs */ | ||
28 | #include <linux/io.h> /* outb, outb_p */ | ||
29 | #include <linux/uaccess.h> /* copy to/from user */ | ||
30 | #include <media/v4l2-device.h> | ||
31 | #include <media/v4l2-ioctl.h> | ||
32 | 32 | ||
33 | #define RADIO_VERSION KERNEL_VERSION(0,0,2) | 33 | MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); |
34 | MODULE_DESCRIPTION("A driver for the SF16MI radio."); | ||
35 | MODULE_LICENSE("GPL"); | ||
34 | 36 | ||
35 | static struct v4l2_queryctrl radio_qctrl[] = { | 37 | static int io = -1; |
36 | { | 38 | static int radio_nr = -1; |
37 | .id = V4L2_CID_AUDIO_MUTE, | 39 | |
38 | .name = "Mute", | 40 | module_param(io, int, 0); |
39 | .minimum = 0, | 41 | MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)"); |
40 | .maximum = 1, | 42 | module_param(radio_nr, int, 0); |
41 | .default_value = 1, | 43 | |
42 | .type = V4L2_CTRL_TYPE_BOOLEAN, | 44 | #define RADIO_VERSION KERNEL_VERSION(0, 0, 2) |
43 | } | ||
44 | }; | ||
45 | 45 | ||
46 | struct fmi_device | 46 | struct fmi |
47 | { | 47 | { |
48 | unsigned long in_use; | 48 | struct v4l2_device v4l2_dev; |
49 | int port; | 49 | struct video_device vdev; |
50 | int io; | ||
50 | int curvol; /* 1 or 0 */ | 51 | int curvol; /* 1 or 0 */ |
51 | unsigned long curfreq; /* freq in kHz */ | 52 | unsigned long curfreq; /* freq in kHz */ |
52 | __u32 flags; | 53 | __u32 flags; |
54 | struct mutex lock; | ||
53 | }; | 55 | }; |
54 | 56 | ||
55 | static int io = -1; | 57 | static struct fmi fmi_card; |
56 | static int radio_nr = -1; | 58 | static struct pnp_dev *dev; |
57 | static struct pnp_dev *dev = NULL; | ||
58 | static struct mutex lock; | ||
59 | 59 | ||
60 | /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ | 60 | /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ |
61 | /* It is only useful to give freq in intervall of 800 (=0.05Mhz), | 61 | /* It is only useful to give freq in intervall of 800 (=0.05Mhz), |
62 | * other bits will be truncated, e.g 92.7400016 -> 92.7, but | 62 | * other bits will be truncated, e.g 92.7400016 -> 92.7, but |
63 | * 92.7400017 -> 92.75 | 63 | * 92.7400017 -> 92.75 |
64 | */ | 64 | */ |
65 | #define RSF16_ENCODE(x) ((x)/800+214) | 65 | #define RSF16_ENCODE(x) ((x) / 800 + 214) |
66 | #define RSF16_MINFREQ 87*16000 | 66 | #define RSF16_MINFREQ (87 * 16000) |
67 | #define RSF16_MAXFREQ 108*16000 | 67 | #define RSF16_MAXFREQ (108 * 16000) |
68 | 68 | ||
69 | static void outbits(int bits, unsigned int data, int port) | 69 | static void outbits(int bits, unsigned int data, int io) |
70 | { | 70 | { |
71 | while(bits--) { | 71 | while (bits--) { |
72 | if(data & 1) { | 72 | if (data & 1) { |
73 | outb(5, port); | 73 | outb(5, io); |
74 | udelay(6); | 74 | udelay(6); |
75 | outb(7, port); | 75 | outb(7, io); |
76 | udelay(6); | 76 | udelay(6); |
77 | } else { | 77 | } else { |
78 | outb(1, port); | 78 | outb(1, io); |
79 | udelay(6); | 79 | udelay(6); |
80 | outb(3, port); | 80 | outb(3, io); |
81 | udelay(6); | 81 | udelay(6); |
82 | } | 82 | } |
83 | data>>=1; | 83 | data >>= 1; |
84 | } | 84 | } |
85 | } | 85 | } |
86 | 86 | ||
87 | static inline void fmi_mute(int port) | 87 | static inline void fmi_mute(struct fmi *fmi) |
88 | { | 88 | { |
89 | mutex_lock(&lock); | 89 | mutex_lock(&fmi->lock); |
90 | outb(0x00, port); | 90 | outb(0x00, fmi->io); |
91 | mutex_unlock(&lock); | 91 | mutex_unlock(&fmi->lock); |
92 | } | 92 | } |
93 | 93 | ||
94 | static inline void fmi_unmute(int port) | 94 | static inline void fmi_unmute(struct fmi *fmi) |
95 | { | 95 | { |
96 | mutex_lock(&lock); | 96 | mutex_lock(&fmi->lock); |
97 | outb(0x08, port); | 97 | outb(0x08, fmi->io); |
98 | mutex_unlock(&lock); | 98 | mutex_unlock(&fmi->lock); |
99 | } | 99 | } |
100 | 100 | ||
101 | static inline int fmi_setfreq(struct fmi_device *dev) | 101 | static inline int fmi_setfreq(struct fmi *fmi, unsigned long freq) |
102 | { | 102 | { |
103 | int myport = dev->port; | 103 | mutex_lock(&fmi->lock); |
104 | unsigned long freq = dev->curfreq; | 104 | fmi->curfreq = freq; |
105 | 105 | ||
106 | mutex_lock(&lock); | 106 | outbits(16, RSF16_ENCODE(freq), fmi->io); |
107 | 107 | outbits(8, 0xC0, fmi->io); | |
108 | outbits(16, RSF16_ENCODE(freq), myport); | ||
109 | outbits(8, 0xC0, myport); | ||
110 | msleep(143); /* was schedule_timeout(HZ/7) */ | 108 | msleep(143); /* was schedule_timeout(HZ/7) */ |
111 | mutex_unlock(&lock); | 109 | mutex_unlock(&fmi->lock); |
112 | if (dev->curvol) fmi_unmute(myport); | 110 | if (fmi->curvol) |
111 | fmi_unmute(fmi); | ||
113 | return 0; | 112 | return 0; |
114 | } | 113 | } |
115 | 114 | ||
116 | static inline int fmi_getsigstr(struct fmi_device *dev) | 115 | static inline int fmi_getsigstr(struct fmi *fmi) |
117 | { | 116 | { |
118 | int val; | 117 | int val; |
119 | int res; | 118 | int res; |
120 | int myport = dev->port; | ||
121 | |||
122 | 119 | ||
123 | mutex_lock(&lock); | 120 | mutex_lock(&fmi->lock); |
124 | val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ | 121 | val = fmi->curvol ? 0x08 : 0x00; /* unmute/mute */ |
125 | outb(val, myport); | 122 | outb(val, fmi->io); |
126 | outb(val | 0x10, myport); | 123 | outb(val | 0x10, fmi->io); |
127 | msleep(143); /* was schedule_timeout(HZ/7) */ | 124 | msleep(143); /* was schedule_timeout(HZ/7) */ |
128 | res = (int)inb(myport+1); | 125 | res = (int)inb(fmi->io + 1); |
129 | outb(val, myport); | 126 | outb(val, fmi->io); |
130 | 127 | ||
131 | mutex_unlock(&lock); | 128 | mutex_unlock(&fmi->lock); |
132 | return (res & 2) ? 0 : 0xFFFF; | 129 | return (res & 2) ? 0 : 0xFFFF; |
133 | } | 130 | } |
134 | 131 | ||
@@ -137,9 +134,9 @@ static int vidioc_querycap(struct file *file, void *priv, | |||
137 | { | 134 | { |
138 | strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); | 135 | strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); |
139 | strlcpy(v->card, "SF16-FMx radio", sizeof(v->card)); | 136 | strlcpy(v->card, "SF16-FMx radio", sizeof(v->card)); |
140 | sprintf(v->bus_info, "ISA"); | 137 | strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); |
141 | v->version = RADIO_VERSION; | 138 | v->version = RADIO_VERSION; |
142 | v->capabilities = V4L2_CAP_TUNER; | 139 | v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; |
143 | return 0; | 140 | return 0; |
144 | } | 141 | } |
145 | 142 | ||
@@ -147,18 +144,18 @@ static int vidioc_g_tuner(struct file *file, void *priv, | |||
147 | struct v4l2_tuner *v) | 144 | struct v4l2_tuner *v) |
148 | { | 145 | { |
149 | int mult; | 146 | int mult; |
150 | struct fmi_device *fmi = video_drvdata(file); | 147 | struct fmi *fmi = video_drvdata(file); |
151 | 148 | ||
152 | if (v->index > 0) | 149 | if (v->index > 0) |
153 | return -EINVAL; | 150 | return -EINVAL; |
154 | 151 | ||
155 | strcpy(v->name, "FM"); | 152 | strlcpy(v->name, "FM", sizeof(v->name)); |
156 | v->type = V4L2_TUNER_RADIO; | 153 | v->type = V4L2_TUNER_RADIO; |
157 | mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; | 154 | mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; |
158 | v->rangelow = RSF16_MINFREQ/mult; | 155 | v->rangelow = RSF16_MINFREQ / mult; |
159 | v->rangehigh = RSF16_MAXFREQ/mult; | 156 | v->rangehigh = RSF16_MAXFREQ / mult; |
160 | v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; | 157 | v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; |
161 | v->capability = fmi->flags&V4L2_TUNER_CAP_LOW; | 158 | v->capability = fmi->flags & V4L2_TUNER_CAP_LOW; |
162 | v->audmode = V4L2_TUNER_MODE_STEREO; | 159 | v->audmode = V4L2_TUNER_MODE_STEREO; |
163 | v->signal = fmi_getsigstr(fmi); | 160 | v->signal = fmi_getsigstr(fmi); |
164 | return 0; | 161 | return 0; |
@@ -167,32 +164,29 @@ static int vidioc_g_tuner(struct file *file, void *priv, | |||
167 | static int vidioc_s_tuner(struct file *file, void *priv, | 164 | static int vidioc_s_tuner(struct file *file, void *priv, |
168 | struct v4l2_tuner *v) | 165 | struct v4l2_tuner *v) |
169 | { | 166 | { |
170 | if (v->index > 0) | 167 | return v->index ? -EINVAL : 0; |
171 | return -EINVAL; | ||
172 | return 0; | ||
173 | } | 168 | } |
174 | 169 | ||
175 | static int vidioc_s_frequency(struct file *file, void *priv, | 170 | static int vidioc_s_frequency(struct file *file, void *priv, |
176 | struct v4l2_frequency *f) | 171 | struct v4l2_frequency *f) |
177 | { | 172 | { |
178 | struct fmi_device *fmi = video_drvdata(file); | 173 | struct fmi *fmi = video_drvdata(file); |
179 | 174 | ||
180 | if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) | 175 | if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) |
181 | f->frequency *= 1000; | 176 | f->frequency *= 1000; |
182 | if (f->frequency < RSF16_MINFREQ || | 177 | if (f->frequency < RSF16_MINFREQ || |
183 | f->frequency > RSF16_MAXFREQ ) | 178 | f->frequency > RSF16_MAXFREQ) |
184 | return -EINVAL; | 179 | return -EINVAL; |
185 | /*rounding in steps of 800 to match th freq | 180 | /* rounding in steps of 800 to match the freq |
186 | that will be used */ | 181 | that will be used */ |
187 | fmi->curfreq = (f->frequency/800)*800; | 182 | fmi_setfreq(fmi, (f->frequency / 800) * 800); |
188 | fmi_setfreq(fmi); | ||
189 | return 0; | 183 | return 0; |
190 | } | 184 | } |
191 | 185 | ||
192 | static int vidioc_g_frequency(struct file *file, void *priv, | 186 | static int vidioc_g_frequency(struct file *file, void *priv, |
193 | struct v4l2_frequency *f) | 187 | struct v4l2_frequency *f) |
194 | { | 188 | { |
195 | struct fmi_device *fmi = video_drvdata(file); | 189 | struct fmi *fmi = video_drvdata(file); |
196 | 190 | ||
197 | f->type = V4L2_TUNER_RADIO; | 191 | f->type = V4L2_TUNER_RADIO; |
198 | f->frequency = fmi->curfreq; | 192 | f->frequency = fmi->curfreq; |
@@ -204,14 +198,9 @@ static int vidioc_g_frequency(struct file *file, void *priv, | |||
204 | static int vidioc_queryctrl(struct file *file, void *priv, | 198 | static int vidioc_queryctrl(struct file *file, void *priv, |
205 | struct v4l2_queryctrl *qc) | 199 | struct v4l2_queryctrl *qc) |
206 | { | 200 | { |
207 | int i; | 201 | switch (qc->id) { |
208 | 202 | case V4L2_CID_AUDIO_MUTE: | |
209 | for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { | 203 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); |
210 | if (qc->id && qc->id == radio_qctrl[i].id) { | ||
211 | memcpy(qc, &(radio_qctrl[i]), | ||
212 | sizeof(*qc)); | ||
213 | return 0; | ||
214 | } | ||
215 | } | 204 | } |
216 | return -EINVAL; | 205 | return -EINVAL; |
217 | } | 206 | } |
@@ -219,7 +208,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, | |||
219 | static int vidioc_g_ctrl(struct file *file, void *priv, | 208 | static int vidioc_g_ctrl(struct file *file, void *priv, |
220 | struct v4l2_control *ctrl) | 209 | struct v4l2_control *ctrl) |
221 | { | 210 | { |
222 | struct fmi_device *fmi = video_drvdata(file); | 211 | struct fmi *fmi = video_drvdata(file); |
223 | 212 | ||
224 | switch (ctrl->id) { | 213 | switch (ctrl->id) { |
225 | case V4L2_CID_AUDIO_MUTE: | 214 | case V4L2_CID_AUDIO_MUTE: |
@@ -232,31 +221,20 @@ static int vidioc_g_ctrl(struct file *file, void *priv, | |||
232 | static int vidioc_s_ctrl(struct file *file, void *priv, | 221 | static int vidioc_s_ctrl(struct file *file, void *priv, |
233 | struct v4l2_control *ctrl) | 222 | struct v4l2_control *ctrl) |
234 | { | 223 | { |
235 | struct fmi_device *fmi = video_drvdata(file); | 224 | struct fmi *fmi = video_drvdata(file); |
236 | 225 | ||
237 | switch (ctrl->id) { | 226 | switch (ctrl->id) { |
238 | case V4L2_CID_AUDIO_MUTE: | 227 | case V4L2_CID_AUDIO_MUTE: |
239 | if (ctrl->value) | 228 | if (ctrl->value) |
240 | fmi_mute(fmi->port); | 229 | fmi_mute(fmi); |
241 | else | 230 | else |
242 | fmi_unmute(fmi->port); | 231 | fmi_unmute(fmi); |
243 | fmi->curvol = ctrl->value; | 232 | fmi->curvol = ctrl->value; |
244 | return 0; | 233 | return 0; |
245 | } | 234 | } |
246 | return -EINVAL; | 235 | return -EINVAL; |
247 | } | 236 | } |
248 | 237 | ||
249 | static int vidioc_g_audio(struct file *file, void *priv, | ||
250 | struct v4l2_audio *a) | ||
251 | { | ||
252 | if (a->index > 1) | ||
253 | return -EINVAL; | ||
254 | |||
255 | strcpy(a->name, "Radio"); | ||
256 | a->capability = V4L2_AUDCAP_STEREO; | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | 238 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) |
261 | { | 239 | { |
262 | *i = 0; | 240 | *i = 0; |
@@ -265,36 +243,38 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | |||
265 | 243 | ||
266 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | 244 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) |
267 | { | 245 | { |
268 | if (i != 0) | 246 | return i ? -EINVAL : 0; |
269 | return -EINVAL; | ||
270 | return 0; | ||
271 | } | 247 | } |
272 | 248 | ||
273 | static int vidioc_s_audio(struct file *file, void *priv, | 249 | static int vidioc_g_audio(struct file *file, void *priv, |
274 | struct v4l2_audio *a) | 250 | struct v4l2_audio *a) |
275 | { | 251 | { |
276 | if (a->index != 0) | 252 | a->index = 0; |
277 | return -EINVAL; | 253 | strlcpy(a->name, "Radio", sizeof(a->name)); |
254 | a->capability = V4L2_AUDCAP_STEREO; | ||
278 | return 0; | 255 | return 0; |
279 | } | 256 | } |
280 | 257 | ||
281 | static struct fmi_device fmi_unit; | 258 | static int vidioc_s_audio(struct file *file, void *priv, |
259 | struct v4l2_audio *a) | ||
260 | { | ||
261 | return a->index ? -EINVAL : 0; | ||
262 | } | ||
282 | 263 | ||
283 | static int fmi_exclusive_open(struct file *file) | 264 | static int fmi_open(struct file *file) |
284 | { | 265 | { |
285 | return test_and_set_bit(0, &fmi_unit.in_use) ? -EBUSY : 0; | 266 | return 0; |
286 | } | 267 | } |
287 | 268 | ||
288 | static int fmi_exclusive_release(struct file *file) | 269 | static int fmi_release(struct file *file) |
289 | { | 270 | { |
290 | clear_bit(0, &fmi_unit.in_use); | ||
291 | return 0; | 271 | return 0; |
292 | } | 272 | } |
293 | 273 | ||
294 | static const struct v4l2_file_operations fmi_fops = { | 274 | static const struct v4l2_file_operations fmi_fops = { |
295 | .owner = THIS_MODULE, | 275 | .owner = THIS_MODULE, |
296 | .open = fmi_exclusive_open, | 276 | .open = fmi_open, |
297 | .release = fmi_exclusive_release, | 277 | .release = fmi_release, |
298 | .ioctl = video_ioctl2, | 278 | .ioctl = video_ioctl2, |
299 | }; | 279 | }; |
300 | 280 | ||
@@ -313,13 +293,6 @@ static const struct v4l2_ioctl_ops fmi_ioctl_ops = { | |||
313 | .vidioc_s_ctrl = vidioc_s_ctrl, | 293 | .vidioc_s_ctrl = vidioc_s_ctrl, |
314 | }; | 294 | }; |
315 | 295 | ||
316 | static struct video_device fmi_radio = { | ||
317 | .name = "SF16FMx radio", | ||
318 | .fops = &fmi_fops, | ||
319 | .ioctl_ops = &fmi_ioctl_ops, | ||
320 | .release = video_device_release_empty, | ||
321 | }; | ||
322 | |||
323 | /* ladis: this is my card. does any other types exist? */ | 296 | /* ladis: this is my card. does any other types exist? */ |
324 | static struct isapnp_device_id id_table[] __devinitdata = { | 297 | static struct isapnp_device_id id_table[] __devinitdata = { |
325 | { ISAPNP_ANY_ID, ISAPNP_ANY_ID, | 298 | { ISAPNP_ANY_ID, ISAPNP_ANY_ID, |
@@ -344,7 +317,7 @@ static int __init isapnp_fmi_probe(void) | |||
344 | if (pnp_device_attach(dev) < 0) | 317 | if (pnp_device_attach(dev) < 0) |
345 | return -EAGAIN; | 318 | return -EAGAIN; |
346 | if (pnp_activate_dev(dev) < 0) { | 319 | if (pnp_activate_dev(dev) < 0) { |
347 | printk ("radio-sf16fmi: PnP configure failed (out of resources?)\n"); | 320 | printk(KERN_ERR "radio-sf16fmi: PnP configure failed (out of resources?)\n"); |
348 | pnp_device_detach(dev); | 321 | pnp_device_detach(dev); |
349 | return -ENOMEM; | 322 | return -ENOMEM; |
350 | } | 323 | } |
@@ -354,59 +327,72 @@ static int __init isapnp_fmi_probe(void) | |||
354 | } | 327 | } |
355 | 328 | ||
356 | i = pnp_port_start(dev, 0); | 329 | i = pnp_port_start(dev, 0); |
357 | printk ("radio-sf16fmi: PnP reports card at %#x\n", i); | 330 | printk(KERN_INFO "radio-sf16fmi: PnP reports card at %#x\n", i); |
358 | 331 | ||
359 | return i; | 332 | return i; |
360 | } | 333 | } |
361 | 334 | ||
362 | static int __init fmi_init(void) | 335 | static int __init fmi_init(void) |
363 | { | 336 | { |
337 | struct fmi *fmi = &fmi_card; | ||
338 | struct v4l2_device *v4l2_dev = &fmi->v4l2_dev; | ||
339 | int res; | ||
340 | |||
364 | if (io < 0) | 341 | if (io < 0) |
365 | io = isapnp_fmi_probe(); | 342 | io = isapnp_fmi_probe(); |
366 | if (io < 0) { | 343 | strlcpy(v4l2_dev->name, "sf16fmi", sizeof(v4l2_dev->name)); |
367 | printk(KERN_ERR "radio-sf16fmi: No PnP card found.\n"); | 344 | fmi->io = io; |
368 | return io; | 345 | if (fmi->io < 0) { |
346 | v4l2_err(v4l2_dev, "No PnP card found.\n"); | ||
347 | return fmi->io; | ||
369 | } | 348 | } |
370 | if (!request_region(io, 2, "radio-sf16fmi")) { | 349 | if (!request_region(io, 2, "radio-sf16fmi")) { |
371 | printk(KERN_ERR "radio-sf16fmi: port 0x%x already in use\n", io); | 350 | v4l2_err(v4l2_dev, "port 0x%x already in use\n", fmi->io); |
372 | pnp_device_detach(dev); | 351 | pnp_device_detach(dev); |
373 | return -EBUSY; | 352 | return -EBUSY; |
374 | } | 353 | } |
375 | 354 | ||
376 | fmi_unit.port = io; | 355 | res = v4l2_device_register(NULL, v4l2_dev); |
377 | fmi_unit.curvol = 0; | 356 | if (res < 0) { |
378 | fmi_unit.curfreq = 0; | 357 | release_region(fmi->io, 2); |
379 | fmi_unit.flags = V4L2_TUNER_CAP_LOW; | 358 | pnp_device_detach(dev); |
380 | video_set_drvdata(&fmi_radio, &fmi_unit); | 359 | v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); |
360 | return res; | ||
361 | } | ||
362 | |||
363 | fmi->flags = V4L2_TUNER_CAP_LOW; | ||
364 | strlcpy(fmi->vdev.name, v4l2_dev->name, sizeof(fmi->vdev.name)); | ||
365 | fmi->vdev.v4l2_dev = v4l2_dev; | ||
366 | fmi->vdev.fops = &fmi_fops; | ||
367 | fmi->vdev.ioctl_ops = &fmi_ioctl_ops; | ||
368 | fmi->vdev.release = video_device_release_empty; | ||
369 | video_set_drvdata(&fmi->vdev, fmi); | ||
381 | 370 | ||
382 | mutex_init(&lock); | 371 | mutex_init(&fmi->lock); |
383 | 372 | ||
384 | if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) < 0) { | 373 | if (video_register_device(&fmi->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { |
385 | release_region(io, 2); | 374 | v4l2_device_unregister(v4l2_dev); |
375 | release_region(fmi->io, 2); | ||
376 | pnp_device_detach(dev); | ||
386 | return -EINVAL; | 377 | return -EINVAL; |
387 | } | 378 | } |
388 | 379 | ||
389 | printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io); | 380 | v4l2_info(v4l2_dev, "card driver at 0x%x\n", fmi->io); |
390 | /* mute card - prevents noisy bootups */ | 381 | /* mute card - prevents noisy bootups */ |
391 | fmi_mute(io); | 382 | fmi_mute(fmi); |
392 | return 0; | 383 | return 0; |
393 | } | 384 | } |
394 | 385 | ||
395 | MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); | 386 | static void __exit fmi_exit(void) |
396 | MODULE_DESCRIPTION("A driver for the SF16MI radio."); | ||
397 | MODULE_LICENSE("GPL"); | ||
398 | |||
399 | module_param(io, int, 0); | ||
400 | MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)"); | ||
401 | module_param(radio_nr, int, 0); | ||
402 | |||
403 | static void __exit fmi_cleanup_module(void) | ||
404 | { | 387 | { |
405 | video_unregister_device(&fmi_radio); | 388 | struct fmi *fmi = &fmi_card; |
406 | release_region(io, 2); | 389 | |
390 | video_unregister_device(&fmi->vdev); | ||
391 | v4l2_device_unregister(&fmi->v4l2_dev); | ||
392 | release_region(fmi->io, 2); | ||
407 | if (dev) | 393 | if (dev) |
408 | pnp_device_detach(dev); | 394 | pnp_device_detach(dev); |
409 | } | 395 | } |
410 | 396 | ||
411 | module_init(fmi_init); | 397 | module_init(fmi_init); |
412 | module_exit(fmi_cleanup_module); | 398 | module_exit(fmi_exit); |