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

Linux/Unix编程

开发平台:

Unix_Linux

  1. /* radiotrack (radioreveal) driver for Linux radio support
  2.  * (c) 1997 M. Kirkwood
  3.  * Coverted to new API by Alan Cox <Alan.Cox@linux.org>
  4.  * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
  5.  *
  6.  * History:
  7.  * 1999-02-24 Russell Kroll <rkroll@exploits.org>
  8.  *  Fine tuning/VIDEO_TUNER_LOW
  9.  * Frequency range expanded to start at 87 MHz
  10.  *
  11.  * TODO: Allow for more than one of these foolish entities :-)
  12.  *
  13.  * Notes on the hardware (reverse engineered from other peoples'
  14.  * reverse engineering of AIMS' code :-)
  15.  *
  16.  *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
  17.  *
  18.  *  The signal strength query is unsurprisingly inaccurate.  And it seems
  19.  *  to indicate that (on my card, at least) the frequency setting isn't
  20.  *  too great.  (I have to tune up .025MHz from what the freq should be
  21.  *  to get a report that the thing is tuned.)
  22.  *
  23.  *  Volume control is (ugh) analogue:
  24.  *   out(port, start_increasing_volume);
  25.  *   wait(a_wee_while);
  26.  *   out(port, stop_changing_the_volume);
  27.  *  
  28.  */
  29. #include <linux/module.h> /* Modules  */
  30. #include <linux/init.h> /* Initdata */
  31. #include <linux/ioport.h> /* check_region, request_region */
  32. #include <linux/delay.h> /* udelay */
  33. #include <asm/io.h> /* outb, outb_p */
  34. #include <asm/uaccess.h> /* copy to/from user */
  35. #include <linux/videodev.h> /* kernel radio structs */
  36. #include <linux/config.h> /* CONFIG_RADIO_RTRACK_PORT  */
  37. #include <asm/semaphore.h> /* Lock for the I/O  */
  38. #ifndef CONFIG_RADIO_RTRACK_PORT
  39. #define CONFIG_RADIO_RTRACK_PORT -1
  40. #endif
  41. static int io = CONFIG_RADIO_RTRACK_PORT; 
  42. static int radio_nr = -1;
  43. static int users = 0;
  44. static struct semaphore lock;
  45. struct rt_device
  46. {
  47. int port;
  48. int curvol;
  49. unsigned long curfreq;
  50. int muted;
  51. };
  52. /* local things */
  53. static void sleep_delay(long n)
  54. {
  55. /* Sleep nicely for 'n' uS */
  56. int d=n/(1000000/HZ);
  57. if(!d)
  58. udelay(n);
  59. else
  60. {
  61. /* Yield CPU time */
  62. unsigned long x=jiffies;
  63. while((jiffies-x)<=d)
  64. schedule();
  65. }
  66. }
  67. static void rt_decvol(void)
  68. {
  69. outb(0x58, io); /* volume down + sigstr + on */
  70. sleep_delay(100000);
  71. outb(0xd8, io); /* volume steady + sigstr + on */
  72. }
  73. static void rt_incvol(void)
  74. {
  75. outb(0x98, io); /* volume up + sigstr + on */
  76. sleep_delay(100000);
  77. outb(0xd8, io); /* volume steady + sigstr + on */
  78. }
  79. static void rt_mute(struct rt_device *dev)
  80. {
  81. dev->muted = 1;
  82. down(&lock);
  83. outb(0xd0, io); /* volume steady, off */
  84. up(&lock);
  85. }
  86. static int rt_setvol(struct rt_device *dev, int vol)
  87. {
  88. int i;
  89. down(&lock);
  90. if(vol == dev->curvol) { /* requested volume = current */
  91. if (dev->muted) { /* user is unmuting the card  */
  92. dev->muted = 0;
  93. outb (0xd8, io); /* enable card */
  94. }
  95. up(&lock);
  96. return 0;
  97. }
  98. if(vol == 0) { /* volume = 0 means mute the card */
  99. outb(0x48, io); /* volume down but still "on" */
  100. sleep_delay(2000000); /* make sure it's totally down */
  101. outb(0xd0, io); /* volume steady, off */
  102. dev->curvol = 0; /* track the volume state! */
  103. up(&lock);
  104. return 0;
  105. }
  106. dev->muted = 0;
  107. if(vol > dev->curvol)
  108. for(i = dev->curvol; i < vol; i++) 
  109. rt_incvol();
  110. else
  111. for(i = dev->curvol; i > vol; i--) 
  112. rt_decvol();
  113. dev->curvol = vol;
  114. up(&lock);
  115. return 0;
  116. }
  117. /* the 128+64 on these outb's is to keep the volume stable while tuning 
  118.  * without them, the volume _will_ creep up with each frequency change
  119.  * and bit 4 (+16) is to keep the signal strength meter enabled
  120.  */
  121. void send_0_byte(int port, struct rt_device *dev)
  122. {
  123. if ((dev->curvol == 0) || (dev->muted)) {
  124. outb_p(128+64+16+  1, port);   /* wr-enable + data low */
  125. outb_p(128+64+16+2+1, port);   /* clock */
  126. }
  127. else {
  128. outb_p(128+64+16+8+  1, port);  /* on + wr-enable + data low */
  129. outb_p(128+64+16+8+2+1, port);  /* clock */
  130. }
  131. sleep_delay(1000); 
  132. }
  133. void send_1_byte(int port, struct rt_device *dev)
  134. {
  135. if ((dev->curvol == 0) || (dev->muted)) {
  136. outb_p(128+64+16+4  +1, port);   /* wr-enable+data high */
  137. outb_p(128+64+16+4+2+1, port);   /* clock */
  138. else {
  139. outb_p(128+64+16+8+4  +1, port); /* on+wr-enable+data high */
  140. outb_p(128+64+16+8+4+2+1, port); /* clock */
  141. }
  142. sleep_delay(1000); 
  143. }
  144. static int rt_setfreq(struct rt_device *dev, unsigned long freq)
  145. {
  146. int i;
  147. /* adapted from radio-aztech.c */
  148. /* now uses VIDEO_TUNER_LOW for fine tuning */
  149. freq += 171200; /* Add 10.7 MHz IF  */
  150. freq /= 800; /* Convert to 50 kHz units */
  151. down(&lock); /* Stop other ops interfering */
  152.  
  153. send_0_byte (io, dev); /*  0: LSB of frequency */
  154. for (i = 0; i < 13; i++) /*   : frequency bits (1-13) */
  155. if (freq & (1 << i))
  156. send_1_byte (io, dev);
  157. else
  158. send_0_byte (io, dev);
  159. send_0_byte (io, dev); /* 14: test bit - always 0    */
  160. send_0_byte (io, dev); /* 15: test bit - always 0    */
  161. send_0_byte (io, dev); /* 16: band data 0 - always 0 */
  162. send_0_byte (io, dev); /* 17: band data 1 - always 0 */
  163. send_0_byte (io, dev); /* 18: band data 2 - always 0 */
  164. send_0_byte (io, dev); /* 19: time base - always 0   */
  165. send_0_byte (io, dev); /* 20: spacing (0 = 25 kHz)   */
  166. send_1_byte (io, dev); /* 21: spacing (1 = 25 kHz)   */
  167. send_0_byte (io, dev); /* 22: spacing (0 = 25 kHz)   */
  168. send_1_byte (io, dev); /* 23: AM/FM (FM = 1, always) */
  169. if ((dev->curvol == 0) || (dev->muted))
  170. outb (0xd0, io); /* volume steady + sigstr */
  171. else
  172. outb (0xd8, io); /* volume steady + sigstr + on */
  173. up(&lock);
  174. return 0;
  175. }
  176. static int rt_getsigstr(struct rt_device *dev)
  177. {
  178. if (inb(io) & 2) /* bit set = no signal present */
  179. return 0;
  180. return 1; /* signal present */
  181. }
  182. static int rt_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
  183. {
  184. struct rt_device *rt=dev->priv;
  185. switch(cmd)
  186. {
  187. case VIDIOCGCAP:
  188. {
  189. struct video_capability v;
  190. v.type=VID_TYPE_TUNER;
  191. v.channels=1;
  192. v.audios=1;
  193. /* No we don't do pictures */
  194. v.maxwidth=0;
  195. v.maxheight=0;
  196. v.minwidth=0;
  197. v.minheight=0;
  198. strcpy(v.name, "RadioTrack");
  199. if(copy_to_user(arg,&v,sizeof(v)))
  200. return -EFAULT;
  201. return 0;
  202. }
  203. case VIDIOCGTUNER:
  204. {
  205. struct video_tuner v;
  206. if(copy_from_user(&v, arg,sizeof(v))!=0) 
  207. return -EFAULT;
  208. if(v.tuner) /* Only 1 tuner */ 
  209. return -EINVAL;
  210. v.rangelow=(87*16000);
  211. v.rangehigh=(108*16000);
  212. v.flags=VIDEO_TUNER_LOW;
  213. v.mode=VIDEO_MODE_AUTO;
  214. strcpy(v.name, "FM");
  215. v.signal=0xFFFF*rt_getsigstr(rt);
  216. if(copy_to_user(arg,&v, sizeof(v)))
  217. return -EFAULT;
  218. return 0;
  219. }
  220. case VIDIOCSTUNER:
  221. {
  222. struct video_tuner v;
  223. if(copy_from_user(&v, arg, sizeof(v)))
  224. return -EFAULT;
  225. if(v.tuner!=0)
  226. return -EINVAL;
  227. /* Only 1 tuner so no setting needed ! */
  228. return 0;
  229. }
  230. case VIDIOCGFREQ:
  231. if(copy_to_user(arg, &rt->curfreq, sizeof(rt->curfreq)))
  232. return -EFAULT;
  233. return 0;
  234. case VIDIOCSFREQ:
  235. if(copy_from_user(&rt->curfreq, arg,sizeof(rt->curfreq)))
  236. return -EFAULT;
  237. rt_setfreq(rt, rt->curfreq);
  238. return 0;
  239. case VIDIOCGAUDIO:
  240. {
  241. struct video_audio v;
  242. memset(&v,0, sizeof(v));
  243. v.flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
  244. v.volume=rt->curvol * 6554;
  245. v.step=6554;
  246. strcpy(v.name, "Radio");
  247. if(copy_to_user(arg,&v, sizeof(v)))
  248. return -EFAULT;
  249. return 0;
  250. }
  251. case VIDIOCSAUDIO:
  252. {
  253. struct video_audio v;
  254. if(copy_from_user(&v, arg, sizeof(v))) 
  255. return -EFAULT;
  256. if(v.audio) 
  257. return -EINVAL;
  258. if(v.flags&VIDEO_AUDIO_MUTE) 
  259. rt_mute(rt);
  260. else
  261. rt_setvol(rt,v.volume/6554);
  262. return 0;
  263. }
  264. default:
  265. return -ENOIOCTLCMD;
  266. }
  267. }
  268. static int rt_open(struct video_device *dev, int flags)
  269. {
  270. if(users)
  271. return -EBUSY;
  272. users++;
  273. return 0;
  274. }
  275. static void rt_close(struct video_device *dev)
  276. {
  277. users--;
  278. }
  279. static struct rt_device rtrack_unit;
  280. static struct video_device rtrack_radio=
  281. {
  282. owner: THIS_MODULE,
  283. name: "RadioTrack radio",
  284. type: VID_TYPE_TUNER,
  285. hardware: VID_HARDWARE_RTRACK,
  286. open: rt_open,
  287. close: rt_close,
  288. ioctl: rt_ioctl,
  289. };
  290. static int __init rtrack_init(void)
  291. {
  292. if(io==-1)
  293. {
  294. printk(KERN_ERR "You must set an I/O address with io=0x???n");
  295. return -EINVAL;
  296. }
  297. if (!request_region(io, 2, "rtrack")) 
  298. {
  299. printk(KERN_ERR "rtrack: port 0x%x already in usen", io);
  300. return -EBUSY;
  301. }
  302. rtrack_radio.priv=&rtrack_unit;
  303. if(video_register_device(&rtrack_radio, VFL_TYPE_RADIO, radio_nr)==-1)
  304. {
  305. release_region(io, 2);
  306. return -EINVAL;
  307. }
  308. printk(KERN_INFO "AIMSlab RadioTrack/RadioReveal card driver.n");
  309. /* Set up the I/O locking */
  310. init_MUTEX(&lock);
  311.   /* mute card - prevents noisy bootups */
  312. /* this ensures that the volume is all the way down  */
  313. outb(0x48, io); /* volume down but still "on" */
  314. sleep_delay(2000000); /* make sure it's totally down */
  315. outb(0xc0, io); /* steady volume, mute card */
  316. rtrack_unit.curvol = 0;
  317. return 0;
  318. }
  319. MODULE_AUTHOR("M.Kirkwood");
  320. MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
  321. MODULE_LICENSE("GPL");
  322. MODULE_PARM(io, "i");
  323. MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
  324. MODULE_PARM(radio_nr, "i");
  325. EXPORT_NO_SYMBOLS;
  326. static void __exit cleanup_rtrack_module(void)
  327. {
  328. video_unregister_device(&rtrack_radio);
  329. release_region(io,2);
  330. }
  331. module_init(rtrack_init);
  332. module_exit(cleanup_rtrack_module);