radio-aztech.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:8k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /* radio-aztech.c - Aztech radio card driver for Linux 2.2 
  2.  *
  3.  * Adapted to support the Video for Linux API by 
  4.  * Russell Kroll <rkroll@exploits.org>.  Based on original tuner code by:
  5.  *
  6.  * Quay Ly
  7.  * Donald Song
  8.  * Jason Lewis      (jlewis@twilight.vtc.vsc.edu) 
  9.  * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
  10.  * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
  11.  *
  12.  * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
  13.  * along with more information on the card itself.
  14.  *
  15.  * History:
  16.  * 1999-02-24 Russell Kroll <rkroll@exploits.org>
  17.  * Fine tuning/VIDEO_TUNER_LOW
  18.  *  Range expanded to 87-108 MHz (from 87.9-107.8)
  19.  *
  20.  * Notable changes from the original source:
  21.  * - includes stripped down to the essentials
  22.  * - for loops used as delays replaced with udelay()
  23.  * - #defines removed, changed to static values
  24.  * - tuning structure changed - no more character arrays, other changes
  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_AZTECH_PORT  */
  34. /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
  35. #ifndef CONFIG_RADIO_AZTECH_PORT
  36. #define CONFIG_RADIO_AZTECH_PORT -1
  37. #endif
  38. static int io = CONFIG_RADIO_AZTECH_PORT; 
  39. static int radio_nr = -1;
  40. static int radio_wait_time = 1000;
  41. static int users = 0;
  42. static struct semaphore lock;
  43. struct az_device
  44. {
  45. int curvol;
  46. unsigned long curfreq;
  47. int stereo;
  48. };
  49. static int volconvert(int level)
  50. {
  51. level>>=14;   /* Map 16bits down to 2 bit */
  52.   level&=3;
  53. /* convert to card-friendly values */
  54. switch (level) 
  55. {
  56. case 0: 
  57. return 0;
  58. case 1: 
  59. return 1;
  60. case 2:
  61. return 4;
  62. case 3:
  63. return 5;
  64. }
  65. return 0; /* Quieten gcc */
  66. }
  67. static void send_0_byte (struct az_device *dev)
  68. {
  69. udelay(radio_wait_time);
  70. outb_p(2+volconvert(dev->curvol), io);
  71. outb_p(64+2+volconvert(dev->curvol), io);
  72. }
  73. static void send_1_byte (struct az_device *dev)
  74. {
  75. udelay (radio_wait_time);
  76. outb_p(128+2+volconvert(dev->curvol), io);
  77. outb_p(128+64+2+volconvert(dev->curvol), io);
  78. }
  79. static int az_setvol(struct az_device *dev, int vol)
  80. {
  81. down(&lock);
  82. outb (volconvert(vol), io);
  83. up(&lock);
  84. return 0;
  85. }
  86. /* thanks to Michael Dwyer for giving me a dose of clues in
  87.  * the signal strength department..
  88.  *
  89.  * This card has a stereo bit - bit 0 set = mono, not set = stereo
  90.  * It also has a "signal" bit - bit 1 set = bad signal, not set = good
  91.  *
  92.  */
  93. static int az_getsigstr(struct az_device *dev)
  94. {
  95. if (inb(io) & 2) /* bit set = no signal present */
  96. return 0;
  97. return 1; /* signal present */
  98. }
  99. static int az_getstereo(struct az_device *dev)
  100. {
  101. if (inb(io) & 1)  /* bit set = mono */
  102. return 0;
  103. return 1; /* stereo */
  104. }
  105. static int az_setfreq(struct az_device *dev, unsigned long frequency)
  106. {
  107. int  i;
  108. frequency += 171200; /* Add 10.7 MHz IF */
  109. frequency /= 800; /* Convert to 50 kHz units */
  110. down(&lock);
  111. send_0_byte (dev); /*  0: LSB of frequency       */
  112. for (i = 0; i < 13; i++) /*   : frequency bits (1-13)  */
  113. if (frequency & (1 << i))
  114. send_1_byte (dev);
  115. else
  116. send_0_byte (dev);
  117. send_0_byte (dev); /* 14: test bit - always 0    */
  118. send_0_byte (dev); /* 15: test bit - always 0    */
  119. send_0_byte (dev); /* 16: band data 0 - always 0 */
  120. if (dev->stereo) /* 17: stereo (1 to enable)   */
  121. send_1_byte (dev);
  122. else
  123. send_0_byte (dev);
  124. send_1_byte (dev); /* 18: band data 1 - unknown  */
  125. send_0_byte (dev); /* 19: time base - always 0   */
  126. send_0_byte (dev); /* 20: spacing (0 = 25 kHz)   */
  127. send_1_byte (dev); /* 21: spacing (1 = 25 kHz)   */
  128. send_0_byte (dev); /* 22: spacing (0 = 25 kHz)   */
  129. send_1_byte (dev); /* 23: AM/FM (FM = 1, always) */
  130. /* latch frequency */
  131. udelay (radio_wait_time);
  132. outb_p(128+64+volconvert(dev->curvol), io);
  133. up(&lock);
  134. return 0;
  135. }
  136. static int az_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
  137. {
  138. struct az_device *az=dev->priv;
  139. switch(cmd)
  140. {
  141. case VIDIOCGCAP:
  142. {
  143. struct video_capability v;
  144. v.type=VID_TYPE_TUNER;
  145. v.channels=1;
  146. v.audios=1;
  147. /* No we don't do pictures */
  148. v.maxwidth=0;
  149. v.maxheight=0;
  150. v.minwidth=0;
  151. v.minheight=0;
  152. strcpy(v.name, "Aztech Radio");
  153. if(copy_to_user(arg,&v,sizeof(v)))
  154. return -EFAULT;
  155. return 0;
  156. }
  157. case VIDIOCGTUNER:
  158. {
  159. struct video_tuner v;
  160. if(copy_from_user(&v, arg,sizeof(v))!=0) 
  161. return -EFAULT;
  162. if(v.tuner) /* Only 1 tuner */ 
  163. return -EINVAL;
  164. v.rangelow=(87*16000);
  165. v.rangehigh=(108*16000);
  166. v.flags=VIDEO_TUNER_LOW;
  167. v.mode=VIDEO_MODE_AUTO;
  168. v.signal=0xFFFF*az_getsigstr(az);
  169. if(az_getstereo(az))
  170. v.flags|=VIDEO_TUNER_STEREO_ON;
  171. strcpy(v.name, "FM");
  172. if(copy_to_user(arg,&v, sizeof(v)))
  173. return -EFAULT;
  174. return 0;
  175. }
  176. case VIDIOCSTUNER:
  177. {
  178. struct video_tuner v;
  179. if(copy_from_user(&v, arg, sizeof(v)))
  180. return -EFAULT;
  181. if(v.tuner!=0)
  182. return -EINVAL;
  183. /* Only 1 tuner so no setting needed ! */
  184. return 0;
  185. }
  186. case VIDIOCGFREQ:
  187. if(copy_to_user(arg, &az->curfreq, sizeof(az->curfreq)))
  188. return -EFAULT;
  189. return 0;
  190. case VIDIOCSFREQ:
  191. if(copy_from_user(&az->curfreq, arg,sizeof(az->curfreq)))
  192. return -EFAULT;
  193. az_setfreq(az, az->curfreq);
  194. return 0;
  195. case VIDIOCGAUDIO:
  196. {
  197. struct video_audio v;
  198. memset(&v,0, sizeof(v));
  199. v.flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
  200. if(az->stereo)
  201. v.mode=VIDEO_SOUND_STEREO;
  202. else
  203. v.mode=VIDEO_SOUND_MONO;
  204. v.volume=az->curvol;
  205. v.step=16384;
  206. strcpy(v.name, "Radio");
  207. if(copy_to_user(arg,&v, sizeof(v)))
  208. return -EFAULT;
  209. return 0;
  210. }
  211. case VIDIOCSAUDIO:
  212. {
  213. struct video_audio v;
  214. if(copy_from_user(&v, arg, sizeof(v))) 
  215. return -EFAULT;
  216. if(v.audio) 
  217. return -EINVAL;
  218. az->curvol=v.volume;
  219. az->stereo=(v.mode&VIDEO_SOUND_STEREO)?1:0;
  220. if(v.flags&VIDEO_AUDIO_MUTE) 
  221. az_setvol(az,0);
  222. else
  223. az_setvol(az,az->curvol);
  224. return 0;
  225. }
  226. default:
  227. return -ENOIOCTLCMD;
  228. }
  229. }
  230. static int az_open(struct video_device *dev, int flags)
  231. {
  232. if(users)
  233. return -EBUSY;
  234. users++;
  235. return 0;
  236. }
  237. static void az_close(struct video_device *dev)
  238. {
  239. users--;
  240. }
  241. static struct az_device aztech_unit;
  242. static struct video_device aztech_radio=
  243. {
  244. owner: THIS_MODULE,
  245. name: "Aztech radio",
  246. type: VID_TYPE_TUNER,
  247. hardware: VID_HARDWARE_AZTECH,
  248. open: az_open,
  249. close: az_close,
  250. ioctl: az_ioctl,
  251. };
  252. static int __init aztech_init(void)
  253. {
  254. if(io==-1)
  255. {
  256. printk(KERN_ERR "You must set an I/O address with io=0x???n");
  257. return -EINVAL;
  258. }
  259. if (!request_region(io, 2, "aztech")) 
  260. {
  261. printk(KERN_ERR "aztech: port 0x%x already in usen", io);
  262. return -EBUSY;
  263. }
  264. init_MUTEX(&lock);
  265. aztech_radio.priv=&aztech_unit;
  266. if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1)
  267. {
  268. release_region(io,2);
  269. return -EINVAL;
  270. }
  271. printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.orgn");
  272. /* mute card - prevents noisy bootups */
  273. outb (0, io);
  274. return 0;
  275. }
  276. MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
  277. MODULE_DESCRIPTION("A driver for the Aztech radio card.");
  278. MODULE_LICENSE("GPL");
  279. MODULE_PARM(io, "i");
  280. MODULE_PARM(radio_nr, "i");
  281. MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
  282. EXPORT_NO_SYMBOLS;
  283. static void __exit aztech_cleanup(void)
  284. {
  285. video_unregister_device(&aztech_radio);
  286. release_region(io,2);
  287. }
  288. module_init(aztech_init);
  289. module_exit(aztech_cleanup);