seq_prioq.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. /*
  2. * ALSA sequencer Priority Queue
  3. * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
  4. *
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19. *
  20. */
  21. #include <linux/time.h>
  22. #include <linux/slab.h>
  23. #include <sound/core.h>
  24. #include "seq_timer.h"
  25. #include "seq_prioq.h"
  26. /* Implementation is a simple linked list for now...
  27. This priority queue orders the events on timestamp. For events with an
  28. equeal timestamp the queue behaves as a FIFO.
  29. *
  30. * +-------+
  31. * Head --> | first |
  32. * +-------+
  33. * |next
  34. * +-----v-+
  35. * | |
  36. * +-------+
  37. * |
  38. * +-----v-+
  39. * | |
  40. * +-------+
  41. * |
  42. * +-----v-+
  43. * Tail --> | last |
  44. * +-------+
  45. *
  46. */
  47. /* create new prioq (constructor) */
  48. struct snd_seq_prioq *snd_seq_prioq_new(void)
  49. {
  50. struct snd_seq_prioq *f;
  51. f = kzalloc(sizeof(*f), GFP_KERNEL);
  52. if (!f)
  53. return NULL;
  54. spin_lock_init(&f->lock);
  55. f->head = NULL;
  56. f->tail = NULL;
  57. f->cells = 0;
  58. return f;
  59. }
  60. /* delete prioq (destructor) */
  61. void snd_seq_prioq_delete(struct snd_seq_prioq **fifo)
  62. {
  63. struct snd_seq_prioq *f = *fifo;
  64. *fifo = NULL;
  65. if (f == NULL) {
  66. pr_debug("ALSA: seq: snd_seq_prioq_delete() called with NULL prioq\n");
  67. return;
  68. }
  69. /* release resources...*/
  70. /*....................*/
  71. if (f->cells > 0) {
  72. /* drain prioQ */
  73. while (f->cells > 0)
  74. snd_seq_cell_free(snd_seq_prioq_cell_out(f));
  75. }
  76. kfree(f);
  77. }
  78. /* compare timestamp between events */
  79. /* return 1 if a >= b; 0 */
  80. static inline int compare_timestamp(struct snd_seq_event *a,
  81. struct snd_seq_event *b)
  82. {
  83. if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
  84. /* compare ticks */
  85. return (snd_seq_compare_tick_time(&a->time.tick, &b->time.tick));
  86. } else {
  87. /* compare real time */
  88. return (snd_seq_compare_real_time(&a->time.time, &b->time.time));
  89. }
  90. }
  91. /* compare timestamp between events */
  92. /* return negative if a < b;
  93. * zero if a = b;
  94. * positive if a > b;
  95. */
  96. static inline int compare_timestamp_rel(struct snd_seq_event *a,
  97. struct snd_seq_event *b)
  98. {
  99. if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
  100. /* compare ticks */
  101. if (a->time.tick > b->time.tick)
  102. return 1;
  103. else if (a->time.tick == b->time.tick)
  104. return 0;
  105. else
  106. return -1;
  107. } else {
  108. /* compare real time */
  109. if (a->time.time.tv_sec > b->time.time.tv_sec)
  110. return 1;
  111. else if (a->time.time.tv_sec == b->time.time.tv_sec) {
  112. if (a->time.time.tv_nsec > b->time.time.tv_nsec)
  113. return 1;
  114. else if (a->time.time.tv_nsec == b->time.time.tv_nsec)
  115. return 0;
  116. else
  117. return -1;
  118. } else
  119. return -1;
  120. }
  121. }
  122. /* enqueue cell to prioq */
  123. int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
  124. struct snd_seq_event_cell * cell)
  125. {
  126. struct snd_seq_event_cell *cur, *prev;
  127. unsigned long flags;
  128. int count;
  129. int prior;
  130. if (snd_BUG_ON(!f || !cell))
  131. return -EINVAL;
  132. /* check flags */
  133. prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
  134. spin_lock_irqsave(&f->lock, flags);
  135. /* check if this element needs to inserted at the end (ie. ordered
  136. data is inserted) This will be very likeley if a sequencer
  137. application or midi file player is feeding us (sequential) data */
  138. if (f->tail && !prior) {
  139. if (compare_timestamp(&cell->event, &f->tail->event)) {
  140. /* add new cell to tail of the fifo */
  141. f->tail->next = cell;
  142. f->tail = cell;
  143. cell->next = NULL;
  144. f->cells++;
  145. spin_unlock_irqrestore(&f->lock, flags);
  146. return 0;
  147. }
  148. }
  149. /* traverse list of elements to find the place where the new cell is
  150. to be inserted... Note that this is a order n process ! */
  151. prev = NULL; /* previous cell */
  152. cur = f->head; /* cursor */
  153. count = 10000; /* FIXME: enough big, isn't it? */
  154. while (cur != NULL) {
  155. /* compare timestamps */
  156. int rel = compare_timestamp_rel(&cell->event, &cur->event);
  157. if (rel < 0)
  158. /* new cell has earlier schedule time, */
  159. break;
  160. else if (rel == 0 && prior)
  161. /* equal schedule time and prior to others */
  162. break;
  163. /* new cell has equal or larger schedule time, */
  164. /* move cursor to next cell */
  165. prev = cur;
  166. cur = cur->next;
  167. if (! --count) {
  168. spin_unlock_irqrestore(&f->lock, flags);
  169. pr_err("ALSA: seq: cannot find a pointer.. infinite loop?\n");
  170. return -EINVAL;
  171. }
  172. }
  173. /* insert it before cursor */
  174. if (prev != NULL)
  175. prev->next = cell;
  176. cell->next = cur;
  177. if (f->head == cur) /* this is the first cell, set head to it */
  178. f->head = cell;
  179. if (cur == NULL) /* reached end of the list */
  180. f->tail = cell;
  181. f->cells++;
  182. spin_unlock_irqrestore(&f->lock, flags);
  183. return 0;
  184. }
  185. /* dequeue cell from prioq */
  186. struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f)
  187. {
  188. struct snd_seq_event_cell *cell;
  189. unsigned long flags;
  190. if (f == NULL) {
  191. pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
  192. return NULL;
  193. }
  194. spin_lock_irqsave(&f->lock, flags);
  195. cell = f->head;
  196. if (cell) {
  197. f->head = cell->next;
  198. /* reset tail if this was the last element */
  199. if (f->tail == cell)
  200. f->tail = NULL;
  201. cell->next = NULL;
  202. f->cells--;
  203. }
  204. spin_unlock_irqrestore(&f->lock, flags);
  205. return cell;
  206. }
  207. /* return number of events available in prioq */
  208. int snd_seq_prioq_avail(struct snd_seq_prioq * f)
  209. {
  210. if (f == NULL) {
  211. pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
  212. return 0;
  213. }
  214. return f->cells;
  215. }
  216. /* peek at cell at the head of the prioq */
  217. struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq * f)
  218. {
  219. if (f == NULL) {
  220. pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
  221. return NULL;
  222. }
  223. return f->head;
  224. }
  225. static inline int prioq_match(struct snd_seq_event_cell *cell,
  226. int client, int timestamp)
  227. {
  228. if (cell->event.source.client == client ||
  229. cell->event.dest.client == client)
  230. return 1;
  231. if (!timestamp)
  232. return 0;
  233. switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
  234. case SNDRV_SEQ_TIME_STAMP_TICK:
  235. if (cell->event.time.tick)
  236. return 1;
  237. break;
  238. case SNDRV_SEQ_TIME_STAMP_REAL:
  239. if (cell->event.time.time.tv_sec ||
  240. cell->event.time.time.tv_nsec)
  241. return 1;
  242. break;
  243. }
  244. return 0;
  245. }
  246. /* remove cells for left client */
  247. void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
  248. {
  249. register struct snd_seq_event_cell *cell, *next;
  250. unsigned long flags;
  251. struct snd_seq_event_cell *prev = NULL;
  252. struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
  253. /* collect all removed cells */
  254. spin_lock_irqsave(&f->lock, flags);
  255. cell = f->head;
  256. while (cell) {
  257. next = cell->next;
  258. if (prioq_match(cell, client, timestamp)) {
  259. /* remove cell from prioq */
  260. if (cell == f->head) {
  261. f->head = cell->next;
  262. } else {
  263. prev->next = cell->next;
  264. }
  265. if (cell == f->tail)
  266. f->tail = cell->next;
  267. f->cells--;
  268. /* add cell to free list */
  269. cell->next = NULL;
  270. if (freefirst == NULL) {
  271. freefirst = cell;
  272. } else {
  273. freeprev->next = cell;
  274. }
  275. freeprev = cell;
  276. } else {
  277. #if 0
  278. pr_debug("ALSA: seq: type = %i, source = %i, dest = %i, "
  279. "client = %i\n",
  280. cell->event.type,
  281. cell->event.source.client,
  282. cell->event.dest.client,
  283. client);
  284. #endif
  285. prev = cell;
  286. }
  287. cell = next;
  288. }
  289. spin_unlock_irqrestore(&f->lock, flags);
  290. /* remove selected cells */
  291. while (freefirst) {
  292. freenext = freefirst->next;
  293. snd_seq_cell_free(freefirst);
  294. freefirst = freenext;
  295. }
  296. }
  297. static int prioq_remove_match(struct snd_seq_remove_events *info,
  298. struct snd_seq_event *ev)
  299. {
  300. int res;
  301. if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) {
  302. if (ev->dest.client != info->dest.client ||
  303. ev->dest.port != info->dest.port)
  304. return 0;
  305. }
  306. if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) {
  307. if (! snd_seq_ev_is_channel_type(ev))
  308. return 0;
  309. /* data.note.channel and data.control.channel are identical */
  310. if (ev->data.note.channel != info->channel)
  311. return 0;
  312. }
  313. if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) {
  314. if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
  315. res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
  316. else
  317. res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
  318. if (!res)
  319. return 0;
  320. }
  321. if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) {
  322. if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
  323. res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
  324. else
  325. res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
  326. if (res)
  327. return 0;
  328. }
  329. if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) {
  330. if (ev->type != info->type)
  331. return 0;
  332. }
  333. if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) {
  334. /* Do not remove off events */
  335. switch (ev->type) {
  336. case SNDRV_SEQ_EVENT_NOTEOFF:
  337. /* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */
  338. return 0;
  339. default:
  340. break;
  341. }
  342. }
  343. if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) {
  344. if (info->tag != ev->tag)
  345. return 0;
  346. }
  347. return 1;
  348. }
  349. /* remove cells matching remove criteria */
  350. void snd_seq_prioq_remove_events(struct snd_seq_prioq * f, int client,
  351. struct snd_seq_remove_events *info)
  352. {
  353. struct snd_seq_event_cell *cell, *next;
  354. unsigned long flags;
  355. struct snd_seq_event_cell *prev = NULL;
  356. struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
  357. /* collect all removed cells */
  358. spin_lock_irqsave(&f->lock, flags);
  359. cell = f->head;
  360. while (cell) {
  361. next = cell->next;
  362. if (cell->event.source.client == client &&
  363. prioq_remove_match(info, &cell->event)) {
  364. /* remove cell from prioq */
  365. if (cell == f->head) {
  366. f->head = cell->next;
  367. } else {
  368. prev->next = cell->next;
  369. }
  370. if (cell == f->tail)
  371. f->tail = cell->next;
  372. f->cells--;
  373. /* add cell to free list */
  374. cell->next = NULL;
  375. if (freefirst == NULL) {
  376. freefirst = cell;
  377. } else {
  378. freeprev->next = cell;
  379. }
  380. freeprev = cell;
  381. } else {
  382. prev = cell;
  383. }
  384. cell = next;
  385. }
  386. spin_unlock_irqrestore(&f->lock, flags);
  387. /* remove selected cells */
  388. while (freefirst) {
  389. freenext = freefirst->next;
  390. snd_seq_cell_free(freefirst);
  391. freefirst = freenext;
  392. }
  393. }