calssification: more heuristics to guess bluetooth device classes
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / router.c
1 /*
2  * module-murphy-ivi -- PulseAudio module for providing audio routing support
3  * Copyright (c) 2012, Intel Corporation.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU Lesser General Public License,
7  * version 2.1, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.
12  * See the GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
17  * MA 02110-1301 USA.
18  *
19  */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24
25 #include <pulsecore/pulsecore-config.h>
26
27 #include <pulse/proplist.h>
28 #include <pulsecore/module.h>
29
30 #include "router.h"
31 #include "node.h"
32 #include "switch.h"
33 #include "constrain.h"
34 #include "volume.h"
35 #include "fader.h"
36 #include "utils.h"
37 #include "classify.h"
38
39
40 static void rtgroup_destroy(struct userdata *, mir_rtgroup *);
41 static int rtgroup_print(mir_rtgroup *, char *, int);
42 static void rtgroup_update_module_property(struct userdata *, mir_rtgroup *);
43
44 static void add_rtentry(struct userdata *, mir_rtgroup *, mir_node *);
45 static void remove_rtentry(struct userdata *, mir_rtentry *);
46
47 static void make_explicit_routes(struct userdata *, uint32_t);
48 static mir_node *find_default_route(struct userdata *, mir_node *, uint32_t);
49
50
51 static int uint32_cmp(uint32_t, uint32_t);
52
53 static int node_priority(struct userdata *, mir_node *);
54
55
56 static void pa_hashmap_rtgroup_free(void *rtg, void *u)
57 {
58     rtgroup_destroy(u, rtg);
59 }
60
61
62 pa_router *pa_router_init(struct userdata *u)
63 {
64     size_t     num_classes = mir_application_class_end;
65     pa_router *router = pa_xnew0(pa_router, 1);
66     
67     router->rtgroups = pa_hashmap_new(pa_idxset_string_hash_func,
68                                       pa_idxset_string_compare_func);
69     router->maplen = num_classes;
70     router->classmap = pa_xnew0(mir_rtgroup *, num_classes);
71     router->priormap = pa_xnew0(int, num_classes);
72     MIR_DLIST_INIT(router->nodlist);
73     MIR_DLIST_INIT(router->connlist);
74     
75     return router;
76 }
77
78 void pa_router_done(struct userdata *u)
79 {
80     pa_router      *router;
81     mir_connection *conn, *c;
82     mir_node       *e,*n;
83
84     if (u && (router = u->router)) {
85         MIR_DLIST_FOR_EACH_SAFE(mir_node, rtentries, e,n, &router->nodlist) {
86             MIR_DLIST_UNLINK(mir_node, rtentries, e);
87         }
88
89         MIR_DLIST_FOR_EACH_SAFE(mir_connection,link, conn,c,&router->connlist){
90             MIR_DLIST_UNLINK(mir_connection, link, conn);
91             pa_xfree(conn);
92         }
93
94         pa_hashmap_free(router->rtgroups, pa_hashmap_rtgroup_free,u);
95
96         pa_xfree(router->classmap);
97         pa_xfree(router->priormap);
98         pa_xfree(router);
99
100         u->router = NULL;
101     }
102 }
103
104
105 void mir_router_assign_class_priority(struct userdata *u,
106                                       mir_node_type    class,
107                                       int              pri)
108 {
109     pa_router *router;
110     int *priormap;
111
112     pa_assert(u);
113     pa_assert_se((router = u->router));
114     pa_assert_se((priormap = router->priormap));
115
116     if (class >= 0 && class < router->maplen) {
117         pa_log_debug("assigning priority %d to class '%s'",
118                      pri, mir_node_type_str(class));
119         priormap[class] = pri;
120     }
121 }
122
123
124 pa_bool_t mir_router_create_rtgroup(struct userdata      *u,
125                                     const char           *name,
126                                     mir_rtgroup_accept_t  accept,
127                                     mir_rtgroup_compare_t compare)
128 {
129     pa_router   *router;
130     mir_rtgroup *rtg;
131
132     pa_assert(u);
133     pa_assert(name);
134     pa_assert(accept);
135     pa_assert(compare);
136     pa_assert_se((router = u->router));
137
138     rtg = pa_xnew0(mir_rtgroup, 1);
139     rtg->name    = pa_xstrdup(name);
140     rtg->accept  = accept;
141     rtg->compare = compare;
142     MIR_DLIST_INIT(rtg->entries);
143
144     if (pa_hashmap_put(router->rtgroups, rtg->name, rtg) < 0) {
145         pa_xfree(rtg->name);
146         pa_xfree(rtg);
147         return FALSE;
148     }
149
150     pa_log_debug("routing group '%s' created", name);
151
152     return TRUE;
153 }
154
155 void mir_router_destroy_rtgroup(struct userdata *u, const char *name)
156 {
157     pa_router *router;
158     mir_rtgroup *rtg;
159
160     pa_assert(u);
161     pa_assert(name);
162     pa_assert_se((router = u->router));
163
164     if (!(rtg = pa_hashmap_remove(router->rtgroups, name)))
165         pa_log_debug("can't destroy routing group '%s': group not found",name);
166     else {
167         rtgroup_destroy(u, rtg);
168         pa_log_debug("routing group '%s' destroyed", name);
169     }
170 }
171
172
173 pa_bool_t mir_router_assign_class_to_rtgroup(struct userdata *u,
174                                              mir_node_type    class,
175                                              const char      *rtgrpnam)
176 {
177     pa_router *router;
178     mir_rtgroup *rtg;
179     const char * clnam;
180
181     pa_assert(u);
182     pa_assert(rtgrpnam);
183     pa_assert_se((router = u->router));
184
185     if (class < 0 || class >= router->maplen) {
186         pa_log_debug("can't assign class (%d) to  routing group '%s': "
187                      "class id is out of range (0 - %d)",
188                      class, rtgrpnam, router->maplen);
189         return FALSE;
190     }
191
192     clnam = mir_node_type_str(class);
193
194     if (!(rtg = pa_hashmap_get(router->rtgroups, rtgrpnam))) {
195         pa_log_debug("can't assign class '%s' to routing group '%s': "
196                      "router group not found", clnam, rtgrpnam);
197     }
198
199     router->classmap[class] = rtg;
200
201     pa_log_debug("class '%s' assigned to routing group '%s'", clnam, rtgrpnam);
202
203     return TRUE;
204 }
205
206
207 #if 0
208 void mir_router_register_node(struct userdata *u, mir_node *node)
209 {
210     pa_router   *router;
211     mir_rtgroup *rtg;
212     void        *state;
213     int          priority;
214     mir_node    *before;
215
216     pa_assert(u);
217     pa_assert(node);
218     pa_assert_se((router = u->router));
219     
220     if (node->implement == mir_device) {
221         if (node->direction == mir_output) {
222             PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
223                 add_rtentry(u, rtg, node);
224             }
225         }
226         return;
227     }
228     
229     if (node->implement == mir_stream) {
230         if (node->direction == mir_input) {
231             priority = node_priority(u, node);
232             
233             MIR_DLIST_FOR_EACH(mir_node, rtentries, before, &router->nodlist) {
234                 if (priority < node_priority(u, before)) {
235                     MIR_DLIST_INSERT_BEFORE(mir_node, rtentries, node,
236                                             &before->rtentries);
237                     return;
238                 }
239             }
240             
241             MIR_DLIST_APPEND(mir_node, rtentries, node, &router->nodlist);
242         }
243         return;
244     }
245 }
246 #endif
247
248 void mir_router_register_node(struct userdata *u, mir_node *node)
249 {
250     pa_router   *router;
251     mir_rtgroup *rtg;
252     void        *state;
253     int          priority;
254     mir_node    *before;
255
256     pa_assert(u);
257     pa_assert(node);
258     pa_assert_se((router = u->router));
259     
260     if (node->direction == mir_output) {
261         if (node->implement == mir_device) {
262             PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
263                 add_rtentry(u, rtg, node);
264             }
265         }
266         return;
267     }
268
269     
270     if (node->direction == mir_input) {
271         if (node->implement == mir_device &&
272             !pa_classify_loopback_stream(node))
273             return;
274
275         priority = node_priority(u, node);
276             
277         MIR_DLIST_FOR_EACH(mir_node, rtentries, before, &router->nodlist) {
278             if (priority < node_priority(u, before)) {
279                 MIR_DLIST_INSERT_BEFORE(mir_node, rtentries, node,
280                                         &before->rtentries);
281                 return;
282             }
283         }
284             
285         MIR_DLIST_APPEND(mir_node, rtentries, node, &router->nodlist);
286
287         return;
288     }
289 }
290
291 void mir_router_unregister_node(struct userdata *u, mir_node *node)
292 {
293     pa_router *router;
294     mir_rtentry *rte, *n;
295     
296     pa_assert(u);
297     pa_assert(node);
298     pa_assert_se((router = u->router));
299
300     if (node->direction == mir_output && node->implement == mir_device) {
301         MIR_DLIST_FOR_EACH_SAFE(mir_rtentry,nodchain, rte,n, &node->rtentries){
302             remove_rtentry(u, rte);
303         }
304         return;
305     }
306
307     if (node->direction == mir_input) {
308         MIR_DLIST_UNLINK(mir_node, rtentries, node);
309         return;
310     }
311 }
312
313 mir_connection *mir_router_add_explicit_route(struct userdata *u,
314                                               uint16_t   amid,
315                                               mir_node  *from,
316                                               mir_node  *to)
317 {
318     pa_router *router;
319     mir_connection *conn;
320
321     pa_assert(u);
322     pa_assert(from);
323     pa_assert(to);
324     pa_assert_se((router = u->router));
325
326     conn = pa_xnew0(mir_connection, 1);
327     MIR_DLIST_INIT(conn->link);
328     conn->amid = amid;
329     conn->from = from->index;
330     conn->to = to->index;
331     
332     MIR_DLIST_APPEND(mir_connection, link, conn, &router->connlist);
333
334     mir_router_make_routing(u);
335
336     return conn;
337 }
338
339 void mir_router_remove_explicit_route(struct userdata *u, mir_connection *conn)
340 {
341     pa_core   *core;
342     pa_router *router;
343     mir_node  *from;
344     mir_node  *to;
345
346     pa_assert(u);
347     pa_assert(conn);
348     pa_assert_se((core = u->core));
349     pa_assert_se((router = u->router));
350
351     MIR_DLIST_UNLINK(mir_connection, link, conn);
352
353     if (!(from = mir_node_find_by_index(u, conn->from)) ||
354         !(to   = mir_node_find_by_index(u, conn->to))     )
355     {
356         pa_log_debug("can't remove explicit route: some node was not found");
357     }
358     else {
359         pa_log_debug("tear down link '%s' => '%s'", from->amname, to->amname);
360
361         if (!mir_switch_teardown_link(u, from, to)) {
362             pa_log_debug("can't remove explicit route: "
363                          "failed to teardown link");
364         }
365         else {
366             if (!conn->blocked)
367                 mir_router_make_routing(u);
368         }
369     }
370
371     pa_xfree(conn);
372 }
373
374 int mir_router_print_rtgroups(struct userdata *u, char *buf, int len)
375 {
376     pa_router *router;
377     mir_rtgroup *rtg;
378     void *state;
379     char *p, *e;
380
381     pa_assert(u);
382     pa_assert(buf);
383     pa_assert(len > 0);
384     pa_assert_se((router = u->router));
385     pa_assert(router->rtgroups);
386
387     e = (p = buf) + len;
388
389     if (len > 0) {
390         p += snprintf(p, e-p, "routing table:\n");
391
392         if (p < e) {
393             PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
394                 if (p >= e) break;
395                 p += snprintf(p, e-p, "   %s:", rtg->name);
396                 
397                 if (p >= e) break;
398                 p += rtgroup_print(rtg, p, e-p);
399                 
400                 if (p >= e) break;
401                 p += snprintf(p, e-p, "\n");
402             }
403         }
404     }
405
406     return p - buf;
407 }
408
409
410 mir_node *mir_router_make_prerouting(struct userdata *u, mir_node *data)
411 {
412     pa_router     *router;
413     mir_node      *from;
414     mir_node      *to;
415     int            priority;
416     pa_bool_t      done;
417     mir_node      *target;
418     uint32_t       stamp;
419
420     pa_assert(u);
421     pa_assert_se((router = u->router));
422     pa_assert_se((data->implement == mir_stream));
423     pa_assert_se((data->direction == mir_input));
424
425     priority = node_priority(u, data);
426     done = FALSE;
427     target = NULL;
428     stamp = pa_utils_new_stamp();
429
430     make_explicit_routes(u, stamp);
431
432     MIR_DLIST_FOR_EACH_BACKWARD(mir_node, rtentries, from, &router->nodlist) {
433         if (from->implement == mir_device) {
434             if (from->direction == mir_output)
435                 continue;       /* we should never get here */
436             if (!from->mux && !from->loop)
437                 continue;       /* skip not looped back input nodes */
438         }
439
440         if (priority >= node_priority(u, from)) {
441             if ((target = find_default_route(u, data, stamp))) {
442                 mir_switch_setup_link(u, NULL, target, FALSE);
443                 mir_volume_add_limiting_class(u, target, data->type, stamp);
444             }
445             done = TRUE;
446         }
447
448         if (from->stamp >= stamp)
449             continue;
450
451         if ((to = find_default_route(u, from, stamp))) {
452             mir_switch_setup_link(u, from, to, FALSE);
453             mir_volume_add_limiting_class(u, to, from->type, stamp);
454         }
455     }    
456
457     if (!done && (target = find_default_route(u, data, stamp))) {
458         mir_switch_setup_link(u, NULL, target, FALSE);
459         mir_volume_add_limiting_class(u, target, data->type, stamp);
460     }
461
462     return target;
463 }
464
465
466 void mir_router_make_routing(struct userdata *u)
467 {
468     static pa_bool_t ongoing_routing;
469
470     pa_router  *router;
471     mir_node   *from;
472     mir_node   *to;
473     uint32_t    stamp;
474
475     pa_assert(u);
476     pa_assert_se((router = u->router));
477
478     if (ongoing_routing)
479         return;
480
481     ongoing_routing = TRUE;
482     stamp = pa_utils_new_stamp();
483
484     make_explicit_routes(u, stamp);
485
486     MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, from, &router->nodlist) {
487         if (from->implement == mir_device) {
488             if (from->direction == mir_output)
489                 continue;       /* we should never get here */
490             if (!from->mux && !from->loop)
491                 continue;       /* skip not looped back input nodes */
492         }
493
494         if (from->stamp >= stamp)
495             continue;
496
497         if ((to = find_default_route(u, from, stamp))) {
498             mir_switch_setup_link(u, from, to, FALSE);
499             mir_volume_add_limiting_class(u, to, from->type, stamp);
500         }
501     }    
502
503     pa_fader_apply_volume_limits(u, stamp);
504
505     ongoing_routing = FALSE;
506 }
507
508
509
510 pa_bool_t mir_router_default_accept(struct userdata *u, mir_rtgroup *rtg,
511                                     mir_node *node)
512 {
513     pa_bool_t accept;
514
515     pa_assert(u);
516     pa_assert(rtg);
517     pa_assert(node);
518
519     accept = (node->type >= mir_device_class_begin &&
520               node->type < mir_device_class_end);
521         
522     return accept;
523 }
524
525
526 pa_bool_t mir_router_phone_accept(struct userdata *u, mir_rtgroup *rtg,
527                                   mir_node *node)
528 {
529     mir_node_type class;
530
531     pa_assert(u);
532     pa_assert(rtg);
533     pa_assert(node);
534
535     class = node->type;
536
537     if (class >= mir_device_class_begin &&  class < mir_device_class_end) {
538         if (class != mir_bluetooth_a2dp  &&
539             class != mir_usb_headphone   &&
540             class != mir_wired_headphone &&
541             class != mir_hdmi            &&
542             class != mir_spdif             )
543         {
544             return TRUE;
545         }
546     }
547
548     return FALSE;
549 }
550
551
552 int mir_router_default_compare(struct userdata *u, mir_node *n1, mir_node *n2)
553 {
554     uint32_t p1, p2;
555
556     (void)u;
557
558     pa_assert(n1);
559     pa_assert(n2);
560
561     if (n1->type == mir_null)
562         return -1;
563     if (n2->type == mir_null)
564         return 1;
565
566     p1 = ((((n1->channels & 31) << 5) + n1->privacy) << 2) + n1->location;
567     p2 = ((((n2->channels & 31) << 5) + n2->privacy) << 2) + n2->location;
568
569     p1 = (p1 << 8) + ((n1->type - mir_device_class_begin) & 0xff);
570     p2 = (p2 << 8) + ((n2->type - mir_device_class_begin) & 0xff);
571
572     return uint32_cmp(p1,p2);
573 }
574
575
576 int mir_router_phone_compare(struct userdata *u, mir_node *n1, mir_node *n2)
577 {
578     uint32_t p1, p2;
579
580     (void)u;
581
582     pa_assert(n1);
583     pa_assert(n2);
584
585     if (n1->type == mir_null)
586         return -1;
587     if (n2->type == mir_null)
588         return 1;
589
590     p1 = (n1->privacy << 8) + ((n1->type - mir_device_class_begin) & 0xff);
591     p2 = (n2->privacy << 8) + ((n2->type - mir_device_class_begin) & 0xff);
592
593     return uint32_cmp(p1,p2);
594 }
595
596
597 static void rtgroup_destroy(struct userdata *u, mir_rtgroup *rtg)
598 {
599     mir_rtentry *rte, *n;
600
601     pa_assert(u);
602     pa_assert(rtg);
603
604     MIR_DLIST_FOR_EACH_SAFE(mir_rtentry, link, rte,n, &rtg->entries) {
605         remove_rtentry(u, rte);
606     }
607
608     pa_xfree(rtg->name);
609     pa_xfree(rtg);
610 }
611
612 static int rtgroup_print(mir_rtgroup *rtg, char *buf, int len)
613 {
614     mir_rtentry *rte;
615     mir_node *node;
616     char *p, *e;
617
618     e = (p = buf) + len;
619
620     MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
621         node = rte->node;
622         if (p >= e)
623             break;
624         p += snprintf(p, e-p, " '%s'", node->amname);
625     }
626
627     return p - buf;
628 }
629
630 static void rtgroup_update_module_property(struct userdata *u,mir_rtgroup *rtg)
631 {
632     pa_module *module;
633     char       key[64];
634     char       value[512];
635
636     pa_assert(u);
637     pa_assert(rtg);
638     pa_assert_se((module = u->module));
639
640     snprintf(key, sizeof(key), PA_PROP_ROUTING_TABLE ".%s", rtg->name);
641     rtgroup_print(rtg, value, sizeof(value));
642
643     pa_proplist_sets(module->proplist, key, value+1); /* skip ' '@beginning */
644 }
645
646 static void add_rtentry(struct userdata *u, mir_rtgroup *rtg, mir_node *node)
647 {
648     pa_router *router;
649     mir_rtentry *rte, *before;
650
651     pa_assert(u);
652     pa_assert(rtg);
653     pa_assert(node);
654     pa_assert_se((router = u->router));
655
656     if (!rtg->accept(u, rtg, node)) {
657         pa_log_debug("refuse node '%s' registration to routing group '%s'",
658                      node->amname, rtg->name);
659         return;
660     }
661
662     rte = pa_xnew0(mir_rtentry, 1);
663
664     MIR_DLIST_APPEND(mir_rtentry, nodchain, rte, &node->rtentries);
665     rte->group = rtg;
666     rte->node  = node;
667
668     MIR_DLIST_FOR_EACH(mir_rtentry, link, before, &rtg->entries) {
669         if (rtg->compare(u, node, before->node) < 0) {
670             MIR_DLIST_INSERT_BEFORE(mir_rtentry, link, rte, &before->link);
671             goto added;
672         }
673     }
674
675     MIR_DLIST_APPEND(mir_rtentry, link, rte, &rtg->entries);
676
677  added:
678     rtgroup_update_module_property(u, rtg);
679     pa_log_debug("node '%s' added to routing group '%s'",
680                  node->amname, rtg->name);
681 }
682
683 static void remove_rtentry(struct userdata *u, mir_rtentry *rte)
684 {
685     pa_assert(u);
686     pa_assert(rte);
687
688     MIR_DLIST_UNLINK(mir_rtentry, link, rte);
689     MIR_DLIST_UNLINK(mir_rtentry, nodchain, rte);
690
691     pa_xfree(rte);
692 }
693
694 static void make_explicit_routes(struct userdata *u, uint32_t stamp)
695 {
696     pa_router *router;
697     mir_connection *conn;
698     mir_node *from;
699     mir_node *to;
700
701     pa_assert(u);
702     pa_assert_se((router = u->router));
703
704     MIR_DLIST_FOR_EACH_BACKWARD(mir_connection,link, conn, &router->connlist) {
705         if (conn->blocked)
706             continue;
707         
708         if (!(from = mir_node_find_by_index(u, conn->from)) ||
709             !(to   = mir_node_find_by_index(u, conn->to))     )
710         {
711             pa_log_debug("ignoring explicit route %u: some of the nodes "
712                          "not found", conn->amid);
713             continue;
714         }
715
716         if (!mir_switch_setup_link(u, from, to, TRUE))
717             continue;
718
719         if (from->implement == mir_stream)
720             from->stamp = stamp;
721
722         if (to->implement == mir_device)
723             mir_volume_add_limiting_class(u, to, from->type, stamp);
724     }
725 }
726
727
728 static mir_node *find_default_route(struct userdata *u,
729                                     mir_node        *from,
730                                     uint32_t         stamp)
731 {
732     pa_router     *router = u->router;
733     mir_node_type  class  = pa_classify_guess_application_class(from);
734     mir_node      *to;
735     mir_rtgroup   *rtg;
736     mir_rtentry   *rte;
737
738
739     if (class < 0 || class > router->maplen) {
740         pa_log_debug("can't route '%s': class %d is out of range (0 - %d)",
741                      from->amname, class, router->maplen);
742         return NULL;
743     }
744     
745     if (!(rtg = router->classmap[class])) {
746         pa_log_debug("node '%s' won't be routed beacuse its class '%s' "
747                      "is not assigned to any router group",
748                      from->amname, mir_node_type_str(class));
749         return NULL;
750     }
751     
752     pa_log_debug("using '%s' router group when routing '%s'",
753                  rtg->name, from->amname);
754
755         
756     MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
757         if (!(to = rte->node)) {
758             pa_log("   node was null in mir_rtentry");
759             continue;
760         }
761         
762         if (to->ignore) {
763             pa_log_debug("   '%s' ignored. Skipping...",to->amname);
764             continue;
765         }
766
767         if (!to->available) {
768             pa_log_debug("   '%s' not available. Skipping...", to->amname);
769             continue;
770         }
771
772         if (to->paidx == PA_IDXSET_INVALID) {
773             if (to->paport == NULL &&
774                 to->type   != mir_bluetooth_a2dp &&
775                 to->type   != mir_bluetooth_sco)
776             {
777                 pa_log_debug("   '%s' has no sink. Skipping...", to->amname);
778                 continue;
779             }
780         }
781
782         if (rte->stamp < stamp)
783             mir_constrain_apply(u, to, stamp);
784         else {
785             if (rte->blocked) {
786                 pa_log_debug("   '%s' is blocked by constraints. Skipping...",
787                              to->amname);
788                 continue;
789             }
790         }
791         
792         pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
793
794         return to;
795     }
796     
797     pa_log_debug("could not find route for '%s'", from->amname);
798
799     return NULL;
800 }
801
802
803
804 static int uint32_cmp(uint32_t v1, uint32_t v2)
805 {
806     if (v1 > v2)
807         return 1;
808     if (v1 < v2)
809         return -1;
810     return 0;
811 }
812
813
814 static int node_priority(struct userdata *u, mir_node *node)
815 {
816     pa_router *router;
817     mir_node_type type;
818     int class;
819
820     pa_assert(u);
821     pa_assert(node);
822     pa_assert_se((router = u->router));
823     pa_assert(router->priormap);
824
825     class = pa_classify_guess_application_class(node);
826
827     if (class < 0 || class >= router->maplen)
828         return 0;
829
830     return router->priormap[class];
831 }
832
833
834 /*
835  * Local Variables:
836  * c-basic-offset: 4
837  * indent-tabs-mode: nil
838  * End:
839  *
840  */