dahdi_transcode.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /*
  2. * Transcoder Interface for DAHDI
  3. *
  4. * Written by Mark Spencer <markster@digium.com>
  5. *
  6. * Copyright (C) 2006-2008, Digium, Inc.
  7. *
  8. * All rights reserved.
  9. *
  10. */
  11. /*
  12. * See http://www.asterisk.org for more information about
  13. * the Asterisk project. Please do not directly contact
  14. * any of the maintainers of this project for assistance;
  15. * the project provides a web site, mailing lists and IRC
  16. * channels for your use.
  17. *
  18. * This program is free software, distributed under the terms of
  19. * the GNU General Public License Version 2 as published by the
  20. * Free Software Foundation. See the LICENSE file included with
  21. * this program for more details.
  22. */
  23. #include <linux/kernel.h>
  24. #include <linux/errno.h>
  25. #include <linux/module.h>
  26. #include <linux/init.h>
  27. #include <linux/spinlock.h>
  28. #include <linux/slab.h>
  29. #include <linux/kmod.h>
  30. #include <linux/sched.h>
  31. #include <linux/interrupt.h>
  32. #include <linux/vmalloc.h>
  33. #include <linux/mm.h>
  34. #include <linux/page-flags.h>
  35. #include <asm/io.h>
  36. #include <dahdi/kernel.h>
  37. static int debug;
  38. /* The registration list contains transcoders in the order in which they were
  39. * registered. */
  40. static LIST_HEAD(registration_list);
  41. /* The active list is sorted by the most recently used transcoder is last. This
  42. * is used as a simplistic way to spread the load amongst the different hardware
  43. * transcoders in the system. */
  44. static LIST_HEAD(active_list);
  45. static DEFINE_SPINLOCK(translock);
  46. EXPORT_SYMBOL(dahdi_transcoder_register);
  47. EXPORT_SYMBOL(dahdi_transcoder_unregister);
  48. EXPORT_SYMBOL(dahdi_transcoder_alert);
  49. EXPORT_SYMBOL(dahdi_transcoder_alloc);
  50. EXPORT_SYMBOL(dahdi_transcoder_free);
  51. struct dahdi_transcoder *dahdi_transcoder_alloc(int numchans)
  52. {
  53. struct dahdi_transcoder *tc;
  54. unsigned int x;
  55. size_t size = sizeof(*tc) + (sizeof(tc->channels[0]) * numchans);
  56. if (!(tc = kmalloc(size, GFP_KERNEL)))
  57. return NULL;
  58. memset(tc, 0, size);
  59. strcpy(tc->name, "<unspecified>");
  60. INIT_LIST_HEAD(&tc->registration_list_node);
  61. INIT_LIST_HEAD(&tc->active_list_node);
  62. tc->numchannels = numchans;
  63. for (x=0; x < tc->numchannels; x++) {
  64. init_waitqueue_head(&tc->channels[x].ready);
  65. tc->channels[x].parent = tc;
  66. }
  67. WARN_ON(!dahdi_transcode_fops);
  68. /* Individual transcoders should supply their own file_operations for
  69. * write and read. But they will by default use the file_operations
  70. * provided by the dahdi_transcode layer. */
  71. memcpy(&tc->fops, dahdi_transcode_fops, sizeof(*dahdi_transcode_fops));
  72. return tc;
  73. }
  74. void dahdi_transcoder_free(struct dahdi_transcoder *tc)
  75. {
  76. kfree(tc);
  77. }
  78. /* Returns 1 if the item is on the list pointed to by head, otherwise, returns
  79. * 0 */
  80. static int is_on_list(struct list_head *entry, struct list_head *head)
  81. {
  82. struct list_head *cur;
  83. list_for_each(cur, head) {
  84. if (cur == entry) return 1;
  85. }
  86. return 0;
  87. }
  88. /* Register a transcoder */
  89. int dahdi_transcoder_register(struct dahdi_transcoder *tc)
  90. {
  91. spin_lock(&translock);
  92. BUG_ON(is_on_list(&tc->registration_list_node, &registration_list));
  93. list_add_tail(&tc->registration_list_node, &registration_list);
  94. list_add_tail(&tc->active_list_node, &active_list);
  95. spin_unlock(&translock);
  96. printk(KERN_INFO "%s: Registered codec translator '%s' " \
  97. "with %d transcoders (srcs=%08x, dsts=%08x)\n",
  98. THIS_MODULE->name, tc->name, tc->numchannels,
  99. tc->srcfmts, tc->dstfmts);
  100. return 0;
  101. }
  102. /* Unregister a transcoder */
  103. int dahdi_transcoder_unregister(struct dahdi_transcoder *tc)
  104. {
  105. int res = -EINVAL;
  106. /* \todo Perhaps we should check to make sure there isn't a channel
  107. * that is still in use? */
  108. spin_lock(&translock);
  109. if (!is_on_list(&tc->registration_list_node, &registration_list)) {
  110. spin_unlock(&translock);
  111. printk(KERN_WARNING "%s: Failed to unregister %s, which is " \
  112. "not currently registered.\n", THIS_MODULE->name, tc->name);
  113. return -EINVAL;
  114. }
  115. list_del_init(&tc->registration_list_node);
  116. list_del_init(&tc->active_list_node);
  117. spin_unlock(&translock);
  118. printk(KERN_INFO "Unregistered codec translator '%s' with %d " \
  119. "transcoders (srcs=%08x, dsts=%08x)\n",
  120. tc->name, tc->numchannels, tc->srcfmts, tc->dstfmts);
  121. res = 0;
  122. return res;
  123. }
  124. /* Alert a transcoder */
  125. int dahdi_transcoder_alert(struct dahdi_transcoder_channel *chan)
  126. {
  127. wake_up_interruptible(&chan->ready);
  128. return 0;
  129. }
  130. static int dahdi_tc_open(struct inode *inode, struct file *file)
  131. {
  132. const struct file_operations *original_fops;
  133. BUG_ON(!dahdi_transcode_fops);
  134. original_fops = file->f_op;
  135. file->f_op = dahdi_transcode_fops;
  136. file->private_data = NULL;
  137. /* Under normal operation, this releases the reference on the DAHDI
  138. * module that was created when the file was opened. dahdi_open is
  139. * responsible for taking a reference out on this module before
  140. * calling this function. */
  141. module_put(original_fops->owner);
  142. return 0;
  143. }
  144. static void dtc_release(struct dahdi_transcoder_channel *chan)
  145. {
  146. BUG_ON(!chan);
  147. if (chan->parent && chan->parent->release) {
  148. chan->parent->release(chan);
  149. } else {
  150. dahdi_tc_clear_busy(chan);
  151. }
  152. }
  153. static int dahdi_tc_release(struct inode *inode, struct file *file)
  154. {
  155. struct dahdi_transcoder_channel *chan = file->private_data;
  156. /* There will not be a transcoder channel associated with this file if
  157. * the ALLOCATE ioctl never succeeded.
  158. */
  159. if (chan) {
  160. dtc_release(chan);
  161. }
  162. return 0;
  163. }
  164. /* Find a free channel on the transcoder and mark it busy. */
  165. static inline struct dahdi_transcoder_channel *
  166. get_free_channel(struct dahdi_transcoder *tc,
  167. const struct dahdi_transcoder_formats *fmts)
  168. {
  169. struct dahdi_transcoder_channel *chan;
  170. int i;
  171. /* Should be called with the translock held. */
  172. #ifdef CONFIG_SMP
  173. WARN_ON(!spin_is_locked(&translock));
  174. #endif
  175. for (i = 0; i < tc->numchannels; i++) {
  176. chan = &tc->channels[i];
  177. if (!dahdi_tc_is_busy(chan)) {
  178. if (!dahdi_tc_is_built(chan)) {
  179. dahdi_tc_set_busy(chan);
  180. return chan;
  181. } else {
  182. /* If the channel is already built, we must
  183. * make sure that it can support the formats
  184. * that we're interested in. */
  185. if ((fmts->srcfmt|fmts->dstfmt) == chan->built_fmts) {
  186. dahdi_tc_set_busy(chan);
  187. return chan;
  188. }
  189. }
  190. }
  191. }
  192. return NULL;
  193. }
  194. /* Search the list for a transcoder that supports the specified format, and
  195. * allocate and return an available channel on it.
  196. *
  197. * Returns either a pointer to the allocated channel, -EBUSY if the format is
  198. * supported but all the channels are busy, or -ENODEV if there are not any
  199. * transcoders that support the formats.
  200. */
  201. static struct dahdi_transcoder_channel *
  202. __find_free_channel(struct list_head *list, const struct dahdi_transcoder_formats *fmts)
  203. {
  204. struct dahdi_transcoder *tc;
  205. struct dahdi_transcoder_channel *chan = NULL;
  206. unsigned int match = 0;
  207. list_for_each_entry(tc, list, active_list_node) {
  208. if ((tc->dstfmts & fmts->dstfmt) && (tc->srcfmts & fmts->srcfmt)) {
  209. /* We found a transcoder that can handle our formats.
  210. * Now look for an available channel. */
  211. match = 1;
  212. if ((chan = get_free_channel(tc, fmts))) {
  213. /* transcoder tc has a free channel. In order
  214. * to spread the load among available
  215. * transcoders (when there are more than one
  216. * transcoder in the system) we'll move tc
  217. * to the end of the list. */
  218. list_move_tail(&tc->active_list_node, list);
  219. return chan;
  220. }
  221. }
  222. }
  223. return (void*)((long)((match) ? -EBUSY : -ENODEV));
  224. }
  225. static long dahdi_tc_allocate(struct file *file, unsigned long data)
  226. {
  227. struct dahdi_transcoder_channel *chan = NULL;
  228. struct dahdi_transcoder_formats fmts;
  229. if (copy_from_user(&fmts, (__user const void *) data, sizeof(fmts))) {
  230. return -EFAULT;
  231. }
  232. spin_lock(&translock);
  233. chan = __find_free_channel(&active_list, &fmts);
  234. spin_unlock(&translock);
  235. if (IS_ERR(chan)) {
  236. return PTR_ERR(chan);
  237. }
  238. /* Every transcoder channel must be associated with a parent
  239. * transcoder. */
  240. BUG_ON(!chan->parent);
  241. chan->srcfmt = fmts.srcfmt;
  242. chan->dstfmt = fmts.dstfmt;
  243. if (file->private_data) {
  244. /* This open file is moving to a new channel. Cleanup and
  245. * close the old channel here. */
  246. dtc_release(file->private_data);
  247. }
  248. file->private_data = chan;
  249. if (chan->parent->fops.owner != file->f_op->owner) {
  250. if (!try_module_get(chan->parent->fops.owner)) {
  251. /* Failed to get a reference on the driver for the
  252. * actual transcoding hardware. */
  253. return -EINVAL;
  254. }
  255. /* Release the reference on the existing driver. */
  256. module_put(file->f_op->owner);
  257. file->f_op = &chan->parent->fops;
  258. }
  259. if (file->f_flags & O_NONBLOCK) {
  260. dahdi_tc_set_nonblock(chan);
  261. } else {
  262. dahdi_tc_clear_nonblock(chan);
  263. }
  264. /* Actually reset the transcoder channel */
  265. if (chan->parent->allocate)
  266. return chan->parent->allocate(chan);
  267. return -EINVAL;
  268. }
  269. static long dahdi_tc_getinfo(unsigned long data)
  270. {
  271. struct dahdi_transcoder_info info;
  272. struct dahdi_transcoder *cur;
  273. struct dahdi_transcoder *tc = NULL;
  274. unsigned int count = 0;
  275. if (copy_from_user(&info, (__user const void *) data, sizeof(info))) {
  276. return -EFAULT;
  277. }
  278. spin_lock(&translock);
  279. list_for_each_entry(cur, &registration_list, registration_list_node) {
  280. if (info.tcnum == count++) {
  281. tc = cur;
  282. break;
  283. }
  284. }
  285. spin_unlock(&translock);
  286. if (!tc) {
  287. return -ENOSYS;
  288. }
  289. strlcpy(info.name, tc->name, sizeof(info.name));
  290. info.numchannels = tc->numchannels;
  291. info.srcfmts = tc->srcfmts;
  292. info.dstfmts = tc->dstfmts;
  293. return copy_to_user((__user void *) data, &info, sizeof(info)) ? -EFAULT : 0;
  294. }
  295. static ssize_t dahdi_tc_write(struct file *file, __user const char *usrbuf, size_t count, loff_t *ppos)
  296. {
  297. if (file->private_data) {
  298. /* file->private_data will not be NULL if DAHDI_TC_ALLOCATE was
  299. * called, and therefore indicates that the transcoder driver
  300. * did not export a read function. */
  301. WARN_ON(1);
  302. return -ENOSYS;
  303. } else {
  304. printk(KERN_INFO "%s: Attempt to write to unallocated " \
  305. "channel.\n", THIS_MODULE->name);
  306. return -EINVAL;
  307. }
  308. }
  309. static ssize_t dahdi_tc_read(struct file *file, __user char *usrbuf, size_t count, loff_t *ppos)
  310. {
  311. if (file->private_data) {
  312. /* file->private_data will not be NULL if DAHDI_TC_ALLOCATE was
  313. * called, and therefore indicates that the transcoder driver
  314. * did not export a write function. */
  315. WARN_ON(1);
  316. return -ENOSYS;
  317. } else {
  318. printk(KERN_INFO "%s: Attempt to read from unallocated " \
  319. "channel.\n", THIS_MODULE->name);
  320. return -EINVAL;
  321. }
  322. }
  323. static long dahdi_tc_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long data)
  324. {
  325. switch (cmd) {
  326. case DAHDI_TC_ALLOCATE:
  327. return dahdi_tc_allocate(file, data);
  328. case DAHDI_TC_GETINFO:
  329. return dahdi_tc_getinfo(data);
  330. case DAHDI_TRANSCODE_OP:
  331. /* This is a deprecated call from the previous transcoder
  332. * interface, which was all routed through the dahdi_ioctl in
  333. * dahdi-base.c, and this ioctl request was used to indicate
  334. * that the call should be forwarded to this function. Now
  335. * when the file is opened, the f_ops pointer is updated to
  336. * point directly to this function, and we don't need a
  337. * general indication that the ioctl is destined for the
  338. * transcoder.
  339. *
  340. * I'm keeping this ioctl here in order to explain why there
  341. * might be a hole in the ioctl numbering scheme in the header
  342. * files.
  343. */
  344. printk(KERN_WARNING "%s: DAHDI_TRANSCODE_OP is no longer " \
  345. "supported. Please call DAHDI_TC ioctls directly.\n",
  346. THIS_MODULE->name);
  347. return -EINVAL;
  348. default:
  349. return -EINVAL;
  350. };
  351. }
  352. #ifndef HAVE_UNLOCKED_IOCTL
  353. static int dahdi_tc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data)
  354. {
  355. return (int)dahdi_tc_unlocked_ioctl(file, cmd, data);
  356. }
  357. #endif
  358. static unsigned int dahdi_tc_poll(struct file *file, struct poll_table_struct *wait_table)
  359. {
  360. int ret;
  361. struct dahdi_transcoder_channel *chan = file->private_data;
  362. if (!chan) {
  363. /* This is because the DAHDI_TC_ALLOCATE ioctl was not called
  364. * before calling poll, which is invalid. */
  365. return -EINVAL;
  366. }
  367. poll_wait(file, &chan->ready, wait_table);
  368. ret = dahdi_tc_is_busy(chan) ? 0 : POLLPRI;
  369. ret |= dahdi_tc_is_built(chan) ? POLLOUT : 0;
  370. ret |= dahdi_tc_is_data_waiting(chan) ? POLLIN : 0;
  371. return ret;
  372. }
  373. static struct file_operations __dahdi_transcode_fops = {
  374. .owner = THIS_MODULE,
  375. .open = dahdi_tc_open,
  376. .release = dahdi_tc_release,
  377. #ifdef HAVE_UNLOCKED_IOCTL
  378. .unlocked_ioctl = dahdi_tc_unlocked_ioctl,
  379. #else
  380. .ioctl = dahdi_tc_ioctl,
  381. #endif
  382. .read = dahdi_tc_read,
  383. .write = dahdi_tc_write,
  384. .poll = dahdi_tc_poll,
  385. };
  386. static struct dahdi_chardev transcode_chardev = {
  387. .name = "transcode",
  388. .minor = DAHDI_TRANSCODE,
  389. };
  390. static int dahdi_transcode_init(void)
  391. {
  392. int res;
  393. if (dahdi_transcode_fops) {
  394. printk(KERN_WARNING "dahdi_transcode_fops already set.\n");
  395. return -EBUSY;
  396. }
  397. dahdi_transcode_fops = &__dahdi_transcode_fops;
  398. if ((res = dahdi_register_chardev(&transcode_chardev)))
  399. return res;
  400. printk(KERN_INFO "%s: Loaded.\n", THIS_MODULE->name);
  401. return 0;
  402. }
  403. static void dahdi_transcode_cleanup(void)
  404. {
  405. dahdi_unregister_chardev(&transcode_chardev);
  406. dahdi_transcode_fops = NULL;
  407. printk(KERN_DEBUG "%s: Unloaded.\n", THIS_MODULE->name);
  408. }
  409. module_param(debug, int, S_IRUGO | S_IWUSR);
  410. MODULE_DESCRIPTION("DAHDI Transcoder Support");
  411. MODULE_AUTHOR("Mark Spencer <markster@digium.com>");
  412. #ifdef MODULE_LICENSE
  413. MODULE_LICENSE("GPL");
  414. #endif
  415. module_init(dahdi_transcode_init);
  416. module_exit(dahdi_transcode_cleanup);