From c46faad66a8d44b67b9b270649c0b9812bf9eff7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 8 Dec 2013 22:10:39 +0000 Subject: Update vmeta to BMMv2 Update vmeta to use the dma_buf handling now provided by libbmm v2. This permits more flexible buffer management, as the buffers can now be passed via a standardized mechanism to other subsystems (such as DRM), and image data to be encoded can be accepted directly from other subsystems without needing to be copied. Signed-off-by: Russell King --- Makefile.am | 2 + configure.ac | 2 +- debian/changelog | 6 + rb.c | 549 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ rb.h | 128 +++++++++++++ uio_vmeta.h | 9 + vmeta_lib.c | 354 +++++++++++++++++++++++++++++++---- vmeta_lib.h | 3 + 8 files changed, 1019 insertions(+), 34 deletions(-) create mode 100644 rb.c create mode 100644 rb.h diff --git a/Makefile.am b/Makefile.am index 371482e..a714788 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,6 +7,8 @@ libvmeta_la_LDFLAGS = -avoid-version -no-undefined -export-symbols-regex "vdec_o libvmeta_la_LIBADD = @LIBBMM_LIBS@ libvmeta_la_SOURCES = \ + rb.c \ + rb.h \ vmeta_lib.c \ uio_vmeta.h diff --git a/configure.ac b/configure.ac index 5b8ab19..ffe5450 100644 --- a/configure.ac +++ b/configure.ac @@ -18,7 +18,7 @@ AC_USE_SYSTEM_EXTENSIONS LT_PREREQ([2.2]) LT_INIT -PKG_CHECK_MODULES(LIBBMM, libbmm >= 1.1.0) +PKG_CHECK_MODULES(LIBBMM, libbmm >= 2.0.0) AC_SUBST(LIBBMM_CFLAGS) AC_SUBST(LIBBMM_LIBS) diff --git a/debian/changelog b/debian/changelog index 93f3ae5..e20dcb6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +libvmeta (2.0) precise; urgency=low + + * Convert to use new BMM dma_buf APIs + + -- Russell King Sat, 7 Dec 2013 16:10:02 +0000 + libvmeta (1.0ubuntu2.rmk4) precise; urgency=low * Convert to use bmm_malloc_aligned_phys() API diff --git a/rb.c b/rb.c new file mode 100644 index 0000000..34a270e --- /dev/null +++ b/rb.c @@ -0,0 +1,549 @@ +/* +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* Revision 1.2. Jim Plank */ + +/* Original code by Jim Plank (plank@cs.utk.edu) */ +/* modified for THINK C 6.0 for Macintosh by Chris Bartley */ + +/* Modified by Russell King to support embedded keys only */ + +#include +#include +#include +#include +#include +#include "rb.h" + +static void mk_new_int(Rb_node l, Rb_node r, Rb_node p, int il); +static Rb_node lprev(Rb_node n); +static Rb_node rprev(Rb_node n); +static void recolor(Rb_node n); +static void single_rotate(Rb_node y, int l); +#ifdef DEBUG +static void rb_print_tree(Rb_node t, int level); +static void rb_iprint_tree(Rb_node t, int level); +#endif + + +#define isred(n) (n->s.red) +#define isblack(n) (!isred(n)) +#define isleft(n) (n->s.left) +#define isright(n) (!isleft(n)) +#define isint(n) (n->s.internal) +#define isext(n) (!isint(n)) +#define ishead(n) (n->s.head) +#define isroot(n) (n->s.root) +#define setred(n) n->s.red = 1 +#define setblack(n) n->s.red = 0 +#define setleft(n) n->s.left = 1 +#define setright(n) n->s.left = 0 +#define sethead(n) n->s.head = 1 +#define setroot(n) n->s.root = 1 +#define setint(n) n->s.internal = 1 +#define setext(n) n->s.internal = 0 +#define setnormal(n) { n->s.root = 0; n ->s.head = 0; } +#define sibling(n) ((isleft(n)) ? n->p.parent->c.child.right \ + : n->p.parent->c.child.left) + +static void insert(Rb_node item, Rb_node list) /* Inserts to the end of a list */ +{ + Rb_node last_node; + + last_node = list->c.list.blink; + + list->c.list.blink = item; + last_node->c.list.flink = item; + item->c.list.blink = last_node; + item->c.list.flink = list; +} + +static void delete_item(Rb_node item) /* Deletes an arbitrary iterm */ +{ + item->c.list.flink->c.list.blink = item->c.list.blink; + item->c.list.blink->c.list.flink = item->c.list.flink; +} + +#define mk_new_ext(new, vvval) {\ + new = (Rb_node) malloc(sizeof(struct rb_node));\ + new->v.val = vvval;\ + setext(new);\ + setblack(new);\ + setnormal(new);\ +} + +static void mk_new_int(Rb_node l, Rb_node r, Rb_node p, int il) +{ + Rb_node newnode; + + newnode = (Rb_node) malloc(sizeof(struct rb_node)); + setint(newnode); + setred(newnode); + setnormal(newnode); + newnode->c.child.left = l; + newnode->c.child.right = r; + newnode->p.parent = p; + newnode->k.lext = l; + newnode->v.rext = r; + l->p.parent = newnode; + r->p.parent = newnode; + setleft(l); + setright(r); + if (ishead(p)) { + p->p.root = newnode; + setroot(newnode); + } else if (il) { + setleft(newnode); + p->c.child.left = newnode; + } else { + setright(newnode); + p->c.child.right = newnode; + } + recolor(newnode); +} + + +Rb_node lprev(Rb_node n) +{ + if (ishead(n)) return n; + while (!isroot(n)) { + if (isright(n)) return n->p.parent; + n = n->p.parent; + } + return n->p.parent; +} + +Rb_node rprev(Rb_node n) +{ + if (ishead(n)) return n; + while (!isroot(n)) { + if (isleft(n)) return n->p.parent; + n = n->p.parent; + } + return n->p.parent; +} + +Rb_node make_rb(void) +{ + Rb_node head; + + head = (Rb_node) malloc (sizeof(struct rb_node)); + head->c.list.flink = head; + head->c.list.blink = head; + head->p.root = head; + sethead(head); + return head; +} + +Rb_node rb_find_key_n(Rb_node n, const void *key, rb_key_cmp_fn cmp, int *fnd) +{ + int rc; + *fnd = 0; +#ifdef DEBUG + if (!ishead(n)) { + fprintf(stderr, "%s called on non-head %p\n", __FUNCTION__, n); + exit(1); + } +#else + assert(ishead(n)); +#endif + if (n->p.root == n) return n; + rc = cmp(key, n->c.list.blink->v.val); + if (rc == 0) { + *fnd = 1; + return n->c.list.blink; + } + if (rc > 0) return n; + else n = n->p.root; + while (1) { + if (isext(n)) return n; + rc = cmp(key, n->k.lext->v.val); + if (rc == 0) { + *fnd = 1; + return n->k.lext; + } + n = rc < 0 ? n->c.child.left : n->c.child.right; + } +} + +Rb_node rb_find_key(Rb_node n, const void *key, rb_key_cmp_fn cmp) +{ + int fnd; + return rb_find_key_n(n, key, cmp, &fnd); +} + +Rb_node rb_insert_b(Rb_node n, void *val) +{ + Rb_node newleft, newright, newnode, p; + + if (ishead(n)) { + if (n->p.root == n) { /* Tree is empty */ + mk_new_ext(newnode, val); + insert(newnode, n); + n->p.root = newnode; + newnode->p.parent = n; + setroot(newnode); + return newnode; + } else { + mk_new_ext(newright, val); + insert(newright, n); + newleft = newright->c.list.blink; + setnormal(newleft); + mk_new_int(newleft, newright, newleft->p.parent, isleft(newleft)); + p = rprev(newright); + if (!ishead(p)) p->k.lext = newright; + return newright; + } + } else { + mk_new_ext(newleft, val); + insert(newleft, n); + setnormal(n); + mk_new_int(newleft, n, n->p.parent, isleft(n)); + p = lprev(newleft); + if (!ishead(p)) p->v.rext = newleft; + return newleft; + } +} + +static void recolor(Rb_node n) +{ + Rb_node p, gp, s; + int done = 0; + + while(!done) { + if (isroot(n)) { + setblack(n); + return; + } + + p = n->p.parent; + + if (isblack(p)) return; + + if (isroot(p)) { + setblack(p); + return; + } + + gp = p->p.parent; + s = sibling(p); + if (isred(s)) { + setblack(p); + setred(gp); + setblack(s); + n = gp; + } else { + done = 1; + } + } + /* p's sibling is black, p is red, gp is black */ + + if ((isleft(n) == 0) == (isleft(p) == 0)) { + single_rotate(gp, isleft(n)); + setblack(p); + setred(gp); + } else { + single_rotate(p, isleft(n)); + single_rotate(gp, isleft(n)); + setblack(n); + setred(gp); + } +} + +static void single_rotate(Rb_node y, int l) +{ + int rl, ir; + Rb_node x, yp; + + ir = isroot(y); + yp = y->p.parent; + if (!ir) { + rl = isleft(y); + } + + if (l) { + x = y->c.child.left; + y->c.child.left = x->c.child.right; + setleft(y->c.child.left); + y->c.child.left->p.parent = y; + x->c.child.right = y; + setright(y); + } else { + x = y->c.child.right; + y->c.child.right = x->c.child.left; + setright(y->c.child.right); + y->c.child.right->p.parent = y; + x->c.child.left = y; + setleft(y); + } + + x->p.parent = yp; + y->p.parent = x; + if (ir) { + yp->p.root = x; + setnormal(y); + setroot(x); + } else { + if (rl) { + yp->c.child.left = x; + setleft(x); + } else { + yp->c.child.right = x; + setright(x); + } + } +} + +void rb_delete_node(Rb_node n) +{ + Rb_node s, p, gp; + char ir; + +#ifdef DEBUG + if (isint(n)) { + fprintf(stderr, "Cannot delete an internal node: %p\n", n); + exit(1); + } + if (ishead(n)) { + fprintf(stderr, "Cannot delete the head of an rb_tree: %p\n", n); + exit(1); + } +#else + assert(!isint(n)); + assert(!ishead(n)); +#endif + delete_item(n); /* Delete it from the list */ + p = n->p.parent; /* The only node */ + if (isroot(n)) { + p->p.root = p; + free(n); + return; + } + s = sibling(n); /* The only node after deletion */ + if (isroot(p)) { + s->p.parent = p->p.parent; + s->p.parent->p.root = s; + setroot(s); + free(p); + free(n); + return; + } + gp = p->p.parent; /* Set parent to sibling */ + s->p.parent = gp; + if (isleft(p)) { + gp->c.child.left = s; + setleft(s); + } else { + gp->c.child.right = s; + setright(s); + } + ir = isred(p); + free(p); + free(n); + + if (isext(s)) { /* Update proper rext and lext values */ + p = lprev(s); + if (!ishead(p)) p->v.rext = s; + p = rprev(s); + if (!ishead(p)) p->k.lext = s; + } else if (isblack(s)) { +#ifdef DEBUG + fprintf(stderr, "DELETION PROB -- sib is black, internal\n"); + exit(1); +#else + assert(1); +#endif + } else { + p = lprev(s); + if (!ishead(p)) p->v.rext = s->c.child.left; + p = rprev(s); + if (!ishead(p)) p->k.lext = s->c.child.right; + setblack(s); + return; + } + + if (ir) return; + + /* Recolor */ + + n = s; + p = n->p.parent; + s = sibling(n); + while(isblack(p) && isblack(s) && isint(s) && + isblack(s->c.child.left) && isblack(s->c.child.right)) { + setred(s); + n = p; + if (isroot(n)) return; + p = n->p.parent; + s = sibling(n); + } + + if (isblack(p) && isred(s)) { /* Rotation 2.3b */ + single_rotate(p, isright(n)); + setred(p); + setblack(s); + s = sibling(n); + } + + { Rb_node x, z; char il; +#ifdef DEBUG + if (isext(s)) { + fprintf(stderr, "DELETION ERROR: sibling not internal\n"); + exit(1); + } +#else + assert(!isext(s)); +#endif + + il = isleft(n); + x = il ? s->c.child.left : s->c.child.right; + z = sibling(x); + + if (isred(z)) { /* Rotation 2.3f */ + single_rotate(p, !il); + setblack(z); + if (isred(p)) setred(s); else setblack(s); + setblack(p); + } else if (isblack(x)) { /* Recoloring only (2.3c) */ +#ifdef DEBUG + if (isred(s) || isblack(p)) { + fprintf(stderr, "DELETION ERROR: 2.3c not quite right\n"); + exit(1); + } +#else + assert(!isred(s) && !isblack(p)); +#endif + setblack(p); + setred(s); + return; + } else if (isred(p)) { /* 2.3d */ + single_rotate(s, il); + single_rotate(p, !il); + setblack(x); + setred(s); + return; + } else { /* 2.3e */ + single_rotate(s, il); + single_rotate(p, !il); + setblack(x); + return; + } + } +} + + +#ifdef DEBUG +void rb_print_tree(Rb_node t, int level) +{ + int i; + if (ishead(t) && t->p.parent == t) { + printf("tree %p is empty\n", t); + } else if (ishead(t)) { + printf("Head: %p. Root = %p\n", t, t->p.root); + rb_print_tree(t->p.root, 0); + } else { + if (isext(t)) { + for (i = 0; i < level; i++) putchar(' '); + printf("Ext node %p: %c,%c: p=0x%x, k=%s\n", + t, isred(t)?'R':'B', isleft(t)?'l':'r', t->p.parent, t->k.key); + } else { + rb_print_tree(t->c.child.left, level+2); + rb_print_tree(t->c.child.right, level+2); + for (i = 0; i < level; i++) putchar(' '); + printf("Int node %p: %c,%c: l=0x%x, r=0x%x, p=0x%x, lr=(%s,%s)\n", + t, isred(t)?'R':'B', isleft(t)?'l':'r', t->c.child.left, + t->c.child.right, + t->p.parent, t->k.lext->k.key, t->v.rext->k.key); + } + } +} +#endif + +int rb_nblack(Rb_node n) +{ + int nb; +#ifdef DEBUG + if (ishead(n) || isint(n)) { + fprintf(stderr, "ERROR: rb_nblack called on a non-external node %p\n", + n); + exit(1); + } +#else + assert(!ishead(n)); + assert(!isint(n)); +#endif + nb = 0; + while(!ishead(n)) { + if (isblack(n)) nb++; + n = n->p.parent; + } + return nb; +} + +int rb_plength(Rb_node n) +{ + int pl; +#ifdef DEBUG + if (ishead(n) || isint(n)) { + fprintf(stderr, "ERROR: rb_plength called on a non-external node %p\n", + n); + exit(1); + } +#else + assert(!ishead(n)); + assert(!isint(n)); +#endif + pl = 0; + while(!ishead(n)) { + pl++; + n = n->p.parent; + } + return pl; +} + +void rb_free_tree(Rb_node n) +{ +#ifdef DEBUG + if (!ishead(n)) { + fprintf(stderr, "ERROR: Rb_free_tree called on a non-head node\n"); + exit(1); + } +#else + assert(ishead(n)); +#endif + + while(rb_first(n) != rb_nil(n)) { + rb_delete_node(rb_first(n)); + } + free(n); +} + +void *rb_val(Rb_node n) +{ + return n->v.val; +} + +Rb_node rb_insert_a(Rb_node nd, void *val) +{ + return rb_insert_b(nd->c.list.flink, val); +} + +Rb_node rb_insert(Rb_node tree, const void *key, rb_key_cmp_fn cmp, void *val) +{ + return rb_insert_b(rb_find_key(tree, key, cmp), val); +} diff --git a/rb.h b/rb.h new file mode 100644 index 0000000..80f433c --- /dev/null +++ b/rb.h @@ -0,0 +1,128 @@ +/* +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Revision 1.2. Jim Plank */ + +/* Original code by Jim Plank (plank@cs.utk.edu) */ +/* modified for THINK C 6.0 for Macintosh by Chris Bartley */ + +/* Modified by Russell King to support embedded keys only */ + +typedef struct { + unsigned red : 1 ; + unsigned internal : 1 ; + unsigned left : 1 ; + unsigned root : 1 ; + unsigned head : 1 ; +} status; + +/* Main rb_node. You only ever use the fields + + c.list.flink + c.list.blink + + k.key or k.ikey + v.val +*/ + +typedef int (*rb_key_cmp_fn)(const void *, const void *); + +typedef struct rb_node { + union { + struct { + struct rb_node *flink; + struct rb_node *blink; + } list; + struct { + struct rb_node *left; + struct rb_node *right; + } child; + } c; + union { + struct rb_node *parent; + struct rb_node *root; + } p; + status s; + union { + struct rb_node *lext; + } k; + union { + void *val; + struct rb_node *rext; + } v; +} *Rb_node; + + +extern Rb_node make_rb(void); /* Creates a new rb-tree */ + + + +/* Creates a node with key key and val val and inserts it into the tree. + rb_insert uses strcmp() as comparison funcion. rb_inserti uses <>=, + rb_insertg uses func() */ + +extern Rb_node rb_insert(Rb_node tree, const void *key, + rb_key_cmp_fn fn, void *data); + + +/* returns an external node in t whose value is equal + k or whose value is the smallest value greater than k. */ + +extern Rb_node rb_find_key(Rb_node root, const void *key, + rb_key_cmp_fn fn); + + +/* Works just like the find_key versions only it returns whether or not + it found the key in the integer variable found */ + +extern Rb_node rb_find_key_n(Rb_node root, const void *key, + rb_key_cmp_fn fn, int *found); + + +/* Creates a node with key key and val val and inserts it into the + tree before/after node nd. Does not check to ensure that you are + keeping the correct order */ + +extern Rb_node rb_insert_b(Rb_node nd, void *val); +extern Rb_node rb_insert_a(Rb_node nd, void *val); + + +extern void rb_delete_node(Rb_node node); /* Deletes and frees a node (but + not the key or val) */ +extern void rb_free_tree(Rb_node root); /* Deletes and frees an entire tree */ + +extern void *rb_val(Rb_node node); /* Returns node->v.val -- this is to shut + lint up */ + +extern int rb_nblack(Rb_node n); /* returns # of black nodes in path from + n to the root */ +int rb_plength(Rb_node n); /* returns the # of nodes in path from + n to the root */ + +#define rb_first(n) (n->c.list.flink) +#define rb_last(n) (n->c.list.blink) +#define rb_next(n) (n->c.list.flink) +#define rb_prev(n) (n->c.list.blink) +#define rb_empty(t) (t->c.list.flink == t) +#ifndef rb_nil +#define rb_nil(t) (t) +#endif + +#define rb_traverse(ptr, lst) \ + for(ptr = rb_first(lst); ptr != rb_nil(lst); ptr = rb_next(ptr)) diff --git a/uio_vmeta.h b/uio_vmeta.h index 08ebaa1..d9e55a5 100644 --- a/uio_vmeta.h +++ b/uio_vmeta.h @@ -63,6 +63,13 @@ struct vmeta_mmap { uint32_t size; }; +struct vmeta_dmabuf_import { + uint64_t phys; + uint32_t size; + int32_t fd; + int32_t id; +}; + #define VMETA_STATUS_BIT_USED 0 #define VMETA_STATUS_BIT_REGISTED 1 #define VMETA_STATUS_USED BIT(0) @@ -93,5 +100,7 @@ struct vmeta_mmap { #define VMETA_CMD_GET_USER_NUM _IOR(IOP_MAGIC, 20, unsigned) #define VMETA_CMD_GET_INFO _IOR(IOP_MAGIC, 21, struct vmeta_info) #define VMETA_CMD_MAP_SW_CONTEXT _IOWR(IOP_MAGIC, 22, struct vmeta_mmap) +#define VMETA_CMD_DMABUF_IMPORT _IOWR(IOP_MAGIC, 23, struct vmeta_dmabuf_import) +#define VMETA_CMD_DMABUF_RELEASE _IO(IOP_MAGIC, 24) #endif /* __UIO_VMETA_H */ diff --git a/vmeta_lib.c b/vmeta_lib.c index 6959a1f..735e8ce 100644 --- a/vmeta_lib.c +++ b/vmeta_lib.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include "vmeta_lib.h" #include "bmm_lib.h" +#include "rb.h" #include "uio_vmeta.h" #define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) @@ -76,9 +78,68 @@ typedef struct vdec_os_driver_cb_s { // global variable static vdec_os_driver_cb_t *vdec_iface = NULL; +static int vmeta_fd = -1; static UNSG32 globalDbgLevel = VDEC_DEBUG_NONE; static UNSG32 syncTimeout = 500; static pthread_mutex_t pmt = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t rb_mutex = PTHREAD_MUTEX_INITIALIZER; +static Rb_node phys_rb, virt_rb; + +typedef unsigned long phys_t; + +struct v2p { + void *vaddr; + size_t size; + phys_t paddr; + int32_t fd; + int32_t id; +}; + +static int cmp_phys(const void *key, const void *val) +{ + const struct v2p *v2p = val; + phys_t k = *(const phys_t *)key; + + return (k < v2p->paddr) ? -1 : (k - v2p->paddr < v2p->size) ? 0 : 1; +} + +static struct v2p *find_phys(phys_t paddr) +{ + int found = 0; + Rb_node node = rb_find_key_n(phys_rb, &paddr, cmp_phys, &found); + + return found ? rb_val(node) : NULL; +} + +static struct v2p *find_remove_phys(phys_t paddr) +{ + struct v2p *v2p = NULL; + int found = 0; + Rb_node node = rb_find_key_n(phys_rb, &paddr, cmp_phys, &found); + + if (found) { + v2p = rb_val(node); + rb_delete_node(node); + } + + return v2p; +} + +static int cmp_virt(const void *key, const void *val) +{ + const struct v2p *v2p = val; + void *k = (void *)key; + + return (k < v2p->vaddr) ? -1 : (k - v2p->vaddr < v2p->size) ? 0 : 1; +} + +static struct v2p *find_virt(void *vaddr) +{ + int found = 0; + Rb_node node = rb_find_key_n(virt_rb, vaddr, cmp_virt, &found); + + return found ? rb_val(node) : NULL; +} UNSG8 vdec_os_api_rd8(UNSG32 addr) { @@ -116,15 +177,191 @@ UNSG32 vdec_os_api_get_regbase_addr(void) return (UNSG32) vdec->regs; } +static void __attribute__((destructor)) vdec_os_api_exit(void) +{ + if (phys_rb) { + Rb_node node; + + /* + * Release all resources on the virtual rb tree, but leave + * the v2p structs in place, as these will also be on the + * phys rb tree. + */ + rb_traverse(node, virt_rb) { + struct v2p *v2p = rb_val(node); + + ioctl(vmeta_fd, VMETA_CMD_DMABUF_RELEASE, v2p->id); + bmm_dmabuf_unmap(v2p->vaddr); + bmm_dmabuf_free(v2p->fd); + v2p->id = -1; + } + rb_free_tree(virt_rb); + + rb_traverse(node, phys_rb) { + struct v2p *v2p = rb_val(node); + + if (v2p->id != -1) { + ioctl(vmeta_fd, VMETA_CMD_DMABUF_RELEASE, v2p->id); + v2p->id = -1; + } + free(v2p); + } + rb_free_tree(phys_rb); + } + + if (vmeta_fd >= 0) + close(vmeta_fd); + bmm_exit(); +} + +static int vdec_os_api_init(void) +{ + int fd; + + if (!virt_rb) { + virt_rb = make_rb(); + phys_rb = make_rb(); + if (!virt_rb || !phys_rb) + goto rb; + + fd = open("/dev/vmeta", O_RDWR | O_CLOEXEC); + if (fd == -1) + goto rb; + + if (bmm_init() < 0) + goto vmeta; + + vmeta_fd = fd; + } + return 0; + +vmeta: + close(fd); +rb: + if (virt_rb) + rb_free_tree(virt_rb); + if (phys_rb) + rb_free_tree(phys_rb); + phys_rb = virt_rb = NULL; + + return 1; +} + +static int vdec_os_api_add_mapping(void *ptr, size_t size, phys_t paddr, + int fd, int id) +{ + struct v2p *v2p; + Rb_node node; + int found = 0; + + v2p = malloc(sizeof(*v2p)); + if (!v2p) + return -1; + + v2p->vaddr = ptr; + v2p->size = size; + v2p->paddr = paddr; + v2p->fd = fd; + v2p->id = id; + + pthread_mutex_lock(&rb_mutex); + node = rb_find_key_n(phys_rb, &paddr, cmp_phys, &found); + assert(found == 0); + rb_insert_b(node, v2p); + + if (ptr) { + node = rb_find_key_n(virt_rb, ptr, cmp_virt, &found); + assert(found == 0); + rb_insert_b(node, v2p); + } + pthread_mutex_unlock(&rb_mutex); + + return 0; +} + +/** + * vdec_os_api_dmabuf_import - import a DMA buffer into vmeta + * @fd: dmabuf file descriptor + * @phys: pointer to unsigned long for physical address of buffer + * @size: pointer ti size_t for size of buffer + * + * Returns: ID of buffer, physical address in *phys and size in *size, + * or -1 on error. + */ +int vdec_os_api_dmabuf_import(int fd, unsigned long *phys, size_t *size) +{ + struct vmeta_dmabuf_import arg; + int ret; + + if (vdec_os_api_init()) + return -1; + + arg.fd = fd; + ret = ioctl(vmeta_fd, VMETA_CMD_DMABUF_IMPORT, &arg); + if (ret == -1) + return ret; + + if ((phys_t)arg.phys != arg.phys) { + ret = -1; + goto release; + } + + ret = vdec_os_api_add_mapping(NULL, arg.size, arg.phys, arg.fd, arg.id); + if (ret) + goto release; + + return arg.id; + + release: + ioctl(vmeta_fd, VMETA_CMD_DMABUF_RELEASE, arg.id); + return ret; +} + +void vdec_os_api_dmabuf_release(unsigned long phys) +{ + struct v2p *v2p; + + pthread_mutex_lock(&rb_mutex); + v2p = find_remove_phys(phys); + pthread_mutex_unlock(&rb_mutex); + + ioctl(vmeta_fd, VMETA_CMD_DMABUF_RELEASE, v2p->id); + free(v2p); +} + //Mem map to bmm_lib -UNSG32 vdec_os_api_get_pa(UNSG32 vaddr) +UNSG32 vdec_os_api_get_pa(UNSG32 virt) { - return bmm_get_paddr((void *)vaddr); + unsigned long paddr; + struct v2p *v2p; + void *vaddr = (void *)virt; + + pthread_mutex_lock(&rb_mutex); + v2p = find_virt(vaddr); + pthread_mutex_unlock(&rb_mutex); + + assert(v2p); + paddr = v2p->paddr + vaddr - v2p->vaddr; + + return paddr; } UNSG32 vdec_os_api_get_va(UNSG32 paddr) -{ - return ((UNSG32)bmm_get_vaddr(paddr)); +{ + struct v2p *v2p; + void *vaddr; + + pthread_mutex_lock(&rb_mutex); + v2p = find_phys(paddr); + pthread_mutex_unlock(&rb_mutex); + + assert(v2p); + if (v2p->vaddr) + vaddr = v2p->vaddr + paddr - v2p->paddr; + else + vaddr = NULL; + + return (UNSG32)vaddr; } void vdec_os_api_vfree(void *ptr) @@ -162,33 +399,83 @@ void *vdec_os_api_vmalloc(UNSG32 size, UNSG32 align) void vdec_os_api_dma_free(void *ptr) { - bmm_free(ptr); + struct v2p *v2p; + Rb_node node; + int found = 0; + + dbg_printf(VDEC_DEBUG_MEM, "vdec_os_api_free: 0x%p\n", ptr); + + pthread_mutex_lock(&rb_mutex); + node = rb_find_key_n(virt_rb, ptr, cmp_virt, &found); + if (!found) { + pthread_mutex_unlock(&rb_mutex); + dbg_printf(VDEC_DEBUG_MEM, "vdec_os_api_free: not found\n"); + assert(0); + return; + } + rb_delete_node(node); + + v2p = rb_val(node); + assert(find_remove_phys(v2p->paddr) == v2p); + pthread_mutex_unlock(&rb_mutex); + + bmm_dmabuf_unmap(v2p->vaddr); + if (v2p->id != -1) + ioctl(vmeta_fd, VMETA_CMD_DMABUF_RELEASE, v2p->id); + bmm_dmabuf_free(v2p->fd); + free(v2p); } static void *vmeta_bmm_malloc_aligned(UNSG32 size, UNSG32 align, UNSG32 *phys, int attr) { + struct vmeta_dmabuf_import arg; unsigned long paddr; void *ptr; + int fd; + + dbg_printf(VDEC_DEBUG_MEM, "%s: size 0x%x attr %u align %u\n", + __FUNCTION__, size, attr, align); if (size == 0) return NULL; - dbg_printf(VDEC_DEBUG_MEM, "%s: size 0x%x attr %u align %u\n", - __FUNCTION__, size, attr, align); + if (vdec_os_api_init()) + return NULL; - ptr = bmm_malloc_aligned_phys(size, attr, align, &paddr); - if (!ptr) { - dbg_printf(VDEC_DEBUG_MEM, "%s: not enough memory\n", - __FUNCTION__); + fd = bmm_dmabuf_alloc(size, attr, align); + if (fd < 0) { + dbg_printf(VDEC_DEBUG_MEM, "%s: %s\n", + __FUNCTION__, strerror(errno)); return NULL; - } + } - *phys = paddr; + ptr = bmm_dmabuf_map(fd, 0, size); + if (ptr == NULL) + goto free; + + arg.fd = fd; + if (ioctl(vmeta_fd, VMETA_CMD_DMABUF_IMPORT, &arg) == -1) + goto unmap; + + paddr = arg.phys; + + dbg_printf(VDEC_DEBUG_MEM, "%s: virt=%p phys=0x%08lx fd %d id %d\n", + __FUNCTION__, ptr, paddr, fd, arg.id); + + if (vdec_os_api_add_mapping(ptr, size, paddr, fd, arg.id)) + goto release; - dbg_printf(VDEC_DEBUG_MEM, "%s: virt=%p phys=0x%08lx\n", - __FUNCTION__, ptr, paddr); + *phys = paddr; return ptr; + +release: + ioctl(vmeta_fd, VMETA_CMD_DMABUF_RELEASE, arg.id); +unmap: + bmm_dmabuf_unmap(ptr); +free: + bmm_dmabuf_free(fd); + return NULL; } void *vdec_os_api_dma_alloc(UNSG32 size, UNSG32 align, UNSG32 *phys) @@ -208,11 +495,11 @@ void *vdec_os_api_dma_alloc_writecombine(UNSG32 size, UNSG32 align, UNSG32 *phys UNSG32 vdec_os_api_flush_cache(UNSG32 vaddr, UNSG32 size, enum dma_data_direction direction) { - int bmm_direction; + struct v2p *v2p; + unsigned bmm_direction, offset; switch (direction) { case DMA_NONE: - default: bmm_direction = BMM_DMA_NONE; break; case DMA_FROM_DEVICE: @@ -224,16 +511,22 @@ UNSG32 vdec_os_api_flush_cache(UNSG32 vaddr, UNSG32 size, enum dma_data_directio case DMA_BIDIRECTIONAL: bmm_direction = BMM_DMA_BIDIRECTIONAL; break; + default: + assert(0); } if (bmm_direction == BMM_DMA_NONE) return 0; - if (0 < size) { - bmm_flush_cache_range((void *)vaddr, size, bmm_direction); - } else { - bmm_flush_cache((void *)vaddr, bmm_direction); - } + pthread_mutex_lock(&rb_mutex); + v2p = find_virt((void *)vaddr); + pthread_mutex_unlock(&rb_mutex); + + assert(v2p); + offset = (void *)vaddr - v2p->vaddr; + + bmm_dmabuf_flush(v2p->fd, v2p->vaddr, offset, size - offset, direction); + return 0; } @@ -346,6 +639,11 @@ SIGN32 vdec_os_driver_init(void) return VDEC_OS_DRIVER_OK; } + if (vdec_os_api_init()) { + pthread_mutex_unlock(&pmt); + return -VDEC_OS_DRIVER_INIT_FAIL; + } + #if (VMETA_LOG_ON && 0) fp_log = fopen(VMETA_LOG_FILE,"w"); if(fp_log == NULL) { @@ -366,12 +664,7 @@ SIGN32 vdec_os_driver_init(void) // initialize reference count vdec->refcount++; - // Try to open the (new) vmeta device - vdec->fd = open("/dev/vmeta", O_RDWR | O_CLOEXEC); - if (vdec->fd == -1) { - ret = -VDEC_OS_DRIVER_OPEN_FAIL; - goto err_open_fail; - } + vdec->fd = vmeta_fd; ret = ioctl(vdec->fd, VMETA_CMD_GET_INFO, &info); if (ret == -1) { @@ -412,7 +705,6 @@ SIGN32 vdec_os_driver_init(void) return VDEC_OS_DRIVER_OK; err_mmap_fail: - close(vdec->fd); err_open_fail: free(vdec); @@ -461,10 +753,6 @@ SIGN32 vdec_os_driver_clean(void) munmap(vdec->vdec_obj_va, vdec->vdec_obj_size); } - // close fd - close(vdec->fd); - dbg_printf(VDEC_DEBUG_ALL, "kernel close\n"); - // free vdec_iface free(vdec); dbg_printf(VDEC_DEBUG_ALL, "free vdec_iface\n"); diff --git a/vmeta_lib.h b/vmeta_lib.h index fe7ac40..8609dc5 100644 --- a/vmeta_lib.h +++ b/vmeta_lib.h @@ -69,6 +69,9 @@ UNSG32 vdec_os_api_flush_cache(UNSG32 vaddr, UNSG32 size, enum dma_data_directio SIGN32 vdec_os_api_get_hw_obj_addr(UNSG32* vaddr, UNSG32 size); SIGN32 vdec_os_api_get_hw_context_addr(UNSG32* paddr, UNSG32* vaddr, UNSG32 size, SIGN32 flag); +int vdec_os_api_dmabuf_import(int fd, unsigned long *phys, size_t *size); +void vdec_os_api_dmabuf_release(unsigned long phys); + //--------------------------------------------------------------------------- // Mem/IO R/W API //--------------------------------------------------------------------------- -- cgit