Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 /* Terratec ActiveRadio ISA Standalone card driver for Linux radio support
  2  * (c) 1999 R. Offermanns (rolf@offermanns.de)
  3  * based on the aimslab radio driver from M. Kirkwood
  4  * many thanks to Michael Becker and Friedhelm Birth (from TerraTec)
  5  * 
  6  *
  7  * History:
  8  * 1999-05-21   First preview release
  9  * 
 10  *  Notes on the hardware:
 11  *  There are two "main" chips on the card:
 12  *  - Philips OM5610 (http://www-us.semiconductors.philips.com/acrobat/datasheets/OM5610_2.pdf)
 13  *  - Philips SAA6588 (http://www-us.semiconductors.philips.com/acrobat/datasheets/SAA6588_1.pdf)
 14  *  (you can get the datasheet at the above links)
 15  *
 16  *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
 17  *  Volume Control is done digitally
 18  *
 19  *  there is a I2C controlled RDS decoder (SAA6588)  onboard, which i would like to support someday
 20  *  (as soon i have understand how to get started :)
 21  *  If you can help me out with that, please contact me!!
 22  *
 23  *  
 24  */
 25 
 26 #include <linux/module.h>       /* Modules                      */
 27 #include <linux/init.h>         /* Initdata                     */
 28 #include <linux/ioport.h>       /* check_region, request_region */
 29 #include <linux/delay.h>        /* udelay                       */
 30 #include <asm/io.h>             /* outb, outb_p                 */
 31 #include <asm/uaccess.h>        /* copy to/from user            */
 32 #include <linux/videodev.h>     /* kernel radio structs         */
 33 #include <linux/config.h>       /* CONFIG_RADIO_TERRATEC_PORT   */
 34 #include <linux/spinlock.h>
 35 
 36 #ifndef CONFIG_RADIO_TERRATEC_PORT
 37 #define CONFIG_RADIO_TERRATEC_PORT 0x590
 38 #endif
 39 
 40 /**************** this ones are for the terratec *******************/
 41 #define BASEPORT        0x590
 42 #define VOLPORT         0x591
 43 #define WRT_DIS         0x00
 44 #define CLK_OFF         0x00
 45 #define IIC_DATA        0x01
 46 #define IIC_CLK         0x02
 47 #define DATA            0x04
 48 #define CLK_ON          0x08
 49 #define WRT_EN          0x10
 50 /*******************************************************************/
 51 
 52 static int io = CONFIG_RADIO_TERRATEC_PORT; 
 53 static int radio_nr = -1;
 54 static spinlock_t lock;
 55 
 56 struct tt_device
 57 {
 58         int port;
 59         int curvol;
 60         unsigned long curfreq;
 61         int muted;
 62 };
 63 
 64 
 65 /* local things */
 66 
 67 static void cardWriteVol(int volume)
 68 {
 69         int i;
 70         volume = volume+(volume * 32); // change both channels
 71         spin_lock(&lock);
 72         for (i=0;i<8;i++)
 73         {
 74                 if (volume & (0x80>>i))
 75                         outb(0x80, VOLPORT);
 76                 else outb(0x00, VOLPORT);
 77         }
 78         spin_unlock(&lock);
 79 }
 80 
 81 
 82 
 83 static void tt_mute(struct tt_device *dev)
 84 {
 85         dev->muted = 1;
 86         cardWriteVol(0);
 87 }
 88 
 89 static int tt_setvol(struct tt_device *dev, int vol)
 90 {
 91         
 92 //      printk(KERN_ERR "setvol called, vol = %d\n", vol);
 93 
 94         if(vol == dev->curvol) {        /* requested volume = current */
 95                 if (dev->muted) {       /* user is unmuting the card  */
 96                         dev->muted = 0;
 97                         cardWriteVol(vol);      /* enable card */
 98                 }       
 99         
100                 return 0;
101         }
102 
103         if(vol == 0) {                  /* volume = 0 means mute the card */
104                 cardWriteVol(0);        /* "turn off card" by setting vol to 0 */
105                 dev->curvol = vol;      /* track the volume state!      */
106                 return 0;
107         }
108 
109         dev->muted = 0;
110         
111         cardWriteVol(vol);
112          
113         dev->curvol = vol;
114 
115         return 0;
116 
117 }
118 
119 
120 /* this is the worst part in this driver */
121 /* many more or less strange things are going on here, but hey, it works :) */
122 
123 static int tt_setfreq(struct tt_device *dev, unsigned long freq1)
124 {       
125         int freq;
126         int i;
127         int p;
128         int  temp;
129         long rest;
130      
131         unsigned char buffer[25];               /* we have to bit shift 25 registers */
132         freq = freq1/160;                       /* convert the freq. to a nice to handle value */
133         for(i=24;i>-1;i--)
134                 buffer[i]=0;
135 
136         rest = freq*10+10700;           /* i once had understood what is going on here */
137                                         /* maybe some wise guy (friedhelm?) can comment this stuff */
138         i=13;
139         p=10;
140         temp=102400;
141         while (rest!=0)
142         {
143                 if (rest%temp  == rest)
144                         buffer[i] = 0;
145                 else 
146                 {
147                         buffer[i] = 1; 
148                         rest = rest-temp;
149                 }
150                 i--;
151                 p--;
152                 temp = temp/2;
153        }
154 
155         spin_lock(&lock);
156         
157         for (i=24;i>-1;i--)                     /* bit shift the values to the radiocard */
158         {
159                 if (buffer[i]==1) 
160                 {
161                         outb(WRT_EN|DATA, BASEPORT);
162                         outb(WRT_EN|DATA|CLK_ON  , BASEPORT);
163                         outb(WRT_EN|DATA, BASEPORT);
164                 }
165                 else
166                 {
167                         outb(WRT_EN|0x00, BASEPORT);
168                         outb(WRT_EN|0x00|CLK_ON  , BASEPORT);
169                 }
170         }
171         outb(0x00, BASEPORT);     
172         
173         spin_unlock(&lock);
174   
175         return 0;
176 }
177 
178 static int tt_getsigstr(struct tt_device *dev)          /* TODO */
179 {
180         if (inb(io) & 2)        /* bit set = no signal present  */
181                 return 0;
182         return 1;               /* signal present               */
183 }
184 
185 
186 /* implement the video4linux api */
187 
188 static int tt_do_ioctl(struct inode *inode, struct file *file,
189                        unsigned int cmd, void *arg)
190 {
191         struct video_device *dev = video_devdata(file);
192         struct tt_device *tt=dev->priv;
193         
194         switch(cmd)
195         {
196                 case VIDIOCGCAP:
197                 {
198                         struct video_capability *v = arg;
199                         memset(v,0,sizeof(*v));
200                         v->type=VID_TYPE_TUNER;
201                         v->channels=1;
202                         v->audios=1;
203                         strcpy(v->name, "ActiveRadio");
204                         return 0;
205                 }
206                 case VIDIOCGTUNER:
207                 {
208                         struct video_tuner *v = arg;
209                         if(v->tuner)    /* Only 1 tuner */ 
210                                 return -EINVAL;
211                         v->rangelow=(87*16000);
212                         v->rangehigh=(108*16000);
213                         v->flags=VIDEO_TUNER_LOW;
214                         v->mode=VIDEO_MODE_AUTO;
215                         strcpy(v->name, "FM");
216                         v->signal=0xFFFF*tt_getsigstr(tt);
217                         return 0;
218                 }
219                 case VIDIOCSTUNER:
220                 {
221                         struct video_tuner *v = arg;
222                         if(v->tuner!=0)
223                                 return -EINVAL;
224                         /* Only 1 tuner so no setting needed ! */
225                         return 0;
226                 }
227                 case VIDIOCGFREQ:
228                 {
229                         unsigned long *freq = arg;
230                         *freq = tt->curfreq;
231                         return 0;
232                 }
233                 case VIDIOCSFREQ:
234                 {
235                         unsigned long *freq = arg;
236                         tt->curfreq = *freq;
237                         tt_setfreq(tt, tt->curfreq);
238                         return 0;
239                 }
240                 case VIDIOCGAUDIO:
241                 {       
242                         struct video_audio *v = arg;
243                         memset(v,0, sizeof(*v));
244                         v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
245                         v->volume=tt->curvol * 6554;
246                         v->step=6554;
247                         strcpy(v->name, "Radio");
248                         return 0;                       
249                 }
250                 case VIDIOCSAUDIO:
251                 {
252                         struct video_audio *v = arg;
253                         if(v->audio) 
254                                 return -EINVAL;
255                         if(v->flags&VIDEO_AUDIO_MUTE) 
256                                 tt_mute(tt);
257                         else
258                                 tt_setvol(tt,v->volume/6554);
259                         return 0;
260                 }
261                 default:
262                         return -ENOIOCTLCMD;
263         }
264 }
265 
266 static int tt_ioctl(struct inode *inode, struct file *file,
267                     unsigned int cmd, unsigned long arg)
268 {
269         return video_usercopy(inode, file, cmd, arg, tt_do_ioctl);
270 }
271 
272 static struct tt_device terratec_unit;
273 
274 static struct file_operations terratec_fops = {
275         .owner          = THIS_MODULE,
276         .open           = video_exclusive_open,
277         .release        = video_exclusive_release,
278         .ioctl          = tt_ioctl,
279         .llseek         = no_llseek,
280 };
281 
282 static struct video_device terratec_radio=
283 {
284         .owner          = THIS_MODULE,
285         .name           = "TerraTec ActiveRadio",
286         .type           = VID_TYPE_TUNER,
287         .hardware       = VID_HARDWARE_TERRATEC,
288         .fops           = &terratec_fops,
289 };
290 
291 static int __init terratec_init(void)
292 {
293         if(io==-1)
294         {
295                 printk(KERN_ERR "You must set an I/O address with io=0x???\n");
296                 return -EINVAL;
297         }
298         if (!request_region(io, 2, "terratec")) 
299         {
300                 printk(KERN_ERR "TerraTec: port 0x%x already in use\n", io);
301                 return -EBUSY;
302         }
303 
304         terratec_radio.priv=&terratec_unit;
305         
306         spin_lock_init(&lock);
307         
308         if(video_register_device(&terratec_radio, VFL_TYPE_RADIO, radio_nr)==-1)
309         {
310                 release_region(io,2);
311                 return -EINVAL;
312         }
313                 
314         printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver.\n");
315 
316         /* mute card - prevents noisy bootups */
317 
318         /* this ensures that the volume is all the way down  */
319         cardWriteVol(0);
320         terratec_unit.curvol = 0;
321 
322         return 0;
323 }
324 
325 MODULE_AUTHOR("R.OFFERMANNS & others");
326 MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
327 MODULE_LICENSE("GPL");
328 module_param(io, int, 0);
329 MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
330 module_param(radio_nr, int, 0);
331 
332 static void __exit terratec_cleanup_module(void)
333 {
334         video_unregister_device(&terratec_radio);
335         release_region(io,2);
336         printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver unloaded.\n");     
337 }
338 
339 module_init(terratec_init);
340 module_exit(terratec_cleanup_module);
341 
342 
  This page was automatically generated by the LXR engine.