1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "MemoryHeapBase"
18
19#include <errno.h>
20#include <fcntl.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <sys/ioctl.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <unistd.h>
27
28#include <binder/MemoryHeapBase.h>
29#include <cutils/ashmem.h>
30#include <cutils/atomic.h>
31#include <log/log.h>
32
33namespace android {
34
35// ---------------------------------------------------------------------------
36
37MemoryHeapBase::MemoryHeapBase()
38    : mFD(-1), mSize(0), mBase(MAP_FAILED),
39      mDevice(NULL), mNeedUnmap(false), mOffset(0)
40{
41}
42
43MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
44    : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
45      mDevice(0), mNeedUnmap(false), mOffset(0)
46{
47    const size_t pagesize = getpagesize();
48    size = ((size + pagesize-1) & ~(pagesize-1));
49    int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
50    ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
51    if (fd >= 0) {
52        if (mapfd(fd, size) == NO_ERROR) {
53            if (flags & READ_ONLY) {
54                ashmem_set_prot_region(fd, PROT_READ);
55            }
56        }
57    }
58}
59
60MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags)
61    : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
62      mDevice(0), mNeedUnmap(false), mOffset(0)
63{
64    int open_flags = O_RDWR;
65    if (flags & NO_CACHING)
66        open_flags |= O_SYNC;
67
68    int fd = open(device, open_flags);
69    ALOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno));
70    if (fd >= 0) {
71        const size_t pagesize = getpagesize();
72        size = ((size + pagesize-1) & ~(pagesize-1));
73        if (mapfd(fd, size) == NO_ERROR) {
74            mDevice = device;
75        }
76    }
77}
78
79MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset)
80    : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
81      mDevice(0), mNeedUnmap(false), mOffset(0)
82{
83    const size_t pagesize = getpagesize();
84    size = ((size + pagesize-1) & ~(pagesize-1));
85    mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
86}
87
88status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device)
89{
90    if (mFD != -1) {
91        return INVALID_OPERATION;
92    }
93    mFD = fd;
94    mBase = base;
95    mSize = size;
96    mFlags = flags;
97    mDevice = device;
98    return NO_ERROR;
99}
100
101status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)
102{
103    if (size == 0) {
104        // try to figure out the size automatically
105        struct stat sb;
106        if (fstat(fd, &sb) == 0)
107            size = sb.st_size;
108        // if it didn't work, let mmap() fail.
109    }
110
111    if ((mFlags & DONT_MAP_LOCALLY) == 0) {
112        void* base = (uint8_t*)mmap(0, size,
113                PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
114        if (base == MAP_FAILED) {
115            ALOGE("mmap(fd=%d, size=%u) failed (%s)",
116                    fd, uint32_t(size), strerror(errno));
117            close(fd);
118            return -errno;
119        }
120        //ALOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size);
121        mBase = base;
122        mNeedUnmap = true;
123    } else  {
124        mBase = 0; // not MAP_FAILED
125        mNeedUnmap = false;
126    }
127    mFD = fd;
128    mSize = size;
129    mOffset = offset;
130    return NO_ERROR;
131}
132
133MemoryHeapBase::~MemoryHeapBase()
134{
135    dispose();
136}
137
138void MemoryHeapBase::dispose()
139{
140    int fd = android_atomic_or(-1, &mFD);
141    if (fd >= 0) {
142        if (mNeedUnmap) {
143            //ALOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize);
144            munmap(mBase, mSize);
145        }
146        mBase = 0;
147        mSize = 0;
148        close(fd);
149    }
150}
151
152int MemoryHeapBase::getHeapID() const {
153    return mFD;
154}
155
156void* MemoryHeapBase::getBase() const {
157    return mBase;
158}
159
160size_t MemoryHeapBase::getSize() const {
161    return mSize;
162}
163
164uint32_t MemoryHeapBase::getFlags() const {
165    return mFlags;
166}
167
168const char* MemoryHeapBase::getDevice() const {
169    return mDevice;
170}
171
172uint32_t MemoryHeapBase::getOffset() const {
173    return mOffset;
174}
175
176// ---------------------------------------------------------------------------
177}; // namespace android
178