Ray Liu
/
NuMaker-mbed-NuBrick-example
Modify the file main.cpp for M487
NuMaker-mbed-NuBrick/NuBrickMaster.cpp
- Committer:
- shliu1
- Date:
- 2017-09-29
- Revision:
- 0:a67fc999dd68
File content as of revision 0:a67fc999dd68:
/* mbed Microcontroller Library * Copyright (c) 2016 ARM Limited * * 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. */ #include "NuBrickMaster.h" #include <cstring> SingletonPtr<PlatformMutex> NuBrickMaster::_mutex; NuBrickMaster::NuBrickMaster(I2C &i2c, int i2c_addr, bool debug) : _i2c(i2c), _i2c_addr(i2c_addr), _i2c_buf_pos(_i2c_buf), _i2c_buf_end(_i2c_buf + sizeof (_i2c_buf) / sizeof (_i2c_buf[0])), _i2c_buf_overflow(false), _connected(false), _debug(debug), _null_field(0, ""), _feature_report_fields(NULL), _num_feature_report_fields(0), _input_report_fields(NULL), _num_input_report_fields(0), _output_report_fields(NULL), _num_output_report_fields(0) { // No lock needed in the constructor // Set I2C bus clock to 100K. _i2c.frequency(100000); memset(_i2c_buf, 0x00, sizeof (_i2c_buf)); } NuBrickMaster::~NuBrickMaster() { // Remove fields of feature report allocated by subclass remove_feature_fields(); // Remove fields of input report allocated by subclass remove_input_fields(); // Remove fields of output report allocated by subclass remove_output_fields(); } bool NuBrickMaster::connect(void) { // Support thread-safe MutexGuard guard; if (_connected) { return true; } // Get device descriptor if (! pull_device_desc()) { _connected = false; NUBRICK_ERROR_RETURN_FALSE("pull_device_desc() failed\r\n"); } // Get report descriptor if (! pull_report_desc()) { _connected = false; NUBRICK_ERROR_RETURN_FALSE("pull_report_desc() failed\r\n"); } _connected = true; return true; } NuBrickField &NuBrickMaster::operator[](const char *report_field_name) { // Support thread-safe MutexGuard guard; if (! report_field_name) { NUBRICK_ERROR_RETURN_NULL_FIELD("NULL string not support\r\n"); } const char *dot_plus_field_name = strchr(report_field_name, '.'); if (dot_plus_field_name == NULL) { NUBRICK_ERROR_RETURN_NULL_FIELD("%s not support\r\n", report_field_name); } const char *field_name = dot_plus_field_name + 1; unsigned report_name_len = dot_plus_field_name - report_field_name; if (strncmp("feature", report_field_name, report_name_len) == 0) { NuBrickField *field = _feature_report_fields; NuBrickField *field_end = _feature_report_fields + _num_feature_report_fields; for (; field != field_end; field ++) { const char *field_name_iter = field->_name; if (strcmp(field_name, field_name_iter) == 0) { return *field; } } NUBRICK_ERROR_RETURN_NULL_FIELD("%s not support\r\n", report_field_name); } else if (strncmp("input", report_field_name, report_name_len) == 0) { NuBrickField *field = _input_report_fields; NuBrickField *field_end = _input_report_fields + _num_input_report_fields; for (; field != field_end; field ++) { const char *field_name_iter = field->_name; if (strcmp(field_name, field_name_iter) == 0) { return *field; } } NUBRICK_ERROR_RETURN_NULL_FIELD("%s not support\r\n", report_field_name); } else if (strncmp("output", report_field_name, report_name_len) == 0) { NuBrickField *field = _output_report_fields; NuBrickField *field_end = _output_report_fields + _num_output_report_fields; for (; field != field_end; field ++) { const char *field_name_iter = field->_name; if (strcmp(field_name, field_name_iter) == 0) { return *field; } } NUBRICK_ERROR_RETURN_NULL_FIELD("%s not support\r\n", report_field_name); } else { NUBRICK_ERROR_RETURN_NULL_FIELD("%s not support\r\n", report_field_name); } } bool NuBrickMaster::pull_device_desc(void) { // Support thread-safe MutexGuard guard; // Send GetDeviceDescriptor command nu_set16_le(_i2c_buf, NuBrick_Comm_GetDeviceDesc); if (_i2c.write(_i2c_addr, (char *) _i2c_buf, 2, true)) { NUBRICK_ERROR_RETURN_FALSE("i2c.write() failed\r\n"); } // Receive device descriptor if (_i2c.read(_i2c_addr, (char *) _i2c_buf, NuBrick_DeviceDesc_Len, false)) { NUBRICK_ERROR_RETURN_FALSE("i2c.read() failed\r\n"); } // Un-serialize device descriptor _i2c_buf_pos = _i2c_buf; if (! unserialize_device_desc()) { NUBRICK_ERROR_RETURN_FALSE("unserialize_device_desc() failed\r\n"); } return true; } bool NuBrickMaster::pull_report_desc(void) { // Support thread-safe MutexGuard guard; // Send GetReportDescriptor command nu_set16_le(_i2c_buf, NuBrick_Comm_GetReportDesc); if (_i2c.write(_i2c_addr, (char *) _i2c_buf, 2, true)) { NUBRICK_ERROR_RETURN_FALSE("i2c.write() failed\r\n"); } // Receive report descriptor if (_i2c.read(_i2c_addr, (char *) _i2c_buf, _dev_desc.report_desc_len, false)) { NUBRICK_ERROR_RETURN_FALSE("i2c.read() failed\r\n"); } // Un-serialize report descriptor _i2c_buf_pos = _i2c_buf; if (! unserialize_report_desc()) { NUBRICK_ERROR_RETURN_FALSE("unserialize_report_desc() failed\r\n"); } return true; } bool NuBrickMaster::pull_input_report(void) { // Support thread-safe MutexGuard guard; NUBRICK_CHECK_CONNECT(); // Send GetInputReport command nu_set16_le(_i2c_buf, NuBrick_Comm_GetInputReport); if (_i2c.write(_i2c_addr, (char *) _i2c_buf, 2, true)) { NUBRICK_ERROR_RETURN_FALSE("i2c.write() failed\r\n"); } // Receive input report if (_i2c.read(_i2c_addr, (char *) _i2c_buf, _dev_desc.input_report_len, false)) { NUBRICK_ERROR_RETURN_FALSE("i2c.read() failed\r\n"); } // Un-serialize input report _i2c_buf_pos = _i2c_buf; if (! unserialize_input_report()) { NUBRICK_ERROR_RETURN_FALSE("unserialize_input_report() failed\r\n"); } return true; } bool NuBrickMaster::push_output_report(void) { // Support thread-safe MutexGuard guard; NUBRICK_CHECK_CONNECT(); _i2c_buf_pos = _i2c_buf; // Send SetOutputReport command set16_le_next(NuBrick_Comm_SetOutputReport); // Serialize output report if (! serialize_output_report()) { NUBRICK_ERROR_RETURN_FALSE("serialize_output_report() failed\r\n"); } // Send Output report if (_i2c.write(_i2c_addr, (char *) _i2c_buf, _i2c_buf_pos - _i2c_buf, false)) { NUBRICK_ERROR_RETURN_FALSE("i2c.write() failed\r\n"); } return true; } bool NuBrickMaster::pull_feature_report(void) { // Support thread-safe MutexGuard guard; NUBRICK_CHECK_CONNECT(); // Send GetFeatureReport command nu_set16_le(_i2c_buf, NuBrick_Comm_GetFeatureReport); if (_i2c.write(_i2c_addr, (char *) _i2c_buf, 2, true)) { NUBRICK_ERROR_RETURN_FALSE("i2c.write() failed\r\n"); } // Receive feature report if (_i2c.read(_i2c_addr, (char *) _i2c_buf, _dev_desc.getfeat_report_len, false)) { NUBRICK_ERROR_RETURN_FALSE("i2c.read() failed\r\n"); } // Un-serialize feature report _i2c_buf_pos = _i2c_buf; if (! unserialize_feature_report()) { NUBRICK_ERROR_RETURN_FALSE("unserialize_feature_report() failed\r\n"); } return true; } bool NuBrickMaster::push_feature_report(void) { // Support thread-safe MutexGuard guard; NUBRICK_CHECK_CONNECT(); _i2c_buf_pos = _i2c_buf; // Send SetFeatureReport command set16_le_next(NuBrick_Comm_SetFeatureReport); // Serialize feature report if (! serialize_feature_report()) { NUBRICK_ERROR_RETURN_FALSE("serialize_feature_report() failed\r\n"); } // Send feature report if (_i2c.write(_i2c_addr, (char *) _i2c_buf, _i2c_buf_pos - _i2c_buf, false)) { NUBRICK_ERROR_RETURN_FALSE("i2c.write() failed\r\n"); } return true; } bool NuBrickMaster::print_device_desc(void) { // Support thread-safe MutexGuard guard; NUBRICK_CHECK_CONNECT(); printf("Device descriptor length\t\t%d\r\n", _dev_desc.dev_desc_len); printf("Report descriptor length\t\t%d\r\n", _dev_desc.report_desc_len); printf("Input report length\t\t\t%d\r\n", _dev_desc.input_report_len); printf("Output report length\t\t\t%d\r\n", _dev_desc.output_report_len); printf("Get feature report length\t\t%d\r\n", _dev_desc.getfeat_report_len); printf("Set feature report length\t\t%d\r\n", _dev_desc.setfeat_report_len); printf("Company ID\t\t\t\t%d\r\n", _dev_desc.cid); printf("Device ID\t\t\t\t%d\r\n", _dev_desc.did); printf("Product ID\t\t\t\t%d\r\n", _dev_desc.pid); printf("Product ID\t\t\t\t%d\r\n", _dev_desc.uid); printf("Product ID\t\t\t\t%d\r\n", _dev_desc.ucid); return true; } bool NuBrickMaster::print_feature_report(void) { // Support thread-safe MutexGuard guard; NUBRICK_CHECK_CONNECT(); print_report(_feature_report_fields, _num_feature_report_fields, "feature report"); return true; } bool NuBrickMaster::print_input_report(void) { // Support thread-safe MutexGuard guard; NUBRICK_CHECK_CONNECT(); print_report(_input_report_fields, _num_input_report_fields, "input report"); return true; } bool NuBrickMaster::print_output_report(void) { // Support thread-safe MutexGuard guard; NUBRICK_CHECK_CONNECT(); print_report(_output_report_fields, _num_output_report_fields, "output report"); return true; } void NuBrickMaster::add_feature_fields(const NuBrickField::IndexName *field_index_name, unsigned num_index_name) { remove_report_fields(_feature_report_fields, _num_feature_report_fields); add_report_fields(field_index_name, num_index_name, _feature_report_fields, _num_feature_report_fields); } void NuBrickMaster::remove_feature_fields(void) { remove_report_fields(_feature_report_fields, _num_feature_report_fields); } void NuBrickMaster::add_input_fields(const NuBrickField::IndexName *field_index_name, unsigned num_index_name) { remove_report_fields(_input_report_fields, _num_input_report_fields); add_report_fields(field_index_name, num_index_name, _input_report_fields, _num_input_report_fields); } void NuBrickMaster::remove_input_fields(void) { remove_report_fields(_input_report_fields, _num_input_report_fields); } void NuBrickMaster::add_output_fields(const NuBrickField::IndexName *field_index_name, unsigned num_index_name) { remove_report_fields(_output_report_fields, _num_output_report_fields); add_report_fields(field_index_name, num_index_name, _output_report_fields, _num_output_report_fields); } void NuBrickMaster::remove_output_fields(void) { remove_report_fields(_output_report_fields, _num_output_report_fields); } void NuBrickMaster::add_report_fields(const NuBrickField::IndexName *field_index_name, unsigned num_index_name, NuBrickField *&report_fields, unsigned &num_report_fields) { MBED_ASSERT(report_fields == NULL); MBED_ASSERT(num_report_fields == 0); unsigned i; num_report_fields = num_index_name; void *raw_memory = ::operator new(sizeof (NuBrickField) * num_index_name); report_fields = static_cast<NuBrickField *>(raw_memory); for (i = 0; i < num_index_name; i ++) { NuBrickField *field = report_fields + i; new (field) NuBrickField(field_index_name[i].first, field_index_name[i].second); } } void NuBrickMaster::NuBrickMaster::remove_report_fields(NuBrickField *&report_fields, unsigned &num_report_fields) { unsigned i; for (i = 0; i < num_report_fields; i ++) { NuBrickField *field = report_fields + i; field->~NuBrickField(); } ::operator delete((void *) report_fields); report_fields = NULL; num_report_fields = 0; } bool NuBrickMaster::unserialize_device_desc(void) { // Device descriptor length _dev_desc.dev_desc_len = get16_le_next(); // Report descriptor length _dev_desc.report_desc_len = get16_le_next(); // Input report length _dev_desc.input_report_len = get16_le_next(); // Output report length _dev_desc.output_report_len = get16_le_next(); // Get feature report length _dev_desc.getfeat_report_len = get16_le_next(); // Set feature report length _dev_desc.setfeat_report_len = get16_le_next(); // CID _dev_desc.cid = get16_le_next(); // DID _dev_desc.did = get16_le_next(); // PID _dev_desc.pid = get16_le_next(); // UID _dev_desc.uid = get16_le_next(); // UCID _dev_desc.ucid = get16_le_next(); // Reserved 1 _dev_desc.reserved1 = get16_le_next(); // Reserved 2 _dev_desc.reserved2 = get16_le_next(); if (_dev_desc.dev_desc_len != NuBrick_DeviceDesc_Len) { NUBRICK_ERROR_RETURN_FALSE("Length of device descriptor doesn't match\r\n"); } return true; } bool NuBrickMaster::unserialize_report_desc(void) { // Report descriptor length uint16_t report_desc_len = get16_le_next(); if (report_desc_len != _dev_desc.report_desc_len) { NUBRICK_ERROR_RETURN_FALSE("Length of report descriptor doesn't match\r\n"); } uint16_t desc_type; NuBrickField *field = NULL; NuBrickField *field_end = NULL; // Check no feature report descriptor if (! _num_feature_report_fields) { return true; } // Feature report descriptor type desc_type = get16_be_next(); if (desc_type != NuBrick_DescType_FeatureReport) { NUBRICK_ERROR_RETURN_FALSE("Expect feature report descriptor type %d, but %d received\r\n", NuBrick_DescType_FeatureReport, desc_type); } // Un-serialize feature report fields from report descriptor field = _feature_report_fields; field_end = _feature_report_fields + _num_feature_report_fields; for (; field != field_end; field ++) { if (! unserialize_field_from_report_desc(field)) { NUBRICK_ERROR_RETURN_FALSE("unserialize_field_from_report_desc() failed\r\n"); } } // Check no input report descriptor if (! _num_input_report_fields) { return true; } // Input report descriptor type desc_type = get16_be_next(); if (desc_type != NuBrick_DescType_InputReport) { NUBRICK_ERROR_RETURN_FALSE("Expect input report descriptor type %d, but %d received\r\n", NuBrick_DescType_InputReport, desc_type); } // Un-serialize input report fields from report descriptor field = _input_report_fields; field_end = _input_report_fields + _num_input_report_fields; for (; field != field_end; field ++) { if (! unserialize_field_from_report_desc(field)) { NUBRICK_ERROR_RETURN_FALSE("unserialize_field_from_report_desc() failed\r\n"); } } // Check no output report descriptor if (! _num_output_report_fields) { return true; } // Output report descriptor type desc_type = get16_be_next(); if (desc_type != NuBrick_DescType_OutputReport) { NUBRICK_ERROR_RETURN_FALSE("Expect output report descriptor type %d, but %d received\r\n", NuBrick_DescType_OutputReport, desc_type); } // Un-serialize output report fields from report descriptor field = _output_report_fields; field_end = _output_report_fields + _num_output_report_fields; for (; field != field_end; field ++) { if (! unserialize_field_from_report_desc(field)) { NUBRICK_ERROR_RETURN_FALSE("unserialize_field_from_report_desc() failed\r\n"); } } return true; } bool NuBrickMaster::unserialize_input_report(void) { // Input report length uint16_t report_len = get16_le_next(); if (report_len != _dev_desc.input_report_len) { NUBRICK_ERROR_RETURN_FALSE("Length of input report doesn't match\r\n"); } // Un-serialize fields from input report NuBrickField *field = _input_report_fields; NuBrickField *field_end = _input_report_fields + _num_input_report_fields; for (; field != field_end; field ++) { if (! unserialize_field_from_report(field)) { NUBRICK_ERROR_RETURN_FALSE("unserialize_field_from_report() failed\r\n"); } } return true; } bool NuBrickMaster::serialize_output_report(void) { uint8_t *i2c_buf_beg = _i2c_buf_pos; // Output report length set16_le_next(_dev_desc.output_report_len); // Serialize fields to output report NuBrickField *field = _output_report_fields; NuBrickField *field_end = _output_report_fields + _num_output_report_fields; for (; field != field_end; field ++) { if (! serialize_field_to_report(field)) { NUBRICK_ERROR_RETURN_FALSE("serialize_field_to_report() failed\r\n"); } } if ((_i2c_buf_pos - i2c_buf_beg) != _dev_desc.output_report_len) { NUBRICK_ERROR_RETURN_FALSE("Length of output report doesn't match\r\n"); } return true; } bool NuBrickMaster::unserialize_feature_report(void) { // Feature report length uint16_t report_len = get16_le_next(); if (report_len != _dev_desc.getfeat_report_len) { NUBRICK_ERROR_RETURN_FALSE("Length of feature report doesn't match\r\n"); } // Un-serialize fields from feature report NuBrickField *field = _feature_report_fields; NuBrickField *field_end = _feature_report_fields + _num_feature_report_fields; for (; field != field_end; field ++) { if (! unserialize_field_from_report(field)) { NUBRICK_ERROR_RETURN_FALSE("unserialize_field_from_report() failed\r\n"); } } return true; } bool NuBrickMaster::serialize_feature_report(void) { uint8_t *i2c_buf_beg = _i2c_buf_pos; // Feature report length set16_le_next(_dev_desc.setfeat_report_len); // Serialize fields to feature report NuBrickField *field = _feature_report_fields; NuBrickField *field_end = _feature_report_fields + _num_feature_report_fields; for (; field != field_end; field ++) { if (! serialize_field_to_report(field)) { NUBRICK_ERROR_RETURN_FALSE("serialize_field_to_report() failed\r\n"); } } if ((_i2c_buf_pos - i2c_buf_beg) != _dev_desc.setfeat_report_len) { NUBRICK_ERROR_RETURN_FALSE("Length of set feature report doesn't match\r\n"); } return true; } bool NuBrickMaster::unserialize_field_from_report_desc(NuBrickField *field) { // Number/length of the field uint8_t field_index = get8_next(); if (field_index != field->_field_index) { NUBRICK_ERROR_RETURN_FALSE("Expect field index %d, but %d received\r\n", field->_field_index, field_index); } // Length of the field field->_length = get8_next(); if (field->_length != 1 && field->_length != 2) { NUBRICK_ERROR_RETURN_FALSE("Expect field length 1/2, but %d received\r\n", field->_length); } // Minimum of the field uint8_t min = get8_next(); switch (min) { case NuBrick_ReportDesc_Min_Plus1: field->_minimum = get8_next(); break; case NuBrick_ReportDesc_Min_Plus2: field->_minimum = get16_le_next(); break; default: NUBRICK_ERROR_RETURN_FALSE("Expect field minimum %d/%d, but %d received\r\n", NuBrick_ReportDesc_Min_Plus1, NuBrick_ReportDesc_Min_Plus2, min); } // Maximum of the field uint8_t max = get8_next(); switch (max) { case NuBrick_ReportDesc_Max_Plus1: field->_maximum = get8_next(); break; case NuBrick_ReportDesc_Max_Plus2: field->_maximum = get16_le_next(); break; default: NUBRICK_ERROR_RETURN_FALSE("Expect field maximum %d/%d, but %d received\r\n", NuBrick_ReportDesc_Max_Plus1, NuBrick_ReportDesc_Max_Plus2, max); } return true; } bool NuBrickMaster::unserialize_field_from_report(NuBrickField *field) { // Value of the field switch (field->_length) { case 1: field->_value = get8_next(); break; case 2: field->_value = get16_le_next(); break; default: NUBRICK_ERROR_RETURN_FALSE("Expect field length 1/2, but %d received\r\n", field->_length); } return true; } bool NuBrickMaster::serialize_field_to_report(const NuBrickField *field) { // Value of the field switch (field->_length) { case 1: set8_next(field->_value); break; case 2: set16_le_next(field->_value); break; default: NUBRICK_ERROR_RETURN_FALSE("Expect field length 1/2, but %d received\r\n", field->_length); } return true; } uint8_t NuBrickMaster::get8_next(void) { NUBRICK_CHECK_GETN_NEXT(1); uint8_t val = *_i2c_buf_pos ++; return val; } void NuBrickMaster::set8_next(uint8_t val) { NUBRICK_CHECK_SETN_NEXT(1); *_i2c_buf_pos ++ = val; } uint16_t NuBrickMaster::get16_le_next(void) { NUBRICK_CHECK_GETN_NEXT(2); uint16_t val = nu_get16_le(_i2c_buf_pos); _i2c_buf_pos += 2; return val; } void NuBrickMaster::set16_le_next(uint16_t val) { NUBRICK_CHECK_SETN_NEXT(2); nu_set16_le(_i2c_buf_pos, val); _i2c_buf_pos += 2; } uint16_t NuBrickMaster::get16_be_next(void) { NUBRICK_CHECK_GETN_NEXT(2); uint16_t val = nu_get16_be(_i2c_buf_pos); _i2c_buf_pos += 2; return val; } void NuBrickMaster::print_report(NuBrickField *fields, unsigned num_fields, const char *report_name) { printf("Number of fields of %s\t%d\r\n", report_name, num_fields); unsigned i; for (i = 0; i < num_fields; i ++) { NuBrickField *field = fields + i; printf("Name\t\t%s\r\n", field->_name); printf("Length\t\t%d\r\n", field->_length); printf("Value\t\t%d\r\n", field->_value); printf("Minimum\t\t%d\r\n", field->_minimum); printf("Maximum\t\t%d\r\n", field->_maximum); printf("\r\n"); } }