xref: /linux/drivers/media/radio/radio-gemtek.c (revision 5e8d780d745c1619aba81fe7166c5a4b5cad2b84)
1 /* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi>
2  *
3  * GemTek hasn't released any specs on the card, so the protocol had to
4  * be reverse engineered with dosemu.
5  *
6  * Besides the protocol changes, this is mostly a copy of:
7  *
8  *    RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
9  *
10  *    Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
11  *    Converted to new API by Alan Cox <Alan.Cox@linux.org>
12  *    Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
13  *
14  * TODO: Allow for more than one of these foolish entities :-)
15  *
16  */
17 
18 #include <linux/module.h>	/* Modules 			*/
19 #include <linux/init.h>		/* Initdata			*/
20 #include <linux/ioport.h>	/* request_region		*/
21 #include <linux/delay.h>	/* udelay			*/
22 #include <asm/io.h>		/* outb, outb_p			*/
23 #include <asm/uaccess.h>	/* copy to/from user		*/
24 #include <linux/videodev.h>	/* kernel radio structs		*/
25 #include <media/v4l2-common.h>
26 #include <linux/config.h>	/* CONFIG_RADIO_GEMTEK_PORT 	*/
27 #include <linux/spinlock.h>
28 
29 #ifndef CONFIG_RADIO_GEMTEK_PORT
30 #define CONFIG_RADIO_GEMTEK_PORT -1
31 #endif
32 
33 static int io = CONFIG_RADIO_GEMTEK_PORT;
34 static int radio_nr = -1;
35 static spinlock_t lock;
36 
37 struct gemtek_device
38 {
39 	int port;
40 	unsigned long curfreq;
41 	int muted;
42 };
43 
44 
45 /* local things */
46 
47 /* the correct way to mute the gemtek may be to write the last written
48  * frequency || 0x10, but just writing 0x10 once seems to do it as well
49  */
50 static void gemtek_mute(struct gemtek_device *dev)
51 {
52 	if(dev->muted)
53 		return;
54 	spin_lock(&lock);
55 	outb(0x10, io);
56 	spin_unlock(&lock);
57 	dev->muted = 1;
58 }
59 
60 static void gemtek_unmute(struct gemtek_device *dev)
61 {
62 	if(dev->muted == 0)
63 		return;
64 	spin_lock(&lock);
65 	outb(0x20, io);
66 	spin_unlock(&lock);
67 	dev->muted = 0;
68 }
69 
70 static void zero(void)
71 {
72 	outb_p(0x04, io);
73 	udelay(5);
74 	outb_p(0x05, io);
75 	udelay(5);
76 }
77 
78 static void one(void)
79 {
80 	outb_p(0x06, io);
81 	udelay(5);
82 	outb_p(0x07, io);
83 	udelay(5);
84 }
85 
86 static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq)
87 {
88 	int i;
89 
90 /*        freq = 78.25*((float)freq/16000.0 + 10.52); */
91 
92 	freq /= 16;
93 	freq += 10520;
94 	freq *= 7825;
95 	freq /= 100000;
96 
97 	spin_lock(&lock);
98 
99 	/* 2 start bits */
100 	outb_p(0x03, io);
101 	udelay(5);
102 	outb_p(0x07, io);
103 	udelay(5);
104 
105 	/* 28 frequency bits (lsb first) */
106 	for (i = 0; i < 14; i++)
107 		if (freq & (1 << i))
108 			one();
109 		else
110 			zero();
111 	/* 36 unknown bits */
112 	for (i = 0; i < 11; i++)
113 		zero();
114 	one();
115 	for (i = 0; i < 4; i++)
116 		zero();
117 	one();
118 	zero();
119 
120 	/* 2 end bits */
121 	outb_p(0x03, io);
122 	udelay(5);
123 	outb_p(0x07, io);
124 	udelay(5);
125 
126 	spin_unlock(&lock);
127 
128 	return 0;
129 }
130 
131 static int gemtek_getsigstr(struct gemtek_device *dev)
132 {
133 	spin_lock(&lock);
134 	inb(io);
135 	udelay(5);
136 	spin_unlock(&lock);
137 	if (inb(io) & 8)		/* bit set = no signal present */
138 		return 0;
139 	return 1;		/* signal present */
140 }
141 
142 static int gemtek_do_ioctl(struct inode *inode, struct file *file,
143 			   unsigned int cmd, void *arg)
144 {
145 	struct video_device *dev = video_devdata(file);
146 	struct gemtek_device *rt=dev->priv;
147 
148 	switch(cmd)
149 	{
150 		case VIDIOCGCAP:
151 		{
152 			struct video_capability *v = arg;
153 			memset(v,0,sizeof(*v));
154 			v->type=VID_TYPE_TUNER;
155 			v->channels=1;
156 			v->audios=1;
157 			strcpy(v->name, "GemTek");
158 			return 0;
159 		}
160 		case VIDIOCGTUNER:
161 		{
162 			struct video_tuner *v = arg;
163 			if(v->tuner)	/* Only 1 tuner */
164 				return -EINVAL;
165 			v->rangelow=87*16000;
166 			v->rangehigh=108*16000;
167 			v->flags=VIDEO_TUNER_LOW;
168 			v->mode=VIDEO_MODE_AUTO;
169 			v->signal=0xFFFF*gemtek_getsigstr(rt);
170 			strcpy(v->name, "FM");
171 			return 0;
172 		}
173 		case VIDIOCSTUNER:
174 		{
175 			struct video_tuner *v = arg;
176 			if(v->tuner!=0)
177 				return -EINVAL;
178 			/* Only 1 tuner so no setting needed ! */
179 			return 0;
180 		}
181 		case VIDIOCGFREQ:
182 		{
183 			unsigned long *freq = arg;
184 			*freq = rt->curfreq;
185 			return 0;
186 		}
187 		case VIDIOCSFREQ:
188 		{
189 			unsigned long *freq = arg;
190 			rt->curfreq = *freq;
191 			/* needs to be called twice in order for getsigstr to work */
192 			gemtek_setfreq(rt, rt->curfreq);
193 			gemtek_setfreq(rt, rt->curfreq);
194 			return 0;
195 		}
196 		case VIDIOCGAUDIO:
197 		{
198 			struct video_audio *v = arg;
199 			memset(v,0, sizeof(*v));
200 			v->flags|=VIDEO_AUDIO_MUTABLE;
201 			v->volume=1;
202 			v->step=65535;
203 			strcpy(v->name, "Radio");
204 			return 0;
205 		}
206 		case VIDIOCSAUDIO:
207 		{
208 			struct video_audio *v = arg;
209 			if(v->audio)
210 				return -EINVAL;
211 
212 			if(v->flags&VIDEO_AUDIO_MUTE)
213 				gemtek_mute(rt);
214 			else
215 				gemtek_unmute(rt);
216 
217 			return 0;
218 		}
219 		default:
220 			return -ENOIOCTLCMD;
221 	}
222 }
223 
224 static int gemtek_ioctl(struct inode *inode, struct file *file,
225 			unsigned int cmd, unsigned long arg)
226 {
227 	return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl);
228 }
229 
230 static struct gemtek_device gemtek_unit;
231 
232 static struct file_operations gemtek_fops = {
233 	.owner		= THIS_MODULE,
234 	.open           = video_exclusive_open,
235 	.release        = video_exclusive_release,
236 	.ioctl		= gemtek_ioctl,
237 	.compat_ioctl	= v4l_compat_ioctl32,
238 	.llseek         = no_llseek,
239 };
240 
241 static struct video_device gemtek_radio=
242 {
243 	.owner		= THIS_MODULE,
244 	.name		= "GemTek radio",
245 	.type		= VID_TYPE_TUNER,
246 	.hardware	= VID_HARDWARE_GEMTEK,
247 	.fops           = &gemtek_fops,
248 };
249 
250 static int __init gemtek_init(void)
251 {
252 	if(io==-1)
253 	{
254 		printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (io=0x020c or io=0x248 for the combined sound/radiocard)\n");
255 		return -EINVAL;
256 	}
257 
258 	if (!request_region(io, 4, "gemtek"))
259 	{
260 		printk(KERN_ERR "gemtek: port 0x%x already in use\n", io);
261 		return -EBUSY;
262 	}
263 
264 	gemtek_radio.priv=&gemtek_unit;
265 
266 	if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1)
267 	{
268 		release_region(io, 4);
269 		return -EINVAL;
270 	}
271 	printk(KERN_INFO "GemTek Radio Card driver.\n");
272 
273 	spin_lock_init(&lock);
274 
275 	/* this is _maybe_ unnecessary */
276 	outb(0x01, io);
277 
278 	/* mute card - prevents noisy bootups */
279 	gemtek_unit.muted = 0;
280 	gemtek_mute(&gemtek_unit);
281 
282 	return 0;
283 }
284 
285 MODULE_AUTHOR("Jonas Munsin");
286 MODULE_DESCRIPTION("A driver for the GemTek Radio Card");
287 MODULE_LICENSE("GPL");
288 
289 module_param(io, int, 0);
290 MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (0x20c or 0x248 have been reported to work for the combined sound/radiocard)).");
291 module_param(radio_nr, int, 0);
292 
293 static void __exit gemtek_cleanup(void)
294 {
295 	video_unregister_device(&gemtek_radio);
296 	release_region(io,4);
297 }
298 
299 module_init(gemtek_init);
300 module_exit(gemtek_cleanup);
301 
302 /*
303   Local variables:
304   compile-command: "gcc -c -DMODVERSIONS -D__KERNEL__ -DMODULE -O6 -Wall -Wstrict-prototypes -I /home/blp/tmp/linux-2.1.111-rtrack/include radio-rtrack2.c"
305   End:
306 */
307