File: /usr/src/linux/drivers/media/radio/radio-gemtek.c

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      *    Coverted 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>	/* check_region, 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 <linux/config.h>	/* CONFIG_RADIO_GEMTEK_PORT 	*/
26     #include <linux/spinlock.h>
27     
28     #ifndef CONFIG_RADIO_GEMTEK_PORT
29     #define CONFIG_RADIO_GEMTEK_PORT -1
30     #endif
31     
32     static int io = CONFIG_RADIO_GEMTEK_PORT; 
33     static int radio_nr = -1;
34     static int users = 0;
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     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_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
143     {
144     	struct gemtek_device *rt=dev->priv;
145     
146     	switch(cmd)
147     	{
148     		case VIDIOCGCAP:
149     		{
150     			struct video_capability v;
151     			v.type=VID_TYPE_TUNER;
152     			v.channels=1;
153     			v.audios=1;
154     			/* No we don't do pictures */
155     			v.maxwidth=0;
156     			v.maxheight=0;
157     			v.minwidth=0;
158     			v.minheight=0;
159     			strcpy(v.name, "GemTek");
160     			if(copy_to_user(arg,&v,sizeof(v)))
161     				return -EFAULT;
162     			return 0;
163     		}
164     		case VIDIOCGTUNER:
165     		{
166     			struct video_tuner v;
167     			if(copy_from_user(&v, arg,sizeof(v))!=0) 
168     				return -EFAULT;
169     			if(v.tuner)	/* Only 1 tuner */ 
170     				return -EINVAL;
171     			v.rangelow=87*16000;
172     			v.rangehigh=108*16000;
173     			v.flags=VIDEO_TUNER_LOW;
174     			v.mode=VIDEO_MODE_AUTO;
175     			v.signal=0xFFFF*gemtek_getsigstr(rt);
176     			strcpy(v.name, "FM");
177     			if(copy_to_user(arg,&v, sizeof(v)))
178     				return -EFAULT;
179     			return 0;
180     		}
181     		case VIDIOCSTUNER:
182     		{
183     			struct video_tuner v;
184     			if(copy_from_user(&v, arg, sizeof(v)))
185     				return -EFAULT;
186     			if(v.tuner!=0)
187     				return -EINVAL;
188     			/* Only 1 tuner so no setting needed ! */
189     			return 0;
190     		}
191     		case VIDIOCGFREQ:
192     			if(copy_to_user(arg, &rt->curfreq, sizeof(rt->curfreq)))
193     				return -EFAULT;
194     			return 0;
195     		case VIDIOCSFREQ:
196     			if(copy_from_user(&rt->curfreq, arg,sizeof(rt->curfreq)))
197     				return -EFAULT;
198     		/* needs to be called twice in order for getsigstr to work */
199     			gemtek_setfreq(rt, rt->curfreq);
200     			gemtek_setfreq(rt, rt->curfreq);
201     			return 0;
202     		case VIDIOCGAUDIO:
203     		{	
204     			struct video_audio v;
205     			memset(&v,0, sizeof(v));
206     			v.flags|=VIDEO_AUDIO_MUTABLE;
207     			v.volume=1;
208     			v.step=65535;
209     			strcpy(v.name, "Radio");
210     			if(copy_to_user(arg,&v, sizeof(v)))
211     				return -EFAULT;
212     			return 0;			
213     		}
214     		case VIDIOCSAUDIO:
215     		{
216     			struct video_audio v;
217     			if(copy_from_user(&v, arg, sizeof(v))) 
218     				return -EFAULT;	
219     			if(v.audio) 
220     				return -EINVAL;
221     
222     			if(v.flags&VIDEO_AUDIO_MUTE) 
223     				gemtek_mute(rt);
224     			else
225     			        gemtek_unmute(rt);
226     
227     			return 0;
228     		}
229     		default:
230     			return -ENOIOCTLCMD;
231     	}
232     }
233     
234     static int gemtek_open(struct video_device *dev, int flags)
235     {
236     	if(users)
237     		return -EBUSY;
238     	users++;
239     	return 0;
240     }
241     
242     static void gemtek_close(struct video_device *dev)
243     {
244     	users--;
245     }
246     
247     static struct gemtek_device gemtek_unit;
248     
249     static struct video_device gemtek_radio=
250     {
251     	owner:		THIS_MODULE,
252     	name:		"GemTek radio",
253     	type:		VID_TYPE_TUNER,
254     	hardware:	VID_HARDWARE_GEMTEK,
255     	open:		gemtek_open,
256     	close:		gemtek_close,
257     	ioctl:		gemtek_ioctl,
258     };
259     
260     static int __init gemtek_init(void)
261     {
262     	if(io==-1)
263     	{
264     		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");
265     		return -EINVAL;
266     	}
267     
268     	if (!request_region(io, 4, "gemtek")) 
269     	{
270     		printk(KERN_ERR "gemtek: port 0x%x already in use\n", io);
271     		return -EBUSY;
272     	}
273     
274     	gemtek_radio.priv=&gemtek_unit;
275     	
276     	if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1)
277     	{
278     		release_region(io, 4);
279     		return -EINVAL;
280     	}
281     	printk(KERN_INFO "GemTek Radio Card driver.\n");
282     
283     	spin_lock_init(&lock);
284      	/* mute card - prevents noisy bootups */
285     	outb(0x10, io);
286     	udelay(5);
287     	gemtek_unit.muted = 1;
288     
289     	/* this is _maybe_ unnecessary */
290     	outb(0x01, io);
291     
292     	return 0;
293     }
294     
295     MODULE_AUTHOR("Jonas Munsin");
296     MODULE_DESCRIPTION("A driver for the GemTek Radio Card");
297     MODULE_PARM(io, "i");
298     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)).");
299     MODULE_PARM(radio_nr, "i");
300     
301     EXPORT_NO_SYMBOLS;
302     
303     static void __exit gemtek_cleanup(void)
304     {
305     	video_unregister_device(&gemtek_radio);
306     	release_region(io,4);
307     }
308     
309     module_init(gemtek_init);
310     module_exit(gemtek_cleanup);
311     
312     /*
313       Local variables:
314       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"
315       End:
316     */
317