radio-terratec.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:8k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  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. #include <linux/module.h> /* Modules  */
  26. #include <linux/init.h> /* Initdata */
  27. #include <linux/ioport.h> /* check_region, request_region */
  28. #include <linux/delay.h> /* udelay */
  29. #include <asm/io.h> /* outb, outb_p */
  30. #include <asm/uaccess.h> /* copy to/from user */
  31. #include <linux/videodev.h> /* kernel radio structs */
  32. #include <linux/config.h> /* CONFIG_RADIO_TERRATEC_PORT  */
  33. #include <linux/spinlock.h>
  34. #ifndef CONFIG_RADIO_TERRATEC_PORT
  35. #define CONFIG_RADIO_TERRATEC_PORT 0x590
  36. #endif
  37. /**************** this ones are for the terratec *******************/
  38. #define BASEPORT  0x590
  39. #define VOLPORT  0x591
  40. #define WRT_DIS  0x00
  41. #define CLK_OFF 0x00
  42. #define IIC_DATA 0x01
  43. #define IIC_CLK 0x02
  44. #define DATA 0x04
  45. #define CLK_ON  0x08
  46. #define WRT_EN 0x10
  47. /*******************************************************************/
  48. static int io = CONFIG_RADIO_TERRATEC_PORT; 
  49. static int radio_nr = -1;
  50. static int users = 0;
  51. static spinlock_t lock;
  52. struct tt_device
  53. {
  54. int port;
  55. int curvol;
  56. unsigned long curfreq;
  57. int muted;
  58. };
  59. /* local things */
  60. static void cardWriteVol(int volume)
  61. {
  62. int i;
  63. volume = volume+(volume * 32); // change both channels
  64. spin_lock(&lock);
  65. for (i=0;i<8;i++)
  66. {
  67. if (volume & (0x80>>i))
  68. outb(0x80, VOLPORT);
  69. else outb(0x00, VOLPORT);
  70. }
  71. spin_unlock(&lock);
  72. }
  73. static void tt_mute(struct tt_device *dev)
  74. {
  75. dev->muted = 1;
  76. cardWriteVol(0);
  77. }
  78. static int tt_setvol(struct tt_device *dev, int vol)
  79. {
  80. // printk(KERN_ERR "setvol called, vol = %dn", vol);
  81. if(vol == dev->curvol) { /* requested volume = current */
  82. if (dev->muted) { /* user is unmuting the card  */
  83. dev->muted = 0;
  84. cardWriteVol(vol); /* enable card */
  85. }
  86. return 0;
  87. }
  88. if(vol == 0) { /* volume = 0 means mute the card */
  89. cardWriteVol(0); /* "turn off card" by setting vol to 0 */
  90. dev->curvol = vol; /* track the volume state! */
  91. return 0;
  92. }
  93. dev->muted = 0;
  94. cardWriteVol(vol);
  95.  
  96. dev->curvol = vol;
  97. return 0;
  98. }
  99. /* this is the worst part in this driver */
  100. /* many more or less strange things are going on here, but hey, it works :) */
  101. static int tt_setfreq(struct tt_device *dev, unsigned long freq1)
  102. {
  103. int freq;
  104. int i;
  105. int p;
  106. int  temp;
  107. long rest;
  108.      
  109. unsigned char buffer[25]; /* we have to bit shift 25 registers */
  110. freq = freq1/160; /* convert the freq. to a nice to handel value */
  111. for(i=24;i>-1;i--)
  112. buffer[i]=0;
  113. rest = freq*10+10700; /* i once had understood what is going on here */
  114. /* maybe some wise guy (friedhelm?) can comment this stuff */
  115. i=13;
  116. p=10;
  117. temp=102400;
  118. while (rest!=0)
  119. {
  120. if (rest%temp  == rest)
  121. buffer[i] = 0;
  122. else 
  123. {
  124. buffer[i] = 1; 
  125. rest = rest-temp;
  126. }
  127. i--;
  128. p--;
  129. temp = temp/2;
  130.        }
  131. spin_lock(&lock);
  132. for (i=24;i>-1;i--) /* bit shift the values to the radiocard */
  133. {
  134. if (buffer[i]==1) 
  135. {
  136. outb(WRT_EN|DATA, BASEPORT);
  137. outb(WRT_EN|DATA|CLK_ON  , BASEPORT);
  138. outb(WRT_EN|DATA, BASEPORT);
  139. }
  140. else
  141. {
  142. outb(WRT_EN|0x00, BASEPORT);
  143. outb(WRT_EN|0x00|CLK_ON  , BASEPORT);
  144. }
  145. }
  146. outb(0x00, BASEPORT);     
  147. spin_unlock(&lock);
  148.   
  149.    return 0;
  150. }
  151. int tt_getsigstr(struct tt_device *dev) /* TODO */
  152. {
  153. if (inb(io) & 2) /* bit set = no signal present */
  154. return 0;
  155. return 1; /* signal present */
  156. }
  157. /* implement the video4linux api */
  158. static int tt_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
  159. {
  160. struct tt_device *tt=dev->priv;
  161. switch(cmd)
  162. {
  163. case VIDIOCGCAP:
  164. {
  165. struct video_capability v;
  166. v.type=VID_TYPE_TUNER;
  167. v.channels=1;
  168. v.audios=1;
  169. /* No we don't do pictures */
  170. v.maxwidth=0;
  171. v.maxheight=0;
  172. v.minwidth=0;
  173. v.minheight=0;
  174. strcpy(v.name, "ActiveRadio");
  175. if(copy_to_user(arg,&v,sizeof(v)))
  176. return -EFAULT;
  177. return 0;
  178. }
  179. case VIDIOCGTUNER:
  180. {
  181. struct video_tuner v;
  182. if(copy_from_user(&v, arg,sizeof(v))!=0) 
  183. return -EFAULT;
  184. if(v.tuner) /* Only 1 tuner */ 
  185. return -EINVAL;
  186. v.rangelow=(87*16000);
  187. v.rangehigh=(108*16000);
  188. v.flags=VIDEO_TUNER_LOW;
  189. v.mode=VIDEO_MODE_AUTO;
  190. strcpy(v.name, "FM");
  191. v.signal=0xFFFF*tt_getsigstr(tt);
  192. if(copy_to_user(arg,&v, sizeof(v)))
  193. return -EFAULT;
  194. return 0;
  195. }
  196. case VIDIOCSTUNER:
  197. {
  198. struct video_tuner v;
  199. if(copy_from_user(&v, arg, sizeof(v)))
  200. return -EFAULT;
  201. if(v.tuner!=0)
  202. return -EINVAL;
  203. /* Only 1 tuner so no setting needed ! */
  204. return 0;
  205. }
  206. case VIDIOCGFREQ:
  207. if(copy_to_user(arg, &tt->curfreq, sizeof(tt->curfreq)))
  208. return -EFAULT;
  209. return 0;
  210. case VIDIOCSFREQ:
  211. if(copy_from_user(&tt->curfreq, arg,sizeof(tt->curfreq)))
  212. return -EFAULT;
  213. tt_setfreq(tt, tt->curfreq);
  214. return 0;
  215. case VIDIOCGAUDIO:
  216. {
  217. struct video_audio v;
  218. memset(&v,0, sizeof(v));
  219. v.flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
  220. v.volume=tt->curvol * 6554;
  221. v.step=6554;
  222. strcpy(v.name, "Radio");
  223. if(copy_to_user(arg,&v, sizeof(v)))
  224. return -EFAULT;
  225. return 0;
  226. }
  227. case VIDIOCSAUDIO:
  228. {
  229. struct video_audio v;
  230. if(copy_from_user(&v, arg, sizeof(v))) 
  231. return -EFAULT;
  232. if(v.audio) 
  233. return -EINVAL;
  234. if(v.flags&VIDEO_AUDIO_MUTE) 
  235. tt_mute(tt);
  236. else
  237. tt_setvol(tt,v.volume/6554);
  238. return 0;
  239. }
  240. default:
  241. return -ENOIOCTLCMD;
  242. }
  243. }
  244. static int tt_open(struct video_device *dev, int flags)
  245. {
  246. if(users)
  247. return -EBUSY;
  248. users++;
  249. return 0;
  250. }
  251. static void tt_close(struct video_device *dev)
  252. {
  253. users--;
  254. }
  255. static struct tt_device terratec_unit;
  256. static struct video_device terratec_radio=
  257. {
  258. owner: THIS_MODULE,
  259. name: "TerraTec ActiveRadio",
  260. type: VID_TYPE_TUNER,
  261. hardware: VID_HARDWARE_TERRATEC,
  262. open: tt_open,
  263. close: tt_close,
  264. ioctl: tt_ioctl,
  265. };
  266. static int __init terratec_init(void)
  267. {
  268. if(io==-1)
  269. {
  270. printk(KERN_ERR "You must set an I/O address with io=0x???n");
  271. return -EINVAL;
  272. }
  273. if (!request_region(io, 2, "terratec")) 
  274. {
  275. printk(KERN_ERR "TerraTec: port 0x%x already in usen", io);
  276. return -EBUSY;
  277. }
  278. terratec_radio.priv=&terratec_unit;
  279. spin_lock_init(&lock);
  280. if(video_register_device(&terratec_radio, VFL_TYPE_RADIO, radio_nr)==-1)
  281. {
  282. release_region(io,2);
  283. return -EINVAL;
  284. }
  285. printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver.n");
  286.   /* mute card - prevents noisy bootups */
  287. /* this ensures that the volume is all the way down  */
  288. cardWriteVol(0);
  289. terratec_unit.curvol = 0;
  290. return 0;
  291. }
  292. MODULE_AUTHOR("R.OFFERMANNS & others");
  293. MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
  294. MODULE_LICENSE("GPL");
  295. MODULE_PARM(io, "i");
  296. MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
  297. MODULE_PARM(radio_nr, "i");
  298. EXPORT_NO_SYMBOLS;
  299. static void __exit terratec_cleanup_module(void)
  300. {
  301. video_unregister_device(&terratec_radio);
  302. release_region(io,2);
  303. printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver unloaded.n");
  304. }
  305. module_init(terratec_init);
  306. module_exit(terratec_cleanup_module);