1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31#include <linux/module.h>
32#include <linux/init.h>
33#include <linux/ioport.h>
34#include <linux/delay.h>
35#include <linux/videodev2.h>
36#include <linux/version.h>
37#include <linux/io.h>
38#include <media/v4l2-device.h>
39#include <media/v4l2-ioctl.h>
40
41MODULE_AUTHOR("M.Kirkwood");
42MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
43MODULE_LICENSE("GPL");
44
45#ifndef CONFIG_RADIO_RTRACK_PORT
46#define CONFIG_RADIO_RTRACK_PORT -1
47#endif
48
49static int io = CONFIG_RADIO_RTRACK_PORT;
50static int radio_nr = -1;
51
52module_param(io, int, 0);
53MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
54module_param(radio_nr, int, 0);
55
56#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
57
58struct rtrack
59{
60 struct v4l2_device v4l2_dev;
61 struct video_device vdev;
62 int port;
63 int curvol;
64 unsigned long curfreq;
65 int muted;
66 int io;
67 struct mutex lock;
68};
69
70static struct rtrack rtrack_card;
71
72
73
74static void sleep_delay(long n)
75{
76
77 int d = n / msecs_to_jiffies(1000);
78 if (!d)
79 udelay(n);
80 else
81 msleep(jiffies_to_msecs(d));
82}
83
84static void rt_decvol(struct rtrack *rt)
85{
86 outb(0x58, rt->io);
87 sleep_delay(100000);
88 outb(0xd8, rt->io);
89}
90
91static void rt_incvol(struct rtrack *rt)
92{
93 outb(0x98, rt->io);
94 sleep_delay(100000);
95 outb(0xd8, rt->io);
96}
97
98static void rt_mute(struct rtrack *rt)
99{
100 rt->muted = 1;
101 mutex_lock(&rt->lock);
102 outb(0xd0, rt->io);
103 mutex_unlock(&rt->lock);
104}
105
106static int rt_setvol(struct rtrack *rt, int vol)
107{
108 int i;
109
110 mutex_lock(&rt->lock);
111
112 if (vol == rt->curvol) {
113 if (rt->muted) {
114 rt->muted = 0;
115 outb(0xd8, rt->io);
116 }
117 mutex_unlock(&rt->lock);
118 return 0;
119 }
120
121 if (vol == 0) {
122 outb(0x48, rt->io);
123 sleep_delay(2000000);
124 outb(0xd0, rt->io);
125 rt->curvol = 0;
126 mutex_unlock(&rt->lock);
127 return 0;
128 }
129
130 rt->muted = 0;
131 if (vol > rt->curvol)
132 for (i = rt->curvol; i < vol; i++)
133 rt_incvol(rt);
134 else
135 for (i = rt->curvol; i > vol; i--)
136 rt_decvol(rt);
137
138 rt->curvol = vol;
139 mutex_unlock(&rt->lock);
140 return 0;
141}
142
143
144
145
146
147
148static void send_0_byte(struct rtrack *rt)
149{
150 if (rt->curvol == 0 || rt->muted) {
151 outb_p(128+64+16+ 1, rt->io);
152 outb_p(128+64+16+2+1, rt->io);
153 }
154 else {
155 outb_p(128+64+16+8+ 1, rt->io);
156 outb_p(128+64+16+8+2+1, rt->io);
157 }
158 sleep_delay(1000);
159}
160
161static void send_1_byte(struct rtrack *rt)
162{
163 if (rt->curvol == 0 || rt->muted) {
164 outb_p(128+64+16+4 +1, rt->io);
165 outb_p(128+64+16+4+2+1, rt->io);
166 }
167 else {
168 outb_p(128+64+16+8+4 +1, rt->io);
169 outb_p(128+64+16+8+4+2+1, rt->io);
170 }
171
172 sleep_delay(1000);
173}
174
175static int rt_setfreq(struct rtrack *rt, unsigned long freq)
176{
177 int i;
178
179 mutex_lock(&rt->lock);
180
181 rt->curfreq = freq;
182
183
184
185 freq += 171200;
186 freq /= 800;
187
188 send_0_byte(rt);
189
190 for (i = 0; i < 13; i++)
191 if (freq & (1 << i))
192 send_1_byte(rt);
193 else
194 send_0_byte(rt);
195
196 send_0_byte(rt);
197 send_0_byte(rt);
198
199 send_0_byte(rt);
200 send_0_byte(rt);
201 send_0_byte(rt);
202 send_0_byte(rt);
203
204 send_0_byte(rt);
205 send_1_byte(rt);
206 send_0_byte(rt);
207 send_1_byte(rt);
208
209 if (rt->curvol == 0 || rt->muted)
210 outb(0xd0, rt->io);
211 else
212 outb(0xd8, rt->io);
213
214 mutex_unlock(&rt->lock);
215
216 return 0;
217}
218
219static int rt_getsigstr(struct rtrack *rt)
220{
221 int sig = 1;
222
223 mutex_lock(&rt->lock);
224 if (inb(rt->io) & 2)
225 sig = 0;
226 mutex_unlock(&rt->lock);
227 return sig;
228}
229
230static int vidioc_querycap(struct file *file, void *priv,
231 struct v4l2_capability *v)
232{
233 strlcpy(v->driver, "radio-aimslab", sizeof(v->driver));
234 strlcpy(v->card, "RadioTrack", sizeof(v->card));
235 strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
236 v->version = RADIO_VERSION;
237 v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
238 return 0;
239}
240
241static int vidioc_g_tuner(struct file *file, void *priv,
242 struct v4l2_tuner *v)
243{
244 struct rtrack *rt = video_drvdata(file);
245
246 if (v->index > 0)
247 return -EINVAL;
248
249 strlcpy(v->name, "FM", sizeof(v->name));
250 v->type = V4L2_TUNER_RADIO;
251 v->rangelow = 87 * 16000;
252 v->rangehigh = 108 * 16000;
253 v->rxsubchans = V4L2_TUNER_SUB_MONO;
254 v->capability = V4L2_TUNER_CAP_LOW;
255 v->audmode = V4L2_TUNER_MODE_MONO;
256 v->signal = 0xffff * rt_getsigstr(rt);
257 return 0;
258}
259
260static int vidioc_s_tuner(struct file *file, void *priv,
261 struct v4l2_tuner *v)
262{
263 return v->index ? -EINVAL : 0;
264}
265
266static int vidioc_s_frequency(struct file *file, void *priv,
267 struct v4l2_frequency *f)
268{
269 struct rtrack *rt = video_drvdata(file);
270
271 rt_setfreq(rt, f->frequency);
272 return 0;
273}
274
275static int vidioc_g_frequency(struct file *file, void *priv,
276 struct v4l2_frequency *f)
277{
278 struct rtrack *rt = video_drvdata(file);
279
280 f->type = V4L2_TUNER_RADIO;
281 f->frequency = rt->curfreq;
282 return 0;
283}
284
285static int vidioc_queryctrl(struct file *file, void *priv,
286 struct v4l2_queryctrl *qc)
287{
288 switch (qc->id) {
289 case V4L2_CID_AUDIO_MUTE:
290 return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
291 case V4L2_CID_AUDIO_VOLUME:
292 return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
293 }
294 return -EINVAL;
295}
296
297static int vidioc_g_ctrl(struct file *file, void *priv,
298 struct v4l2_control *ctrl)
299{
300 struct rtrack *rt = video_drvdata(file);
301
302 switch (ctrl->id) {
303 case V4L2_CID_AUDIO_MUTE:
304 ctrl->value = rt->muted;
305 return 0;
306 case V4L2_CID_AUDIO_VOLUME:
307 ctrl->value = rt->curvol;
308 return 0;
309 }
310 return -EINVAL;
311}
312
313static int vidioc_s_ctrl(struct file *file, void *priv,
314 struct v4l2_control *ctrl)
315{
316 struct rtrack *rt = video_drvdata(file);
317
318 switch (ctrl->id) {
319 case V4L2_CID_AUDIO_MUTE:
320 if (ctrl->value)
321 rt_mute(rt);
322 else
323 rt_setvol(rt, rt->curvol);
324 return 0;
325 case V4L2_CID_AUDIO_VOLUME:
326 rt_setvol(rt, ctrl->value);
327 return 0;
328 }
329 return -EINVAL;
330}
331
332static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
333{
334 *i = 0;
335 return 0;
336}
337
338static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
339{
340 return i ? -EINVAL : 0;
341}
342
343static int vidioc_g_audio(struct file *file, void *priv,
344 struct v4l2_audio *a)
345{
346 a->index = 0;
347 strlcpy(a->name, "Radio", sizeof(a->name));
348 a->capability = V4L2_AUDCAP_STEREO;
349 return 0;
350}
351
352static int vidioc_s_audio(struct file *file, void *priv,
353 struct v4l2_audio *a)
354{
355 return a->index ? -EINVAL : 0;
356}
357
358static const struct v4l2_file_operations rtrack_fops = {
359 .owner = THIS_MODULE,
360 .ioctl = video_ioctl2,
361};
362
363static const struct v4l2_ioctl_ops rtrack_ioctl_ops = {
364 .vidioc_querycap = vidioc_querycap,
365 .vidioc_g_tuner = vidioc_g_tuner,
366 .vidioc_s_tuner = vidioc_s_tuner,
367 .vidioc_g_audio = vidioc_g_audio,
368 .vidioc_s_audio = vidioc_s_audio,
369 .vidioc_g_input = vidioc_g_input,
370 .vidioc_s_input = vidioc_s_input,
371 .vidioc_g_frequency = vidioc_g_frequency,
372 .vidioc_s_frequency = vidioc_s_frequency,
373 .vidioc_queryctrl = vidioc_queryctrl,
374 .vidioc_g_ctrl = vidioc_g_ctrl,
375 .vidioc_s_ctrl = vidioc_s_ctrl,
376};
377
378static int __init rtrack_init(void)
379{
380 struct rtrack *rt = &rtrack_card;
381 struct v4l2_device *v4l2_dev = &rt->v4l2_dev;
382 int res;
383
384 strlcpy(v4l2_dev->name, "rtrack", sizeof(v4l2_dev->name));
385 rt->io = io;
386
387 if (rt->io == -1) {
388 v4l2_err(v4l2_dev, "you must set an I/O address with io=0x20f or 0x30f\n");
389 return -EINVAL;
390 }
391
392 if (!request_region(rt->io, 2, "rtrack")) {
393 v4l2_err(v4l2_dev, "port 0x%x already in use\n", rt->io);
394 return -EBUSY;
395 }
396
397 res = v4l2_device_register(NULL, v4l2_dev);
398 if (res < 0) {
399 release_region(rt->io, 2);
400 v4l2_err(v4l2_dev, "could not register v4l2_device\n");
401 return res;
402 }
403
404 strlcpy(rt->vdev.name, v4l2_dev->name, sizeof(rt->vdev.name));
405 rt->vdev.v4l2_dev = v4l2_dev;
406 rt->vdev.fops = &rtrack_fops;
407 rt->vdev.ioctl_ops = &rtrack_ioctl_ops;
408 rt->vdev.release = video_device_release_empty;
409 video_set_drvdata(&rt->vdev, rt);
410
411 if (video_register_device(&rt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
412 v4l2_device_unregister(&rt->v4l2_dev);
413 release_region(rt->io, 2);
414 return -EINVAL;
415 }
416 v4l2_info(v4l2_dev, "AIMSlab RadioTrack/RadioReveal card driver.\n");
417
418
419
420 mutex_init(&rt->lock);
421
422
423
424
425 outb(0x48, rt->io);
426 sleep_delay(2000000);
427 outb(0xc0, rt->io);
428
429 return 0;
430}
431
432static void __exit rtrack_exit(void)
433{
434 struct rtrack *rt = &rtrack_card;
435
436 video_unregister_device(&rt->vdev);
437 v4l2_device_unregister(&rt->v4l2_dev);
438 release_region(rt->io, 2);
439}
440
441module_init(rtrack_init);
442module_exit(rtrack_exit);
443
444