/* Copyright (C) 2001-2003 Paul Davis Copyright (C) 2005-2012 Grame Copyright (C) 2013 Samsung Electronics This program 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 program 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 program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define LOG_TAG "JAMSHMSERVICE" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "BnAndroidShm.h" #include "AndroidShm.h" #include "JackConstants.h" #include #include #include #include #include #include #include #include #include "JackError.h" // remove ALOGI log #define jack_d //#define jack_d ALOGI #define jack_error ALOGE #define MEMORY_SIZE 10*1024 namespace android { jack_shmtype_t Shm::jack_shmtype = shm_ANDROID; /* The JACK SHM registry is a chunk of memory for keeping track of the * shared memory used by each active JACK server. This allows the * server to clean up shared memory when it exits. To avoid memory * leakage due to kill -9, crashes or debugger-driven exits, this * cleanup is also done when a new instance of that server starts. */ /* per-process global data for the SHM interfaces */ jack_shm_id_t Shm::registry_id; /* SHM id for the registry */ jack_shm_fd_t Shm::registry_fd = JACK_SHM_REGISTRY_FD; jack_shm_info_t Shm::registry_info = { JACK_SHM_NULL_INDEX, 0, 0, { MAP_FAILED } }; /* pointers to registry header and array */ jack_shm_header_t *Shm::jack_shm_header = NULL; jack_shm_registry_t *Shm::jack_shm_registry = NULL; char Shm::jack_shm_server_prefix[JACK_SERVER_NAME_SIZE+1] = ""; /* jack_shm_lock_registry() serializes updates to the shared memory * segment JACK uses to keep track of the SHM segments allocated to * all its processes, including multiple servers. * * This is not a high-contention lock, but it does need to work across * multiple processes. High transaction rates and realtime safety are * not required. Any solution needs to at least be portable to POSIX * and POSIX-like systems. * * We must be particularly careful to ensure that the lock be released * if the owning process terminates abnormally. Otherwise, a segfault * or kill -9 at the wrong moment could prevent JACK from ever running * again on that machine until after a reboot. */ #define JACK_SEMAPHORE_KEY 0x282929 #define JACK_SHM_REGISTRY_KEY JACK_SEMAPHORE_KEY #define JACK_REGISTRY_NAME "/jack-shm-registry" int Shm::semid = -1; pthread_mutex_t Shm::mutex = PTHREAD_MUTEX_INITIALIZER; //sp Shm::mShmService; sp Shm::mShmMemBase[JACK_SHM_HEAP_ENOUGH_COUNT] = {0,}; Shm* Shm::ref = NULL; Shm* Shm::Instantiate() { if(Shm::ref == NULL) { jack_d("shm::Instantiate is called"); Shm::ref = new Shm; //AndroidShm::instantiate(); } return ref; } Shm::Shm() { } Shm::~Shm() { } sp Shm::getShmService(){ return interface_cast(defaultServiceManager()->getService(String16("com.samsung.android.jam.IAndroidShm"))); } //sp& Shm::getShmService() { // if (mShmService.get() == 0) { // sp sm = defaultServiceManager(); // sp binder; // do { // binder = sm->getService(String16("com.samsung.android.jam.IAndroidShm")); // if (binder != 0) // break; // ALOGW("CameraService not published, waiting..."); // usleep(500000); // 0.5 s // } while(true); // mShmService = interface_cast(binder); // } // ALOGE_IF(mShmService==0, "no CameraService!?"); // return mShmService; //} void Shm::shm_copy_from_registry (jack_shm_info_t* /*si*/, jack_shm_registry_index_t ) { // not used } void Shm::shm_copy_to_registry (jack_shm_info_t* /*si*/, jack_shm_registry_index_t*) { // not used } void Shm::jack_release_shm_entry (jack_shm_registry_index_t index) { /* the registry must be locked */ jack_shm_registry[index].size = 0; jack_shm_registry[index].allocator = 0; memset (&jack_shm_registry[index].id, 0, sizeof (jack_shm_registry[index].id)); jack_shm_registry[index].fd = 0; } int Shm::release_shm_info (jack_shm_registry_index_t index) { /* must NOT have the registry locked */ if (jack_shm_registry[index].allocator == GetPID()) { if (jack_shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } jack_release_shm_entry (index); jack_shm_unlock_registry (); jack_d ("release_shm_info: success!"); } else jack_error ("release_shm_info: error!"); return 0; } char* Shm::shm_addr (unsigned int fd) { if(fd >= JACK_SHM_HEAP_ENOUGH_COUNT) { jack_error("ignore to get memory buffer : index[%d] is too big", fd); return NULL; } sp service = Shm::getShmService(); if(service == NULL){ jack_error("shm service is null"); return NULL; } mShmMemBase[fd] = service->getBuffer(fd); if(mShmMemBase[fd] == NULL) { jack_error("fail to get memory buffer"); return NULL; } return ((char *) mShmMemBase[fd]->getBase()); } int Shm::shm_lock_registry (void) { pthread_mutex_lock (&mutex); return 0; } void Shm::shm_unlock_registry (void) { pthread_mutex_unlock (&mutex); } void Shm::release_shm_entry (jack_shm_registry_index_t index) { /* the registry must be locked */ jack_shm_registry[index].size = 0; jack_shm_registry[index].allocator = 0; memset (&jack_shm_registry[index].id, 0, sizeof (jack_shm_registry[index].id)); } void Shm::remove_shm (jack_shm_id_t *id) { int shm_fd = -1; jack_d("remove_id [%s]",(char*)id); if(!strcmp((const char*)id, JACK_REGISTRY_NAME)) { shm_fd = registry_fd; } else { for (int i = 0; i < MAX_SHM_ID; i++) { if(!strcmp((const char*)id, jack_shm_registry[i].id)) { shm_fd = jack_shm_registry[i].fd; break; } } } if (shm_fd >= 0) { sp service = getShmService(); if(service != NULL) { service->removeShm(shm_fd); } else { jack_error("shm service is null"); } } jack_d ("[APA] jack_remove_shm : ok "); } int Shm::access_registry (jack_shm_info_t * ri) { jack_d("access_registry\n"); /* registry must be locked */ sp service = getShmService(); if(service == NULL){ jack_error("shm service is null"); return EINVAL; } int shm_fd = service->getRegistryIndex(); strncpy (registry_id, JACK_REGISTRY_NAME, sizeof (registry_id) - 1); registry_id[sizeof (registry_id) - 1] = '\0'; if(service->isAllocated(shm_fd) == FALSE) { //jack_error ("Cannot mmap shm registry segment (%s)", // strerror (errno)); jack_error ("Cannot mmap shm registry segment"); //close (shm_fd); ri->ptr.attached_at = NULL; registry_fd = JACK_SHM_REGISTRY_FD; return EINVAL; } ri->fd = shm_fd; registry_fd = shm_fd; ri->ptr.attached_at = shm_addr(shm_fd); if(ri->ptr.attached_at == NULL) { ALOGE("attached pointer is null !"); jack_shm_header = NULL; jack_shm_registry = NULL; return 0; } /* set up global pointers */ ri->index = JACK_SHM_REGISTRY_INDEX; jack_shm_header = (jack_shm_header_t*)(ri->ptr.attached_at); jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); jack_d("jack_shm_header[%p],jack_shm_registry[%p]", jack_shm_header, jack_shm_registry); //close (shm_fd); // steph return 0; } int Shm::GetUID() { return getuid(); } int Shm::GetPID() { return getpid(); } int Shm::jack_shm_lock_registry (void) { // TODO: replace semaphore to mutex pthread_mutex_lock (&mutex); return 0; } void Shm::jack_shm_unlock_registry (void) { // TODO: replace semaphore to mutex pthread_mutex_unlock (&mutex); return; } void Shm::shm_init_registry () { if(jack_shm_header == NULL) return; /* registry must be locked */ memset (jack_shm_header, 0, JACK_SHM_REGISTRY_SIZE); jack_shm_header->magic = JACK_SHM_MAGIC; //jack_shm_header->protocol = JACK_PROTOCOL_VERSION; jack_shm_header->type = jack_shmtype; jack_shm_header->size = JACK_SHM_REGISTRY_SIZE; jack_shm_header->hdr_len = sizeof (jack_shm_header_t); jack_shm_header->entry_len = sizeof (jack_shm_registry_t); for (int i = 0; i < MAX_SHM_ID; ++i) { jack_shm_registry[i].index = i; } } void Shm::set_server_prefix (const char *server_name) { snprintf (jack_shm_server_prefix, sizeof (jack_shm_server_prefix), "jack-%d:%s:", GetUID(), server_name); } /* create a new SHM registry segment * * sets up global registry pointers, if successful * * returns: 0 if registry created successfully * nonzero error code if unable to allocate a new registry */ int Shm::create_registry (jack_shm_info_t * ri) { jack_d("create_registry\n"); /* registry must be locked */ int shm_fd = 0; strncpy (registry_id, JACK_REGISTRY_NAME, sizeof (registry_id) - 1); registry_id[sizeof (registry_id) - 1] = '\0'; sp service = getShmService(); if(service == NULL){ jack_error("shm service is null"); return EINVAL; } if((shm_fd = service->allocShm(JACK_SHM_REGISTRY_SIZE)) < 0) { jack_error("Cannot create shm registry segment"); registry_fd = JACK_SHM_REGISTRY_FD; return EINVAL; } service->setRegistryIndex(shm_fd); /* set up global pointers */ ri->fd = shm_fd; ri->index = JACK_SHM_REGISTRY_INDEX; registry_fd = shm_fd; ri->ptr.attached_at = shm_addr(shm_fd); ri->size = JACK_SHM_REGISTRY_SIZE; jack_shm_header = (jack_shm_header_t*)(ri->ptr.attached_at); jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1); jack_d("create_registry jack_shm_header[%p], jack_shm_registry[%p]", jack_shm_header, jack_shm_registry); /* initialize registry contents */ shm_init_registry (); //close (shm_fd); // steph return 0; } int Shm::shm_validate_registry () { /* registry must be locked */ if(jack_shm_header == NULL) { return -1; } if ((jack_shm_header->magic == JACK_SHM_MAGIC) //&& (jack_shm_header->protocol == JACK_PROTOCOL_VERSION) && (jack_shm_header->type == jack_shmtype) && (jack_shm_header->size == JACK_SHM_REGISTRY_SIZE) && (jack_shm_header->hdr_len == sizeof (jack_shm_header_t)) && (jack_shm_header->entry_len == sizeof (jack_shm_registry_t))) { return 0; /* registry OK */ } return -1; } int Shm::server_initialize_shm (int new_registry) { int rc; jack_d("server_initialize_shm\n"); if (jack_shm_header) return 0; /* already initialized */ if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } rc = access_registry (®istry_info); if (new_registry) { remove_shm (®istry_id); rc = ENOENT; } switch (rc) { case ENOENT: /* registry does not exist */ rc = create_registry (®istry_info); break; case 0: /* existing registry */ if (shm_validate_registry () == 0) break; /* else it was invalid, so fall through */ case EINVAL: /* bad registry */ /* Apparently, this registry was created by an older * JACK version. Delete it so we can try again. */ release_shm (®istry_info); remove_shm (®istry_id); if ((rc = create_registry (®istry_info)) != 0) { //jack_error ("incompatible shm registry (%s)", // strerror (errno)); jack_error ("incompatible shm registry"); //#ifndef USE_POSIX_SHM // jack_error ("to delete, use `ipcrm -M 0x%0.8x'", JACK_SHM_REGISTRY_KEY); //#endif } break; default: /* failure return code */ break; } shm_unlock_registry (); return rc; } // here begin the API int Shm::register_server (const char *server_name, int new_registry) { int i, res = 0; jack_d("register_server new_registry[%d]\n", new_registry); set_server_prefix (server_name); if (server_initialize_shm (new_registry)) return ENOMEM; if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } /* See if server_name already registered. Since server names * are per-user, we register the unique server prefix string. */ for (i = 0; i < MAX_SERVERS; i++) { if (strncmp (jack_shm_header->server[i].name, jack_shm_server_prefix, JACK_SERVER_NAME_SIZE) != 0) continue; /* no match */ if (jack_shm_header->server[i].pid == GetPID()) { res = 0; /* it's me */ goto unlock; } /* see if server still exists */ if (kill (jack_shm_header->server[i].pid, 0) == 0) { res = EEXIST; /* other server running */ goto unlock; } /* it's gone, reclaim this entry */ memset (&jack_shm_header->server[i], 0, sizeof (jack_shm_server_t)); } /* find a free entry */ for (i = 0; i < MAX_SERVERS; i++) { if (jack_shm_header->server[i].pid == 0) break; } if (i >= MAX_SERVERS) { res = ENOSPC; /* out of space */ goto unlock; } /* claim it */ jack_shm_header->server[i].pid = GetPID(); strncpy (jack_shm_header->server[i].name, jack_shm_server_prefix, JACK_SERVER_NAME_SIZE - 1); jack_shm_header->server[i].name[JACK_SERVER_NAME_SIZE - 1] = '\0'; unlock: shm_unlock_registry (); return res; } int Shm::unregister_server (const char * /* server_name */) { int i; if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } for (i = 0; i < MAX_SERVERS; i++) { if (jack_shm_header->server[i].pid == GetPID()) { memset (&jack_shm_header->server[i], 0, sizeof (jack_shm_server_t)); } } shm_unlock_registry (); return 0; } int Shm::initialize_shm (const char *server_name) { int rc; if (jack_shm_header) return 0; /* already initialized */ set_server_prefix (server_name); if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } if ((rc = access_registry (®istry_info)) == 0) { if ((rc = shm_validate_registry ()) != 0) { jack_error ("Incompatible shm registry, " "are jackd and libjack in sync?"); } } shm_unlock_registry (); return rc; } int Shm::initialize_shm_server (void) { // not used return 0; } int Shm::initialize_shm_client (void) { // not used return 0; } int Shm::cleanup_shm (void) { int i; int destroy; jack_shm_info_t copy; if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } for (i = 0; i < MAX_SHM_ID; i++) { jack_shm_registry_t* r; r = &jack_shm_registry[i]; memcpy (©, r, sizeof (jack_shm_info_t)); destroy = FALSE; /* ignore unused entries */ if (r->allocator == 0) continue; /* is this my shm segment? */ if (r->allocator == GetPID()) { /* allocated by this process, so unattach and destroy. */ release_shm (©); destroy = TRUE; } else { /* see if allocator still exists */ if (kill (r->allocator, 0)) { if (errno == ESRCH) { /* allocator no longer exists, * so destroy */ destroy = TRUE; } } } if (destroy) { int index = copy.index; if ((index >= 0) && (index < MAX_SHM_ID)) { remove_shm (&jack_shm_registry[index].id); release_shm_entry (index); } r->size = 0; r->allocator = 0; } } shm_unlock_registry (); return TRUE; } jack_shm_registry_t * Shm::get_free_shm_info () { /* registry must be locked */ jack_shm_registry_t* si = NULL; int i; for (i = 0; i < MAX_SHM_ID; ++i) { if (jack_shm_registry[i].size == 0) { break; } } if (i < MAX_SHM_ID) { si = &jack_shm_registry[i]; } return si; } int Shm::shmalloc (const char * /*shm_name*/, jack_shmsize_t size, jack_shm_info_t* si) { jack_shm_registry_t* registry; int shm_fd; int rc = -1; char name[SHM_NAME_MAX+1]; if (shm_lock_registry () < 0) { jack_error ("jack_shm_lock_registry fails..."); return -1; } sp service = getShmService(); if(service == NULL){ rc = errno; jack_error("shm service is null"); goto unlock; } if ((registry = get_free_shm_info ()) == NULL) { jack_error ("shm registry full"); goto unlock; } snprintf (name, sizeof (name), "/jack-%d-%d", GetUID(), registry->index); if (strlen (name) >= sizeof (registry->id)) { jack_error ("shm segment name too long %s", name); goto unlock; } if((shm_fd = service->allocShm(size)) < 0) { rc = errno; jack_error ("Cannot create shm segment %s", name); goto unlock; } //close (shm_fd); registry->size = size; strncpy (registry->id, name, sizeof (registry->id) - 1); registry->id[sizeof (registry->id) - 1] = '\0'; registry->allocator = GetPID(); registry->fd = shm_fd; si->fd = shm_fd; si->index = registry->index; si->ptr.attached_at = MAP_FAILED; /* not attached */ rc = 0; /* success */ jack_d ("[APA] jack_shmalloc : ok "); unlock: shm_unlock_registry (); return rc; } void Shm::release_shm (jack_shm_info_t* /*si*/) { // do nothing } void Shm::release_lib_shm (jack_shm_info_t* /*si*/) { // do nothing } void Shm::destroy_shm (jack_shm_info_t* si) { /* must NOT have the registry locked */ if (si->index == JACK_SHM_NULL_INDEX) return; /* segment not allocated */ remove_shm (&jack_shm_registry[si->index].id); release_shm_info (si->index); } int Shm::attach_shm (jack_shm_info_t* si) { jack_shm_registry_t *registry = &jack_shm_registry[si->index]; if((si->ptr.attached_at = shm_addr(registry->fd)) == NULL) { jack_error ("Cannot mmap shm segment %s", registry->id); close (si->fd); return -1; } return 0; } int Shm::attach_lib_shm (jack_shm_info_t* si) { int res = attach_shm(si); if (res == 0) si->size = jack_shm_registry[si->index].size; // Keep size in si struct return res; } int Shm::attach_shm_read (jack_shm_info_t* si) { jack_shm_registry_t *registry = &jack_shm_registry[si->index]; if((si->ptr.attached_at = shm_addr(registry->fd)) == NULL) { jack_error ("Cannot mmap shm segment %s", registry->id); close (si->fd); return -1; } return 0; } int Shm::attach_lib_shm_read (jack_shm_info_t* si) { int res = attach_shm_read(si); if (res == 0) si->size = jack_shm_registry[si->index].size; // Keep size in si struct return res; } int Shm::resize_shm (jack_shm_info_t* si, jack_shmsize_t size) { jack_shm_id_t id; /* The underlying type of `id' differs for SYSV and POSIX */ memcpy (&id, &jack_shm_registry[si->index].id, sizeof (id)); release_shm (si); destroy_shm (si); if (shmalloc ((char *) id, size, si)) { return -1; } return attach_shm (si); } void Shm::jack_shm_copy_from_registry (jack_shm_info_t* si, jack_shm_registry_index_t t) { Shm::Instantiate()->shm_copy_from_registry(si,t); } void Shm::jack_shm_copy_to_registry (jack_shm_info_t* si, jack_shm_registry_index_t* t) { Shm::Instantiate()->shm_copy_to_registry(si,t); } int Shm::jack_release_shm_info (jack_shm_registry_index_t t) { return Shm::Instantiate()->release_shm_info(t); } char* Shm::jack_shm_addr (jack_shm_info_t* si) { if(si != NULL) { return (char*)si->ptr.attached_at; } else { jack_error ("jack_shm_addr : jack_shm_info_t is NULL!"); return NULL; } } int Shm::jack_register_server (const char *server_name, int new_registry) { return Shm::Instantiate()->register_server(server_name, new_registry); } int Shm::jack_unregister_server (const char *server_name) { return Shm::Instantiate()->unregister_server(server_name); } int Shm::jack_initialize_shm (const char *server_name) { return Shm::Instantiate()->initialize_shm(server_name); } int Shm::jack_initialize_shm_server (void) { return Shm::Instantiate()->initialize_shm_server(); } int Shm::jack_initialize_shm_client () { return Shm::Instantiate()->initialize_shm_client(); } int Shm::jack_cleanup_shm (void) { return Shm::Instantiate()->cleanup_shm(); } int Shm::jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* result) { return Shm::Instantiate()->shmalloc(shm_name, size, result); } void Shm::jack_release_shm (jack_shm_info_t* si) { Shm::Instantiate()->release_shm(si); } void Shm::jack_release_lib_shm (jack_shm_info_t* si) { Shm::Instantiate()->release_lib_shm(si); } void Shm::jack_destroy_shm (jack_shm_info_t* si) { Shm::Instantiate()->destroy_shm(si); } int Shm::jack_attach_shm (jack_shm_info_t* si) { return Shm::Instantiate()->attach_shm(si); } int Shm::jack_attach_lib_shm (jack_shm_info_t* si) { return Shm::Instantiate()->attach_lib_shm(si); } int Shm::jack_attach_shm_read (jack_shm_info_t* si) { return Shm::Instantiate()->attach_shm_read(si); } int Shm::jack_attach_lib_shm_read (jack_shm_info_t* si) { return Shm::Instantiate()->attach_lib_shm_read(si); } int Shm::jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size) { return Shm::Instantiate()->resize_shm(si, size); } }; void jack_shm_copy_from_registry (jack_shm_info_t* si, jack_shm_registry_index_t t) { android::Shm::jack_shm_copy_from_registry(si, t); } void jack_shm_copy_to_registry (jack_shm_info_t* si, jack_shm_registry_index_t* t) { android::Shm::jack_shm_copy_to_registry(si, t); } int jack_release_shm_info (jack_shm_registry_index_t t) { return android::Shm::jack_release_shm_info(t); } char* jack_shm_addr (jack_shm_info_t* si) { return android::Shm::jack_shm_addr(si); } int jack_register_server (const char *server_name, int new_registry) { return android::Shm::jack_register_server(server_name, new_registry); } int jack_unregister_server (const char *server_name) { return android::Shm::jack_unregister_server(server_name); } int jack_initialize_shm (const char *server_name) { return android::Shm::jack_initialize_shm(server_name); } int jack_initialize_shm_server (void) { return android::Shm::jack_initialize_shm_server(); } int jack_initialize_shm_client (void) { return android::Shm::jack_initialize_shm_client(); } int jack_cleanup_shm (void) { return android::Shm::jack_cleanup_shm(); } int jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* result) { return android::Shm::jack_shmalloc(shm_name, size, result); } void jack_release_shm (jack_shm_info_t* si) { android::Shm::jack_release_shm(si); } void jack_release_lib_shm (jack_shm_info_t* si) { android::Shm::jack_release_lib_shm(si); } void jack_destroy_shm (jack_shm_info_t* si) { android::Shm::jack_destroy_shm(si); } int jack_attach_shm (jack_shm_info_t* si) { return android::Shm::jack_attach_shm(si); } int jack_attach_lib_shm (jack_shm_info_t* si) { return android::Shm::jack_attach_lib_shm(si); } int jack_attach_shm_read (jack_shm_info_t* si) { return android::Shm::jack_attach_shm_read(si); } int jack_attach_lib_shm_read (jack_shm_info_t* si) { return android::Shm::jack_attach_lib_shm_read(si); } int jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size) { return android::Shm::jack_resize_shm(si, size); } void jack_instantiate() { android::AndroidShm::instantiate(); }