mirror of https://github.com/ARMmbed/mbed-os.git
BLE: Add API to manage GAP connection parameter updates.
parent
b6a0c8aea5
commit
869bb308c5
|
@ -324,9 +324,17 @@ public:
|
||||||
*
|
*
|
||||||
* @param event Connection event @see ConnectionCompleteEvent_t for details.
|
* @param event Connection event @see ConnectionCompleteEvent_t for details.
|
||||||
*/
|
*/
|
||||||
void onConnectionComplete(const ble::ConnectionCompleteEvent &event) { }
|
virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event) { }
|
||||||
|
|
||||||
void onDisconnection(const ble::DisconnectionEvent &event) { }
|
virtual void onUpdateConnectionParametersRequest(
|
||||||
|
const ble::UpdateConnectionParametersRequestEvent &event
|
||||||
|
) { }
|
||||||
|
|
||||||
|
virtual void onConnectionParametersUpdateComplete(
|
||||||
|
const ble::ConnectionParametersUpdateCompleteEvent &event
|
||||||
|
) { }
|
||||||
|
|
||||||
|
virtual void onDisconnection(const ble::DisconnectionEvent &event) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function invoked when the current transmitter and receiver PHY have
|
* Function invoked when the current transmitter and receiver PHY have
|
||||||
|
@ -710,6 +718,34 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual ble_error_t cancelConnect();
|
virtual ble_error_t cancelConnect();
|
||||||
|
|
||||||
|
virtual ble_error_t updateConnectionParameters(
|
||||||
|
ble::connection_handle_t connectionHandle,
|
||||||
|
ble::conn_interval_t minConnectionInterval,
|
||||||
|
ble::conn_interval_t maxConnectionInterval,
|
||||||
|
ble::slave_latency_t slaveLatency,
|
||||||
|
ble::supervision_timeout_t supervision_timeout,
|
||||||
|
ble::conn_event_length_t minConnectionEventLength = ble::conn_event_length_t(0),
|
||||||
|
ble::conn_event_length_t maxConnectionEventLength = ble::conn_event_length_t(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual ble_error_t manageConnectionParametersUpdateRequest(
|
||||||
|
bool userManageConnectionUpdateRequest
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual ble_error_t acceptConnectionParametersUpdate(
|
||||||
|
ble::connection_handle_t connectionHandle,
|
||||||
|
ble::conn_interval_t minConnectionInterval,
|
||||||
|
ble::conn_interval_t maxConnectionInterval,
|
||||||
|
ble::slave_latency_t slaveLatency,
|
||||||
|
ble::supervision_timeout_t supervision_timeout,
|
||||||
|
ble::conn_event_length_t minConnectionEventLength = ble::conn_event_length_t(0),
|
||||||
|
ble::conn_event_length_t maxConnectionEventLength = ble::conn_event_length_t(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual ble_error_t rejectConnectionParametersUpdate(
|
||||||
|
ble::connection_handle_t connectionHandle
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiate a disconnection procedure.
|
* Initiate a disconnection procedure.
|
||||||
*
|
*
|
||||||
|
|
|
@ -552,7 +552,6 @@ private:
|
||||||
const address_t &peerAddress;
|
const address_t &peerAddress;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct DisconnectionEvent {
|
struct DisconnectionEvent {
|
||||||
DisconnectionEvent(
|
DisconnectionEvent(
|
||||||
connection_handle_t connectionHandle,
|
connection_handle_t connectionHandle,
|
||||||
|
@ -574,6 +573,104 @@ private:
|
||||||
ble::disconnection_reason_t reason;
|
ble::disconnection_reason_t reason;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct UpdateConnectionParametersRequestEvent {
|
||||||
|
UpdateConnectionParametersRequestEvent(
|
||||||
|
connection_handle_t connectionHandle,
|
||||||
|
const conn_interval_t &minConnectionInterval,
|
||||||
|
const conn_interval_t &maxConnectionInterval,
|
||||||
|
const slave_latency_t &slaveLatency,
|
||||||
|
const supervision_timeout_t &supervision_timeout
|
||||||
|
) :
|
||||||
|
connectionHandle(connectionHandle),
|
||||||
|
minConnectionInterval(minConnectionInterval),
|
||||||
|
maxConnectionInterval(maxConnectionInterval),
|
||||||
|
slaveLatency(slaveLatency),
|
||||||
|
supervisionTimeout(supervision_timeout)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
connection_handle_t getConnectionHandle() const
|
||||||
|
{
|
||||||
|
return connectionHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conn_interval_t &getMinConnectionInterval() const
|
||||||
|
{
|
||||||
|
return minConnectionInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conn_interval_t &getMaxConnectionInterval() const
|
||||||
|
{
|
||||||
|
return maxConnectionInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slave_latency_t &getSlaveLatency() const
|
||||||
|
{
|
||||||
|
return slaveLatency;
|
||||||
|
}
|
||||||
|
|
||||||
|
const supervision_timeout_t &getSupervisionTimeout() const
|
||||||
|
{
|
||||||
|
return supervisionTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ble::connection_handle_t connectionHandle;
|
||||||
|
ble::conn_interval_t minConnectionInterval;
|
||||||
|
ble::conn_interval_t maxConnectionInterval;
|
||||||
|
ble::slave_latency_t slaveLatency;
|
||||||
|
ble::supervision_timeout_t supervisionTimeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConnectionParametersUpdateCompleteEvent {
|
||||||
|
ConnectionParametersUpdateCompleteEvent(
|
||||||
|
ble_error_t status,
|
||||||
|
connection_handle_t connectionHandle,
|
||||||
|
const conn_interval_t &connectionInterval,
|
||||||
|
const slave_latency_t &slaveLatency,
|
||||||
|
const supervision_timeout_t &supervisionTimeout
|
||||||
|
) :
|
||||||
|
status(status),
|
||||||
|
connectionHandle(connectionHandle),
|
||||||
|
connectionInterval(connectionInterval),
|
||||||
|
slaveLatency(slaveLatency),
|
||||||
|
supervisionTimeout(supervisionTimeout)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
ble_error_t getStatus() const
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection_handle_t getConnectionHandle() const
|
||||||
|
{
|
||||||
|
return connectionHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conn_interval_t &getConnectionInterval() const
|
||||||
|
{
|
||||||
|
return connectionInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slave_latency_t &getSlaveLatency() const
|
||||||
|
{
|
||||||
|
return slaveLatency;
|
||||||
|
}
|
||||||
|
|
||||||
|
const supervision_timeout_t &getSupervisionTimeout() const
|
||||||
|
{
|
||||||
|
return supervisionTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ble_error_t status;
|
||||||
|
ble::connection_handle_t connectionHandle;
|
||||||
|
ble::conn_interval_t connectionInterval;
|
||||||
|
ble::slave_latency_t slaveLatency;
|
||||||
|
ble::supervision_timeout_t supervisionTimeout;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace ble
|
} // namespace ble
|
||||||
|
|
||||||
#endif //BLE_GAP_EVENTS_H
|
#endif //BLE_GAP_EVENTS_H
|
||||||
|
|
|
@ -304,6 +304,34 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual ble_error_t cancelConnect();
|
virtual ble_error_t cancelConnect();
|
||||||
|
|
||||||
|
virtual ble_error_t manageConnectionParametersUpdateRequest(
|
||||||
|
bool userManageConnectionUpdateRequest
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual ble_error_t updateConnectionParameters(
|
||||||
|
ble::connection_handle_t connectionHandle,
|
||||||
|
ble::conn_interval_t minConnectionInterval,
|
||||||
|
ble::conn_interval_t maxConnectionInterval,
|
||||||
|
ble::slave_latency_t slaveLatency,
|
||||||
|
ble::supervision_timeout_t supervisionTimeout,
|
||||||
|
ble::conn_event_length_t minConnectionEventLength,
|
||||||
|
ble::conn_event_length_t maxConnectionEventLength
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual ble_error_t acceptConnectionParametersUpdate(
|
||||||
|
ble::connection_handle_t connectionHandle,
|
||||||
|
ble::conn_interval_t minConnectionInterval,
|
||||||
|
ble::conn_interval_t maxConnectionInterval,
|
||||||
|
ble::slave_latency_t slaveLatency,
|
||||||
|
ble::supervision_timeout_t supervisionTimeout,
|
||||||
|
ble::conn_event_length_t minConnectionEventLength,
|
||||||
|
ble::conn_event_length_t maxConnectionEventLength
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual ble_error_t rejectConnectionParametersUpdate(
|
||||||
|
ble::connection_handle_t connectionHandle
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Gap::readPhy
|
* @see Gap::readPhy
|
||||||
*/
|
*/
|
||||||
|
@ -659,6 +687,22 @@ private:
|
||||||
const ble::address_t &address
|
const ble::address_t &address
|
||||||
);
|
);
|
||||||
|
|
||||||
|
virtual void on_connection_update_complete(
|
||||||
|
pal::hci_error_code_t status,
|
||||||
|
connection_handle_t connection_handle,
|
||||||
|
uint16_t connection_interval,
|
||||||
|
uint16_t connection_latency,
|
||||||
|
uint16_t supervision_timeout
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual void on_remote_connection_parameter(
|
||||||
|
connection_handle_t connection_handle,
|
||||||
|
uint16_t connection_interval_min,
|
||||||
|
uint16_t connection_interval_max,
|
||||||
|
uint16_t connection_latency,
|
||||||
|
uint16_t supervision_timeout
|
||||||
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pal::EventQueue& _event_queue;
|
pal::EventQueue& _event_queue;
|
||||||
pal::Gap &_pal_gap;
|
pal::Gap &_pal_gap;
|
||||||
|
@ -722,6 +766,7 @@ private:
|
||||||
// deprecation flags
|
// deprecation flags
|
||||||
mutable bool _deprecated_scan_api_used : 1;
|
mutable bool _deprecated_scan_api_used : 1;
|
||||||
mutable bool _non_deprecated_scan_api_used : 1;
|
mutable bool _non_deprecated_scan_api_used : 1;
|
||||||
|
bool _user_manage_connection_parameter_requests : 1;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ble_error_t setExtendedAdvertisingParameters(
|
ble_error_t setExtendedAdvertisingParameters(
|
||||||
|
|
|
@ -219,6 +219,22 @@ struct Gap {
|
||||||
connection_peer_address_type_t scanner_address_type,
|
connection_peer_address_type_t scanner_address_type,
|
||||||
const address_t &address
|
const address_t &address
|
||||||
) = 0;
|
) = 0;
|
||||||
|
|
||||||
|
virtual void on_connection_update_complete(
|
||||||
|
hci_error_code_t status,
|
||||||
|
connection_handle_t connection_handle,
|
||||||
|
uint16_t connection_interval,
|
||||||
|
uint16_t connection_latency,
|
||||||
|
uint16_t supervision_timeout
|
||||||
|
) = 0;
|
||||||
|
|
||||||
|
virtual void on_remote_connection_parameter(
|
||||||
|
connection_handle_t connection_handle,
|
||||||
|
uint16_t connection_interval_min,
|
||||||
|
uint16_t connection_interval_max,
|
||||||
|
uint16_t connection_latency,
|
||||||
|
uint16_t supervision_timeout
|
||||||
|
) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -271,6 +271,42 @@ ble_error_t Gap::cancelConnect() {
|
||||||
return BLE_ERROR_NOT_IMPLEMENTED;
|
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ble_error_t Gap::updateConnectionParameters(
|
||||||
|
ble::connection_handle_t connectionHandle,
|
||||||
|
ble::conn_interval_t minConnectionInterval,
|
||||||
|
ble::conn_interval_t maxConnectionInterval,
|
||||||
|
ble::slave_latency_t slaveLatency,
|
||||||
|
ble::supervision_timeout_t supervision_timeout,
|
||||||
|
ble::conn_event_length_t minConnectionEventLength,
|
||||||
|
ble::conn_event_length_t maxConnectionEventLength
|
||||||
|
) {
|
||||||
|
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_error_t Gap::manageConnectionParametersUpdateRequest(
|
||||||
|
bool userManageConnectionUpdateRequest
|
||||||
|
) {
|
||||||
|
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_error_t Gap::acceptConnectionParametersUpdate(
|
||||||
|
ble::connection_handle_t connectionHandle,
|
||||||
|
ble::conn_interval_t minConnectionInterval,
|
||||||
|
ble::conn_interval_t maxConnectionInterval,
|
||||||
|
ble::slave_latency_t slaveLatency,
|
||||||
|
ble::supervision_timeout_t supervision_timeout,
|
||||||
|
ble::conn_event_length_t minConnectionEventLength,
|
||||||
|
ble::conn_event_length_t maxConnectionEventLength
|
||||||
|
) {
|
||||||
|
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_error_t Gap::rejectConnectionParametersUpdate(
|
||||||
|
ble::connection_handle_t connectionHandle
|
||||||
|
) {
|
||||||
|
return BLE_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
ble_error_t Gap::disconnect(
|
ble_error_t Gap::disconnect(
|
||||||
ble::connection_handle_t connectionHandle,
|
ble::connection_handle_t connectionHandle,
|
||||||
ble::local_disconnection_reason_t reason
|
ble::local_disconnection_reason_t reason
|
||||||
|
|
|
@ -419,7 +419,8 @@ GenericGap::GenericGap(
|
||||||
_random_address_rotating(false),
|
_random_address_rotating(false),
|
||||||
_advertising_timeout(),
|
_advertising_timeout(),
|
||||||
_scan_timeout(),
|
_scan_timeout(),
|
||||||
_connection_event_handler(NULL)
|
_connection_event_handler(NULL),
|
||||||
|
_user_manage_connection_parameter_requests(false)
|
||||||
{
|
{
|
||||||
_pal_gap.initialize();
|
_pal_gap.initialize();
|
||||||
|
|
||||||
|
@ -646,6 +647,67 @@ ble_error_t GenericGap::connect(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ble_error_t GenericGap::manageConnectionParametersUpdateRequest(bool flag) {
|
||||||
|
_user_manage_connection_parameter_requests = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_error_t GenericGap::updateConnectionParameters(
|
||||||
|
connection_handle_t connectionHandle,
|
||||||
|
conn_interval_t minConnectionInterval,
|
||||||
|
conn_interval_t maxConnectionInterval,
|
||||||
|
slave_latency_t slaveLatency,
|
||||||
|
supervision_timeout_t supervisionTimeout,
|
||||||
|
conn_event_length_t minConnectionEventLength,
|
||||||
|
conn_event_length_t maxConnectionEventLength
|
||||||
|
) {
|
||||||
|
if (supervisionTimeout <= (1 + slaveLatency.value()) * maxConnectionInterval * 2) {
|
||||||
|
return BLE_ERROR_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _pal_gap.connection_parameters_update(
|
||||||
|
connectionHandle,
|
||||||
|
minConnectionInterval.value(),
|
||||||
|
maxConnectionInterval.value(),
|
||||||
|
slaveLatency.value(),
|
||||||
|
supervisionTimeout.value(),
|
||||||
|
minConnectionEventLength.value(),
|
||||||
|
maxConnectionEventLength.value()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_error_t GenericGap::acceptConnectionParametersUpdate(
|
||||||
|
connection_handle_t connectionHandle,
|
||||||
|
conn_interval_t minConnectionInterval,
|
||||||
|
conn_interval_t maxConnectionInterval,
|
||||||
|
slave_latency_t slaveLatency,
|
||||||
|
supervision_timeout_t supervisionTimeout,
|
||||||
|
conn_event_length_t minConnectionEventLength,
|
||||||
|
conn_event_length_t maxConnectionEventLength
|
||||||
|
) {
|
||||||
|
if (supervisionTimeout <= (1 + slaveLatency.value()) * maxConnectionInterval * 2) {
|
||||||
|
return BLE_ERROR_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _pal_gap.accept_connection_parameter_request(
|
||||||
|
connectionHandle,
|
||||||
|
minConnectionInterval.value(),
|
||||||
|
maxConnectionInterval.value(),
|
||||||
|
slaveLatency.value(),
|
||||||
|
supervisionTimeout.value(),
|
||||||
|
minConnectionEventLength.value(),
|
||||||
|
maxConnectionEventLength.value()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_error_t GenericGap::rejectConnectionParametersUpdate(
|
||||||
|
ble::connection_handle_t connectionHandle
|
||||||
|
) {
|
||||||
|
return _pal_gap.reject_connection_parameter_request(
|
||||||
|
connectionHandle,
|
||||||
|
pal::hci_error_code_t::UNACCEPTABLE_CONNECTION_PARAMETERS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ble_error_t GenericGap::cancelConnect()
|
ble_error_t GenericGap::cancelConnect()
|
||||||
{
|
{
|
||||||
return _pal_gap.cancel_connection_creation();
|
return _pal_gap.cancel_connection_creation();
|
||||||
|
@ -2374,6 +2436,63 @@ void GenericGap::on_scan_request_received(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GenericGap::on_connection_update_complete(
|
||||||
|
pal::hci_error_code_t status,
|
||||||
|
connection_handle_t connection_handle,
|
||||||
|
uint16_t connection_interval,
|
||||||
|
uint16_t connection_latency,
|
||||||
|
uint16_t supervision_timeout
|
||||||
|
) {
|
||||||
|
if (!_eventHandler) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_eventHandler->onConnectionParametersUpdateComplete(
|
||||||
|
ConnectionParametersUpdateCompleteEvent(
|
||||||
|
status == pal::hci_error_code_t::SUCCESS ? BLE_ERROR_NONE : BLE_ERROR_UNSPECIFIED,
|
||||||
|
connection_handle,
|
||||||
|
conn_interval_t(connection_interval),
|
||||||
|
slave_latency_t(connection_latency),
|
||||||
|
supervision_timeout_t(supervision_timeout)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericGap::on_remote_connection_parameter(
|
||||||
|
connection_handle_t connection_handle,
|
||||||
|
uint16_t connection_interval_min,
|
||||||
|
uint16_t connection_interval_max,
|
||||||
|
uint16_t connection_latency,
|
||||||
|
uint16_t supervision_timeout
|
||||||
|
) {
|
||||||
|
if (_user_manage_connection_parameter_requests) {
|
||||||
|
// ignore for now as it is
|
||||||
|
_pal_gap.accept_connection_parameter_request(
|
||||||
|
connection_handle,
|
||||||
|
connection_interval_min,
|
||||||
|
connection_interval_max,
|
||||||
|
connection_latency,
|
||||||
|
supervision_timeout,
|
||||||
|
/* connection event length min */ 0,
|
||||||
|
/* connection event length max */ 0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (!_eventHandler) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_eventHandler->onUpdateConnectionParametersRequest(
|
||||||
|
UpdateConnectionParametersRequestEvent(
|
||||||
|
connection_handle,
|
||||||
|
conn_interval_t(connection_interval_min),
|
||||||
|
conn_interval_t(connection_interval_max),
|
||||||
|
connection_latency,
|
||||||
|
supervision_timeout_t(supervision_timeout)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ble_error_t GenericGap::setScanParameters(const ScanParameters ¶ms)
|
ble_error_t GenericGap::setScanParameters(const ScanParameters ¶ms)
|
||||||
{
|
{
|
||||||
useVersionTwoAPI();
|
useVersionTwoAPI();
|
||||||
|
|
|
@ -560,6 +560,36 @@ void Gap::gap_handler(const wsfMsgHdr_t* msg) {
|
||||||
evt->pData
|
evt->pData
|
||||||
);
|
);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case DM_CONN_UPDATE_IND: {
|
||||||
|
if (!handler) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hciLeConnUpdateCmplEvt_t* evt = (const hciLeConnUpdateCmplEvt_t*) msg;
|
||||||
|
handler->on_connection_update_complete(
|
||||||
|
(hci_error_code_t::type) evt->status,
|
||||||
|
evt->hdr.param,
|
||||||
|
evt->connInterval,
|
||||||
|
evt->connLatency,
|
||||||
|
evt->supTimeout
|
||||||
|
);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DM_REM_CONN_PARAM_REQ_IND: {
|
||||||
|
if (!handler) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hciLeRemConnParamReqEvt_t* evt = (const hciLeRemConnParamReqEvt_t*) msg;
|
||||||
|
handler->on_remote_connection_parameter(
|
||||||
|
evt->hdr.param,
|
||||||
|
evt->intervalMin,
|
||||||
|
evt->intervalMax,
|
||||||
|
evt->latency,
|
||||||
|
evt->timeout
|
||||||
|
);
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// all handlers are stored in a static array
|
// all handlers are stored in a static array
|
||||||
|
|
Loading…
Reference in New Issue