// // Copyright 2018 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef __VTS_RESOURCE_VTSFMQDRIVER_H #define __VTS_RESOURCE_VTSFMQDRIVER_H #include #include #include #include #include using android::hardware::kSynchronizedReadWrite; using android::hardware::kUnsynchronizedWrite; using android::hardware::MessageQueue; using android::hardware::MQDescriptorSync; using android::hardware::MQDescriptorUnsync; using namespace std; using QueueId = int; static constexpr const int kInvalidQueueId = -1; namespace android { namespace vts { // struct to store queue information. struct QueueInfo { // type of data in the queue. string queue_data_type; // flavor of the queue (sync or unsync). hardware::MQFlavor queue_flavor; // pointer to the actual queue object. shared_ptr queue_object; }; // A fast message queue class that manages all fast message queues created // on the target side. Reader and writer use their id to read from and write // into the queue. // Example: // VtsFmqDriver manager; // // creates one reader and one writer. // QueueId writer_id = // manager.CreateFmq(2048, false); // QueueId reader_id = // manager.CreateFmq(writer_id); // // write some data // uint16_t write_data[5] {1, 2, 3, 4, 5}; // manager.WriteFmq( // writer_id, write_data, 5); // // read the same data back // uint16_t read_data[5]; // manager.ReadFmq( // reader_id, read_data, 5); class VtsFmqDriver { public: // Constructor to initialize a Fast Message Queue (FMQ) manager. VtsFmqDriver() {} // Destructor to clean up the class. ~VtsFmqDriver() { // Clears objects in the map. fmq_map_.clear(); } // Creates a brand new FMQ, i.e. the "first message queue object". // // @param data_type type of data in the queue. This information is stored // in the driver to verify type later. // @param queue_size number of elements in the queue. // @param blocking whether to enable blocking within the queue. // // @return message queue object id associated with the caller on success, // kInvalidQueueId on failure. template QueueId CreateFmq(const string& data_type, size_t queue_size, bool blocking); // Creates a new FMQ object based on an existing message queue // (Using queue_id assigned by fmq_driver.). // // @param data_type type of data in the queue. This information is stored // in the driver to verify type later. // @param queue_id identifies the message queue object. // @param reset_pointers whether to reset read and write pointers. // // @return message queue object id associated with the caller on success, // kInvalidQueueId on failure. template QueueId CreateFmq(const string& data_type, QueueId queue_id, bool reset_pointers = true); // Creates a new FMQ object based on an existing message queue // (Using queue descriptor.). // This method will reset read/write pointers in the new queue object. // // @param data_type type of data in the queue. This information is // stored in the driver to verify type later. // @param queue_descriptor pointer to descriptor of an existing queue. // // @return message queue object id associated with the caller on success, // kInvalidQueueId on failure. template QueueId CreateFmq(const string& data_type, size_t queue_descriptor); // Reads data_size items from FMQ (no blocking at all). // // @param data_type type of data in the queue. This information is verified // by the driver before calling the API on FMQ. // @param queue_id identifies the message queue object. // @param data pointer to the start of data to be filled. // @param data_size number of items to read. // // @return true if no error happens when reading from FMQ, // false otherwise. template bool ReadFmq(const string& data_type, QueueId queue_id, T* data, size_t data_size); // Reads data_size items from FMQ, block if there is not enough data to // read. // This method can only be called if blocking=true on creation of the "first // message queue object" of the FMQ. // // @param data_type type of data in the queue. This information is // verified by the driver before calling the API on FMQ. // @param queue_id identifies the message queue object. // @param data pointer to the start of data to be filled. // @param data_size number of items to read. // @param time_out_nanos wait time when blocking. // // Returns: true if no error happens when reading from FMQ, // false otherwise. template bool ReadFmqBlocking(const string& data_type, QueueId queue_id, T* data, size_t data_size, int64_t time_out_nanos); // Reads data_size items from FMQ, possibly block on other queues. // // @param data_type type of data in the queue. This information is // verified by the driver before calling the API // on FMQ. // @param queue_id identifies the message queue object. // @param data pointer to the start of data to be filled. // @param data_size number of items to read. // @param read_notification notification bits to set when finish reading. // @param write_notification notification bits to wait on when blocking. // Read will fail if this argument is 0. // @param time_out_nanos wait time when blocking. // @param event_flag_word event flag word shared by multiple queues. // // @return true if no error happens when reading from FMQ, // false otherwise. template bool ReadFmqBlocking(const string& data_type, QueueId queue_id, T* data, size_t data_size, uint32_t read_notification, uint32_t write_notification, int64_t time_out_nanos, atomic* event_flag_word); // Writes data_size items to FMQ (no blocking at all). // // @param data_type type of data in the queue. This information is verified // by the driver before calling the API on FMQ. // @param queue_id identifies the message queue object. // @param data pointer to the start of data to be written. // @param data_size number of items to write. // // @return true if no error happens when writing to FMQ, // false otherwise. template bool WriteFmq(const string& data_type, QueueId queue_id, T* data, size_t data_size); // Writes data_size items to FMQ, block if there is not enough space in // the queue. // This method can only be called if blocking=true on creation of the "first // message queue object" of the FMQ. // // @param data_type type of data in the queue. This information is // verified // by the driver before calling the API on FMQ. // @param queue_id identifies the message queue object. // @param data pointer to the start of data to be written. // @param data_size number of items to write. // @param time_out_nanos wait time when blocking. // // @returns true if no error happens when writing to FMQ, // false otherwise template bool WriteFmqBlocking(const string& data_type, QueueId queue_id, T* data, size_t data_size, int64_t time_out_nanos); // Writes data_size items to FMQ, possibly block on other queues. // // @param data_type type of data in the queue. This information is // verified by the driver before calling the API // on FMQ. // @param queue_id identifies the message queue object. // @param data pointer to the start of data to be written. // @param data_size number of items to write. // @param read_notification notification bits to wait on when blocking. // Write will fail if this argument is 0. // @param write_notification notification bits to set when finish writing. // @param time_out_nanos wait time when blocking. // @param event_flag_word event flag word shared by multiple queues. // // @return true if no error happens when writing to FMQ, // false otherwise. template bool WriteFmqBlocking(const string& data_type, QueueId queue_id, T* data, size_t data_size, uint32_t read_notification, uint32_t write_notification, int64_t time_out_nanos, atomic* event_flag_word); // Gets space available to write in the queue. // // @param data_type type of data in the queue. This information is // verified by the driver before calling the API on FMQ. // @param queue_id identifies the message queue object. // @param result pointer to the result. Use pointer to store result because // the return value signals if the queue is found correctly. // // @return true if queue is found and type matches, and puts actual result in // result pointer, // false otherwise. template bool AvailableToWrite(const string& data_type, QueueId queue_id, size_t* result); // Gets number of items available to read in the queue. // // @param data_type type of data in the queue. This information is // verified by the driver before calling the API on FMQ. // @param queue_id identifies the message queue object. // @param result pointer to the result. Use pointer to store result because // the return value signals if the queue is found correctly. // // @return true if queue is found and type matches, and puts actual result in // result pointer, // false otherwise. template bool AvailableToRead(const string& data_type, QueueId queue_id, size_t* result); // Gets size of item in the queue. // // @param data_type type of data in the queue. This information is // verified by the driver before calling the API on FMQ. // @param queue_id identifies the message queue object. // @param result pointer to the result. Use pointer to store result because // the return value signals if the queue is found correctly. // // @return true if queue is found and type matches, and puts actual result in // result pointer, // false otherwise. template bool GetQuantumSize(const string& data_type, QueueId queue_id, size_t* result); // Gets number of items that fit in the queue. // // @param data_type type of data in the queue. This information is // verified by the driver before calling the API on FMQ. // @param queue_id identifies the message queue object. // @param result pointer to the result. Use pointer to store result because // the return value signals if the queue is found correctly. // // @return true if queue is found and type matches, and puts actual result in // result pointer, // false otherwise. template bool GetQuantumCount(const string& data_type, QueueId queue_id, size_t* result); // Checks if the queue associated with queue_id is valid. // // @param data_type type of data in the queue. This information is // verified by the driver before calling the API on FMQ. // @param queue_id identifies the message queue object. // // @return true if the queue object is valid, false otherwise. template bool IsValid(const string& data_type, QueueId queue_id); // Gets event flag word of the queue, which allows multiple queues // to communicate (i.e. blocking). // The returned event flag word can be passed into readBlocking() and // writeBlocking() to achieve blocking among multiple queues. // // @param data_type type of data in the queue. This information is // verified by the driver before calling the API on FMQ. // @param queue_id identifies the message queue object. // @param result pointer to the result. Use pointer to store result because // the return value signals if the queue is found correctly. // // @return true if queue is found and type matches, and puts actual result in // result pointer, // false otherwise. template bool GetEventFlagWord(const string& data_type, QueueId queue_id, atomic** result); // Gets the address of queue descriptor in memory. This function is called by // driver_manager to preprocess arguments that are FMQs. // // @param data_type type of data in the queue. This information is // verified by the driver before calling the API on FMQ. // @param queue_id identifies the message queue object. // @param result pointer to store result. // // @return true if queue is found, and type matches, and puts actual result in // result pointer, // false otherwise. template bool GetQueueDescAddress(const string& data_type, QueueId queue_id, size_t* result); private: // Finds the queue in the map based on the input queue ID. // // @param data_type type of data in the queue. This function verifies this // information. // @param queue_id identifies the message queue object. // // @return the pointer to message queue object, // nullptr if queue ID is invalid or queue type is misspecified. template MessageQueue* FindQueue(const string& data_type, QueueId queue_id); // Inserts a FMQ object into the map, along with its type of data and queue // flavor. This function ensures only one thread is inserting queue into the // map at once. // // @param data_type type of data in the queue. This information is stored // in the driver. // @param queue_object a shared pointer to MessageQueue. // // @return id associated with the queue. template QueueId InsertQueue(const string& data_type, shared_ptr> queue_object); // a hashtable to keep track of all ongoing FMQ's. // The key of the hashtable is the queue ID. // The value of the hashtable is a smart pointer to message queue object // information struct associated with the queue ID. unordered_map> fmq_map_; // a mutex to ensure mutual exclusion of operations on fmq_map_ mutex map_mutex_; }; // Implementations follow, because all the methods are template methods. template QueueId VtsFmqDriver::CreateFmq(const string& data_type, size_t queue_size, bool blocking) { shared_ptr> new_queue( new (std::nothrow) MessageQueue(queue_size, blocking)); return InsertQueue(data_type, new_queue); } template QueueId VtsFmqDriver::CreateFmq(const string& data_type, QueueId queue_id, bool reset_pointers) { MessageQueue* queue_object = FindQueue(data_type, queue_id); const hardware::MQDescriptor* descriptor = queue_object->getDesc(); if (descriptor == nullptr) { LOG(ERROR) << "FMQ Driver: cannot find descriptor for the specified " << "Fast Message Queue with ID " << queue_id << "."; return kInvalidQueueId; } shared_ptr> new_queue( new (std::nothrow) MessageQueue(*descriptor, reset_pointers)); return InsertQueue(data_type, new_queue); } template QueueId VtsFmqDriver::CreateFmq(const string& data_type, size_t queue_desc_addr) { // Cast the address back to a descriptor object. hardware::MQDescriptor* descriptor_addr = reinterpret_cast*>(queue_desc_addr); shared_ptr> new_queue( new (std::nothrow) MessageQueue(*descriptor_addr)); // Need to manually delete this pointer because HAL driver allocates it. delete descriptor_addr; return InsertQueue(data_type, new_queue); } template bool VtsFmqDriver::ReadFmq(const string& data_type, QueueId queue_id, T* data, size_t data_size) { MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; return queue_object->read(data, data_size); } template bool VtsFmqDriver::ReadFmqBlocking(const string& data_type, QueueId queue_id, T* data, size_t data_size, int64_t time_out_nanos) { if (flavor == kUnsynchronizedWrite) { LOG(ERROR) << "FMQ Driver: blocking read is not allowed in " << "unsynchronized queue."; return false; } MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; return queue_object->readBlocking(data, data_size, time_out_nanos); } template bool VtsFmqDriver::ReadFmqBlocking(const string& data_type, QueueId queue_id, T* data, size_t data_size, uint32_t read_notification, uint32_t write_notification, int64_t time_out_nanos, atomic* event_flag_word) { if (flavor == kUnsynchronizedWrite) { LOG(ERROR) << "FMQ Driver: blocking read is not allowed in " << "unsynchronized queue."; return false; } hardware::EventFlag* ef_group = nullptr; status_t status; // create an event flag out of the event flag word status = hardware::EventFlag::createEventFlag(event_flag_word, &ef_group); if (status != NO_ERROR) { // check status LOG(ERROR) << "FMQ Driver: cannot create event flag with the specified " << "event flag word."; return false; } MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; return queue_object->readBlocking(data, data_size, read_notification, write_notification, time_out_nanos, ef_group); } template bool VtsFmqDriver::WriteFmq(const string& data_type, QueueId queue_id, T* data, size_t data_size) { MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; return queue_object->write(data, data_size); } template bool VtsFmqDriver::WriteFmqBlocking(const string& data_type, QueueId queue_id, T* data, size_t data_size, int64_t time_out_nanos) { if (flavor == kUnsynchronizedWrite) { LOG(ERROR) << "FMQ Driver: blocking write is not allowed in " << "unsynchronized queue."; return false; } MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; return queue_object->writeBlocking(data, data_size, time_out_nanos); } template bool VtsFmqDriver::WriteFmqBlocking(const string& data_type, QueueId queue_id, T* data, size_t data_size, uint32_t read_notification, uint32_t write_notification, int64_t time_out_nanos, atomic* event_flag_word) { if (flavor == kUnsynchronizedWrite) { LOG(ERROR) << "FMQ Driver: blocking write is not allowed in " << "unsynchronized queue."; return false; } hardware::EventFlag* ef_group = nullptr; status_t status; // create an event flag out of the event flag word status = hardware::EventFlag::createEventFlag(event_flag_word, &ef_group); if (status != NO_ERROR) { // check status LOG(ERROR) << "FMQ Driver: cannot create event flag with the specified " << "event flag word."; return false; } MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; return queue_object->writeBlocking(data, data_size, read_notification, write_notification, time_out_nanos, ef_group); } template bool VtsFmqDriver::AvailableToWrite(const string& data_type, QueueId queue_id, size_t* result) { MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; *result = queue_object->availableToWrite(); return true; } template bool VtsFmqDriver::AvailableToRead(const string& data_type, QueueId queue_id, size_t* result) { MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; *result = queue_object->availableToRead(); return true; } template bool VtsFmqDriver::GetQuantumSize(const string& data_type, QueueId queue_id, size_t* result) { MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; *result = queue_object->getQuantumSize(); return true; } template bool VtsFmqDriver::GetQuantumCount(const string& data_type, QueueId queue_id, size_t* result) { MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; *result = queue_object->getQuantumCount(); return true; } template bool VtsFmqDriver::IsValid(const string& data_type, QueueId queue_id) { MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; return queue_object->isValid(); } template bool VtsFmqDriver::GetEventFlagWord(const string& data_type, QueueId queue_id, atomic** result) { MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; *result = queue_object->getEventFlagWord(); return true; } template bool VtsFmqDriver::GetQueueDescAddress(const string& data_type, QueueId queue_id, size_t* result) { MessageQueue* queue_object = FindQueue(data_type, queue_id); if (queue_object == nullptr) return false; *result = reinterpret_cast(queue_object->getDesc()); return true; } template MessageQueue* VtsFmqDriver::FindQueue(const string& data_type, QueueId queue_id) { map_mutex_.lock(); // Ensure mutual exclusion. auto iterator = fmq_map_.find(queue_id); if (iterator == fmq_map_.end()) { // queue not found LOG(ERROR) << "FMQ Driver: cannot find Fast Message Queue with ID " << queue_id; return nullptr; } QueueInfo* queue_info = (iterator->second).get(); if (queue_info->queue_data_type != data_type) { // queue data type incorrect LOG(ERROR) << "FMQ Driver: caller specified data type " << data_type << " doesn't match with the data type " << queue_info->queue_data_type << " stored in driver."; return nullptr; } if (queue_info->queue_flavor != flavor) { // queue flavor incorrect LOG(ERROR) << "FMQ Driver: caller specified flavor " << flavor << "doesn't match with the stored queue flavor " << queue_info->queue_flavor << "."; return nullptr; } // type check passes, extract queue from the struct shared_ptr> queue_object = static_pointer_cast>(queue_info->queue_object); MessageQueue* result = queue_object.get(); map_mutex_.unlock(); return result; } template QueueId VtsFmqDriver::InsertQueue( const string& data_type, shared_ptr> queue_object) { if (queue_object == nullptr) { LOG(ERROR) << "FMQ Driver Error: Failed to create a FMQ " << "using FMQ constructor."; return kInvalidQueueId; } // Create a struct to store queue object, type of data, and // queue flavor. unique_ptr new_queue_info(new QueueInfo{ string(data_type), flavor, static_pointer_cast(queue_object)}); map_mutex_.lock(); size_t new_queue_id = fmq_map_.size(); fmq_map_.emplace(new_queue_id, move(new_queue_info)); map_mutex_.unlock(); return new_queue_id; } } // namespace vts } // namespace android #endif //__VTS_RESOURCE_VTSFMQDRIVER_H