radio-typhoon.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. /* Typhoon Radio Card driver for radio support
  2. * (c) 1999 Dr. Henrik Seidel <Henrik.Seidel@gmx.de>
  3. *
  4. * Card manufacturer:
  5. * http://194.18.155.92/idc/prod2.idc?nr=50753&lang=e
  6. *
  7. * Notes on the hardware
  8. *
  9. * This card has two output sockets, one for speakers and one for line.
  10. * The speaker output has volume control, but only in four discrete
  11. * steps. The line output has neither volume control nor mute.
  12. *
  13. * The card has auto-stereo according to its manual, although it all
  14. * sounds mono to me (even with the Win/DOS drivers). Maybe it's my
  15. * antenna - I really don't know for sure.
  16. *
  17. * Frequency control is done digitally.
  18. *
  19. * Volume control is done digitally, but there are only four different
  20. * possible values. So you should better always turn the volume up and
  21. * use line control. I got the best results by connecting line output
  22. * to the sound card microphone input. For such a configuration the
  23. * volume control has no effect, since volume control only influences
  24. * the speaker output.
  25. *
  26. * There is no explicit mute/unmute. So I set the radio frequency to a
  27. * value where I do expect just noise and turn the speaker volume down.
  28. * The frequency change is necessary since the card never seems to be
  29. * completely silent.
  30. *
  31. * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
  32. */
  33. #include <linux/module.h> /* Modules */
  34. #include <linux/init.h> /* Initdata */
  35. #include <linux/ioport.h> /* request_region */
  36. #include <linux/proc_fs.h> /* radio card status report */
  37. #include <linux/seq_file.h>
  38. #include <asm/io.h> /* outb, outb_p */
  39. #include <asm/uaccess.h> /* copy to/from user */
  40. #include <linux/videodev2.h> /* kernel radio structs */
  41. #include <media/v4l2-common.h>
  42. #include <media/v4l2-ioctl.h>
  43. #include <linux/version.h> /* for KERNEL_VERSION MACRO */
  44. #define RADIO_VERSION KERNEL_VERSION(0,1,1)
  45. #define BANNER "Typhoon Radio Card driver v0.1.1\n"
  46. static struct v4l2_queryctrl radio_qctrl[] = {
  47. {
  48. .id = V4L2_CID_AUDIO_MUTE,
  49. .name = "Mute",
  50. .minimum = 0,
  51. .maximum = 1,
  52. .default_value = 1,
  53. .type = V4L2_CTRL_TYPE_BOOLEAN,
  54. },{
  55. .id = V4L2_CID_AUDIO_VOLUME,
  56. .name = "Volume",
  57. .minimum = 0,
  58. .maximum = 65535,
  59. .step = 1<<14,
  60. .default_value = 0xff,
  61. .type = V4L2_CTRL_TYPE_INTEGER,
  62. }
  63. };
  64. #ifndef CONFIG_RADIO_TYPHOON_PORT
  65. #define CONFIG_RADIO_TYPHOON_PORT -1
  66. #endif
  67. #ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
  68. #define CONFIG_RADIO_TYPHOON_MUTEFREQ 0
  69. #endif
  70. #ifndef CONFIG_PROC_FS
  71. #undef CONFIG_RADIO_TYPHOON_PROC_FS
  72. #endif
  73. struct typhoon_device {
  74. unsigned long in_use;
  75. int iobase;
  76. int curvol;
  77. int muted;
  78. unsigned long curfreq;
  79. unsigned long mutefreq;
  80. struct mutex lock;
  81. };
  82. static void typhoon_setvol_generic(struct typhoon_device *dev, int vol);
  83. static int typhoon_setfreq_generic(struct typhoon_device *dev,
  84. unsigned long frequency);
  85. static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency);
  86. static void typhoon_mute(struct typhoon_device *dev);
  87. static void typhoon_unmute(struct typhoon_device *dev);
  88. static int typhoon_setvol(struct typhoon_device *dev, int vol);
  89. static void typhoon_setvol_generic(struct typhoon_device *dev, int vol)
  90. {
  91. mutex_lock(&dev->lock);
  92. vol >>= 14; /* Map 16 bit to 2 bit */
  93. vol &= 3;
  94. outb_p(vol / 2, dev->iobase); /* Set the volume, high bit. */
  95. outb_p(vol % 2, dev->iobase + 2); /* Set the volume, low bit. */
  96. mutex_unlock(&dev->lock);
  97. }
  98. static int typhoon_setfreq_generic(struct typhoon_device *dev,
  99. unsigned long frequency)
  100. {
  101. unsigned long outval;
  102. unsigned long x;
  103. /*
  104. * The frequency transfer curve is not linear. The best fit I could
  105. * get is
  106. *
  107. * outval = -155 + exp((f + 15.55) * 0.057))
  108. *
  109. * where frequency f is in MHz. Since we don't have exp in the kernel,
  110. * I approximate this function by a third order polynomial.
  111. *
  112. */
  113. mutex_lock(&dev->lock);
  114. x = frequency / 160;
  115. outval = (x * x + 2500) / 5000;
  116. outval = (outval * x + 5000) / 10000;
  117. outval -= (10 * x * x + 10433) / 20866;
  118. outval += 4 * x - 11505;
  119. outb_p((outval >> 8) & 0x01, dev->iobase + 4);
  120. outb_p(outval >> 9, dev->iobase + 6);
  121. outb_p(outval & 0xff, dev->iobase + 8);
  122. mutex_unlock(&dev->lock);
  123. return 0;
  124. }
  125. static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency)
  126. {
  127. typhoon_setfreq_generic(dev, frequency);
  128. dev->curfreq = frequency;
  129. return 0;
  130. }
  131. static void typhoon_mute(struct typhoon_device *dev)
  132. {
  133. if (dev->muted == 1)
  134. return;
  135. typhoon_setvol_generic(dev, 0);
  136. typhoon_setfreq_generic(dev, dev->mutefreq);
  137. dev->muted = 1;
  138. }
  139. static void typhoon_unmute(struct typhoon_device *dev)
  140. {
  141. if (dev->muted == 0)
  142. return;
  143. typhoon_setfreq_generic(dev, dev->curfreq);
  144. typhoon_setvol_generic(dev, dev->curvol);
  145. dev->muted = 0;
  146. }
  147. static int typhoon_setvol(struct typhoon_device *dev, int vol)
  148. {
  149. if (dev->muted && vol != 0) { /* user is unmuting the card */
  150. dev->curvol = vol;
  151. typhoon_unmute(dev);
  152. return 0;
  153. }
  154. if (vol == dev->curvol) /* requested volume == current */
  155. return 0;
  156. if (vol == 0) { /* volume == 0 means mute the card */
  157. typhoon_mute(dev);
  158. dev->curvol = vol;
  159. return 0;
  160. }
  161. typhoon_setvol_generic(dev, vol);
  162. dev->curvol = vol;
  163. return 0;
  164. }
  165. static int vidioc_querycap(struct file *file, void *priv,
  166. struct v4l2_capability *v)
  167. {
  168. strlcpy(v->driver, "radio-typhoon", sizeof(v->driver));
  169. strlcpy(v->card, "Typhoon Radio", sizeof(v->card));
  170. sprintf(v->bus_info, "ISA");
  171. v->version = RADIO_VERSION;
  172. v->capabilities = V4L2_CAP_TUNER;
  173. return 0;
  174. }
  175. static int vidioc_g_tuner(struct file *file, void *priv,
  176. struct v4l2_tuner *v)
  177. {
  178. if (v->index > 0)
  179. return -EINVAL;
  180. strcpy(v->name, "FM");
  181. v->type = V4L2_TUNER_RADIO;
  182. v->rangelow = (87.5*16000);
  183. v->rangehigh = (108*16000);
  184. v->rxsubchans = V4L2_TUNER_SUB_MONO;
  185. v->capability = V4L2_TUNER_CAP_LOW;
  186. v->audmode = V4L2_TUNER_MODE_MONO;
  187. v->signal = 0xFFFF; /* We can't get the signal strength */
  188. return 0;
  189. }
  190. static int vidioc_s_tuner(struct file *file, void *priv,
  191. struct v4l2_tuner *v)
  192. {
  193. if (v->index > 0)
  194. return -EINVAL;
  195. return 0;
  196. }
  197. static int vidioc_s_frequency(struct file *file, void *priv,
  198. struct v4l2_frequency *f)
  199. {
  200. struct typhoon_device *typhoon = video_drvdata(file);
  201. typhoon->curfreq = f->frequency;
  202. typhoon_setfreq(typhoon, typhoon->curfreq);
  203. return 0;
  204. }
  205. static int vidioc_g_frequency(struct file *file, void *priv,
  206. struct v4l2_frequency *f)
  207. {
  208. struct typhoon_device *typhoon = video_drvdata(file);
  209. f->type = V4L2_TUNER_RADIO;
  210. f->frequency = typhoon->curfreq;
  211. return 0;
  212. }
  213. static int vidioc_queryctrl(struct file *file, void *priv,
  214. struct v4l2_queryctrl *qc)
  215. {
  216. int i;
  217. for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
  218. if (qc->id && qc->id == radio_qctrl[i].id) {
  219. memcpy(qc, &(radio_qctrl[i]),
  220. sizeof(*qc));
  221. return 0;
  222. }
  223. }
  224. return -EINVAL;
  225. }
  226. static int vidioc_g_ctrl(struct file *file, void *priv,
  227. struct v4l2_control *ctrl)
  228. {
  229. struct typhoon_device *typhoon = video_drvdata(file);
  230. switch (ctrl->id) {
  231. case V4L2_CID_AUDIO_MUTE:
  232. ctrl->value = typhoon->muted;
  233. return 0;
  234. case V4L2_CID_AUDIO_VOLUME:
  235. ctrl->value = typhoon->curvol;
  236. return 0;
  237. }
  238. return -EINVAL;
  239. }
  240. static int vidioc_s_ctrl (struct file *file, void *priv,
  241. struct v4l2_control *ctrl)
  242. {
  243. struct typhoon_device *typhoon = video_drvdata(file);
  244. switch (ctrl->id) {
  245. case V4L2_CID_AUDIO_MUTE:
  246. if (ctrl->value)
  247. typhoon_mute(typhoon);
  248. else
  249. typhoon_unmute(typhoon);
  250. return 0;
  251. case V4L2_CID_AUDIO_VOLUME:
  252. typhoon_setvol(typhoon, ctrl->value);
  253. return 0;
  254. }
  255. return -EINVAL;
  256. }
  257. static int vidioc_g_audio(struct file *file, void *priv,
  258. struct v4l2_audio *a)
  259. {
  260. if (a->index > 1)
  261. return -EINVAL;
  262. strcpy(a->name, "Radio");
  263. a->capability = V4L2_AUDCAP_STEREO;
  264. return 0;
  265. }
  266. static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
  267. {
  268. *i = 0;
  269. return 0;
  270. }
  271. static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
  272. {
  273. if (i != 0)
  274. return -EINVAL;
  275. return 0;
  276. }
  277. static int vidioc_s_audio(struct file *file, void *priv,
  278. struct v4l2_audio *a)
  279. {
  280. if (a->index != 0)
  281. return -EINVAL;
  282. return 0;
  283. }
  284. static struct typhoon_device typhoon_unit =
  285. {
  286. .iobase = CONFIG_RADIO_TYPHOON_PORT,
  287. .curfreq = CONFIG_RADIO_TYPHOON_MUTEFREQ,
  288. .mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ,
  289. };
  290. static int typhoon_exclusive_open(struct file *file)
  291. {
  292. return test_and_set_bit(0, &typhoon_unit.in_use) ? -EBUSY : 0;
  293. }
  294. static int typhoon_exclusive_release(struct file *file)
  295. {
  296. clear_bit(0, &typhoon_unit.in_use);
  297. return 0;
  298. }
  299. static const struct v4l2_file_operations typhoon_fops = {
  300. .owner = THIS_MODULE,
  301. .open = typhoon_exclusive_open,
  302. .release = typhoon_exclusive_release,
  303. .ioctl = video_ioctl2,
  304. };
  305. static const struct v4l2_ioctl_ops typhoon_ioctl_ops = {
  306. .vidioc_querycap = vidioc_querycap,
  307. .vidioc_g_tuner = vidioc_g_tuner,
  308. .vidioc_s_tuner = vidioc_s_tuner,
  309. .vidioc_g_audio = vidioc_g_audio,
  310. .vidioc_s_audio = vidioc_s_audio,
  311. .vidioc_g_input = vidioc_g_input,
  312. .vidioc_s_input = vidioc_s_input,
  313. .vidioc_g_frequency = vidioc_g_frequency,
  314. .vidioc_s_frequency = vidioc_s_frequency,
  315. .vidioc_queryctrl = vidioc_queryctrl,
  316. .vidioc_g_ctrl = vidioc_g_ctrl,
  317. .vidioc_s_ctrl = vidioc_s_ctrl,
  318. };
  319. static struct video_device typhoon_radio = {
  320. .name = "Typhoon Radio",
  321. .fops = &typhoon_fops,
  322. .ioctl_ops = &typhoon_ioctl_ops,
  323. .release = video_device_release_empty,
  324. };
  325. #ifdef CONFIG_RADIO_TYPHOON_PROC_FS
  326. static int typhoon_proc_show(struct seq_file *m, void *v)
  327. {
  328. #ifdef MODULE
  329. #define MODULEPROCSTRING "Driver loaded as a module"
  330. #else
  331. #define MODULEPROCSTRING "Driver compiled into kernel"
  332. #endif
  333. seq_puts(m, BANNER);
  334. seq_puts(m, "Load type: " MODULEPROCSTRING "\n\n");
  335. seq_printf(m, "frequency = %lu kHz\n",
  336. typhoon_unit.curfreq >> 4);
  337. seq_printf(m, "volume = %d\n", typhoon_unit.curvol);
  338. seq_printf(m, "mute = %s\n", typhoon_unit.muted ?
  339. "on" : "off");
  340. seq_printf(m, "iobase = 0x%x\n", typhoon_unit.iobase);
  341. seq_printf(m, "mute frequency = %lu kHz\n",
  342. typhoon_unit.mutefreq >> 4);
  343. return 0;
  344. }
  345. static int typhoon_proc_open(struct inode *inode, struct file *file)
  346. {
  347. return single_open(file, typhoon_proc_show, NULL);
  348. }
  349. static const struct file_operations typhoon_proc_fops = {
  350. .owner = THIS_MODULE,
  351. .open = typhoon_proc_open,
  352. .read = seq_read,
  353. .llseek = seq_lseek,
  354. .release = single_release,
  355. };
  356. #endif /* CONFIG_RADIO_TYPHOON_PROC_FS */
  357. MODULE_AUTHOR("Dr. Henrik Seidel");
  358. MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
  359. MODULE_LICENSE("GPL");
  360. static int io = -1;
  361. static int radio_nr = -1;
  362. module_param(io, int, 0);
  363. MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
  364. module_param(radio_nr, int, 0);
  365. #ifdef MODULE
  366. static unsigned long mutefreq;
  367. module_param(mutefreq, ulong, 0);
  368. MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
  369. #endif
  370. static int __init typhoon_init(void)
  371. {
  372. #ifdef MODULE
  373. if (io == -1) {
  374. printk(KERN_ERR "radio-typhoon: You must set an I/O address with io=0x316 or io=0x336\n");
  375. return -EINVAL;
  376. }
  377. typhoon_unit.iobase = io;
  378. if (mutefreq < 87000 || mutefreq > 108500) {
  379. printk(KERN_ERR "radio-typhoon: You must set a frequency (in kHz) used when muting the card,\n");
  380. printk(KERN_ERR "radio-typhoon: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
  381. return -EINVAL;
  382. }
  383. typhoon_unit.mutefreq = mutefreq;
  384. #endif /* MODULE */
  385. printk(KERN_INFO BANNER);
  386. mutex_init(&typhoon_unit.lock);
  387. io = typhoon_unit.iobase;
  388. if (!request_region(io, 8, "typhoon")) {
  389. printk(KERN_ERR "radio-typhoon: port 0x%x already in use\n",
  390. typhoon_unit.iobase);
  391. return -EBUSY;
  392. }
  393. video_set_drvdata(&typhoon_radio, &typhoon_unit);
  394. if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
  395. release_region(io, 8);
  396. return -EINVAL;
  397. }
  398. printk(KERN_INFO "radio-typhoon: port 0x%x.\n", typhoon_unit.iobase);
  399. printk(KERN_INFO "radio-typhoon: mute frequency is %lu kHz.\n",
  400. typhoon_unit.mutefreq);
  401. typhoon_unit.mutefreq <<= 4;
  402. /* mute card - prevents noisy bootups */
  403. typhoon_mute(&typhoon_unit);
  404. #ifdef CONFIG_RADIO_TYPHOON_PROC_FS
  405. if (!proc_create("driver/radio-typhoon", 0, NULL, &typhoon_proc_fops))
  406. printk(KERN_ERR "radio-typhoon: registering /proc/driver/radio-typhoon failed\n");
  407. #endif
  408. return 0;
  409. }
  410. static void __exit typhoon_cleanup_module(void)
  411. {
  412. #ifdef CONFIG_RADIO_TYPHOON_PROC_FS
  413. remove_proc_entry("driver/radio-typhoon", NULL);
  414. #endif
  415. video_unregister_device(&typhoon_radio);
  416. release_region(io, 8);
  417. }
  418. module_init(typhoon_init);
  419. module_exit(typhoon_cleanup_module);