seq_prioq.c 9.5 KB

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