PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/dahdi-tools-2.4.1/dahdi-linux-2.4.1/drivers/dahdi/dahdi_transcode.c

#
C | 486 lines | 329 code | 65 blank | 92 comment | 38 complexity | ca4f891ca429391c3d19f5231d2d9995 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-2.0
  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 spinlock_t translock = SPIN_LOCK_UNLOCKED;
  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. }
  150. dahdi_tc_clear_busy(chan);
  151. }
  152. static int dahdi_tc_release(struct inode *inode, struct file *file)
  153. {
  154. struct dahdi_transcoder_channel *chan = file->private_data;
  155. /* There will not be a transcoder channel associated with this file if
  156. * the ALLOCATE ioctl never succeeded.
  157. */
  158. if (chan) {
  159. dtc_release(chan);
  160. }
  161. return 0;
  162. }
  163. /* Find a free channel on the transcoder and mark it busy. */
  164. static inline struct dahdi_transcoder_channel *
  165. get_free_channel(struct dahdi_transcoder *tc,
  166. const struct dahdi_transcoder_formats *fmts)
  167. {
  168. struct dahdi_transcoder_channel *chan;
  169. int i;
  170. /* Should be called with the translock held. */
  171. #ifdef CONFIG_SMP
  172. WARN_ON(!spin_is_locked(&translock));
  173. #endif
  174. for (i = 0; i < tc->numchannels; i++) {
  175. chan = &tc->channels[i];
  176. if (!dahdi_tc_is_busy(chan)) {
  177. if (!dahdi_tc_is_built(chan)) {
  178. dahdi_tc_set_busy(chan);
  179. return chan;
  180. } else {
  181. /* If the channel is already built, we must
  182. * make sure that it can support the formats
  183. * that we're interested in. */
  184. if ((fmts->srcfmt|fmts->dstfmt) == chan->built_fmts) {
  185. dahdi_tc_set_busy(chan);
  186. return chan;
  187. }
  188. }
  189. }
  190. }
  191. return NULL;
  192. }
  193. /* Search the list for a transcoder that supports the specified format, and
  194. * allocate and return an available channel on it.
  195. *
  196. * Returns either a pointer to the allocated channel, -EBUSY if the format is
  197. * supported but all the channels are busy, or -ENODEV if there are not any
  198. * transcoders that support the formats.
  199. */
  200. static struct dahdi_transcoder_channel *
  201. __find_free_channel(struct list_head *list, const struct dahdi_transcoder_formats *fmts)
  202. {
  203. struct dahdi_transcoder *tc;
  204. struct dahdi_transcoder_channel *chan = NULL;
  205. unsigned int match = 0;
  206. list_for_each_entry(tc, list, active_list_node) {
  207. if ((tc->dstfmts & fmts->dstfmt) && (tc->srcfmts & fmts->srcfmt)) {
  208. /* We found a transcoder that can handle our formats.
  209. * Now look for an available channel. */
  210. match = 1;
  211. if ((chan = get_free_channel(tc, fmts))) {
  212. /* transcoder tc has a free channel. In order
  213. * to spread the load among available
  214. * transcoders (when there are more than one
  215. * transcoder in the system) we'll move tc
  216. * to the end of the list. */
  217. list_move_tail(&tc->active_list_node, list);
  218. return chan;
  219. }
  220. }
  221. }
  222. return (void*)((long)((match) ? -EBUSY : -ENODEV));
  223. }
  224. static long dahdi_tc_allocate(struct file *file, unsigned long data)
  225. {
  226. struct dahdi_transcoder_channel *chan = NULL;
  227. struct dahdi_transcoder_formats fmts;
  228. if (copy_from_user(&fmts, (__user const void *) data, sizeof(fmts))) {
  229. return -EFAULT;
  230. }
  231. spin_lock(&translock);
  232. chan = __find_free_channel(&active_list, &fmts);
  233. spin_unlock(&translock);
  234. if (IS_ERR(chan)) {
  235. return PTR_ERR(chan);
  236. }
  237. /* Every transcoder channel must be associated with a parent
  238. * transcoder. */
  239. BUG_ON(!chan->parent);
  240. chan->srcfmt = fmts.srcfmt;
  241. chan->dstfmt = fmts.dstfmt;
  242. if (file->private_data) {
  243. /* This open file is moving to a new channel. Cleanup and
  244. * close the old channel here. */
  245. dtc_release(file->private_data);
  246. }
  247. file->private_data = chan;
  248. if (chan->parent->fops.owner != file->f_op->owner) {
  249. if (!try_module_get(chan->parent->fops.owner)) {
  250. /* Failed to get a reference on the driver for the
  251. * actual transcoding hardware. */
  252. return -EINVAL;
  253. }
  254. /* Release the reference on the existing driver. */
  255. module_put(file->f_op->owner);
  256. file->f_op = &chan->parent->fops;
  257. }
  258. if (file->f_flags & O_NONBLOCK) {
  259. dahdi_tc_set_nonblock(chan);
  260. } else {
  261. dahdi_tc_clear_nonblock(chan);
  262. }
  263. /* Actually reset the transcoder channel */
  264. if (chan->parent->allocate)
  265. return chan->parent->allocate(chan);
  266. return -EINVAL;
  267. }
  268. static long dahdi_tc_getinfo(unsigned long data)
  269. {
  270. struct dahdi_transcoder_info info;
  271. struct dahdi_transcoder *cur;
  272. struct dahdi_transcoder *tc = NULL;
  273. unsigned int count = 0;
  274. if (copy_from_user(&info, (__user const void *) data, sizeof(info))) {
  275. return -EFAULT;
  276. }
  277. spin_lock(&translock);
  278. list_for_each_entry(cur, &registration_list, registration_list_node) {
  279. if (info.tcnum == count++) {
  280. tc = cur;
  281. break;
  282. }
  283. }
  284. spin_unlock(&translock);
  285. if (!tc) {
  286. return -ENOSYS;
  287. }
  288. dahdi_copy_string(info.name, tc->name, sizeof(info.name));
  289. info.numchannels = tc->numchannels;
  290. info.srcfmts = tc->srcfmts;
  291. info.dstfmts = tc->dstfmts;
  292. return copy_to_user((__user void *) data, &info, sizeof(info)) ? -EFAULT : 0;
  293. }
  294. static ssize_t dahdi_tc_write(struct file *file, __user const char *usrbuf, size_t count, loff_t *ppos)
  295. {
  296. if (file->private_data) {
  297. /* file->private_data will not be NULL if DAHDI_TC_ALLOCATE was
  298. * called, and therefore indicates that the transcoder driver
  299. * did not export a read function. */
  300. WARN_ON(1);
  301. return -ENOSYS;
  302. } else {
  303. printk(KERN_INFO "%s: Attempt to write to unallocated " \
  304. "channel.\n", THIS_MODULE->name);
  305. return -EINVAL;
  306. }
  307. }
  308. static ssize_t dahdi_tc_read(struct file *file, __user char *usrbuf, size_t count, loff_t *ppos)
  309. {
  310. if (file->private_data) {
  311. /* file->private_data will not be NULL if DAHDI_TC_ALLOCATE was
  312. * called, and therefore indicates that the transcoder driver
  313. * did not export a write function. */
  314. WARN_ON(1);
  315. return -ENOSYS;
  316. } else {
  317. printk(KERN_INFO "%s: Attempt to read from unallocated " \
  318. "channel.\n", THIS_MODULE->name);
  319. return -EINVAL;
  320. }
  321. }
  322. static long dahdi_tc_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long data)
  323. {
  324. switch (cmd) {
  325. case DAHDI_TC_ALLOCATE:
  326. return dahdi_tc_allocate(file, data);
  327. case DAHDI_TC_GETINFO:
  328. return dahdi_tc_getinfo(data);
  329. case DAHDI_TRANSCODE_OP:
  330. /* This is a deprecated call from the previous transcoder
  331. * interface, which was all routed through the dahdi_ioctl in
  332. * dahdi-base.c, and this ioctl request was used to indicate
  333. * that the call should be forwarded to this function. Now
  334. * when the file is opened, the f_ops pointer is updated to
  335. * point directly to this function, and we don't need a
  336. * general indication that the ioctl is destined for the
  337. * transcoder.
  338. *
  339. * I'm keeping this ioctl here in order to explain why there
  340. * might be a hole in the ioctl numbering scheme in the header
  341. * files.
  342. */
  343. printk(KERN_WARNING "%s: DAHDI_TRANSCODE_OP is no longer " \
  344. "supported. Please call DAHDI_TC ioctls directly.\n",
  345. THIS_MODULE->name);
  346. return -EINVAL;
  347. default:
  348. return -EINVAL;
  349. };
  350. }
  351. #ifndef HAVE_UNLOCKED_IOCTL
  352. static int dahdi_tc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data)
  353. {
  354. return (int)dahdi_tc_unlocked_ioctl(file, cmd, data);
  355. }
  356. #endif
  357. static int dahdi_tc_mmap(struct file *file, struct vm_area_struct *vma)
  358. {
  359. printk(KERN_ERR "%s: mmap interface deprecated.\n", THIS_MODULE->name);
  360. return -ENOSYS;
  361. }
  362. static unsigned int dahdi_tc_poll(struct file *file, struct poll_table_struct *wait_table)
  363. {
  364. int ret;
  365. struct dahdi_transcoder_channel *chan = file->private_data;
  366. if (!chan) {
  367. /* This is because the DAHDI_TC_ALLOCATE ioctl was not called
  368. * before calling poll, which is invalid. */
  369. return -EINVAL;
  370. }
  371. poll_wait(file, &chan->ready, wait_table);
  372. ret = dahdi_tc_is_busy(chan) ? 0 : POLLPRI;
  373. ret |= dahdi_tc_is_built(chan) ? POLLOUT : 0;
  374. ret |= dahdi_tc_is_data_waiting(chan) ? POLLIN : 0;
  375. return ret;
  376. }
  377. static struct file_operations __dahdi_transcode_fops = {
  378. .owner = THIS_MODULE,
  379. .open = dahdi_tc_open,
  380. .release = dahdi_tc_release,
  381. #ifdef HAVE_UNLOCKED_IOCTL
  382. .unlocked_ioctl = dahdi_tc_unlocked_ioctl,
  383. #else
  384. .ioctl = dahdi_tc_ioctl,
  385. #endif
  386. .read = dahdi_tc_read,
  387. .write = dahdi_tc_write,
  388. .poll = dahdi_tc_poll,
  389. .mmap = dahdi_tc_mmap,
  390. };
  391. static struct dahdi_chardev transcode_chardev = {
  392. .name = "transcode",
  393. .minor = 250,
  394. };
  395. static int dahdi_transcode_init(void)
  396. {
  397. int res;
  398. if (dahdi_transcode_fops) {
  399. printk(KERN_WARNING "dahdi_transcode_fops already set.\n");
  400. return -EBUSY;
  401. }
  402. dahdi_transcode_fops = &__dahdi_transcode_fops;
  403. if ((res = dahdi_register_chardev(&transcode_chardev)))
  404. return res;
  405. printk(KERN_INFO "%s: Loaded.\n", THIS_MODULE->name);
  406. return 0;
  407. }
  408. static void dahdi_transcode_cleanup(void)
  409. {
  410. dahdi_unregister_chardev(&transcode_chardev);
  411. dahdi_transcode_fops = NULL;
  412. printk(KERN_DEBUG "%s: Unloaded.\n", THIS_MODULE->name);
  413. }
  414. module_param(debug, int, S_IRUGO | S_IWUSR);
  415. MODULE_DESCRIPTION("DAHDI Transcoder Support");
  416. MODULE_AUTHOR("Mark Spencer <markster@digium.com>");
  417. #ifdef MODULE_LICENSE
  418. MODULE_LICENSE("GPL");
  419. #endif
  420. module_init(dahdi_transcode_init);
  421. module_exit(dahdi_transcode_cleanup);