44#include < hid/app/keyboard.hpp>
55#include < hid/app/mouse.hpp>
66#include < zephyr/logging/log.h>
7+ #if CONFIG_C2USB_HOGP_SCI
8+ #include < zephyr/bluetooth/hci.h>
9+ #include < zephyr/bluetooth/hci_types.h>
10+ #endif
711
812LOG_MODULE_REGISTER (hogp, CONFIG_C2USB_HOGP_LOG_LEVEL );
913
@@ -79,7 +83,7 @@ ssize_t service::control_point_request(::bt_conn* conn, const ::bt_gatt_attr* at
7983 {
8084 return BT_GATT_ERR (BT_ATT_ERR_INVALID_OFFSET );
8185 }
82- if (len > sizeof (uint8_t ))
86+ if (len != sizeof (uint8_t ))
8387 {
8488 return BT_GATT_ERR (BT_ATT_ERR_INVALID_ATTRIBUTE_LEN );
8589 }
@@ -97,6 +101,16 @@ ssize_t service::control_point_request(::bt_conn* conn, const ::bt_gatt_attr* at
97101 }
98102#endif
99103 return len;
104+ #if defined(CONFIG_C2USB_HOGP_SCI)
105+ case uint8_t (sci_mode::DEFAULT ):
106+ case uint8_t (sci_mode::FAST ):
107+ case uint8_t (sci_mode::FULL_RANGE ):
108+ case uint8_t (sci_mode::LOW_POWER ):
109+ return static_cast <service*>(attr->user_data )
110+ ->set_sci_mode (conn, static_cast <sci_mode>(cmd))
111+ ? len
112+ : BT_GATT_ERR (BT_ATT_ERR_NOT_SUPPORTED );
113+ #endif
100114 default :
101115 return BT_GATT_ERR (BT_ATT_ERR_NOT_SUPPORTED );
102116 }
@@ -397,6 +411,153 @@ ssize_t service::ccc_cfg_write(::bt_conn* conn, const gatt::attribute* attr, gat
397411 return (session.protocol () == prot) ? sizeof (flags) : BT_GATT_ERR (BT_ATT_ERR_UNLIKELY );
398412}
399413
414+ #if CONFIG_C2USB_HOGP_SCI
415+ ssize_t service::get_sci_information (::bt_conn* conn, const ::bt_gatt_attr* attr, void * buf,
416+ uint16_t len, uint16_t offset)
417+ {
418+ if (sci_attributes_.min_supported_conn_interval == 0 )
419+ {
420+ return BT_GATT_ERR (BT_ATT_ERR_UNLIKELY );
421+ }
422+ return bt_gatt_attr_read (conn, attr, buf, len, offset,
423+ reinterpret_cast <const uint8_t *>(&sci_attributes_),
424+ offsetof (decltype (sci_attributes_), groups) +
425+ sci_attributes_.num_groups * sizeof (sci_attributes_.groups [0 ]));
426+ }
427+
428+ ssize_t service::get_sci_mode (::bt_conn* conn, const ::bt_gatt_attr* attr, void * buf, uint16_t len,
429+ uint16_t offset)
430+ {
431+ auto * self = static_cast <service*>(attr->user_data );
432+ auto it = std::ranges::find_if (self->sessions_ , [&](const auto & s) { return s.conn == conn; });
433+ if (it == self->sessions_ .end ())
434+ {
435+ uint8_t mode = uint8_t (sci_mode::NONE );
436+ return bt_gatt_attr_read (conn, attr, buf, len, offset, &mode, sizeof (mode));
437+ }
438+ return bt_gatt_attr_read (conn, attr, buf, len, offset,
439+ reinterpret_cast <const uint8_t *>(&it->active_sci_mode_ ),
440+ sizeof (it->active_sci_mode_ ));
441+ }
442+
443+ bool service::set_sci_mode (::bt_conn* conn, sci_mode mode)
444+ {
445+ if (sci_attributes_.min_supported_conn_interval == 0 )
446+ {
447+ return false ;
448+ }
449+ auto it = std::ranges::find_if (sessions_, [&](const auto & s) { return s.conn == conn; });
450+ if (it == sessions_.end ())
451+ {
452+ // no session yet, use a slot for this connection, without a session
453+ auto it = std::ranges::find_if (sessions_, [&](const auto & s) { return s.conn == nullptr ; });
454+ assert ((it != sessions_.end ()) and " no free conn_session slots" );
455+ it->conn = conn;
456+ }
457+
458+ auto params = std::find_if (sci_mode_params_.begin (), sci_mode_params_.end (),
459+ [mode](const auto & p) { return p.mode == mode; });
460+ if (params == sci_mode_params_.end ())
461+ {
462+ assert ((mode == sci_mode::LOW_POWER ) and " Missing mandatory SCI mode parameters" );
463+ return false ;
464+ }
465+
466+ auto & sci_params = *params;
467+ if (auto ret = bt_conn_le_conn_rate_request (conn, &sci_params); ret != 0 )
468+ {
469+ LOG_ERR (" Failed to request connection rate change: %d" , ret);
470+ return false ;
471+ }
472+
473+ it->pending_sci_mode_ = mode;
474+ return true ;
475+ }
476+
477+ void service::connect_callback (bt_conn* conn, uint8_t err)
478+ {
479+ if (sci_attributes_.min_supported_conn_interval == 0 )
480+ {
481+ // first time initialization, fetch and store the SCI information for all subsequent use
482+ const size_t offset =
483+ offsetof (::bt_hci_op_le_read_min_supported_conn_interval, min_supported_conn_interval);
484+ struct net_buf * rsp;
485+ if (auto ret =
486+ bt_hci_cmd_send_sync (BT_HCI_OP_LE_READ_MIN_SUPPORTED_CONN_INTERVAL , nullptr , &rsp);
487+ (ret != 0 ) or (rsp->len < offset + sizeof (sci_attributes<0 >)))
488+ {
489+ LOG_ERR (" Failed to read min supported connection interval: %d" , ret);
490+ return ;
491+ }
492+
493+ size_t size = std::min<size_t >(sizeof (sci_attributes_), rsp->len - offset);
494+ std::copy_n (rsp->data + offset, size,
495+ reinterpret_cast <uint8_t *>(&sci_attributes_.min_supported_conn_interval ));
496+
497+ size -= offsetof (decltype (sci_attributes_), groups);
498+ if (sci_attributes_.num_groups > sci_attributes_.groups .size ())
499+ {
500+ LOG_WRN (" Supported connection interval groups exceeds storage: %d (max %d)" ,
501+ sci_attributes_.num_groups , sci_attributes_.groups .size ());
502+ sci_attributes_.num_groups = sci_attributes_.groups .size ();
503+ }
504+
505+ net_buf_unref (rsp);
506+ }
507+ }
508+
509+ void service::connection_rate_callback (::bt_conn* conn, uint8_t status,
510+ const ::bt_conn_le_conn_rate_changed* params)
511+ {
512+ if (status != BT_HCI_ERR_SUCCESS )
513+ {
514+ return ;
515+ }
516+ auto change_params = connection_rate_params{conn, params};
517+ for_each<const connection_rate_params, &service::connection_rate_changed>(&change_params);
518+ }
519+
520+ void service::connection_rate_changed (const connection_rate_params* p)
521+ {
522+ auto it = std::ranges::find_if (sessions_, [&](const auto & s) { return s.conn == p->conn ; });
523+ if (it == sessions_.end ())
524+ {
525+ return ;
526+ }
527+ auto new_mode = it->pending_sci_mode_ ;
528+ it->pending_sci_mode_ = sci_mode::NONE ;
529+ if (new_mode != sci_mode::NONE )
530+ {
531+ auto mode_params = std::find_if (sci_mode_params_.begin (), sci_mode_params_.end (),
532+ [new_mode](const auto & p) { return p.mode == new_mode; });
533+ if ((mode_params == sci_mode_params_.end ()) or !mode_params->match (*p->params ))
534+ {
535+ LOG_WRN (" Connection %p rate change does not match requested SCI mode %u" , p->conn ,
536+ uint8_t (new_mode));
537+ new_mode = sci_mode::NONE ;
538+ }
539+ }
540+ if (new_mode == sci_mode::NONE )
541+ {
542+ // no pending SCI mode change request, find a matching mode
543+ auto matching_params =
544+ std::find_if (sci_mode_params_.begin (), sci_mode_params_.end (),
545+ [in = p->params ](const auto & p) { return p.match (*in); });
546+ new_mode =
547+ (matching_params != sci_mode_params_.end ()) ? matching_params->mode : sci_mode::NONE ;
548+ }
549+
550+ if (new_mode != it->active_sci_mode_ )
551+ {
552+ it->active_sci_mode_ = new_mode;
553+ LOG_INF (" Connection %p SCI mode changed to %u" , p->conn , uint8_t (new_mode));
554+ sci_mode_attr ()->notify (
555+ std::span<const uint8_t >(reinterpret_cast <const uint8_t *>(&new_mode), sizeof (new_mode)),
556+ p->conn );
557+ }
558+ }
559+ #endif
560+
400561void service::disconnected (::bt_conn* conn)
401562{
402563 auto it = std::ranges::find_if (sessions_, [&](const auto & s) { return s.conn == conn; });
@@ -409,13 +570,17 @@ void service::disconnected(::bt_conn* conn)
409570
410571void service::disconnect_callback (::bt_conn* conn, uint8_t reason)
411572{
412- for_each<::bt_conn* , &service::disconnected>(conn);
573+ for_each<::bt_conn, &service::disconnected>(conn);
413574}
414575
415576} // namespace bluetooth::hid_over_gatt
416577
417- #if CONFIG_C2USB_HOGP_BT_DISCONN_CB
418578BT_CONN_CB_DEFINE (hid_over_gatt_conn_callbacks) = {
579+ #if CONFIG_C2USB_HOGP_SCI
580+ .connected = &bluetooth::hid_over_gatt::service::connect_callback,
581+ #endif
419582 .disconnected = &bluetooth::hid_over_gatt::service::disconnect_callback,
420- };
583+ #if CONFIG_C2USB_HOGP_SCI
584+ .conn_rate_changed = &bluetooth::hid_over_gatt::service::connection_rate_callback,
421585#endif
586+ };
0 commit comments