]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff |
4286c6f6 | 2 | * |
1da177e4 | 3 | * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood |
d9b01449 | 4 | * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> |
1da177e4 LT |
5 | * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> |
6 | * | |
7 | * TODO: Allow for more than one of these foolish entities :-) | |
8 | * | |
f8c559f8 | 9 | * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> |
1da177e4 LT |
10 | */ |
11 | ||
12 | #include <linux/module.h> /* Modules */ | |
13 | #include <linux/init.h> /* Initdata */ | |
fb911ee8 | 14 | #include <linux/ioport.h> /* request_region */ |
1da177e4 | 15 | #include <linux/delay.h> /* udelay */ |
f8c559f8 | 16 | #include <linux/videodev2.h> /* kernel radio structs */ |
922c78e9 HV |
17 | #include <linux/mutex.h> |
18 | #include <linux/version.h> /* for KERNEL_VERSION MACRO */ | |
19 | #include <linux/io.h> /* outb, outb_p */ | |
922c78e9 | 20 | #include <media/v4l2-device.h> |
35ea11ff | 21 | #include <media/v4l2-ioctl.h> |
1da177e4 | 22 | |
922c78e9 HV |
23 | MODULE_AUTHOR("Ben Pfaff"); |
24 | MODULE_DESCRIPTION("A driver for the RadioTrack II radio card."); | |
25 | MODULE_LICENSE("GPL"); | |
f8c559f8 | 26 | |
1da177e4 LT |
27 | #ifndef CONFIG_RADIO_RTRACK2_PORT |
28 | #define CONFIG_RADIO_RTRACK2_PORT -1 | |
29 | #endif | |
30 | ||
4286c6f6 | 31 | static int io = CONFIG_RADIO_RTRACK2_PORT; |
1da177e4 | 32 | static int radio_nr = -1; |
1da177e4 | 33 | |
922c78e9 HV |
34 | module_param(io, int, 0); |
35 | MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)"); | |
36 | module_param(radio_nr, int, 0); | |
37 | ||
38 | #define RADIO_VERSION KERNEL_VERSION(0, 0, 2) | |
39 | ||
40 | struct rtrack2 | |
1da177e4 | 41 | { |
922c78e9 HV |
42 | struct v4l2_device v4l2_dev; |
43 | struct video_device vdev; | |
44 | int io; | |
1da177e4 LT |
45 | unsigned long curfreq; |
46 | int muted; | |
922c78e9 | 47 | struct mutex lock; |
1da177e4 LT |
48 | }; |
49 | ||
922c78e9 HV |
50 | static struct rtrack2 rtrack2_card; |
51 | ||
1da177e4 LT |
52 | |
53 | /* local things */ | |
54 | ||
922c78e9 | 55 | static void rt_mute(struct rtrack2 *dev) |
1da177e4 | 56 | { |
922c78e9 | 57 | if (dev->muted) |
1da177e4 | 58 | return; |
922c78e9 HV |
59 | mutex_lock(&dev->lock); |
60 | outb(1, dev->io); | |
61 | mutex_unlock(&dev->lock); | |
1da177e4 LT |
62 | dev->muted = 1; |
63 | } | |
64 | ||
922c78e9 | 65 | static void rt_unmute(struct rtrack2 *dev) |
1da177e4 LT |
66 | { |
67 | if(dev->muted == 0) | |
68 | return; | |
922c78e9 HV |
69 | mutex_lock(&dev->lock); |
70 | outb(0, dev->io); | |
71 | mutex_unlock(&dev->lock); | |
1da177e4 LT |
72 | dev->muted = 0; |
73 | } | |
74 | ||
922c78e9 | 75 | static void zero(struct rtrack2 *dev) |
1da177e4 | 76 | { |
922c78e9 HV |
77 | outb_p(1, dev->io); |
78 | outb_p(3, dev->io); | |
79 | outb_p(1, dev->io); | |
1da177e4 LT |
80 | } |
81 | ||
922c78e9 | 82 | static void one(struct rtrack2 *dev) |
1da177e4 | 83 | { |
922c78e9 HV |
84 | outb_p(5, dev->io); |
85 | outb_p(7, dev->io); | |
86 | outb_p(5, dev->io); | |
1da177e4 LT |
87 | } |
88 | ||
922c78e9 | 89 | static int rt_setfreq(struct rtrack2 *dev, unsigned long freq) |
1da177e4 LT |
90 | { |
91 | int i; | |
92 | ||
922c78e9 HV |
93 | mutex_lock(&dev->lock); |
94 | dev->curfreq = freq; | |
1da177e4 | 95 | freq = freq / 200 + 856; |
4286c6f6 | 96 | |
922c78e9 HV |
97 | outb_p(0xc8, dev->io); |
98 | outb_p(0xc9, dev->io); | |
99 | outb_p(0xc9, dev->io); | |
1da177e4 LT |
100 | |
101 | for (i = 0; i < 10; i++) | |
922c78e9 | 102 | zero(dev); |
1da177e4 LT |
103 | |
104 | for (i = 14; i >= 0; i--) | |
105 | if (freq & (1 << i)) | |
922c78e9 | 106 | one(dev); |
1da177e4 | 107 | else |
922c78e9 | 108 | zero(dev); |
1da177e4 | 109 | |
922c78e9 | 110 | outb_p(0xc8, dev->io); |
1da177e4 | 111 | if (!dev->muted) |
922c78e9 | 112 | outb_p(0, dev->io); |
4286c6f6 | 113 | |
922c78e9 | 114 | mutex_unlock(&dev->lock); |
1da177e4 LT |
115 | return 0; |
116 | } | |
117 | ||
25f30389 DL |
118 | static int vidioc_querycap(struct file *file, void *priv, |
119 | struct v4l2_capability *v) | |
120 | { | |
121 | strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver)); | |
122 | strlcpy(v->card, "RadioTrack II", sizeof(v->card)); | |
922c78e9 | 123 | strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); |
25f30389 | 124 | v->version = RADIO_VERSION; |
922c78e9 | 125 | v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; |
25f30389 DL |
126 | return 0; |
127 | } | |
128 | ||
129 | static int vidioc_s_tuner(struct file *file, void *priv, | |
130 | struct v4l2_tuner *v) | |
131 | { | |
922c78e9 | 132 | return v->index ? -EINVAL : 0; |
25f30389 DL |
133 | } |
134 | ||
922c78e9 | 135 | static int rt_getsigstr(struct rtrack2 *dev) |
1da177e4 | 136 | { |
922c78e9 HV |
137 | int sig = 1; |
138 | ||
139 | mutex_lock(&dev->lock); | |
140 | if (inb(dev->io) & 2) /* bit set = no signal present */ | |
141 | sig = 0; | |
142 | mutex_unlock(&dev->lock); | |
143 | return sig; | |
1da177e4 LT |
144 | } |
145 | ||
25f30389 DL |
146 | static int vidioc_g_tuner(struct file *file, void *priv, |
147 | struct v4l2_tuner *v) | |
1da177e4 | 148 | { |
922c78e9 | 149 | struct rtrack2 *rt = video_drvdata(file); |
25f30389 DL |
150 | |
151 | if (v->index > 0) | |
152 | return -EINVAL; | |
153 | ||
922c78e9 | 154 | strlcpy(v->name, "FM", sizeof(v->name)); |
25f30389 | 155 | v->type = V4L2_TUNER_RADIO; |
922c78e9 HV |
156 | v->rangelow = 88 * 16000; |
157 | v->rangehigh = 108 * 16000; | |
25f30389 DL |
158 | v->rxsubchans = V4L2_TUNER_SUB_MONO; |
159 | v->capability = V4L2_TUNER_CAP_LOW; | |
160 | v->audmode = V4L2_TUNER_MODE_MONO; | |
922c78e9 | 161 | v->signal = 0xFFFF * rt_getsigstr(rt); |
25f30389 DL |
162 | return 0; |
163 | } | |
f8c559f8 | 164 | |
25f30389 DL |
165 | static int vidioc_s_frequency(struct file *file, void *priv, |
166 | struct v4l2_frequency *f) | |
167 | { | |
922c78e9 | 168 | struct rtrack2 *rt = video_drvdata(file); |
f8c559f8 | 169 | |
a3a9e287 HV |
170 | if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) |
171 | return -EINVAL; | |
922c78e9 | 172 | rt_setfreq(rt, f->frequency); |
25f30389 DL |
173 | return 0; |
174 | } | |
f8c559f8 | 175 | |
25f30389 DL |
176 | static int vidioc_g_frequency(struct file *file, void *priv, |
177 | struct v4l2_frequency *f) | |
178 | { | |
922c78e9 | 179 | struct rtrack2 *rt = video_drvdata(file); |
f8c559f8 | 180 | |
a3a9e287 HV |
181 | if (f->tuner != 0) |
182 | return -EINVAL; | |
25f30389 DL |
183 | f->type = V4L2_TUNER_RADIO; |
184 | f->frequency = rt->curfreq; | |
185 | return 0; | |
186 | } | |
f8c559f8 | 187 | |
25f30389 DL |
188 | static int vidioc_queryctrl(struct file *file, void *priv, |
189 | struct v4l2_queryctrl *qc) | |
190 | { | |
922c78e9 HV |
191 | switch (qc->id) { |
192 | case V4L2_CID_AUDIO_MUTE: | |
193 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | |
194 | case V4L2_CID_AUDIO_VOLUME: | |
195 | return v4l2_ctrl_query_fill(qc, 0, 65535, 65535, 65535); | |
25f30389 DL |
196 | } |
197 | return -EINVAL; | |
198 | } | |
f8c559f8 | 199 | |
25f30389 DL |
200 | static int vidioc_g_ctrl(struct file *file, void *priv, |
201 | struct v4l2_control *ctrl) | |
202 | { | |
922c78e9 | 203 | struct rtrack2 *rt = video_drvdata(file); |
f8c559f8 | 204 | |
25f30389 DL |
205 | switch (ctrl->id) { |
206 | case V4L2_CID_AUDIO_MUTE: | |
207 | ctrl->value = rt->muted; | |
208 | return 0; | |
209 | case V4L2_CID_AUDIO_VOLUME: | |
210 | if (rt->muted) | |
211 | ctrl->value = 0; | |
212 | else | |
213 | ctrl->value = 65535; | |
214 | return 0; | |
1da177e4 | 215 | } |
25f30389 | 216 | return -EINVAL; |
1da177e4 LT |
217 | } |
218 | ||
25f30389 DL |
219 | static int vidioc_s_ctrl(struct file *file, void *priv, |
220 | struct v4l2_control *ctrl) | |
1da177e4 | 221 | { |
922c78e9 | 222 | struct rtrack2 *rt = video_drvdata(file); |
25f30389 DL |
223 | |
224 | switch (ctrl->id) { | |
225 | case V4L2_CID_AUDIO_MUTE: | |
226 | if (ctrl->value) | |
227 | rt_mute(rt); | |
228 | else | |
229 | rt_unmute(rt); | |
230 | return 0; | |
231 | case V4L2_CID_AUDIO_VOLUME: | |
232 | if (ctrl->value) | |
233 | rt_unmute(rt); | |
234 | else | |
235 | rt_mute(rt); | |
236 | return 0; | |
237 | } | |
238 | return -EINVAL; | |
1da177e4 LT |
239 | } |
240 | ||
8b811cf0 DL |
241 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) |
242 | { | |
243 | *i = 0; | |
244 | return 0; | |
245 | } | |
246 | ||
247 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | |
248 | { | |
922c78e9 | 249 | return i ? -EINVAL : 0; |
8b811cf0 DL |
250 | } |
251 | ||
922c78e9 | 252 | static int vidioc_g_audio(struct file *file, void *priv, |
8b811cf0 DL |
253 | struct v4l2_audio *a) |
254 | { | |
922c78e9 HV |
255 | a->index = 0; |
256 | strlcpy(a->name, "Radio", sizeof(a->name)); | |
257 | a->capability = V4L2_AUDCAP_STEREO; | |
8b811cf0 DL |
258 | return 0; |
259 | } | |
260 | ||
922c78e9 HV |
261 | static int vidioc_s_audio(struct file *file, void *priv, |
262 | struct v4l2_audio *a) | |
263 | { | |
264 | return a->index ? -EINVAL : 0; | |
265 | } | |
1da177e4 | 266 | |
bec43661 | 267 | static const struct v4l2_file_operations rtrack2_fops = { |
1da177e4 | 268 | .owner = THIS_MODULE, |
32958fdd | 269 | .unlocked_ioctl = video_ioctl2, |
1da177e4 LT |
270 | }; |
271 | ||
a399810c | 272 | static const struct v4l2_ioctl_ops rtrack2_ioctl_ops = { |
25f30389 DL |
273 | .vidioc_querycap = vidioc_querycap, |
274 | .vidioc_g_tuner = vidioc_g_tuner, | |
275 | .vidioc_s_tuner = vidioc_s_tuner, | |
276 | .vidioc_g_frequency = vidioc_g_frequency, | |
277 | .vidioc_s_frequency = vidioc_s_frequency, | |
278 | .vidioc_queryctrl = vidioc_queryctrl, | |
279 | .vidioc_g_ctrl = vidioc_g_ctrl, | |
280 | .vidioc_s_ctrl = vidioc_s_ctrl, | |
8b811cf0 DL |
281 | .vidioc_g_audio = vidioc_g_audio, |
282 | .vidioc_s_audio = vidioc_s_audio, | |
283 | .vidioc_g_input = vidioc_g_input, | |
284 | .vidioc_s_input = vidioc_s_input, | |
1da177e4 LT |
285 | }; |
286 | ||
287 | static int __init rtrack2_init(void) | |
288 | { | |
922c78e9 HV |
289 | struct rtrack2 *dev = &rtrack2_card; |
290 | struct v4l2_device *v4l2_dev = &dev->v4l2_dev; | |
291 | int res; | |
292 | ||
293 | strlcpy(v4l2_dev->name, "rtrack2", sizeof(v4l2_dev->name)); | |
294 | dev->io = io; | |
295 | if (dev->io == -1) { | |
296 | v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or io=0x30c\n"); | |
1da177e4 LT |
297 | return -EINVAL; |
298 | } | |
922c78e9 HV |
299 | if (!request_region(dev->io, 4, "rtrack2")) { |
300 | v4l2_err(v4l2_dev, "port 0x%x already in use\n", dev->io); | |
1da177e4 LT |
301 | return -EBUSY; |
302 | } | |
303 | ||
922c78e9 HV |
304 | res = v4l2_device_register(NULL, v4l2_dev); |
305 | if (res < 0) { | |
306 | release_region(dev->io, 4); | |
307 | v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); | |
308 | return res; | |
309 | } | |
1da177e4 | 310 | |
922c78e9 HV |
311 | strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); |
312 | dev->vdev.v4l2_dev = v4l2_dev; | |
313 | dev->vdev.fops = &rtrack2_fops; | |
314 | dev->vdev.ioctl_ops = &rtrack2_ioctl_ops; | |
315 | dev->vdev.release = video_device_release_empty; | |
316 | video_set_drvdata(&dev->vdev, dev); | |
317 | ||
32958fdd HV |
318 | /* mute card - prevents noisy bootups */ |
319 | outb(1, dev->io); | |
320 | dev->muted = 1; | |
321 | ||
922c78e9 HV |
322 | mutex_init(&dev->lock); |
323 | if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { | |
324 | v4l2_device_unregister(v4l2_dev); | |
325 | release_region(dev->io, 4); | |
1da177e4 LT |
326 | return -EINVAL; |
327 | } | |
4286c6f6 | 328 | |
922c78e9 | 329 | v4l2_info(v4l2_dev, "AIMSlab Radiotrack II card driver.\n"); |
1da177e4 | 330 | |
1da177e4 LT |
331 | return 0; |
332 | } | |
333 | ||
922c78e9 | 334 | static void __exit rtrack2_exit(void) |
1da177e4 | 335 | { |
922c78e9 HV |
336 | struct rtrack2 *dev = &rtrack2_card; |
337 | ||
338 | video_unregister_device(&dev->vdev); | |
339 | v4l2_device_unregister(&dev->v4l2_dev); | |
340 | release_region(dev->io, 4); | |
1da177e4 LT |
341 | } |
342 | ||
343 | module_init(rtrack2_init); | |
922c78e9 | 344 | module_exit(rtrack2_exit); |