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

Linux/Unix编程

开发平台:

Unix_Linux

  1. /* SF16FMI radio driver for Linux radio support
  2.  * heavily based on rtrack driver...
  3.  * (c) 1997 M. Kirkwood
  4.  * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
  5.  *
  6.  * Fitted to new interface by Alan Cox <alan.cox@linux.org>
  7.  * Made working and cleaned up functions <mikael.hedin@irf.se>
  8.  * Support for ISAPnP by Ladislav Michl <ladis@psi.cz>
  9.  *
  10.  * Notes on the hardware
  11.  *
  12.  *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
  13.  *  No volume control - only mute/unmute - you have to use line volume
  14.  *  control on SB-part of SF16FMI
  15.  *  
  16.  */
  17. #include <linux/kernel.h> /* __setup */
  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 <linux/videodev.h> /* kernel radio structs */
  23. #include <linux/isapnp.h>
  24. #include <asm/io.h> /* outb, outb_p */
  25. #include <asm/uaccess.h> /* copy to/from user */
  26. #include <asm/semaphore.h>
  27. struct fmi_device
  28. {
  29. int port;
  30.         int curvol; /* 1 or 0 */
  31.         unsigned long curfreq; /* freq in kHz */
  32.         __u32 flags;
  33. };
  34. static int io = -1; 
  35. static int radio_nr = -1;
  36. static int users = 0;
  37. static struct pci_dev *dev = NULL;
  38. static struct semaphore lock;
  39. /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
  40. /* It is only useful to give freq in intervall of 800 (=0.05Mhz),
  41.  * other bits will be truncated, e.g 92.7400016 -> 92.7, but 
  42.  * 92.7400017 -> 92.75
  43.  */
  44. #define RSF16_ENCODE(x) ((x)/800+214)
  45. #define RSF16_MINFREQ 87*16000
  46. #define RSF16_MAXFREQ 108*16000
  47. static void outbits(int bits, unsigned int data, int port)
  48. {
  49. while(bits--) {
  50.   if(data & 1) {
  51. outb(5, port);
  52. udelay(6);
  53. outb(7, port);
  54. udelay(6);
  55. } else {
  56. outb(1, port);
  57. udelay(6);
  58. outb(3, port);
  59. udelay(6);
  60. }
  61. data>>=1;
  62. }
  63. }
  64. static inline void fmi_mute(int port)
  65. {
  66. down(&lock);
  67. outb(0x00, port);
  68. up(&lock);
  69. }
  70. static inline void fmi_unmute(int port)
  71. {
  72. down(&lock);
  73. outb(0x08, port);
  74. up(&lock);
  75. }
  76. static inline int fmi_setfreq(struct fmi_device *dev)
  77. {
  78.         int myport = dev->port;
  79. unsigned long freq = dev->curfreq;
  80. down(&lock);
  81. outbits(16, RSF16_ENCODE(freq), myport);
  82. outbits(8, 0xC0, myport);
  83. current->state = TASK_UNINTERRUPTIBLE;
  84. schedule_timeout(HZ/7);
  85. up(&lock);
  86. if (dev->curvol) fmi_unmute(myport);
  87. return 0;
  88. }
  89. static inline int fmi_getsigstr(struct fmi_device *dev)
  90. {
  91. int val;
  92. int res;
  93. int myport = dev->port;
  94. down(&lock);
  95. val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */
  96. outb(val, myport);
  97. outb(val | 0x10, myport);
  98. current->state = TASK_UNINTERRUPTIBLE;
  99. schedule_timeout(HZ/7);
  100. res = (int)inb(myport+1);
  101. outb(val, myport);
  102. up(&lock);
  103. return (res & 2) ? 0 : 0xFFFF;
  104. }
  105. static int fmi_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
  106. {
  107. struct fmi_device *fmi=dev->priv;
  108. switch(cmd)
  109. {
  110. case VIDIOCGCAP:
  111. {
  112. struct video_capability v;
  113. strcpy(v.name, "SF16-FMx radio");
  114. v.type=VID_TYPE_TUNER;
  115. v.channels=1;
  116. v.audios=1;
  117. /* No we don't do pictures */
  118. v.maxwidth=0;
  119. v.maxheight=0;
  120. v.minwidth=0;
  121. v.minheight=0;
  122. if(copy_to_user(arg,&v,sizeof(v)))
  123. return -EFAULT;
  124. return 0;
  125. }
  126. case VIDIOCGTUNER:
  127. {
  128. struct video_tuner v;
  129. int mult;
  130. if(copy_from_user(&v, arg,sizeof(v))!=0)
  131. return -EFAULT;
  132. if(v.tuner) /* Only 1 tuner */
  133. return -EINVAL;
  134. strcpy(v.name, "FM");
  135. mult = (fmi->flags & VIDEO_TUNER_LOW) ? 1 : 1000;
  136. v.rangelow = RSF16_MINFREQ/mult;
  137. v.rangehigh = RSF16_MAXFREQ/mult;
  138. v.flags=fmi->flags;
  139. v.mode=VIDEO_MODE_AUTO;
  140. v.signal = fmi_getsigstr(fmi);
  141. if(copy_to_user(arg,&v, sizeof(v)))
  142. return -EFAULT;
  143. return 0;
  144. }
  145. case VIDIOCSTUNER:
  146. {
  147. struct video_tuner v;
  148. if(copy_from_user(&v, arg, sizeof(v)))
  149. return -EFAULT;
  150. if(v.tuner!=0)
  151. return -EINVAL;
  152. fmi->flags = v.flags & VIDEO_TUNER_LOW;
  153. /* Only 1 tuner so no setting needed ! */
  154. return 0;
  155. }
  156. case VIDIOCGFREQ:
  157. {
  158. unsigned long tmp = fmi->curfreq;
  159. if (!(fmi->flags & VIDEO_TUNER_LOW))
  160. tmp /= 1000;
  161. if(copy_to_user(arg, &tmp, sizeof(tmp)))
  162. return -EFAULT;
  163. return 0;
  164. }
  165. case VIDIOCSFREQ:
  166. {
  167. unsigned long tmp;
  168. if(copy_from_user(&tmp, arg, sizeof(tmp)))
  169. return -EFAULT;
  170. if (!(fmi->flags & VIDEO_TUNER_LOW))
  171. tmp *= 1000;
  172. if ( tmp<RSF16_MINFREQ || tmp>RSF16_MAXFREQ )
  173.   return -EINVAL;
  174. /*rounding in steps of 800 to match th freq
  175.   that will be used */
  176. fmi->curfreq = (tmp/800)*800; 
  177. fmi_setfreq(fmi);
  178. return 0;
  179. }
  180. case VIDIOCGAUDIO:
  181. {
  182. struct video_audio v;
  183. v.audio=0;
  184. v.volume=0;
  185. v.bass=0;
  186. v.treble=0;
  187. v.flags=( (!fmi->curvol)*VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE);
  188. strcpy(v.name, "Radio");
  189. v.mode=VIDEO_SOUND_STEREO;
  190. v.balance=0;
  191. v.step=0; /* No volume, just (un)mute */
  192. if(copy_to_user(arg,&v, sizeof(v)))
  193. return -EFAULT;
  194. return 0;
  195. }
  196. case VIDIOCSAUDIO:
  197. {
  198. struct video_audio v;
  199. if(copy_from_user(&v, arg, sizeof(v)))
  200. return -EFAULT;
  201. if(v.audio)
  202. return -EINVAL;
  203. fmi->curvol= v.flags&VIDEO_AUDIO_MUTE ? 0 : 1;
  204. fmi->curvol ? 
  205.   fmi_unmute(fmi->port) : fmi_mute(fmi->port);
  206. return 0;
  207. }
  208.         case VIDIOCGUNIT:
  209. {
  210.                 struct video_unit v;
  211. v.video=VIDEO_NO_UNIT;
  212. v.vbi=VIDEO_NO_UNIT;
  213. v.radio=dev->minor;
  214. v.audio=0; /* How do we find out this??? */
  215. v.teletext=VIDEO_NO_UNIT;
  216. if(copy_to_user(arg, &v, sizeof(v)))
  217. return -EFAULT;
  218. return 0;
  219. }
  220. default:
  221. return -ENOIOCTLCMD;
  222. }
  223. }
  224. static int fmi_open(struct video_device *dev, int flags)
  225. {
  226. if(users)
  227. return -EBUSY;
  228. users++;
  229. return 0;
  230. }
  231. static void fmi_close(struct video_device *dev)
  232. {
  233. users--;
  234. }
  235. static struct fmi_device fmi_unit;
  236. static struct video_device fmi_radio=
  237. {
  238. owner: THIS_MODULE,
  239. name: "SF16FMx radio",
  240. type: VID_TYPE_TUNER,
  241. hardware: VID_HARDWARE_SF16MI,
  242. open: fmi_open,
  243. close: fmi_close,
  244. ioctl: fmi_ioctl,
  245. };
  246. /* ladis: this is my card. does any other types exist? */
  247. static struct isapnp_device_id id_table[] __devinitdata = {
  248. { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
  249. ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0},
  250. { ISAPNP_CARD_END, },
  251. };
  252. MODULE_DEVICE_TABLE(isapnp, id_table);
  253. static int isapnp_fmi_probe(void)
  254. {
  255. int i = 0;
  256. while (id_table[i].card_vendor != 0 && dev == NULL) {
  257. dev = isapnp_find_dev(NULL, id_table[i].vendor,
  258.       id_table[i].function, NULL);
  259. i++;
  260. }
  261. if (!dev)
  262. return -ENODEV;
  263. if (dev->prepare(dev) < 0)
  264. return -EAGAIN;
  265. if (!(dev->resource[0].flags & IORESOURCE_IO))
  266. return -ENODEV;
  267. if (dev->activate(dev) < 0) {
  268. printk ("radio-sf16fmi: ISAPnP configure failed (out of resources?)n");
  269. return -ENOMEM;
  270. }
  271. i = dev->resource[0].start;
  272. printk ("radio-sf16fmi: ISAPnP reports card at %#xn", i);
  273. return i;
  274. }
  275. static int __init fmi_init(void)
  276. {
  277. if (io < 0)
  278. io = isapnp_fmi_probe();
  279. if (io < 0) {
  280. printk(KERN_ERR "radio-sf16fmi: No PnP card found.n");
  281. return io;
  282. }
  283. if (!request_region(io, 2, "radio-sf16fmi")) {
  284. printk(KERN_ERR "radio-sf16fmi: port 0x%x already in usen", io);
  285. return -EBUSY;
  286. }
  287. fmi_unit.port = io;
  288. fmi_unit.curvol = 0;
  289. fmi_unit.curfreq = 0;
  290. fmi_unit.flags = VIDEO_TUNER_LOW;
  291. fmi_radio.priv = &fmi_unit;
  292. init_MUTEX(&lock);
  293. if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) == -1) {
  294. release_region(io, 2);
  295. return -EINVAL;
  296. }
  297. printk(KERN_INFO "SF16FMx radio card driver at 0x%xn", io);
  298. /* mute card - prevents noisy bootups */
  299. fmi_mute(io);
  300. return 0;
  301. }
  302. MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
  303. MODULE_DESCRIPTION("A driver for the SF16MI radio.");
  304. MODULE_LICENSE("GPL");
  305. MODULE_PARM(io, "i");
  306. MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)");
  307. MODULE_PARM(radio_nr, "i");
  308. EXPORT_NO_SYMBOLS;
  309. static void __exit fmi_cleanup_module(void)
  310. {
  311. video_unregister_device(&fmi_radio);
  312. release_region(io, 2);
  313. if (dev)
  314. dev->deactivate(dev);
  315. }
  316. module_init(fmi_init);
  317. module_exit(fmi_cleanup_module);
  318. #ifndef MODULE
  319. static int __init fmi_setup_io(char *str)
  320. {
  321. get_option(&str, &io);
  322. return 1;
  323. }
  324. __setup("sf16fm=", fmi_setup_io);
  325. #endif