Import 2.1.115pre1
[davej-history.git] / drivers / char / radio-aztech.c
blobe883c712a7b97f1ad9cddfc87b9e32c79068ec51
1 /* aztech.c - Aztech radio card driver for Linux 2.1 by Russell Kroll
3 * Heavily modified to support the new 2.1 radio card interfaces by
4 * Russell Kroll (rkroll@exploits.org)
6 * Based on code by
8 * Quay Ly
9 * Donald Song
10 * Jason Lewis (jlewis@twilight.vtc.vsc.edu)
11 * Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
12 * William McGrath (wmcgrath@twilight.vtc.vsc.edu)
14 * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
15 * along with more information on the card itself.
17 * Notable changes from the original source:
18 * - includes stripped down to the essentials
19 * - for loops used as delays replaced with udelay()
20 * - #defines removed, changed to static values
21 * - tuning structure changed - no more character arrays, other changes
24 #include <linux/module.h> /* Modules */
25 #include <linux/init.h> /* Initdata */
26 #include <linux/ioport.h> /* check_region, request_region */
27 #include <linux/delay.h> /* udelay */
28 #include <asm/io.h> /* outb, outb_p */
29 #include <asm/uaccess.h> /* copy to/from user */
30 #include <linux/videodev.h> /* kernel radio structs */
31 #include <linux/config.h> /* CONFIG_RADIO_AZTECH_PORT */
33 /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
35 #ifndef CONFIG_RADIO_AZTECH_PORT
36 #define CONFIG_RADIO_AZTECH_PORT -1
37 #endif
39 static int io = CONFIG_RADIO_AZTECH_PORT;
40 static int radio_wait_time = 1000;
41 static int users = 0;
43 struct az_device
45 int curvol;
46 unsigned long curfreq;
47 int stereo;
50 static int volconvert(int level)
52 level>>=14; /* Map 16bits down to 2 bit */
53 level&=3;
55 /* convert to card-friendly values */
56 switch (level)
58 case 0:
59 return 0;
60 case 1:
61 return 1;
62 case 2:
63 return 4;
64 case 3:
65 return 5;
67 return 0; /* Quieten gcc */
70 static void send_0_byte (struct az_device *dev)
72 udelay(radio_wait_time);
73 outb_p(2+volconvert(dev->curvol), io);
74 outb_p(64+2+volconvert(dev->curvol), io);
77 static void send_1_byte (struct az_device *dev)
79 udelay (radio_wait_time);
80 outb_p(128+2+volconvert(dev->curvol), io);
81 outb_p(128+64+2+volconvert(dev->curvol), io);
84 static int az_setvol(struct az_device *dev, int vol)
86 outb (volconvert(vol), io);
87 return 0;
90 /* thanks to Michael Dwyer for giving me a dose of clues in
91 * the signal strength department..
93 * This card has a stereo bit - bit 0 set = mono, not set = stereo
94 * It also has a "signal" bit - bit 1 set = bad signal, not set = good
98 static int az_getsigstr(struct az_device *dev)
100 if (inb(io) & 2) /* bit set = no signal present */
101 return 0;
102 return 1; /* signal present */
105 static int az_getstereo(struct az_device *dev)
107 if (inb(io) & 1) /* bit set = mono */
108 return 0;
109 return 1; /* stereo */
112 static int az_setfreq(struct az_device *dev, unsigned long frequency)
114 int i;
116 frequency = (frequency * 100) / 16; /* massage data a bit */
118 frequency += 1070; /* tuning needs 24 data bits */
119 frequency /= 5;
121 send_0_byte (dev); /* 0: LSB of frequency */
123 for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
124 if (frequency & (1 << i))
125 send_1_byte (dev);
126 else
127 send_0_byte (dev);
129 send_0_byte (dev); /* 14: test bit - always 0 */
130 send_0_byte (dev); /* 15: test bit - always 0 */
131 send_0_byte (dev); /* 16: band data 0 - always 0 */
132 if (dev->stereo) /* 17: stereo (1 to enable) */
133 send_1_byte (dev);
134 else
135 send_0_byte (dev);
137 send_1_byte (dev); /* 18: band data 1 - unknown */
138 send_0_byte (dev); /* 19: time base - always 0 */
139 send_0_byte (dev); /* 20: spacing (0 = 25 kHz) */
140 send_1_byte (dev); /* 21: spacing (1 = 25 kHz) */
141 send_0_byte (dev); /* 22: spacing (0 = 25 kHz) */
142 send_1_byte (dev); /* 23: AM/FM (FM = 1, always) */
144 /* latch frequency */
146 udelay (radio_wait_time);
147 outb_p(128+64+volconvert(dev->curvol), io);
149 return 0;
152 static int az_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
154 struct az_device *az=dev->priv;
156 switch(cmd)
158 case VIDIOCGCAP:
160 struct video_capability v;
161 v.type=VID_TYPE_TUNER;
162 v.channels=1;
163 v.audios=1;
164 /* No we don't do pictures */
165 v.maxwidth=0;
166 v.maxheight=0;
167 v.minwidth=0;
168 v.minheight=0;
169 if(copy_to_user(arg,&v,sizeof(v)))
170 return -EFAULT;
171 return 0;
173 case VIDIOCGTUNER:
175 struct video_tuner v;
176 if(copy_from_user(&v, arg,sizeof(v))!=0)
177 return -EFAULT;
178 if(v.tuner) /* Only 1 tuner */
179 return -EINVAL;
180 v.rangelow=(879*16)/10;
181 v.rangehigh=(1078*16)/10;
182 v.flags=0;
183 v.mode=VIDEO_MODE_AUTO;
184 v.signal=0xFFFF*az_getsigstr(az);
185 if(az_getstereo(az))
186 v.flags|=VIDEO_TUNER_STEREO_ON;
187 if(copy_to_user(arg,&v, sizeof(v)))
188 return -EFAULT;
189 return 0;
191 case VIDIOCSTUNER:
193 struct video_tuner v;
194 if(copy_from_user(&v, arg, sizeof(v)))
195 return -EFAULT;
196 if(v.tuner!=0)
197 return -EINVAL;
198 /* Only 1 tuner so no setting needed ! */
199 return 0;
201 case VIDIOCGFREQ:
202 if(copy_to_user(arg, &az->curfreq, sizeof(az->curfreq)))
203 return -EFAULT;
204 return 0;
205 case VIDIOCSFREQ:
206 if(copy_from_user(&az->curfreq, arg,sizeof(az->curfreq)))
207 return -EFAULT;
208 az_setfreq(az, az->curfreq);
209 return 0;
210 case VIDIOCGAUDIO:
212 struct video_audio v;
213 memset(&v,0, sizeof(v));
214 v.flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
215 if(az->stereo)
216 v.mode=VIDEO_SOUND_STEREO;
217 else
218 v.mode=VIDEO_SOUND_MONO;
219 v.volume=az->curvol;
220 strcpy(v.name, "Radio");
221 if(copy_to_user(arg,&v, sizeof(v)))
222 return -EFAULT;
223 return 0;
225 case VIDIOCSAUDIO:
227 struct video_audio v;
228 if(copy_from_user(&v, arg, sizeof(v)))
229 return -EFAULT;
230 if(v.audio)
231 return -EINVAL;
232 az->curvol=v.volume;
234 az->stereo=(v.mode&VIDEO_SOUND_STEREO)?1:0;
235 if(v.flags&VIDEO_AUDIO_MUTE)
236 az_setvol(az,0);
237 else
238 az_setvol(az,az->curvol);
239 return 0;
241 default:
242 return -ENOIOCTLCMD;
246 static int az_open(struct video_device *dev, int flags)
248 if(users)
249 return -EBUSY;
250 users++;
251 MOD_INC_USE_COUNT;
252 return 0;
255 static void az_close(struct video_device *dev)
257 users--;
258 MOD_DEC_USE_COUNT;
261 static struct az_device aztech_unit;
263 static struct video_device aztech_radio=
265 "Aztech radio",
266 VID_TYPE_TUNER,
267 VID_HARDWARE_AZTECH,
268 az_open,
269 az_close,
270 NULL, /* Can't read (no capture ability) */
271 NULL, /* Can't write */
272 az_ioctl,
273 NULL,
274 NULL
277 __initfunc(int aztech_init(struct video_init *v))
279 if (check_region(io, 2))
281 printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
282 return -EBUSY;
285 aztech_radio.priv=&aztech_unit;
287 if(video_register_device(&aztech_radio, VFL_TYPE_RADIO)==-1)
288 return -EINVAL;
290 request_region(io, 2, "aztech");
291 printk(KERN_INFO "Aztech radio card driver v0.40/19980422 rkroll@exploits.org\n");
292 /* mute card - prevents noisy bootups */
293 outb (0, io);
294 return 0;
297 #ifdef MODULE
299 MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
300 MODULE_DESCRIPTION("A driver for the Aztech radio card.");
301 MODULE_PARM(io, "i");
302 MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
304 EXPORT_NO_SYMBOLS;
306 int init_module(void)
308 if(io==-1)
310 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
311 return -EINVAL;
313 return aztech_init(NULL);
316 void cleanup_module(void)
318 video_unregister_device(&aztech_radio);
319 release_region(io,2);
322 #endif