Release coccinelle-0.1
[bpt/coccinelle.git] / tests / video1_ver1.c
1 /* Typhoon Radio Card driver for radio support
2 * (c) 1999 Dr. Henrik Seidel <Henrik.Seidel@gmx.de>
3 *
4 * Card manufacturer:
5 * http://194.18.155.92/idc/prod2.idc?nr=50753&lang=e
6 *
7 * Notes on the hardware
8 *
9 * This card has two output sockets, one for speakers and one for line.
10 * The speaker output has volume control, but only in four discrete
11 * steps. The line output has neither volume control nor mute.
12 *
13 * The card has auto-stereo according to its manual, although it all
14 * sounds mono to me (even with the Win/DOS drivers). Maybe it's my
15 * antenna - I really don't know for sure.
16 *
17 * Frequency control is done digitally.
18 *
19 * Volume control is done digitally, but there are only four different
20 * possible values. So you should better always turn the volume up and
21 * use line control. I got the best results by connecting line output
22 * to the sound card microphone input. For such a configuration the
23 * volume control has no effect, since volume control only influences
24 * the speaker output.
25 *
26 * There is no explicit mute/unmute. So I set the radio frequency to a
27 * value where I do expect just noise and turn the speaker volume down.
28 * The frequency change is necessary since the card never seems to be
29 * completely silent.
30 */
31
32 #include <linux/module.h> /* Modules */
33 #include <linux/init.h> /* Initdata */
34 #include <linux/ioport.h> /* check_region, request_region */
35 #include <linux/proc_fs.h> /* radio card status report */
36 #include <asm/io.h> /* outb, outb_p */
37 #include <asm/uaccess.h> /* copy to/from user */
38 #include <linux/videodev.h> /* kernel radio structs */
39 #include <linux/config.h> /* CONFIG_RADIO_TYPHOON_* */
40
41 #define BANNER "Typhoon Radio Card driver v0.1\n"
42
43 #ifndef CONFIG_RADIO_TYPHOON_PORT
44 #define CONFIG_RADIO_TYPHOON_PORT -1
45 #endif
46
47 #ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
48 #define CONFIG_RADIO_TYPHOON_MUTEFREQ 0
49 #endif
50
51 #ifndef CONFIG_PROC_FS
52 #undef CONFIG_RADIO_TYPHOON_PROC_FS
53 #endif
54
55 struct typhoon_device {
56 int users;
57 int iobase;
58 int curvol;
59 int muted;
60 unsigned long curfreq;
61 unsigned long mutefreq;
62 };
63
64 static void typhoon_setvol_generic(struct typhoon_device *dev, int vol);
65 static int typhoon_setfreq_generic(struct typhoon_device *dev,
66 unsigned long frequency);
67 static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency);
68 static void typhoon_mute(struct typhoon_device *dev);
69 static void typhoon_unmute(struct typhoon_device *dev);
70 static int typhoon_setvol(struct typhoon_device *dev, int vol);
71 static int typhoon_ioctl(struct video_device *dev, unsigned int cmd, void *arg);
72 static int typhoon_open(struct video_device *dev, int flags);
73 static void typhoon_close(struct video_device *dev);
74 #ifdef CONFIG_RADIO_TYPHOON_PROC_FS
75 static int typhoon_get_info(char *buf, char **start, off_t offset, int len);
76 #endif
77
78 static void typhoon_setvol_generic(struct typhoon_device *dev, int vol)
79 {
80 vol >>= 14; /* Map 16 bit to 2 bit */
81 vol &= 3;
82 outb_p(vol / 2, dev->iobase); /* Set the volume, high bit. */
83 outb_p(vol % 2, dev->iobase + 2); /* Set the volume, low bit. */
84 }
85
86 static int typhoon_setfreq_generic(struct typhoon_device *dev,
87 unsigned long frequency)
88 {
89 unsigned long outval;
90 unsigned long x;
91
92 /*
93 * The frequency transfer curve is not linear. The best fit I could
94 * get is
95 *
96 * outval = -155 + exp((f + 15.55) * 0.057))
97 *
98 * where frequency f is in MHz. Since we don't have exp in the kernel,
99 * I approximate this function by a third order polynomial.
100 *
101 */
102
103 x = frequency / 160;
104 outval = (x * x + 2500) / 5000;
105 outval = (outval * x + 5000) / 10000;
106 outval -= (10 * x * x + 10433) / 20866;
107 outval += 4 * x - 11505;
108
109 outb_p((outval >> 8) & 0x01, dev->iobase + 4);
110 outb_p(outval >> 9, dev->iobase + 6);
111 outb_p(outval & 0xff, dev->iobase + 8);
112
113 return 0;
114 }
115
116 static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency)
117 {
118 typhoon_setfreq_generic(dev, frequency);
119 dev->curfreq = frequency;
120 return 0;
121 }
122
123 static void typhoon_mute(struct typhoon_device *dev)
124 {
125 if (dev->muted == 1)
126 return;
127 typhoon_setvol_generic(dev, 0);
128 typhoon_setfreq_generic(dev, dev->mutefreq);
129 dev->muted = 1;
130 }
131
132 static void typhoon_unmute(struct typhoon_device *dev)
133 {
134 if (dev->muted == 0)
135 return;
136 typhoon_setfreq_generic(dev, dev->curfreq);
137 typhoon_setvol_generic(dev, dev->curvol);
138 dev->muted = 0;
139 }
140
141 static int typhoon_setvol(struct typhoon_device *dev, int vol)
142 {
143 if (dev->muted && vol != 0) { /* user is unmuting the card */
144 dev->curvol = vol;
145 typhoon_unmute(dev);
146 return 0;
147 }
148 if (vol == dev->curvol) /* requested volume == current */
149 return 0;
150
151 if (vol == 0) { /* volume == 0 means mute the card */
152 typhoon_mute(dev);
153 dev->curvol = vol;
154 return 0;
155 }
156 typhoon_setvol_generic(dev, vol);
157 dev->curvol = vol;
158 return 0;
159 }
160
161
162 static int typhoon_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
163 {
164 struct typhoon_device *typhoon = dev->priv;
165
166 switch (cmd) {
167 case VIDIOCGCAP:
168 {
169 struct video_capability v;
170 v.type = VID_TYPE_TUNER;
171 v.channels = 1;
172 v.audios = 1;
173 /* No we don't do pictures */
174 v.maxwidth = 0;
175 v.maxheight = 0;
176 v.minwidth = 0;
177 v.minheight = 0;
178 strcpy(v.name, "Typhoon Radio");
179 if (copy_to_user(arg, &v, sizeof(v)))
180 return -EFAULT;
181 return 0;
182 }
183 case VIDIOCGTUNER:
184 {
185 struct video_tuner v;
186 if (copy_from_user(&v, arg, sizeof(v)) != 0)
187 return -EFAULT;
188 if (v.tuner) /* Only 1 tuner */
189 return -EINVAL;
190 v.rangelow = 875 * 1600;
191 v.rangehigh = 1080 * 1600;
192 v.flags = VIDEO_TUNER_LOW;
193 v.mode = VIDEO_MODE_AUTO;
194 v.signal = 0xFFFF; /* We can't get the signal strength */
195 strcpy(v.name, "FM");
196 if (copy_to_user(arg, &v, sizeof(v)))
197 return -EFAULT;
198 return 0;
199 }
200 case VIDIOCSTUNER:
201 {
202 struct video_tuner v;
203 if (copy_from_user(&v, arg, sizeof(v)))
204 return -EFAULT;
205 if (v.tuner != 0)
206 return -EINVAL;
207 /* Only 1 tuner so no setting needed ! */
208 return 0;
209 }
210 case VIDIOCGFREQ:
211 if (copy_to_user(arg, &typhoon->curfreq,
212 sizeof(typhoon->curfreq)))
213 return -EFAULT;
214 return 0;
215 case VIDIOCSFREQ:
216 if (copy_from_user(&typhoon->curfreq, arg,
217 sizeof(typhoon->curfreq)))
218 return -EFAULT;
219 typhoon_setfreq(typhoon, typhoon->curfreq);
220 return 0;
221 case VIDIOCGAUDIO:
222 {
223 struct video_audio v;
224 memset(&v, 0, sizeof(v));
225 v.flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME;
226 v.mode |= VIDEO_SOUND_MONO;
227 v.volume = typhoon->curvol;
228 v.step = 1 << 14;
229 strcpy(v.name, "Typhoon Radio");
230 if (copy_to_user(arg, &v, sizeof(v)))
231 return -EFAULT;
232 return 0;
233 }
234 case VIDIOCSAUDIO:
235 {
236 struct video_audio v;
237 if (copy_from_user(&v, arg, sizeof(v)))
238 return -EFAULT;
239 if (v.audio)
240 return -EINVAL;
241
242 if (v.flags & VIDEO_AUDIO_MUTE)
243 typhoon_mute(typhoon);
244 else
245 typhoon_unmute(typhoon);
246
247 if (v.flags & VIDEO_AUDIO_VOLUME)
248 typhoon_setvol(typhoon, v.volume);
249
250 return 0;
251 }
252 default:
253 return -ENOIOCTLCMD;
254 }
255 }
256
257 static int typhoon_open(struct video_device *dev, int flags)
258 {
259 struct typhoon_device *typhoon = dev->priv;
260 if (typhoon->users)
261 return -EBUSY;
262 typhoon->users++;
263 return 0;
264 }
265
266 static void typhoon_close(struct video_device *dev)
267 {
268 struct typhoon_device *typhoon = dev->priv;
269 typhoon->users--;
270 }
271
272 static struct typhoon_device typhoon_unit =
273 {
274 iobase: CONFIG_RADIO_TYPHOON_PORT,
275 curfreq: CONFIG_RADIO_TYPHOON_MUTEFREQ,
276 mutefreq: CONFIG_RADIO_TYPHOON_MUTEFREQ,
277 };
278
279 static struct video_device typhoon_radio =
280 {
281 owner: THIS_MODULE,
282 name: "Typhoon Radio",
283 type: VID_TYPE_TUNER,
284 hardware: VID_HARDWARE_TYPHOON,
285 open: typhoon_open,
286 close: typhoon_close,
287 ioctl: typhoon_ioctl,
288 };
289
290 #ifdef CONFIG_RADIO_TYPHOON_PROC_FS
291
292 static int typhoon_get_info(char *buf, char **start, off_t offset, int len)
293 {
294 char *out = buf;
295
296 #ifdef MODULE
297 #define MODULEPROCSTRING "Driver loaded as a module"
298 #else
299 #define MODULEPROCSTRING "Driver compiled into kernel"
300 #endif
301
302 /* output must be kept under PAGE_SIZE */
303 out += sprintf(out, BANNER);
304 out += sprintf(out, "Load type: " MODULEPROCSTRING "\n\n");
305 out += sprintf(out, "frequency = %lu kHz\n",
306 typhoon_unit.curfreq >> 4);
307 out += sprintf(out, "volume = %d\n", typhoon_unit.curvol);
308 out += sprintf(out, "mute = %s\n", typhoon_unit.muted ?
309 "on" : "off");
310 out += sprintf(out, "iobase = 0x%x\n", typhoon_unit.iobase);
311 out += sprintf(out, "mute frequency = %lu kHz\n",
312 typhoon_unit.mutefreq >> 4);
313 return out - buf;
314 }
315
316 #endif /* CONFIG_RADIO_TYPHOON_PROC_FS */
317
318 MODULE_AUTHOR("Dr. Henrik Seidel");
319 MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
320 MODULE_LICENSE("GPL");
321
322 MODULE_PARM(io, "i");
323 MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
324 MODULE_PARM(mutefreq, "i");
325 MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
326 MODULE_PARM(radio_nr, "i");
327
328 EXPORT_NO_SYMBOLS;
329
330 static int io = -1;
331 static int radio_nr = -1;
332
333 #ifdef MODULE
334 static unsigned long mutefreq = 0;
335 #endif
336
337 static int __init typhoon_init(void)
338 {
339 #ifdef MODULE
340 if (io == -1) {
341 printk(KERN_ERR "radio-typhoon: You must set an I/O address with io=0x316 or io=0x336\n");
342 return -EINVAL;
343 }
344 typhoon_unit.iobase = io;
345
346 if (mutefreq < 87000 || mutefreq > 108500) {
347 printk(KERN_ERR "radio-typhoon: You must set a frequency (in kHz) used when muting the card,\n");
348 printk(KERN_ERR "radio-typhoon: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
349 return -EINVAL;
350 }
351 typhoon_unit.mutefreq = mutefreq;
352 #endif /* MODULE */
353
354 printk(KERN_INFO BANNER);
355 io = typhoon_unit.iobase;
356 if (!request_region(io, 8, "typhoon")) {
357 printk(KERN_ERR "radio-typhoon: port 0x%x already in use\n",
358 typhoon_unit.iobase);
359 return -EBUSY;
360 }
361
362 typhoon_radio.priv = &typhoon_unit;
363 if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO, radio_nr) == -1)
364 {
365 release_region(io, 8);
366 return -EINVAL;
367 }
368 printk(KERN_INFO "radio-typhoon: port 0x%x.\n", typhoon_unit.iobase);
369 printk(KERN_INFO "radio-typhoon: mute frequency is %lu kHz.\n",
370 typhoon_unit.mutefreq);
371 typhoon_unit.mutefreq <<= 4;
372
373 /* mute card - prevents noisy bootups */
374 typhoon_mute(&typhoon_unit);
375
376 #ifdef CONFIG_RADIO_TYPHOON_PROC_FS
377 if (!create_proc_info_entry("driver/radio-typhoon", 0, NULL,
378 typhoon_get_info))
379 printk(KERN_ERR "radio-typhoon: registering /proc/driver/radio-typhoon failed\n");
380 #endif
381
382 return 0;
383 }
384
385 static void __exit typhoon_cleanup_module(void)
386 {
387
388 #ifdef CONFIG_RADIO_TYPHOON_PROC_FS
389 remove_proc_entry("driver/radio-typhoon", NULL);
390 #endif
391
392 video_unregister_device(&typhoon_radio);
393 release_region(io, 8);
394 }
395
396 module_init(typhoon_init);
397 module_exit(typhoon_cleanup_module);
398