diff options
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/radio-zoltrix.c | 379 |
1 files changed, 178 insertions, 201 deletions
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c index d2ac17eeec5f..18dbaf1650de 100644 --- a/drivers/media/radio/radio-zoltrix.c +++ b/drivers/media/radio/radio-zoltrix.c | |||
@@ -33,33 +33,17 @@ | |||
33 | #include <linux/init.h> /* Initdata */ | 33 | #include <linux/init.h> /* Initdata */ |
34 | #include <linux/ioport.h> /* request_region */ | 34 | #include <linux/ioport.h> /* request_region */ |
35 | #include <linux/delay.h> /* udelay, msleep */ | 35 | #include <linux/delay.h> /* udelay, msleep */ |
36 | #include <asm/io.h> /* outb, outb_p */ | ||
37 | #include <asm/uaccess.h> /* copy to/from user */ | ||
38 | #include <linux/videodev2.h> /* kernel radio structs */ | 36 | #include <linux/videodev2.h> /* kernel radio structs */ |
39 | #include <media/v4l2-common.h> | 37 | #include <linux/mutex.h> |
38 | #include <linux/version.h> /* for KERNEL_VERSION MACRO */ | ||
39 | #include <linux/io.h> /* outb, outb_p */ | ||
40 | #include <linux/uaccess.h> /* copy to/from user */ | ||
41 | #include <media/v4l2-device.h> | ||
40 | #include <media/v4l2-ioctl.h> | 42 | #include <media/v4l2-ioctl.h> |
41 | 43 | ||
42 | #include <linux/version.h> /* for KERNEL_VERSION MACRO */ | 44 | MODULE_AUTHOR("C.van Schaik"); |
43 | #define RADIO_VERSION KERNEL_VERSION(0,0,2) | 45 | MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus."); |
44 | 46 | MODULE_LICENSE("GPL"); | |
45 | static struct v4l2_queryctrl radio_qctrl[] = { | ||
46 | { | ||
47 | .id = V4L2_CID_AUDIO_MUTE, | ||
48 | .name = "Mute", | ||
49 | .minimum = 0, | ||
50 | .maximum = 1, | ||
51 | .default_value = 1, | ||
52 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
53 | },{ | ||
54 | .id = V4L2_CID_AUDIO_VOLUME, | ||
55 | .name = "Volume", | ||
56 | .minimum = 0, | ||
57 | .maximum = 65535, | ||
58 | .step = 4096, | ||
59 | .default_value = 0xff, | ||
60 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
61 | } | ||
62 | }; | ||
63 | 47 | ||
64 | #ifndef CONFIG_RADIO_ZOLTRIX_PORT | 48 | #ifndef CONFIG_RADIO_ZOLTRIX_PORT |
65 | #define CONFIG_RADIO_ZOLTRIX_PORT -1 | 49 | #define CONFIG_RADIO_ZOLTRIX_PORT -1 |
@@ -68,9 +52,16 @@ static struct v4l2_queryctrl radio_qctrl[] = { | |||
68 | static int io = CONFIG_RADIO_ZOLTRIX_PORT; | 52 | static int io = CONFIG_RADIO_ZOLTRIX_PORT; |
69 | static int radio_nr = -1; | 53 | static int radio_nr = -1; |
70 | 54 | ||
71 | struct zol_device { | 55 | module_param(io, int, 0); |
72 | unsigned long in_use; | 56 | MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)"); |
73 | int port; | 57 | module_param(radio_nr, int, 0); |
58 | |||
59 | #define RADIO_VERSION KERNEL_VERSION(0, 0, 2) | ||
60 | |||
61 | struct zoltrix { | ||
62 | struct v4l2_device v4l2_dev; | ||
63 | struct video_device vdev; | ||
64 | int io; | ||
74 | int curvol; | 65 | int curvol; |
75 | unsigned long curfreq; | 66 | unsigned long curfreq; |
76 | int muted; | 67 | int muted; |
@@ -78,161 +69,158 @@ struct zol_device { | |||
78 | struct mutex lock; | 69 | struct mutex lock; |
79 | }; | 70 | }; |
80 | 71 | ||
81 | static int zol_setvol(struct zol_device *dev, int vol) | 72 | static struct zoltrix zoltrix_card; |
73 | |||
74 | static int zol_setvol(struct zoltrix *zol, int vol) | ||
82 | { | 75 | { |
83 | dev->curvol = vol; | 76 | zol->curvol = vol; |
84 | if (dev->muted) | 77 | if (zol->muted) |
85 | return 0; | 78 | return 0; |
86 | 79 | ||
87 | mutex_lock(&dev->lock); | 80 | mutex_lock(&zol->lock); |
88 | if (vol == 0) { | 81 | if (vol == 0) { |
89 | outb(0, io); | 82 | outb(0, zol->io); |
90 | outb(0, io); | 83 | outb(0, zol->io); |
91 | inb(io + 3); /* Zoltrix needs to be read to confirm */ | 84 | inb(zol->io + 3); /* Zoltrix needs to be read to confirm */ |
92 | mutex_unlock(&dev->lock); | 85 | mutex_unlock(&zol->lock); |
93 | return 0; | 86 | return 0; |
94 | } | 87 | } |
95 | 88 | ||
96 | outb(dev->curvol-1, io); | 89 | outb(zol->curvol-1, zol->io); |
97 | msleep(10); | 90 | msleep(10); |
98 | inb(io + 2); | 91 | inb(zol->io + 2); |
99 | mutex_unlock(&dev->lock); | 92 | mutex_unlock(&zol->lock); |
100 | return 0; | 93 | return 0; |
101 | } | 94 | } |
102 | 95 | ||
103 | static void zol_mute(struct zol_device *dev) | 96 | static void zol_mute(struct zoltrix *zol) |
104 | { | 97 | { |
105 | dev->muted = 1; | 98 | zol->muted = 1; |
106 | mutex_lock(&dev->lock); | 99 | mutex_lock(&zol->lock); |
107 | outb(0, io); | 100 | outb(0, zol->io); |
108 | outb(0, io); | 101 | outb(0, zol->io); |
109 | inb(io + 3); /* Zoltrix needs to be read to confirm */ | 102 | inb(zol->io + 3); /* Zoltrix needs to be read to confirm */ |
110 | mutex_unlock(&dev->lock); | 103 | mutex_unlock(&zol->lock); |
111 | } | 104 | } |
112 | 105 | ||
113 | static void zol_unmute(struct zol_device *dev) | 106 | static void zol_unmute(struct zoltrix *zol) |
114 | { | 107 | { |
115 | dev->muted = 0; | 108 | zol->muted = 0; |
116 | zol_setvol(dev, dev->curvol); | 109 | zol_setvol(zol, zol->curvol); |
117 | } | 110 | } |
118 | 111 | ||
119 | static int zol_setfreq(struct zol_device *dev, unsigned long freq) | 112 | static int zol_setfreq(struct zoltrix *zol, unsigned long freq) |
120 | { | 113 | { |
121 | /* tunes the radio to the desired frequency */ | 114 | /* tunes the radio to the desired frequency */ |
115 | struct v4l2_device *v4l2_dev = &zol->v4l2_dev; | ||
122 | unsigned long long bitmask, f, m; | 116 | unsigned long long bitmask, f, m; |
123 | unsigned int stereo = dev->stereo; | 117 | unsigned int stereo = zol->stereo; |
124 | int i; | 118 | int i; |
125 | 119 | ||
126 | if (freq == 0) { | 120 | if (freq == 0) { |
127 | printk(KERN_WARNING "zoltrix: received zero freq. Failed to set.\n"); | 121 | v4l2_warn(v4l2_dev, "cannot set a frequency of 0.\n"); |
128 | return -EINVAL; | 122 | return -EINVAL; |
129 | } | 123 | } |
130 | 124 | ||
131 | m = (freq / 160 - 8800) * 2; | 125 | m = (freq / 160 - 8800) * 2; |
132 | f = (unsigned long long) m + 0x4d1c; | 126 | f = (unsigned long long)m + 0x4d1c; |
133 | 127 | ||
134 | bitmask = 0xc480402c10080000ull; | 128 | bitmask = 0xc480402c10080000ull; |
135 | i = 45; | 129 | i = 45; |
136 | 130 | ||
137 | mutex_lock(&dev->lock); | 131 | mutex_lock(&zol->lock); |
138 | 132 | ||
139 | outb(0, io); | 133 | zol->curfreq = freq; |
140 | outb(0, io); | ||
141 | inb(io + 3); /* Zoltrix needs to be read to confirm */ | ||
142 | 134 | ||
143 | outb(0x40, io); | 135 | outb(0, zol->io); |
144 | outb(0xc0, io); | 136 | outb(0, zol->io); |
137 | inb(zol->io + 3); /* Zoltrix needs to be read to confirm */ | ||
145 | 138 | ||
146 | bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ ( stereo << 31)); | 139 | outb(0x40, zol->io); |
140 | outb(0xc0, zol->io); | ||
141 | |||
142 | bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ (stereo << 31)); | ||
147 | while (i--) { | 143 | while (i--) { |
148 | if ((bitmask & 0x8000000000000000ull) != 0) { | 144 | if ((bitmask & 0x8000000000000000ull) != 0) { |
149 | outb(0x80, io); | 145 | outb(0x80, zol->io); |
150 | udelay(50); | 146 | udelay(50); |
151 | outb(0x00, io); | 147 | outb(0x00, zol->io); |
152 | udelay(50); | 148 | udelay(50); |
153 | outb(0x80, io); | 149 | outb(0x80, zol->io); |
154 | udelay(50); | 150 | udelay(50); |
155 | } else { | 151 | } else { |
156 | outb(0xc0, io); | 152 | outb(0xc0, zol->io); |
157 | udelay(50); | 153 | udelay(50); |
158 | outb(0x40, io); | 154 | outb(0x40, zol->io); |
159 | udelay(50); | 155 | udelay(50); |
160 | outb(0xc0, io); | 156 | outb(0xc0, zol->io); |
161 | udelay(50); | 157 | udelay(50); |
162 | } | 158 | } |
163 | bitmask *= 2; | 159 | bitmask *= 2; |
164 | } | 160 | } |
165 | /* termination sequence */ | 161 | /* termination sequence */ |
166 | outb(0x80, io); | 162 | outb(0x80, zol->io); |
167 | outb(0xc0, io); | 163 | outb(0xc0, zol->io); |
168 | outb(0x40, io); | 164 | outb(0x40, zol->io); |
169 | udelay(1000); | 165 | udelay(1000); |
170 | inb(io+2); | 166 | inb(zol->io + 2); |
171 | 167 | ||
172 | udelay(1000); | 168 | udelay(1000); |
173 | 169 | ||
174 | if (dev->muted) | 170 | if (zol->muted) { |
175 | { | 171 | outb(0, zol->io); |
176 | outb(0, io); | 172 | outb(0, zol->io); |
177 | outb(0, io); | 173 | inb(zol->io + 3); |
178 | inb(io + 3); | ||
179 | udelay(1000); | 174 | udelay(1000); |
180 | } | 175 | } |
181 | 176 | ||
182 | mutex_unlock(&dev->lock); | 177 | mutex_unlock(&zol->lock); |
183 | 178 | ||
184 | if(!dev->muted) | 179 | if (!zol->muted) |
185 | { | 180 | zol_setvol(zol, zol->curvol); |
186 | zol_setvol(dev, dev->curvol); | ||
187 | } | ||
188 | return 0; | 181 | return 0; |
189 | } | 182 | } |
190 | 183 | ||
191 | /* Get signal strength */ | 184 | /* Get signal strength */ |
192 | 185 | static int zol_getsigstr(struct zoltrix *zol) | |
193 | static int zol_getsigstr(struct zol_device *dev) | ||
194 | { | 186 | { |
195 | int a, b; | 187 | int a, b; |
196 | 188 | ||
197 | mutex_lock(&dev->lock); | 189 | mutex_lock(&zol->lock); |
198 | outb(0x00, io); /* This stuff I found to do nothing */ | 190 | outb(0x00, zol->io); /* This stuff I found to do nothing */ |
199 | outb(dev->curvol, io); | 191 | outb(zol->curvol, zol->io); |
200 | msleep(20); | 192 | msleep(20); |
201 | 193 | ||
202 | a = inb(io); | 194 | a = inb(zol->io); |
203 | msleep(10); | 195 | msleep(10); |
204 | b = inb(io); | 196 | b = inb(zol->io); |
205 | 197 | ||
206 | mutex_unlock(&dev->lock); | 198 | mutex_unlock(&zol->lock); |
207 | 199 | ||
208 | if (a != b) | 200 | if (a != b) |
209 | return (0); | 201 | return 0; |
210 | 202 | ||
211 | if ((a == 0xcf) || (a == 0xdf) /* I found this out by playing */ | 203 | /* I found this out by playing with a binary scanner on the card io */ |
212 | || (a == 0xef)) /* with a binary scanner on the card io */ | 204 | return a == 0xcf || a == 0xdf || a == 0xef; |
213 | return (1); | ||
214 | return (0); | ||
215 | } | 205 | } |
216 | 206 | ||
217 | static int zol_is_stereo (struct zol_device *dev) | 207 | static int zol_is_stereo(struct zoltrix *zol) |
218 | { | 208 | { |
219 | int x1, x2; | 209 | int x1, x2; |
220 | 210 | ||
221 | mutex_lock(&dev->lock); | 211 | mutex_lock(&zol->lock); |
222 | 212 | ||
223 | outb(0x00, io); | 213 | outb(0x00, zol->io); |
224 | outb(dev->curvol, io); | 214 | outb(zol->curvol, zol->io); |
225 | msleep(20); | 215 | msleep(20); |
226 | 216 | ||
227 | x1 = inb(io); | 217 | x1 = inb(zol->io); |
228 | msleep(10); | 218 | msleep(10); |
229 | x2 = inb(io); | 219 | x2 = inb(zol->io); |
230 | 220 | ||
231 | mutex_unlock(&dev->lock); | 221 | mutex_unlock(&zol->lock); |
232 | 222 | ||
233 | if ((x1 == x2) && (x1 == 0xcf)) | 223 | return x1 == x2 && x1 == 0xcf; |
234 | return 1; | ||
235 | return 0; | ||
236 | } | 224 | } |
237 | 225 | ||
238 | static int vidioc_querycap(struct file *file, void *priv, | 226 | static int vidioc_querycap(struct file *file, void *priv, |
@@ -240,59 +228,54 @@ static int vidioc_querycap(struct file *file, void *priv, | |||
240 | { | 228 | { |
241 | strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver)); | 229 | strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver)); |
242 | strlcpy(v->card, "Zoltrix Radio", sizeof(v->card)); | 230 | strlcpy(v->card, "Zoltrix Radio", sizeof(v->card)); |
243 | sprintf(v->bus_info, "ISA"); | 231 | strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); |
244 | v->version = RADIO_VERSION; | 232 | v->version = RADIO_VERSION; |
245 | v->capabilities = V4L2_CAP_TUNER; | 233 | v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; |
246 | return 0; | 234 | return 0; |
247 | } | 235 | } |
248 | 236 | ||
249 | static int vidioc_g_tuner(struct file *file, void *priv, | 237 | static int vidioc_g_tuner(struct file *file, void *priv, |
250 | struct v4l2_tuner *v) | 238 | struct v4l2_tuner *v) |
251 | { | 239 | { |
252 | struct zol_device *zol = video_drvdata(file); | 240 | struct zoltrix *zol = video_drvdata(file); |
253 | 241 | ||
254 | if (v->index > 0) | 242 | if (v->index > 0) |
255 | return -EINVAL; | 243 | return -EINVAL; |
256 | 244 | ||
257 | strcpy(v->name, "FM"); | 245 | strlcpy(v->name, "FM", sizeof(v->name)); |
258 | v->type = V4L2_TUNER_RADIO; | 246 | v->type = V4L2_TUNER_RADIO; |
259 | v->rangelow = (88*16000); | 247 | v->rangelow = 88 * 16000; |
260 | v->rangehigh = (108*16000); | 248 | v->rangehigh = 108 * 16000; |
261 | v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; | 249 | v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; |
262 | v->capability = V4L2_TUNER_CAP_LOW; | 250 | v->capability = V4L2_TUNER_CAP_LOW; |
263 | if (zol_is_stereo(zol)) | 251 | if (zol_is_stereo(zol)) |
264 | v->audmode = V4L2_TUNER_MODE_STEREO; | 252 | v->audmode = V4L2_TUNER_MODE_STEREO; |
265 | else | 253 | else |
266 | v->audmode = V4L2_TUNER_MODE_MONO; | 254 | v->audmode = V4L2_TUNER_MODE_MONO; |
267 | v->signal = 0xFFFF*zol_getsigstr(zol); | 255 | v->signal = 0xFFFF * zol_getsigstr(zol); |
268 | return 0; | 256 | return 0; |
269 | } | 257 | } |
270 | 258 | ||
271 | static int vidioc_s_tuner(struct file *file, void *priv, | 259 | static int vidioc_s_tuner(struct file *file, void *priv, |
272 | struct v4l2_tuner *v) | 260 | struct v4l2_tuner *v) |
273 | { | 261 | { |
274 | if (v->index > 0) | 262 | return v->index ? -EINVAL : 0; |
275 | return -EINVAL; | ||
276 | return 0; | ||
277 | } | 263 | } |
278 | 264 | ||
279 | static int vidioc_s_frequency(struct file *file, void *priv, | 265 | static int vidioc_s_frequency(struct file *file, void *priv, |
280 | struct v4l2_frequency *f) | 266 | struct v4l2_frequency *f) |
281 | { | 267 | { |
282 | struct zol_device *zol = video_drvdata(file); | 268 | struct zoltrix *zol = video_drvdata(file); |
283 | 269 | ||
284 | zol->curfreq = f->frequency; | 270 | if (zol_setfreq(zol, f->frequency) != 0) |
285 | if (zol_setfreq(zol, zol->curfreq) != 0) { | ||
286 | printk(KERN_WARNING "zoltrix: Set frequency failed.\n"); | ||
287 | return -EINVAL; | 271 | return -EINVAL; |
288 | } | ||
289 | return 0; | 272 | return 0; |
290 | } | 273 | } |
291 | 274 | ||
292 | static int vidioc_g_frequency(struct file *file, void *priv, | 275 | static int vidioc_g_frequency(struct file *file, void *priv, |
293 | struct v4l2_frequency *f) | 276 | struct v4l2_frequency *f) |
294 | { | 277 | { |
295 | struct zol_device *zol = video_drvdata(file); | 278 | struct zoltrix *zol = video_drvdata(file); |
296 | 279 | ||
297 | f->type = V4L2_TUNER_RADIO; | 280 | f->type = V4L2_TUNER_RADIO; |
298 | f->frequency = zol->curfreq; | 281 | f->frequency = zol->curfreq; |
@@ -302,14 +285,11 @@ static int vidioc_g_frequency(struct file *file, void *priv, | |||
302 | static int vidioc_queryctrl(struct file *file, void *priv, | 285 | static int vidioc_queryctrl(struct file *file, void *priv, |
303 | struct v4l2_queryctrl *qc) | 286 | struct v4l2_queryctrl *qc) |
304 | { | 287 | { |
305 | int i; | 288 | switch (qc->id) { |
306 | 289 | case V4L2_CID_AUDIO_MUTE: | |
307 | for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { | 290 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); |
308 | if (qc->id && qc->id == radio_qctrl[i].id) { | 291 | case V4L2_CID_AUDIO_VOLUME: |
309 | memcpy(qc, &(radio_qctrl[i]), | 292 | return v4l2_ctrl_query_fill(qc, 0, 65535, 4096, 65535); |
310 | sizeof(*qc)); | ||
311 | return 0; | ||
312 | } | ||
313 | } | 293 | } |
314 | return -EINVAL; | 294 | return -EINVAL; |
315 | } | 295 | } |
@@ -317,7 +297,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, | |||
317 | static int vidioc_g_ctrl(struct file *file, void *priv, | 297 | static int vidioc_g_ctrl(struct file *file, void *priv, |
318 | struct v4l2_control *ctrl) | 298 | struct v4l2_control *ctrl) |
319 | { | 299 | { |
320 | struct zol_device *zol = video_drvdata(file); | 300 | struct zoltrix *zol = video_drvdata(file); |
321 | 301 | ||
322 | switch (ctrl->id) { | 302 | switch (ctrl->id) { |
323 | case V4L2_CID_AUDIO_MUTE: | 303 | case V4L2_CID_AUDIO_MUTE: |
@@ -333,7 +313,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, | |||
333 | static int vidioc_s_ctrl(struct file *file, void *priv, | 313 | static int vidioc_s_ctrl(struct file *file, void *priv, |
334 | struct v4l2_control *ctrl) | 314 | struct v4l2_control *ctrl) |
335 | { | 315 | { |
336 | struct zol_device *zol = video_drvdata(file); | 316 | struct zoltrix *zol = video_drvdata(file); |
337 | 317 | ||
338 | switch (ctrl->id) { | 318 | switch (ctrl->id) { |
339 | case V4L2_CID_AUDIO_MUTE: | 319 | case V4L2_CID_AUDIO_MUTE: |
@@ -341,43 +321,30 @@ static int vidioc_s_ctrl(struct file *file, void *priv, | |||
341 | zol_mute(zol); | 321 | zol_mute(zol); |
342 | else { | 322 | else { |
343 | zol_unmute(zol); | 323 | zol_unmute(zol); |
344 | zol_setvol(zol,zol->curvol); | 324 | zol_setvol(zol, zol->curvol); |
345 | } | 325 | } |
346 | return 0; | 326 | return 0; |
347 | case V4L2_CID_AUDIO_VOLUME: | 327 | case V4L2_CID_AUDIO_VOLUME: |
348 | zol_setvol(zol,ctrl->value/4096); | 328 | zol_setvol(zol, ctrl->value / 4096); |
349 | return 0; | 329 | return 0; |
350 | } | 330 | } |
351 | zol->stereo = 1; | 331 | zol->stereo = 1; |
352 | if (zol_setfreq(zol, zol->curfreq) != 0) { | 332 | if (zol_setfreq(zol, zol->curfreq) != 0) |
353 | printk(KERN_WARNING "zoltrix: Set frequency failed.\n"); | ||
354 | return -EINVAL; | 333 | return -EINVAL; |
355 | } | ||
356 | #if 0 | 334 | #if 0 |
357 | /* FIXME: Implement stereo/mono switch on V4L2 */ | 335 | /* FIXME: Implement stereo/mono switch on V4L2 */ |
358 | if (v->mode & VIDEO_SOUND_STEREO) { | 336 | if (v->mode & VIDEO_SOUND_STEREO) { |
359 | zol->stereo = 1; | 337 | zol->stereo = 1; |
360 | zol_setfreq(zol, zol->curfreq); | 338 | zol_setfreq(zol, zol->curfreq); |
361 | } | 339 | } |
362 | if (v->mode & VIDEO_SOUND_MONO) { | 340 | if (v->mode & VIDEO_SOUND_MONO) { |
363 | zol->stereo = 0; | 341 | zol->stereo = 0; |
364 | zol_setfreq(zol, zol->curfreq); | 342 | zol_setfreq(zol, zol->curfreq); |
365 | } | 343 | } |
366 | #endif | 344 | #endif |
367 | return -EINVAL; | 345 | return -EINVAL; |
368 | } | 346 | } |
369 | 347 | ||
370 | static int vidioc_g_audio(struct file *file, void *priv, | ||
371 | struct v4l2_audio *a) | ||
372 | { | ||
373 | if (a->index > 1) | ||
374 | return -EINVAL; | ||
375 | |||
376 | strcpy(a->name, "Radio"); | ||
377 | a->capability = V4L2_AUDCAP_STEREO; | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | 348 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) |
382 | { | 349 | { |
383 | *i = 0; | 350 | *i = 0; |
@@ -386,37 +353,39 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | |||
386 | 353 | ||
387 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | 354 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) |
388 | { | 355 | { |
389 | if (i != 0) | 356 | return i ? -EINVAL : 0; |
390 | return -EINVAL; | ||
391 | return 0; | ||
392 | } | 357 | } |
393 | 358 | ||
394 | static int vidioc_s_audio(struct file *file, void *priv, | 359 | static int vidioc_g_audio(struct file *file, void *priv, |
395 | struct v4l2_audio *a) | 360 | struct v4l2_audio *a) |
396 | { | 361 | { |
397 | if (a->index != 0) | 362 | a->index = 0; |
398 | return -EINVAL; | 363 | strlcpy(a->name, "Radio", sizeof(a->name)); |
364 | a->capability = V4L2_AUDCAP_STEREO; | ||
399 | return 0; | 365 | return 0; |
400 | } | 366 | } |
401 | 367 | ||
402 | static struct zol_device zoltrix_unit; | 368 | static int vidioc_s_audio(struct file *file, void *priv, |
369 | struct v4l2_audio *a) | ||
370 | { | ||
371 | return a->index ? -EINVAL : 0; | ||
372 | } | ||
403 | 373 | ||
404 | static int zoltrix_exclusive_open(struct file *file) | 374 | static int zoltrix_open(struct file *file) |
405 | { | 375 | { |
406 | return test_and_set_bit(0, &zoltrix_unit.in_use) ? -EBUSY : 0; | 376 | return 0; |
407 | } | 377 | } |
408 | 378 | ||
409 | static int zoltrix_exclusive_release(struct file *file) | 379 | static int zoltrix_release(struct file *file) |
410 | { | 380 | { |
411 | clear_bit(0, &zoltrix_unit.in_use); | ||
412 | return 0; | 381 | return 0; |
413 | } | 382 | } |
414 | 383 | ||
415 | static const struct v4l2_file_operations zoltrix_fops = | 384 | static const struct v4l2_file_operations zoltrix_fops = |
416 | { | 385 | { |
417 | .owner = THIS_MODULE, | 386 | .owner = THIS_MODULE, |
418 | .open = zoltrix_exclusive_open, | 387 | .open = zoltrix_open, |
419 | .release = zoltrix_exclusive_release, | 388 | .release = zoltrix_release, |
420 | .ioctl = video_ioctl2, | 389 | .ioctl = video_ioctl2, |
421 | }; | 390 | }; |
422 | 391 | ||
@@ -435,67 +404,75 @@ static const struct v4l2_ioctl_ops zoltrix_ioctl_ops = { | |||
435 | .vidioc_s_ctrl = vidioc_s_ctrl, | 404 | .vidioc_s_ctrl = vidioc_s_ctrl, |
436 | }; | 405 | }; |
437 | 406 | ||
438 | static struct video_device zoltrix_radio = { | ||
439 | .name = "Zoltrix Radio Plus", | ||
440 | .fops = &zoltrix_fops, | ||
441 | .ioctl_ops = &zoltrix_ioctl_ops, | ||
442 | .release = video_device_release_empty, | ||
443 | }; | ||
444 | |||
445 | static int __init zoltrix_init(void) | 407 | static int __init zoltrix_init(void) |
446 | { | 408 | { |
447 | if (io == -1) { | 409 | struct zoltrix *zol = &zoltrix_card; |
448 | printk(KERN_ERR "You must set an I/O address with io=0x???\n"); | 410 | struct v4l2_device *v4l2_dev = &zol->v4l2_dev; |
411 | int res; | ||
412 | |||
413 | strlcpy(v4l2_dev->name, "zoltrix", sizeof(v4l2_dev->name)); | ||
414 | zol->io = io; | ||
415 | if (zol->io == -1) { | ||
416 | v4l2_err(v4l2_dev, "You must set an I/O address with io=0x???\n"); | ||
449 | return -EINVAL; | 417 | return -EINVAL; |
450 | } | 418 | } |
451 | if ((io != 0x20c) && (io != 0x30c)) { | 419 | if (zol->io != 0x20c && zol->io != 0x30c) { |
452 | printk(KERN_ERR "zoltrix: invalid port, try 0x20c or 0x30c\n"); | 420 | v4l2_err(v4l2_dev, "invalid port, try 0x20c or 0x30c\n"); |
453 | return -ENXIO; | 421 | return -ENXIO; |
454 | } | 422 | } |
455 | 423 | ||
456 | video_set_drvdata(&zoltrix_radio, &zoltrix_unit); | 424 | if (!request_region(zol->io, 2, "zoltrix")) { |
457 | if (!request_region(io, 2, "zoltrix")) { | 425 | v4l2_err(v4l2_dev, "port 0x%x already in use\n", zol->io); |
458 | printk(KERN_ERR "zoltrix: port 0x%x already in use\n", io); | ||
459 | return -EBUSY; | 426 | return -EBUSY; |
460 | } | 427 | } |
461 | 428 | ||
462 | if (video_register_device(&zoltrix_radio, VFL_TYPE_RADIO, radio_nr) < 0) { | 429 | res = v4l2_device_register(NULL, v4l2_dev); |
463 | release_region(io, 2); | 430 | if (res < 0) { |
431 | release_region(zol->io, 2); | ||
432 | v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); | ||
433 | return res; | ||
434 | } | ||
435 | |||
436 | strlcpy(zol->vdev.name, v4l2_dev->name, sizeof(zol->vdev.name)); | ||
437 | zol->vdev.v4l2_dev = v4l2_dev; | ||
438 | zol->vdev.fops = &zoltrix_fops; | ||
439 | zol->vdev.ioctl_ops = &zoltrix_ioctl_ops; | ||
440 | zol->vdev.release = video_device_release_empty; | ||
441 | video_set_drvdata(&zol->vdev, zol); | ||
442 | |||
443 | if (video_register_device(&zol->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { | ||
444 | v4l2_device_unregister(v4l2_dev); | ||
445 | release_region(zol->io, 2); | ||
464 | return -EINVAL; | 446 | return -EINVAL; |
465 | } | 447 | } |
466 | printk(KERN_INFO "Zoltrix Radio Plus card driver.\n"); | 448 | v4l2_info(v4l2_dev, "Zoltrix Radio Plus card driver.\n"); |
467 | 449 | ||
468 | mutex_init(&zoltrix_unit.lock); | 450 | mutex_init(&zol->lock); |
469 | 451 | ||
470 | /* mute card - prevents noisy bootups */ | 452 | /* mute card - prevents noisy bootups */ |
471 | 453 | ||
472 | /* this ensures that the volume is all the way down */ | 454 | /* this ensures that the volume is all the way down */ |
473 | 455 | ||
474 | outb(0, io); | 456 | outb(0, zol->io); |
475 | outb(0, io); | 457 | outb(0, zol->io); |
476 | msleep(20); | 458 | msleep(20); |
477 | inb(io + 3); | 459 | inb(zol->io + 3); |
478 | 460 | ||
479 | zoltrix_unit.curvol = 0; | 461 | zol->curvol = 0; |
480 | zoltrix_unit.stereo = 1; | 462 | zol->stereo = 1; |
481 | 463 | ||
482 | return 0; | 464 | return 0; |
483 | } | 465 | } |
484 | 466 | ||
485 | MODULE_AUTHOR("C.van Schaik"); | 467 | static void __exit zoltrix_exit(void) |
486 | MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus."); | ||
487 | MODULE_LICENSE("GPL"); | ||
488 | |||
489 | module_param(io, int, 0); | ||
490 | MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)"); | ||
491 | module_param(radio_nr, int, 0); | ||
492 | |||
493 | static void __exit zoltrix_cleanup_module(void) | ||
494 | { | 468 | { |
495 | video_unregister_device(&zoltrix_radio); | 469 | struct zoltrix *zol = &zoltrix_card; |
496 | release_region(io, 2); | 470 | |
471 | video_unregister_device(&zol->vdev); | ||
472 | v4l2_device_unregister(&zol->v4l2_dev); | ||
473 | release_region(zol->io, 2); | ||
497 | } | 474 | } |
498 | 475 | ||
499 | module_init(zoltrix_init); | 476 | module_init(zoltrix_init); |
500 | module_exit(zoltrix_cleanup_module); | 477 | module_exit(zoltrix_exit); |
501 | 478 | ||