diff --git a/include/zephyr/bluetooth/classic/bip.h b/include/zephyr/bluetooth/classic/bip.h new file mode 100644 index 0000000000000..74240b4baa550 --- /dev/null +++ b/include/zephyr/bluetooth/classic/bip.h @@ -0,0 +1,2071 @@ +/* bip.h - Bluetooth Basic Imaging Profile handling */ + +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_BIP_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_BIP_H_ + +/** + * @brief Basic Imaging Profile (BIP) + * @defgroup bt_bip Basic Imaging Profile (BIP) + * @ingroup bluetooth + * @{ + */ + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief OBEX Type header for Get Capabilities operation */ +#define BT_BIP_HDR_TYPE_GET_CAPS "x-bt/img-capabilities" +/** @brief OBEX Type header for Get Image List operation */ +#define BT_BIP_HDR_TYPE_GET_IMAGE_LIST "x-bt/img-listing" +/** @brief OBEX Type header for Get Image Properties operation */ +#define BT_BIP_HDR_TYPE_GET_IMAGE_PROPERTIES "x-bt/img-properties" +/** @brief OBEX Type header for Get Image operation */ +#define BT_BIP_HDR_TYPE_GET_IMAGE "x-bt/img-img" +/** @brief OBEX Type header for Get Linked Thumbnail operation */ +#define BT_BIP_HDR_TYPE_GET_LINKED_THUMBNAIL "x-bt/img-thm" +/** @brief OBEX Type header for Get Linked Attachment operation */ +#define BT_BIP_HDR_TYPE_GET_LINKED_ATTACHMENT "x-bt/img-attachment" +/** @brief OBEX Type header for Get Partial Image operation */ +#define BT_BIP_HDR_TYPE_GET_PARTIAL_IMAGE "x-bt/img-partial" +/** @brief OBEX Type header for Get Monitoring Image operation */ +#define BT_BIP_HDR_TYPE_GET_MONITORING_IMAGE "x-bt/img-monitoring" +/** @brief OBEX Type header for Get Status operation */ +#define BT_BIP_HDR_TYPE_GET_STATUS "x-bt/img-status" +/** @brief OBEX Type header for Put Image operation */ +#define BT_BIP_HDR_TYPE_PUT_IMAGE "x-bt/img-img" +/** @brief OBEX Type header for Put Linked Thumbnail operation */ +#define BT_BIP_HDR_TYPE_PUT_LINKED_THUMBNAIL "x-bt/img-thm" +/** @brief OBEX Type header for Put Linked Attachment operation */ +#define BT_BIP_HDR_TYPE_PUT_LINKED_ATTACHMENT "x-bt/img-attachment" +/** @brief OBEX Type header for Remote Display operation */ +#define BT_BIP_HDR_TYPE_REMOTE_DISPLAY "x-bt/img-display" +/** @brief OBEX Type header for Delete Image operation */ +#define BT_BIP_HDR_TYPE_DELETE_IMAGE "x-bt/img-img" +/** @brief OBEX Type header for Start Print operation */ +#define BT_BIP_HDR_TYPE_START_PRINT "x-bt/img-print" +/** @brief OBEX Type header for Start Archive operation */ +#define BT_BIP_HDR_TYPE_START_ARCHIVE "x-bt/img-archive" + +/** + * @brief Img-Descriptor header ID + * + * Byte sequence, length prefixed with a two-byte unsigned integer + */ +#define BT_BIP_HEADER_ID_IMG_DESC 0x71 + +/** + * @brief Img-Handle header ID + * + * Null terminated, UTF-16 encoded Unicode text length prefixed with a two-byte unsigned integer + */ +#define BT_BIP_HEADER_ID_IMG_HANDLE 0x30 + +/** + * @brief BIP Application Parameter Tag IDs + * + * These tag IDs are used in OBEX application parameter headers for various BIP operations. + */ +enum bt_bip_appl_param_tag_id { + /** @brief Number of returned handles parameter */ + BT_BIP_APPL_PARAM_TAG_ID_RETURNED_HANDLES = 0x01, + /** @brief List start offset parameter */ + BT_BIP_APPL_PARAM_TAG_ID_LIST_START_OFFSET = 0x02, + /** @brief Latest captured images parameter */ + BT_BIP_APPL_PARAM_TAG_ID_LATEST_CAPTURED_IMAGES = 0x03, + /** @brief Partial file length parameter */ + BT_BIP_APPL_PARAM_TAG_ID_PARTIAL_FILE_LEN = 0x04, + /** @brief Partial file start offset parameter */ + BT_BIP_APPL_PARAM_TAG_ID_PARTIAL_FILE_START_OFFSET = 0x05, + /** @brief Total file size parameter */ + BT_BIP_APPL_PARAM_TAG_ID_TOTAL_FILE_SIZE = 0x06, + /** @brief End flag parameter */ + BT_BIP_APPL_PARAM_TAG_ID_END_FLAG = 0x07, + /** @brief Remote display parameter */ + BT_BIP_APPL_PARAM_TAG_ID_REMOTE_DISPLAY = 0x08, + /** @brief Service ID parameter */ + BT_BIP_APPL_PARAM_TAG_ID_SERVICE_ID = 0x09, + /** @brief Store flag parameter */ + BT_BIP_APPL_PARAM_TAG_ID_STORE_FLAG = 0x0a, +}; + +/** @brief Forward declaration of BIP instance structure */ +struct bt_bip; + +/** + * @brief BIP transport layer operations + * + * Callback structure for transport layer events + */ +struct bt_bip_transport_ops { + /** + * @brief Transport connected callback + * + * Called when the underlying transport (RFCOMM/L2CAP) is connected + * + * @param conn Bluetooth connection + * @param bip BIP instance + */ + void (*connected)(struct bt_conn *conn, struct bt_bip *bip); + + /** + * @brief Transport disconnected callback + * + * Called when the underlying transport is disconnected + * + * @param bip BIP instance + */ + void (*disconnected)(struct bt_bip *bip); +}; + +/** + * @brief BIP RFCOMM server structure + * + * Structure for BIP server using RFCOMM transport + */ +struct bt_bip_rfcomm_server { + /** @brief Underlying GOEP RFCOMM server */ + struct bt_goep_transport_rfcomm_server server; + + /** + * @brief Accept connection callback + * + * Called when a new RFCOMM connection is accepted + * + * @param conn Bluetooth connection + * @param server RFCOMM server instance + * @param bip Pointer to store the created BIP instance + * @return 0 on success, negative error code on failure + */ + int (*accept)(struct bt_conn *conn, struct bt_bip_rfcomm_server *server, + struct bt_bip **bip); +}; + +/** + * @brief Register BIP RFCOMM server + * + * @param server RFCOMM server to register + * @return 0 on success, negative error code on failure + */ +int bt_bip_rfcomm_register(struct bt_bip_rfcomm_server *server); + +/** + * @brief Create transport connection over RFCOMM + * + * @param conn ACL conn object + * @param bip BIP instance + * @param channel RFCOMM channel number + * @return 0 on success, negative error code on failure + */ +int bt_bip_rfcomm_connect(struct bt_conn *conn, struct bt_bip *bip, uint8_t channel); + +/** + * @brief Disconnect BIP RFCOMM connection + * + * @param bip BIP instance + * @return 0 on success, negative error code on failure + */ +int bt_bip_rfcomm_disconnect(struct bt_bip *bip); + +/** + * @brief BIP L2CAP server structure + * + * Structure for BIP server using L2CAP transport + */ +struct bt_bip_l2cap_server { + /** @brief Underlying GOEP L2CAP server */ + struct bt_goep_transport_l2cap_server server; + + /** + * @brief Accept connection callback + * + * Called when a new L2CAP connection is accepted + * + * @param conn Bluetooth connection + * @param server L2CAP server instance + * @param bip Pointer to store the created BIP instance + * @return 0 on success, negative error code on failure + */ + int (*accept)(struct bt_conn *conn, struct bt_bip_l2cap_server *server, + struct bt_bip **bip); +}; + +/** + * @brief Register BIP L2CAP server + * + * @param server L2CAP server to register + * @return 0 on success, negative error code on failure + */ +int bt_bip_l2cap_register(struct bt_bip_l2cap_server *server); + +/** + * @brief Create transport connection over L2CAP + * + * @param conn ACL conn object + * @param bip BIP instance + * @param psm L2CAP PSM (Protocol Service Multiplexer) + * @return 0 on success, negative error code on failure + */ +int bt_bip_l2cap_connect(struct bt_conn *conn, struct bt_bip *bip, uint16_t psm); + +/** + * @brief Disconnect BIP L2CAP connection + * + * @param bip BIP instance + * @return 0 on success, negative error code on failure + */ +int bt_bip_l2cap_disconnect(struct bt_bip *bip); + +/** + * @brief BIP OBEX connection types + * + * Defines the different types of BIP connections as per BIP specification + */ +enum __packed bt_bip_conn_type { + /** @brief Primary Image Push connection */ + BT_BIP_PRIM_CONN_TYPE_IMAGE_PUSH = 0, + /** @brief Primary Image Pull connection */ + BT_BIP_PRIM_CONN_TYPE_IMAGE_PULL = 1, + /** @brief Primary Advanced Image Printing connection */ + BT_BIP_PRIM_CONN_TYPE_ADVANCED_IMAGE_PRINTING = 2, + /** @brief Primary Auto Archive connection */ + BT_BIP_PRIM_CONN_TYPE_AUTO_ARCHIVE = 3, + /** @brief Primary Remote Camera connection */ + BT_BIP_PRIM_CONN_TYPE_REMOTE_CAMERA = 4, + /** @brief Primary Remote Display connection */ + BT_BIP_PRIM_CONN_TYPE_REMOTE_DISPLAY = 5, + /** @brief Secondary Referenced Objects connection */ + BT_BIP_2ND_CONN_TYPE_REFERENCED_OBJECTS = 10, + /** @brief Secondary Archived Objects connection */ + BT_BIP_2ND_CONN_TYPE_ARCHIVED_OBJECTS = 11, +}; + +/** + * @brief BIP role types + * + * Defines the role of the device in BIP connection + */ +enum __packed bt_bip_role { + /** @brief Unknown role */ + BT_BIP_ROLE_UNKNOWN = 0, + /** @brief Connection initiator */ + BT_BIP_ROLE_INITIATOR = 1, + /** @brief Connection responder */ + BT_BIP_ROLE_RESPONDER = 2, +}; + +/** + * @brief BIP transport state + * + * Defines the state of the underlying transport connection + */ +enum __packed bt_bip_transport_state { + /** @brief Transport is disconnected */ + BT_BIP_TRANSPORT_STATE_DISCONNECTED = 0, + /** @brief Transport is connecting */ + BT_BIP_TRANSPORT_STATE_CONNECTING = 1, + /** @brief Transport is connected */ + BT_BIP_TRANSPORT_STATE_CONNECTED = 2, + /** @brief Transport is disconnecting */ + BT_BIP_TRANSPORT_STATE_DISCONNECTING = 3, +}; + +/** + * @brief BIP supported capabilities + * + * Defines the supported capabilities of the BIP device (responder). + * These capabilities indicate the primary BIP services that the device supports. + * Multiple capabilities can be combined using bitwise OR operations. + */ +enum __packed bt_bip_supported_capabilities { + /** @brief Generic imaging */ + BT_BIP_SUPP_CAP_GENERIC_IMAGE = 0, + /** @brief Capturing */ + BT_BIP_SUPP_CAP_CAPTURING = 1, + /** @brief Printing */ + BT_BIP_SUPP_CAP_PRINTING = 2, + /** @brief Displaying */ + BT_BIP_SUPP_CAP_DISPLAYING = 3, +}; + +/** + * @brief BIP supported features + * + * Defines the supported features of the BIP device (responder). + * These features indicate the primary BIP services that the device supports. + * Multiple features can be combined using bitwise OR operations. + */ +enum __packed bt_bip_supported_features { + /** @brief Image Push feature - allows pushing images to the peer device */ + BT_BIP_SUPP_FEAT_IMAGE_PUSH = 0, + /** @brief Image Push Store feature - supports storing pushed images permanently */ + BT_BIP_SUPP_FEAT_IMAGE_PUSH_STORE = 1, + /** @brief Image Push Print feature - supports printing pushed images directly */ + BT_BIP_SUPP_FEAT_IMAGE_PUSH_PRINT = 2, + /** @brief Image Push Display feature - supports displaying pushed images */ + BT_BIP_SUPP_FEAT_IMAGE_PUSH_DISPLAY = 3, + /** @brief Image Pull feature - allows pulling/retrieving images from the peer */ + BT_BIP_SUPP_FEAT_IMAGE_PULL = 4, + /** @brief Advanced Image Printing feature - supports advanced printing operations */ + BT_BIP_SUPP_FEAT_ADVANCED_IMAGE_PRINT = 5, + /** @brief Auto Archive feature - supports automatic archiving of images */ + BT_BIP_SUPP_FEAT_AUTO_ARCHIVE = 6, + /** @brief Remote Camera feature - supports remote camera control and image capture */ + BT_BIP_SUPP_FEAT_REMOTE_CAMERA = 7, + /** @brief Remote Display feature - supports remote display control */ + BT_BIP_SUPP_FEAT_REMOTE_DISPLAY = 8, +}; + +/** + * @brief BIP supported functions + * + * Defines the supported functions of the BIP device (responder). + * These functions indicate the specific BIP operations that the device can perform. + * Multiple functions can be combined using bitwise OR operations. + * + * @note These values correspond to the BIP specification function definitions + * and are used to determine which operations can be performed on the peer device. + */ +enum __packed bt_bip_supported_functions { + /** @brief Get Capabilities function - supports retrieving device capabilities */ + BT_BIP_SUPP_FUNC_GET_CAPS = 0, + /** @brief Put Image function - supports receiving/storing images */ + BT_BIP_SUPP_FUNC_PUT_IMAGE = 1, + /** @brief Put Linked Attachment function - supports receiving image attachments */ + BT_BIP_SUPP_FUNC_PUT_LINKED_ATTACHMENT = 2, + /** @brief Put Linked Thumbnail function - supports receiving image thumbnails */ + BT_BIP_SUPP_FUNC_PUT_LINKED_THUMBNAIL = 3, + /** @brief Remote Display function - supports remote display operations */ + BT_BIP_SUPP_FUNC_REMOTE_DISPLAY = 4, + /** @brief Get Images List function - supports retrieving available image lists */ + BT_BIP_SUPP_FUNC_GET_IMAGE_LIST = 5, + /** @brief Get Image Properties function - supports retrieving image metadata */ + BT_BIP_SUPP_FUNC_GET_IMAGE_PROPERTIES = 6, + /** @brief Get Image function - supports retrieving/sending images */ + BT_BIP_SUPP_FUNC_GET_IMAGE = 7, + /** @brief Get Linked Thumbnail function - supports retrieving image thumbnails */ + BT_BIP_SUPP_FUNC_GET_LINKED_THUMBNAIL = 8, + /** @brief Get Linked Attachment function - supports retrieving image attachments */ + BT_BIP_SUPP_FUNC_GET_LINKED_ATTACHMENT = 9, + /** @brief Delete Image function - supports deleting images on the peer device */ + BT_BIP_SUPP_FUNC_DELETE_IMAGE = 10, + /** @brief Start Print function - supports initiating print operations */ + BT_BIP_SUPP_FUNC_START_PRINT = 11, + /** @brief Get Partial Image function - supports retrieving partial image data */ + BT_BIP_SUPP_FUNC_GET_PARTIAL_IMAGE = 12, + /** @brief Start Archive function - supports initiating archive operations */ + BT_BIP_SUPP_FUNC_START_ARCHIVE = 13, + /** @brief Get Monitoring Image function - supports retrieving monitoring images from + * remote camera + */ + BT_BIP_SUPP_FUNC_GET_MONITORING_IMAGE = 14, + /** @brief Get Status function - supports retrieving device/operation status */ + BT_BIP_SUPP_FUNC_GET_STATUS = 16, +}; + +/** + * @brief BIP instance structure + * + * Main structure representing a BIP session + */ +struct bt_bip { + /** @brief Underlying GOEP instance */ + struct bt_goep goep; + /** @brief Role in the connection */ + enum bt_bip_role role; + + /** @brief Transport operation callbacks */ + const struct bt_bip_transport_ops *ops; + + /** @internal Transport state (atomic) */ + atomic_t _transport_state; + + /** @internal Responder supported capabilities */ + uint8_t _supp_caps; + + /** @internal Responder supported features */ + uint16_t _supp_feats; + + /** @internal Responder supported functions */ + uint32_t _supp_funcs; + + /** @internal List of clients */ + sys_slist_t _clients; + + /** @internal List of servers */ + sys_slist_t _servers; + + /** @internal Node for linking in lists */ + sys_snode_t _node; +}; + +/** + * @brief Set supported capabilities of BIP responder + * + * Set the supported capabilities bitmask of peer BIP responder to local BIP initiator. + * + * @param bip BIP initiator instance + * @param capabilities Bitmask of supported capabilities. It is discovered from BIP responder + * by SDP discovery. + * + * @return 0 on success, negative error code on failure + * + * @note This function should be called before establishing any BIP OBEX connections. + * @note The capabilities value is typically obtained through SDP discovery. + */ +int bt_bip_set_supported_capabilities(struct bt_bip *bip, uint8_t capabilities); + +/** + * @brief Set supported features of BIP responder + * + * Set the supported features bitmask of peer BIP responder to local BIP initiator. + * + * @param bip BIP initiator instance + * @param features Bitmask of supported features. It is discovered from BIP responder + * by SDP discovery. + * + * @return 0 on success, negative error code on failure + * + * @note This function should be called before establishing any BIP OBEX connections. + * @note The features value is typically obtained through SDP discovery. + */ +int bt_bip_set_supported_features(struct bt_bip *bip, uint16_t features); + +/** + * @brief Set supported functions of BIP responder + * + * Set the supported functions bitmask of peer BIP responder to local BIP initiator. + * + * @param bip BIP initiator instance + * @param functions Bitmask of supported functions. It is discovered from BIP responder + * by SDP discovery. + * + * @return 0 on success, negative error code on failure + * + * @note This function should be called before establishing any BIP OBEX connections. + * @note The functions value is typically obtained through SDP discovery. + */ +int bt_bip_set_supported_functions(struct bt_bip *bip, uint32_t functions); + +/** + * @brief BIP connection state + * + * Defines the state of a BIP OBEX connection + */ +enum __packed bt_bip_state { + /** @brief Connection is disconnected */ + BT_BIP_STATE_DISCONNECTED = 0, + /** @brief Connection is being established */ + BT_BIP_STATE_CONNECTING = 1, + /** @brief Connection is established */ + BT_BIP_STATE_CONNECTED = 2, + /** @brief Connection is being terminated */ + BT_BIP_STATE_DISCONNECTING = 3, +}; + +/** @brief Forward declaration of BIP server structure */ +struct bt_bip_server; + +/** + * @brief BIP server callback structure + * + * Callback functions for handling BIP server operations + */ +struct bt_bip_server_cb { + /** + * @brief OBEX Connect request received + * + * @param server BIP server instance + * @param version OBEX protocol version + * @param mopl Maximum OBEX packet length + * @param buf Request data buffer + */ + void (*connect)(struct bt_bip_server *server, uint8_t version, uint16_t mopl, + struct net_buf *buf); + + /** + * @brief OBEX Disconnect request received + * + * @param server BIP server instance + * @param buf Request data buffer + */ + void (*disconnect)(struct bt_bip_server *server, struct net_buf *buf); + + /** + * @brief OBEX Abort request received + * + * @param server BIP server instance + * @param buf Request data buffer + */ + void (*abort)(struct bt_bip_server *server, struct net_buf *buf); + + /** + * @brief Get Capabilities request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*get_caps)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Get Image List request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*get_image_list)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Get Image Properties request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*get_image_properties)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Get Image request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*get_image)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Get Linked Thumbnail request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*get_linked_thumbnail)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Get Linked Attachment request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*get_linked_attachment)(struct bt_bip_server *server, bool final, + struct net_buf *buf); + + /** + * @brief Get Partial Image request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*get_partial_image)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Get Monitoring Image request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*get_monitoring_image)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Get Status request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*get_status)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Put Image request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*put_image)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Put Linked Thumbnail request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*put_linked_thumbnail)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Put Linked Attachment request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*put_linked_attachment)(struct bt_bip_server *server, bool final, + struct net_buf *buf); + + /** + * @brief Remote Display request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*remote_display)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Delete Image request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*delete_image)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Start Print request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*start_print)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** + * @brief Start Archive request received + * + * @param server BIP server instance + * @param final True if this is the final packet + * @param buf Request data buffer + */ + void (*start_archive)(struct bt_bip_server *server, bool final, struct net_buf *buf); +}; + +/** @brief Forward declaration of BIP client structure */ +struct bt_bip_client; + +/** + * @brief BIP client callback structure + * + * Callback functions for handling BIP client operation responses + */ +struct bt_bip_client_cb { + /** + * @brief OBEX Connect response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param version OBEX protocol version + * @param mopl Maximum OBEX packet length + * @param buf Response data buffer + */ + void (*connect)(struct bt_bip_client *client, uint8_t rsp_code, uint8_t version, + uint16_t mopl, struct net_buf *buf); + + /** + * @brief OBEX Disconnect response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*disconnect)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); + + /** + * @brief OBEX Abort response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*abort)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); + + /** + * @brief Get Capabilities response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*get_caps)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); + + /** + * @brief Get Image List response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*get_image_list)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); + + /** + * @brief Get Image Properties response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*get_image_properties)(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf); + + /** + * @brief Get Image response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*get_image)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); + + /** + * @brief Get Linked Thumbnail response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*get_linked_thumbnail)(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf); + + /** + * @brief Get Linked Attachment response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*get_linked_attachment)(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf); + + /** + * @brief Get Partial Image response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*get_partial_image)(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf); + + /** + * @brief Get Monitoring Image response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*get_monitoring_image)(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf); + + /** + * @brief Get Status response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*get_status)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); + + /** + * @brief Put Image response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*put_image)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); + + /** + * @brief Put Linked Thumbnail response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*put_linked_thumbnail)(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf); + + /** + * @brief Put Linked Attachment response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*put_linked_attachment)(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf); + + /** + * @brief Remote Display response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*remote_display)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); + + /** + * @brief Delete Image response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*delete_image)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); + + /** + * @brief Start Print response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*start_print)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); + + /** + * @brief Start Archive response received + * + * @param client BIP client instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + */ + void (*start_archive)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); +}; + +/** + * @brief BIP server instance structure + * + * Structure representing a BIP server endpoint + */ +struct bt_bip_server { + /** @internal Server callback functions */ + const struct bt_bip_server_cb *_cb; + + /** @internal Parent BIP instance */ + struct bt_bip *_bip; + + /** @internal Service UUID */ + const struct bt_uuid_128 *_uuid; + + /** @internal Connection ID */ + uint32_t _conn_id; + + /** @internal Connection type */ + enum bt_bip_conn_type _type; + + /** @internal Underlying OBEX server */ + struct bt_obex_server _server; + + /** @internal Current operation code */ + uint8_t _opcode; + /** @internal Current operation type string */ + const char *_optype; + + /** @internal Server state (atomic) */ + atomic_t _state; + + /** @internal Associated client (for secondary connections) */ + union { + struct bt_bip_client *_primary_client; + struct bt_bip_client *_secondary_client; + }; + + /** @internal Pending request callback */ + void (*_req_cb)(struct bt_bip_server *server, bool final, struct net_buf *buf); + + /** @internal Node for linking in lists */ + sys_snode_t _node; +}; + +/** + * @brief BIP client instance structure + * + * Structure representing a BIP client endpoint + */ +struct bt_bip_client { + /** @internal Client callback functions */ + const struct bt_bip_client_cb *_cb; + + /** @internal Parent BIP instance */ + struct bt_bip *_bip; + + /** @internal Target service UUID */ + struct bt_uuid_128 _uuid; + + /** @internal Connection ID */ + uint32_t _conn_id; + + /** @internal Connection type */ + enum bt_bip_conn_type _type; + + /** @internal Underlying OBEX client */ + struct bt_obex_client _client; + + /** @internal Client state (atomic) */ + atomic_t _state; + + /** @internal Associated server (for secondary connections) */ + union { + struct bt_bip_server *_primary_server; + struct bt_bip_server *_secondary_server; + }; + + /** @internal Pending response callback */ + void (*_rsp_cb)(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf); + + /** @internal current request function type */ + const char *_req_type; + + /** @internal Node for linking in lists */ + sys_snode_t _node; +}; + +/** + * @brief Primary Image Push server callback initializer + * + * Helper macro to initialize callbacks for Primary Image Push server + */ +#define BT_BIP_PRIM_IMAGE_PUSH_CB(_connect, _disconnect, _get_caps, _put_image, \ + _put_linked_thumbnail, _put_linked_attachment) \ + { \ + .connect = _connect, .disconnect = _disconnect, .get_caps = _get_caps, \ + .put_image = _put_image, .put_linked_thumbnail = _put_linked_thumbnail, \ + .put_linked_attachment = _put_linked_attachment, \ + } + +/** + * @brief Primary Image Pull server callback initializer + * + * Helper macro to initialize callbacks for Primary Image Pull server + */ +#define BT_BIP_PRIM_IMAGE_PULL_CB(_connect, _disconnect, _get_caps, _get_image_list, \ + _get_image_properties, _get_image, _get_linked_thumbnail, \ + _get_linked_attachment, _delete_image) \ + { \ + .connect = _connect, .disconnect = _disconnect, .get_caps = _get_caps, \ + .get_image_list = _get_image_list, .get_image_properties = _get_image_properties, \ + .get_image = _get_image, .get_linked_thumbnail = _get_linked_thumbnail, \ + .get_linked_attachment = _get_linked_attachment, .delete_image = _delete_image, \ + } + +/** + * @brief Primary Image Print server callback initializer + * + * Helper macro to initialize callbacks for Primary Image Print server + */ +#define BT_BIP_PRIM_IMAGE_PRINT_CB(_connect, _disconnect, _get_caps, _get_status, _start_print) \ + { \ + .connect = _connect, .disconnect = _disconnect, .get_caps = _get_caps, \ + .get_status = _get_status, .start_print = _start_print, \ + } + +/** + * @brief Secondary Image Print server callback initializer + * + * Helper macro to initialize callbacks for Secondary Image Print server + */ +#define BT_BIP_2ND_IMAGE_PRINT_CB(_connect, _disconnect, _get_partial_image) \ + { \ + .connect = _connect, .disconnect = _disconnect, \ + .get_partial_image = _get_partial_image, \ + } + +/** + * @brief Primary Auto Archive server callback initializer + * + * Helper macro to initialize callbacks for Primary Auto Archive server + */ +#define BT_BIP_PRIM_AUTO_ARCHIVE_CB(_connect, _disconnect, _get_status, _start_archive) \ + { \ + .connect = _connect, .disconnect = _disconnect, .get_status = _get_status, \ + .start_archive = _start_archive, \ + } + +/** + * @brief Secondary Auto Archive server callback initializer + * + * Helper macro to initialize callbacks for Secondary Auto Archive server + */ +#define BT_BIP_2ND_AUTO_ARCHIVE_CB(_connect, _disconnect, _get_caps, _get_image_list, \ + _get_image_properties, _get_image, _get_linked_thumbnail, \ + _get_linked_attachment, _delete_image) \ + { \ + .connect = _connect, .disconnect = _disconnect, .get_caps = _get_caps, \ + .get_image_list = _get_image_list, .get_image_properties = _get_image_properties, \ + .get_image = _get_image, .get_linked_thumbnail = _get_linked_thumbnail, \ + .get_linked_attachment = _get_linked_attachment, .delete_image = _delete_image, \ + } + +/** + * @brief Primary Remote Camera server callback initializer + * + * Helper macro to initialize callbacks for Primary Remote Camera server + */ +#define BT_BIP_PRIM_REMOTE_CAMERA_CB(_connect, _disconnect, _get_image_properties, _get_image, \ + _get_linked_thumbnail, _get_monitoring_image) \ + { \ + .connect = _connect, .disconnect = _disconnect, \ + .get_image_properties = _get_image_properties, .get_image = _get_image, \ + .get_linked_thumbnail = _get_linked_thumbnail, \ + .get_monitoring_image = _get_monitoring_image, \ + } + +/** + * @brief Primary Remote Display server callback initializer + * + * Helper macro to initialize callbacks for Primary Remote Display server + */ +#define BT_BIP_PRIM_REMOTE_DISPLAY_CB(_connect, _disconnect, _get_caps, _get_image_list, \ + _put_image, _put_linked_thumbnail, _remote_display) \ + { \ + .connect = _connect, .disconnect = _disconnect, .get_caps = _get_caps, \ + .get_image_list = _get_image_list, .put_image = _put_image, \ + .put_linked_thumbnail = _put_linked_thumbnail, .remote_display = _remote_display, \ + } + +/** @brief BIP Image Push service UUID */ +#define BT_BIP_UUID_IMAGE_PUSH \ + (const struct bt_uuid_128 *)BT_UUID_DECLARE_128( \ + BT_UUID_128_ENCODE(0xE33D9545, 0x8374, 0x4AD7, 0x9EC5, 0xC16BE31EDE8E)) + +/** @brief BIP Image Pull service UUID */ +#define BT_BIP_UUID_IMAGE_PULL \ + (const struct bt_uuid_128 *)BT_UUID_DECLARE_128( \ + BT_UUID_128_ENCODE(0x8EE9B3D0, 0x4608, 0x11D5, 0x841A, 0x0002A5325B4E)) + +/** @brief BIP Advanced Image Printing service UUID */ +#define BT_BIP_UUID_IMAGE_PRINT \ + (const struct bt_uuid_128 *)BT_UUID_DECLARE_128( \ + BT_UUID_128_ENCODE(0x92353350, 0x4608, 0x11D5, 0x841A, 0x0002A5325B4E)) + +/** @brief BIP Auto Archive service UUID */ +#define BT_BIP_UUID_AUTO_ARCHIVE \ + (const struct bt_uuid_128 *)BT_UUID_DECLARE_128( \ + BT_UUID_128_ENCODE(0x940126C0, 0x4608, 0x11D5, 0x841A, 0x0002A5325B4E)) + +/** @brief BIP Remote Camera service UUID */ +#define BT_BIP_UUID_REMOTE_CAMERA \ + (const struct bt_uuid_128 *)BT_UUID_DECLARE_128( \ + BT_UUID_128_ENCODE(0x947E7420, 0x4608, 0x11D5, 0x841A, 0x0002A5325B4E)) + +/** @brief BIP Remote Display service UUID */ +#define BT_BIP_UUID_REMOTE_DISPLAY \ + (const struct bt_uuid_128 *)BT_UUID_DECLARE_128( \ + BT_UUID_128_ENCODE(0x94C7CD20, 0x4608, 0x11D5, 0x841A, 0x0002A5325B4E)) + +/** @brief BIP Referenced Objects service UUID */ +#define BT_BIP_UUID_REFERENCED_OBJ \ + (const struct bt_uuid_128 *)BT_UUID_DECLARE_128( \ + BT_UUID_128_ENCODE(0x8E61F95D, 0x1A79, 0x11D4, 0x8EA4, 0x00805F9B9834)) + +/** @brief BIP Archived Objects service UUID */ +#define BT_BIP_UUID_ARCHIVED_OBJ \ + (const struct bt_uuid_128 *)BT_UUID_DECLARE_128( \ + BT_UUID_128_ENCODE(0x8E61F95E, 0x1A79, 0x11D4, 0x8EA4, 0x00805F9B9834)) + +/** + * @brief Register a primary BIP OBEX server + * + * Registers a primary BIP server for the specified connection type and service UUID. + * Primary servers handle the main BIP operations for their respective service types. + * + * @param bip BIP instance + * @param server BIP server structure to register + * @param type Primary connection type (IMAGE_PUSH, IMAGE_PULL, etc.) + * @param uuid Service UUID for the server + * @param cb Callback functions for handling server operations + * @return 0 on success, negative error code on failure + */ +int bt_bip_primary_server_register(struct bt_bip *bip, struct bt_bip_server *server, + enum bt_bip_conn_type type, const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb); + +/** + * @brief Register a secondary BIP OBEX server + * + * Registers a secondary BIP server that works in conjunction with a primary client. + * Secondary servers handle operations like referenced objects or archived objects. + * + * @param bip BIP instance + * @param server BIP server structure to register + * @param type Secondary connection type (REFERENCED_OBJECTS or ARCHIVED_OBJECTS) + * @param uuid Service UUID for the server + * @param cb Callback functions for handling server operations + * @param primary_client Associated primary client + * @return 0 on success, negative error code on failure + */ +int bt_bip_secondary_server_register(struct bt_bip *bip, struct bt_bip_server *server, + enum bt_bip_conn_type type, const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb, + struct bt_bip_client *primary_client); + +/** + * @brief Register Primary Image Push OBEX server + * + * Convenience function to register a Primary Image Push server. + * + * @param bip BIP instance + * @param server BIP server structure + * @param uuid Service UUID (typically BT_BIP_UUID_IMAGE_PUSH) + * @param cb Server callback functions + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_image_push_server_register(struct bt_bip *bip, + struct bt_bip_server *server, + const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb) +{ + return bt_bip_primary_server_register(bip, server, BT_BIP_PRIM_CONN_TYPE_IMAGE_PUSH, uuid, + cb); +} + +/** + * @brief Register Primary Image Pull OBEX server + * + * Convenience function to register a Primary Image Pull server. + * + * @param bip BIP instance + * @param server BIP server structure + * @param uuid Service UUID (typically BT_BIP_UUID_IMAGE_PULL) + * @param cb Server callback functions + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_image_pull_server_register(struct bt_bip *bip, + struct bt_bip_server *server, + const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb) +{ + return bt_bip_primary_server_register(bip, server, BT_BIP_PRIM_CONN_TYPE_IMAGE_PULL, uuid, + cb); +} + +/** + * @brief Register Primary Advanced Image Printing OBEX server + * + * Convenience function to register a Primary Advanced Image Printing server. + * + * @param bip BIP instance + * @param server BIP server structure + * @param uuid Service UUID (typically BT_BIP_UUID_IMAGE_PRINT) + * @param cb Server callback functions + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_advanced_image_printing_server_register( + struct bt_bip *bip, struct bt_bip_server *server, const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb) +{ + return bt_bip_primary_server_register( + bip, server, BT_BIP_PRIM_CONN_TYPE_ADVANCED_IMAGE_PRINTING, uuid, cb); +} + +/** + * @brief Register Primary Auto Archive OBEX server + * + * Convenience function to register a Primary Auto Archive server. + * + * @param bip BIP instance + * @param server BIP server structure + * @param uuid Service UUID (typically BT_BIP_UUID_AUTO_ARCHIVE) + * @param cb Server callback functions + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_auto_archive_server_register(struct bt_bip *bip, + struct bt_bip_server *server, + const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb) +{ + return bt_bip_primary_server_register(bip, server, BT_BIP_PRIM_CONN_TYPE_AUTO_ARCHIVE, uuid, + cb); +} + +/** + * @brief Register Primary Remote Camera OBEX server + * + * Convenience function to register a Primary Remote Camera server. + * + * @param bip BIP instance + * @param server BIP server structure + * @param uuid Service UUID (typically BT_BIP_UUID_REMOTE_CAMERA) + * @param cb Server callback functions + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_remote_camera_server_register(struct bt_bip *bip, + struct bt_bip_server *server, + const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb) +{ + return bt_bip_primary_server_register(bip, server, BT_BIP_PRIM_CONN_TYPE_REMOTE_CAMERA, + uuid, cb); +} + +/** + * @brief Register Primary Remote Display OBEX server + * + * Convenience function to register a Primary Remote Display server. + * + * @param bip BIP instance + * @param server BIP server structure + * @param uuid Service UUID (typically BT_BIP_UUID_REMOTE_DISPLAY) + * @param cb Server callback functions + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_remote_display_server_register(struct bt_bip *bip, + struct bt_bip_server *server, + const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb) +{ + return bt_bip_primary_server_register(bip, server, BT_BIP_PRIM_CONN_TYPE_REMOTE_DISPLAY, + uuid, cb); +} + +/** + * @brief Register Secondary Advanced Image Printing OBEX server + * + * Convenience function to register a Secondary Advanced Image Printing server. + * This server handles referenced objects for the printing service. + * + * @param bip BIP instance + * @param server BIP server structure + * @param uuid Service UUID (typically BT_BIP_UUID_REFERENCED_OBJ) + * @param cb Server callback functions + * @param primary_client Associated primary client + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_secondary_advanced_image_printing_server_register( + struct bt_bip *bip, struct bt_bip_server *server, const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb, struct bt_bip_client *primary_client) +{ + return bt_bip_secondary_server_register( + bip, server, BT_BIP_2ND_CONN_TYPE_REFERENCED_OBJECTS, uuid, cb, primary_client); +} + +/** + * @brief Register Secondary Auto Archive OBEX server + * + * Convenience function to register a Secondary Auto Archive server. + * This server handles archived objects for the auto archive service. + * + * @param bip BIP instance + * @param server BIP server structure + * @param uuid Service UUID (typically BT_BIP_UUID_ARCHIVED_OBJ) + * @param cb Server callback functions + * @param primary_client Associated primary client + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_secondary_auto_archive_server_register( + struct bt_bip *bip, struct bt_bip_server *server, const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb, struct bt_bip_client *primary_client) +{ + return bt_bip_secondary_server_register(bip, server, BT_BIP_2ND_CONN_TYPE_ARCHIVED_OBJECTS, + uuid, cb, primary_client); +} + +/** + * @brief Unregister a BIP OBEX server + * + * Unregisters a previously registered BIP server. + * + * @note The specific server can only be unregistered if it is has been disconnected. + * + * @param server BIP server to unregister + * @return 0 on success, negative error code on failure + */ +int bt_bip_server_unregister(struct bt_bip_server *server); + +/** + * @brief Connect a primary BIP OBEX client + * + * Initiates a primary BIP client connection for the specified service type BT_BIP_PRIM_CONN_TYPE_* + * of @ref bt_bip_conn_type. + * + * @param bip BIP instance + * @param client BIP client structure + * @param type Primary connection type + * @param cb Client callback functions + * @param buf Connection request data + * @return 0 on success, negative error code on failure + */ +int bt_bip_primary_client_connect(struct bt_bip *bip, struct bt_bip_client *client, + enum bt_bip_conn_type type, struct bt_bip_client_cb *cb, + struct net_buf *buf); + +/** + * @brief Connect a secondary BIP OBEX client + * + * Initiates a secondary BIP client connection with the specific type BT_BIP_2ND_CONN_TYPE_ of + * @ref bt_bip_conn_type to the secondary server of BIP initiator that works as primary OBEX client. + * + * @param bip BIP instance + * @param client BIP client structure + * @param type Secondary connection type + * @param cb Client callback functions + * @param buf Connection request data + * @param primary_server Associated primary server + * @return 0 on success, negative error code on failure + */ +int bt_bip_secondary_client_connect(struct bt_bip *bip, struct bt_bip_client *client, + enum bt_bip_conn_type type, struct bt_bip_client_cb *cb, + struct net_buf *buf, struct bt_bip_server *primary_server); + +/** + * @brief Connect Primary Image Push OBEX client + * + * Convenience function to connect a Primary Image Push client. + * + * @param bip BIP instance + * @param client BIP client structure + * @param cb Client callback functions + * @param buf Connection request data + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_image_push_client_connect(struct bt_bip *bip, + struct bt_bip_client *client, + struct bt_bip_client_cb *cb, + struct net_buf *buf) +{ + return bt_bip_primary_client_connect(bip, client, BT_BIP_PRIM_CONN_TYPE_IMAGE_PUSH, cb, + buf); +} + +/** + * @brief Connect Primary Image Pull OBEX client + * + * Convenience function to connect a Primary Image Pull client. + * + * @param bip BIP instance + * @param client BIP client structure + * @param cb Client callback functions + * @param buf Connection request data + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_image_pull_client_connect(struct bt_bip *bip, + struct bt_bip_client *client, + struct bt_bip_client_cb *cb, + struct net_buf *buf) +{ + return bt_bip_primary_client_connect(bip, client, BT_BIP_PRIM_CONN_TYPE_IMAGE_PULL, cb, + buf); +} + +/** + * @brief Connect Primary Advanced Image Printing OBEX client + * + * Convenience function to connect a Primary Advanced Image Printing client. + * + * @param bip BIP instance + * @param client BIP client structure + * @param cb Client callback functions + * @param buf Connection request data + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_advanced_image_printing_client_connect( + struct bt_bip *bip, struct bt_bip_client *client, struct bt_bip_client_cb *cb, + struct net_buf *buf) +{ + return bt_bip_primary_client_connect( + bip, client, BT_BIP_PRIM_CONN_TYPE_ADVANCED_IMAGE_PRINTING, cb, buf); +} + +/** + * @brief Connect Primary Auto Archive OBEX client + * + * Convenience function to connect a Primary Auto Archive client. + * + * @param bip BIP instance + * @param client BIP client structure + * @param cb Client callback functions + * @param buf Connection request data + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_auto_archive_client_connect(struct bt_bip *bip, + struct bt_bip_client *client, + struct bt_bip_client_cb *cb, + struct net_buf *buf) +{ + return bt_bip_primary_client_connect(bip, client, BT_BIP_PRIM_CONN_TYPE_AUTO_ARCHIVE, cb, + buf); +} + +/** + * @brief Connect Primary Remote Camera OBEX client + * + * Convenience function to connect a Primary Remote Camera client. + * + * @param bip BIP instance + * @param client BIP client structure + * @param cb Client callback functions + * @param buf Connection request data + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_remote_camera_client_connect(struct bt_bip *bip, + struct bt_bip_client *client, + struct bt_bip_client_cb *cb, + struct net_buf *buf) +{ + return bt_bip_primary_client_connect(bip, client, BT_BIP_PRIM_CONN_TYPE_REMOTE_CAMERA, cb, + buf); +} + +/** + * @brief Connect Primary Remote Display OBEX client + * + * Convenience function to connect a Primary Remote Display client. + * + * @param bip BIP instance + * @param client BIP client structure + * @param cb Client callback functions + * @param buf Connection request data + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_primary_remote_display_client_connect(struct bt_bip *bip, + struct bt_bip_client *client, + struct bt_bip_client_cb *cb, + struct net_buf *buf) +{ + return bt_bip_primary_client_connect(bip, client, BT_BIP_PRIM_CONN_TYPE_REMOTE_DISPLAY, cb, + buf); +} + +/** + * @brief Connect Secondary Advanced Image Printing OBEX client + * + * Convenience function to connect a Secondary Advanced Image Printing client. + * + * @param bip BIP instance + * @param client BIP client structure + * @param cb Client callback functions + * @param buf Connection request data + * @param primary_server Associated primary server + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_secondary_advanced_image_printing_client_connect( + struct bt_bip *bip, struct bt_bip_client *client, struct bt_bip_client_cb *cb, + struct net_buf *buf, struct bt_bip_server *primary_server) +{ + return bt_bip_secondary_client_connect(bip, client, BT_BIP_2ND_CONN_TYPE_REFERENCED_OBJECTS, + cb, buf, primary_server); +} + +/** + * @brief Connect Secondary Auto Archive OBEX client + * + * Convenience function to connect a Secondary Auto Archive client. + * + * @param bip BIP instance + * @param client BIP client structure + * @param cb Client callback functions + * @param buf Connection request data + * @param primary_server Associated primary server + * @return 0 on success, negative error code on failure + */ +static inline int bt_bip_secondary_auto_archive_client_connect(struct bt_bip *bip, + struct bt_bip_client *client, + struct bt_bip_client_cb *cb, + struct net_buf *buf, + struct bt_bip_server *primary_server) +{ + return bt_bip_secondary_client_connect(bip, client, BT_BIP_2ND_CONN_TYPE_ARCHIVED_OBJECTS, + cb, buf, primary_server); +} + +/** + * @brief Send OBEX Connect response + * + * Sends a response to an OBEX Connect request received by the server. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_connect_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf); + +/** + * @brief Send OBEX Disconnect request + * + * Initiates an OBEX Disconnect from the client side. + * + * @param client BIP client instance + * @param buf Request data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_disconnect(struct bt_bip_client *client, struct net_buf *buf); + +/** + * @brief Send OBEX Disconnect response + * + * Sends a response to an OBEX Disconnect request received by the server. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_disconnect_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf); + +/** + * @brief Send OBEX Abort request + * + * Initiates an OBEX Abort from the client side to cancel ongoing operation. + * + * @param client BIP client instance + * @param buf Request data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_abort(struct bt_bip_client *client, struct net_buf *buf); + +/** + * @brief Send OBEX Abort response + * + * Sends a response to an OBEX Abort request received by the server. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_abort_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf); + +/** + * @brief Send Get Capabilities request + * + * Requests the imaging capabilities from the server. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID and @ref BT_OBEX_HEADER_ID_TYPE. + * And the value of type header is @ref BT_BIP_HDR_TYPE_GET_CAPS. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_capabilities(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Get Capabilities response + * + * Responds to a Get Capabilities request with the server's capabilities. + * The following OBEX headers should be included for the first response, + * @ref BT_OBEX_HEADER_ID_BODY or @ref BT_OBEX_HEADER_ID_END_BODY. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (capabilities document) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_capabilities_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf); + +/** + * @brief Send Get Image List request + * + * Requests a list of available images from the server. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, @ref BT_OBEX_HEADER_ID_APP_PARAM, + * and @ref BT_BIP_HEADER_ID_IMG_DESC. + * And the value of type header is @ref BT_BIP_HDR_TYPE_GET_IMAGE_LIST. + * The application parameter should include ID @ref BT_BIP_APPL_PARAM_TAG_ID_RETURNED_HANDLES, + * @ref BT_BIP_APPL_PARAM_TAG_ID_LIST_START_OFFSET, and + * @ref BT_BIP_APPL_PARAM_TAG_ID_LATEST_CAPTURED_IMAGES. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_image_list(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Get Image List response + * + * Responds to a Get Image List request with the available image list. + * The following OBEX headers should be included for the first response, + * @ref BT_OBEX_HEADER_ID_APP_PARAM and @ref BT_BIP_HEADER_ID_IMG_DESC. + * The application parameter should include ID @ref BT_BIP_APPL_PARAM_TAG_ID_RETURNED_HANDLES. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (image list document) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_image_list_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf); + +/** + * @brief Send Get Image Properties request + * + * Requests properties of a specific image from the server. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, and + * @ref BT_BIP_HEADER_ID_IMG_HANDLE. + * And the value of type header is @ref BT_BIP_HDR_TYPE_GET_IMAGE_PROPERTIES. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (should contain image handle) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_image_properties(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Get Image Properties response + * + * Responds to a Get Image Properties request with the image properties. + * The following OBEX headers should be included for the first response, + * @ref BT_OBEX_HEADER_ID_APP_PARAM, and @ref BT_OBEX_HEADER_ID_BODY, + * or @ref BT_OBEX_HEADER_ID_END_BODY. + * The application parameter should include ID @ref BT_BIP_APPL_PARAM_TAG_ID_RETURNED_HANDLES. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (image properties document) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_image_properties_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf); + +/** + * @brief Send Get Image request + * + * Requests an image from the server. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, @ref BT_BIP_HEADER_ID_IMG_HANDLE + * and @ref BT_BIP_HEADER_ID_IMG_DESC. + * And the value of type header is @ref BT_BIP_HDR_TYPE_GET_IMAGE. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (should contain image handle and descriptor) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_image(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Get Image response + * + * Responds to a Get Image request with the requested image data. + * The following OBEX headers should be included for the first response, + * @ref BT_OBEX_HEADER_ID_LEN, and @ref BT_OBEX_HEADER_ID_BODY, + * or @ref BT_OBEX_HEADER_ID_END_BODY. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (image data) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_image_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf); + +/** + * @brief Send Get Linked Thumbnail request + * + * Requests a thumbnail linked to a specific image. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, and + * @ref BT_BIP_HEADER_ID_IMG_HANDLE. + * And the value of type header is @ref BT_BIP_HDR_TYPE_GET_LINKED_THUMBNAIL. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (should contain image handle) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_linked_thumbnail(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Get Linked Thumbnail response + * + * Responds to a Get Linked Thumbnail request with the thumbnail data. + * The following OBEX headers should be included for the first response, + * @ref BT_OBEX_HEADER_ID_BODY, or @ref BT_OBEX_HEADER_ID_END_BODY. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (thumbnail data) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_linked_thumbnail_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf); + +/** + * @brief Send Get Linked Attachment request + * + * Requests an attachment linked to a specific image. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, @ref BT_BIP_HEADER_ID_IMG_HANDLE, + * and @ref BT_OBEX_HEADER_ID_NAME. + * And the value of type header is @ref BT_BIP_HDR_TYPE_GET_LINKED_ATTACHMENT. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (should contain image handle) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_linked_attachment(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Get Linked Attachment response + * + * Responds to a Get Linked Attachment request with the attachment data. + * The following OBEX headers should be included for the first response, + * @ref BT_OBEX_HEADER_ID_BODY, or @ref BT_OBEX_HEADER_ID_END_BODY. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (attachment data) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_linked_attachment_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf); + +/** + * @brief Send Get Partial Image request + * + * Requests a partial image (portion of an image) from the server. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, @ref BT_OBEX_HEADER_ID_NAME, + * and @ref BT_OBEX_HEADER_ID_APP_PARAM. + * And the value of type header is @ref BT_BIP_HDR_TYPE_GET_PARTIAL_IMAGE. + * The application parameter should include ID @ref BT_BIP_APPL_PARAM_TAG_ID_PARTIAL_FILE_LEN, + * and @ref BT_BIP_APPL_PARAM_TAG_ID_PARTIAL_FILE_START_OFFSET. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (should contain image handle and range info) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_partial_image(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Get Partial Image response + * + * Responds to a Get Partial Image request with the requested image portion. + * The following OBEX headers should be included for the first response, + * @ref BT_OBEX_HEADER_ID_BODY, or @ref BT_OBEX_HEADER_ID_END_BODY. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (partial image data) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_partial_image_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf); + +/** + * @brief Send Get Monitoring Image request + * + * Requests a monitoring image from a remote camera. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, + * and @ref BT_OBEX_HEADER_ID_APP_PARAM. + * And the value of type header is @ref BT_BIP_HDR_TYPE_GET_PARTIAL_IMAGE. + * The application parameter should include ID @ref BT_BIP_APPL_PARAM_TAG_ID_STORE_FLAG. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_monitoring_image(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Get Monitoring Image response + * + * Responds to a Get Monitoring Image request with the monitoring image. + * The following OBEX headers should be included for the first response, + * @ref BT_BIP_HEADER_ID_IMG_HANDLE and @ref BT_OBEX_HEADER_ID_BODY, or + * @ref BT_OBEX_HEADER_ID_END_BODY. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (monitoring image data) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_monitoring_image_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf); + +/** + * @brief Send Get Status request + * + * Requests status information from the server. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, and @ref BT_OBEX_HEADER_ID_TYPE. + * And the value of type header is @ref BT_BIP_HDR_TYPE_GET_STATUS. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_status(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Get Status response + * + * Responds to a Get Status request with status information. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (status document) + * @return 0 on success, negative error code on failure + */ +int bt_bip_get_status_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf); + +/** + * @brief Send Put Image request + * + * Sends an image to the server. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, @ref BT_OBEX_HEADER_ID_NAME, + * and @ref BT_BIP_HEADER_ID_IMG_DESC. + * And the value of type header is @ref BT_BIP_HDR_TYPE_PUT_IMAGE. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (image data and metadata) + * @return 0 on success, negative error code on failure + */ +int bt_bip_put_image(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Put Image response + * + * Responds to a Put Image request. + * The following OBEX headers should be included for the first response, + * @ref BT_BIP_HEADER_ID_IMG_HANDLE. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (may contain image handle) + * @return 0 on success, negative error code on failure + */ +int bt_bip_put_image_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf); + +/** + * @brief Send Put Linked Thumbnail request + * + * Sends a thumbnail linked to a specific image. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, and + * @ref BT_BIP_HEADER_ID_IMG_HANDLE. + * And the value of type header is @ref BT_BIP_HDR_TYPE_PUT_LINKED_THUMBNAIL. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (thumbnail data and image handle) + * @return 0 on success, negative error code on failure + */ +int bt_bip_put_linked_thumbnail(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Put Linked Thumbnail response + * + * Responds to a Put Linked Thumbnail request. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_put_linked_thumbnail_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf); + +/** + * @brief Send Put Linked Attachment request + * + * Sends an attachment linked to a specific image. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, @ref BT_BIP_HEADER_ID_IMG_HANDLE, + * and @ref BT_BIP_HEADER_ID_IMG_DESC. + * And the value of type header is @ref BT_BIP_HDR_TYPE_PUT_LINKED_ATTACHMENT. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (attachment data and image handle) + * @return 0 on success, negative error code on failure + */ +int bt_bip_put_linked_attachment(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Put Linked Attachment response + * + * Responds to a Put Linked Attachment request. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_put_linked_attachment_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf); + +/** + * @brief Send Remote Display request + * + * Sends a remote display command to the server. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, @ref BT_BIP_HEADER_ID_IMG_HANDLE, + * and @ref BT_OBEX_HEADER_ID_APP_PARAM. + * And the value of type header is @ref BT_BIP_HDR_TYPE_REMOTE_DISPLAY. + * The application parameter should include ID @ref BT_BIP_APPL_PARAM_TAG_ID_REMOTE_DISPLAY. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (display command) + * @return 0 on success, negative error code on failure + */ +int bt_bip_remote_display(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Remote Display response + * + * Responds to a Remote Display request. + * The following OBEX headers should be included for the first response, + * @ref BT_BIP_HEADER_ID_IMG_HANDLE. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_remote_display_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf); + +/** + * @brief Send Delete Image request + * + * Requests deletion of a specific image on the server. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, and + * @ref BT_BIP_HEADER_ID_IMG_HANDLE. + * And the value of type header is @ref BT_BIP_HDR_TYPE_DELETE_IMAGE. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (should contain image handle) + * @return 0 on success, negative error code on failure + */ +int bt_bip_delete_image(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Delete Image response + * + * Responds to a Delete Image request. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer + * @return 0 on success, negative error code on failure + */ +int bt_bip_delete_image_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf); + +/** + * @brief Send Start Print request + * + * Initiates a print job on the server. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, and + * @ref BT_OBEX_HEADER_ID_APP_PARAM. + * And the value of type header is @ref BT_BIP_HDR_TYPE_START_PRINT. + * The application parameter should include ID @ref BT_BIP_APPL_PARAM_TAG_ID_STORE_FLAG. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (print control object) + * @return 0 on success, negative error code on failure + */ +int bt_bip_start_print(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Start Print response + * + * Responds to a Start Print request. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (may contain job ID) + * @return 0 on success, negative error code on failure + */ +int bt_bip_start_print_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf); + +/** + * @brief Send Start Archive request + * + * Initiates an archive operation on the server. + * The following OBEX headers should be included for the first request, + * @ref BT_OBEX_HEADER_ID_CONN_ID, @ref BT_OBEX_HEADER_ID_TYPE, and + * @ref BT_OBEX_HEADER_ID_APP_PARAM. + * And the value of type header is @ref BT_BIP_HDR_TYPE_START_ARCHIVE. + * The application parameter should include ID @ref BT_BIP_APPL_PARAM_TAG_ID_STORE_FLAG. + * + * @param client BIP client instance + * @param final True if this is the final packet + * @param buf Request data buffer (archive control object) + * @return 0 on success, negative error code on failure + */ +int bt_bip_start_archive(struct bt_bip_client *client, bool final, struct net_buf *buf); + +/** + * @brief Send Start Archive response + * + * Responds to a Start Archive request. + * + * @param server BIP server instance + * @param rsp_code OBEX response code + * @param buf Response data buffer (may contain job ID) + * @return 0 on success, negative error code on failure + */ +int bt_bip_start_archive_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf); + +/** + * @brief Add Image Descriptor header to buffer + * + * Adds an Image Descriptor header to the OBEX packet buffer. + * The descriptor contains format and encoding information for the image. + * + * @param buf Buffer to add header to + * @param len Length of descriptor data + * @param desc Pointer to descriptor data + * @return 0 on success, negative error code on failure + */ +int bt_bip_add_header_image_desc(struct net_buf *buf, uint16_t len, const uint8_t *desc); + +/** + * @brief Add Image Handle header to buffer + * + * Adds an Image Handle header to the OBEX packet buffer. + * The handle is a UTF-16 encoded string that uniquely identifies an image. + * + * @param buf Buffer to add header to + * @param len Length of handle data in bytes + * @param handle Pointer to UTF-16 encoded handle data + * @return 0 on success, negative error code on failure + */ +int bt_bip_add_header_image_handle(struct net_buf *buf, uint16_t len, const uint8_t *handle); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_BIP_H_ */ diff --git a/include/zephyr/bluetooth/classic/obex.h b/include/zephyr/bluetooth/classic/obex.h index 3d56edbd0786c..7c148c1e3f7e3 100644 --- a/include/zephyr/bluetooth/classic/obex.h +++ b/include/zephyr/bluetooth/classic/obex.h @@ -21,10 +21,14 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif +#define BT_OBEX_MIN_MTU 255 + /** @brief OBEX Response Code. */ enum __packed bt_obex_rsp_code { /** Continue */ @@ -1151,6 +1155,57 @@ int bt_obex_add_header_body(struct net_buf *buf, uint16_t len, const uint8_t *bo */ int bt_obex_add_header_end_body(struct net_buf *buf, uint16_t len, const uint8_t *body); +/** @brief Add Header: a chunk (may be a final chunk) of the object body. + * + * The function is used to help to add body/end body for the upperlayer. + * When the tail room of the buffer is more than the passed body room, and the total length of + * buffer is not more than the mopl if the body has been added, the header end body will be + * added. Or, the header body will be added. + * + * @param buf Buffer needs to be sent. + * @param mopl The MOPL of the OBEX connection + * @param len Length of body. + * @param body Object Body. + * @param added_len The added length. + * + * @return 0 in case of success or negative value in case of error. + */ +static inline int bt_obex_add_header_body_or_end_body(struct net_buf *buf, uint16_t mopl, + uint16_t len, const uint8_t *body, + uint16_t *added_len) +{ + uint16_t tx_len; + int err; + + if ((buf == NULL) || (body == NULL) || (added_len == NULL) || (mopl < BT_OBEX_MIN_MTU) || + (len == 0)) { + return -EINVAL; + } + + tx_len = BT_OBEX_PDU_LEN(mopl); + if (tx_len <= buf->len) { + return -ENOMEM; + } + + *added_len = 0; + + tx_len = MIN((tx_len - buf->len), net_buf_tailroom(buf)); + if (tx_len <= BT_OBEX_HDR_LEN_OF_HEADER_BODY) { + return 0; + } + + tx_len = BT_OBEX_DATA_LEN_OF_HEADER_BODY(tx_len); + if (tx_len >= len) { + *added_len = len; + err = bt_obex_add_header_end_body(buf, len, body); + } else { + *added_len = tx_len; + err = bt_obex_add_header_body(buf, tx_len, body); + } + + return err; +} + /** @brief Add Header: identifies the OBEX application, used to tell if talking to a peer. * * @param buf Buffer needs to be sent. @@ -1671,6 +1726,25 @@ int bt_obex_get_header_srm_param(struct net_buf *buf, uint8_t *srm_param); */ int bt_obex_make_uuid(union bt_obex_uuid *uuid, const uint8_t *data, uint16_t len); +/** @brief Check if the string is valid + * + * @param id HEADER ID. + * @param len The length of string. + * @param str The address of string. + * + * @return true if the string is valid or false otherwise. + */ +bool bt_obex_string_is_valid(uint8_t id, uint16_t len, const uint8_t *str); + +/** @brief Check whether the buf has the specified header + * + * @param buf Buffer needs to be sent. + * @param id The id of the header. + * + * @return true if the header is found or false otherwise. + */ +bool bt_obex_has_header(struct net_buf *buf, uint8_t id); + #ifdef __cplusplus } #endif diff --git a/include/zephyr/bluetooth/classic/sdp.h b/include/zephyr/bluetooth/classic/sdp.h index 041197f8a6aeb..f680daceaf257 100644 --- a/include/zephyr/bluetooth/classic/sdp.h +++ b/include/zephyr/bluetooth/classic/sdp.h @@ -477,6 +477,32 @@ struct bt_sdp_record { { BT_SDP_TYPE_SIZE(BT_SDP_UINT16), BT_SDP_ARRAY_16(_features) } \ } +/** + * @brief SDP Supported Capabilities Attribute Declaration Macro. + * + * Helper macro to declare supported capabilities of a profile/protocol. + * + * @param _capabilities Capability mask as 8bit unsigned integer. + */ +#define BT_SDP_SUPPORTED_CAPABILITIES(_capabilities) \ +{ \ + BT_SDP_ATTR_SUPPORTED_CAPABILITIES, \ + { BT_SDP_TYPE_SIZE(BT_SDP_UINT8), BT_SDP_ARRAY_8(_capabilities) } \ +} + +/** + * @brief SDP Supported Functions Attribute Declaration Macro. + * + * Helper macro to declare supported functions of a profile/protocol. + * + * @param _functions Function mask as 32bit unsigned integer. + */ +#define BT_SDP_SUPPORTED_FUNCTIONS(_functions) \ +{ \ + BT_SDP_ATTR_SUPPORTED_FUNCTIONS, \ + { BT_SDP_TYPE_SIZE(BT_SDP_UINT32), BT_SDP_ARRAY_32(_functions) } \ +} + /** * @brief SDP Service Declaration Macro. * @@ -672,6 +698,7 @@ int bt_sdp_discover_cancel(struct bt_conn *conn, /** @brief Protocols to be asked about specific parameters */ enum bt_sdp_proto { BT_SDP_PROTO_RFCOMM = 0x0003, + BT_SDP_PROTO_OBEX = 0x0008, BT_SDP_PROTO_AVDTP = 0x0019, BT_SDP_PROTO_L2CAP = 0x0100, }; @@ -737,6 +764,28 @@ int bt_sdp_get_profile_version(const struct net_buf *buf, uint16_t profile, */ int bt_sdp_get_features(const struct net_buf *buf, uint16_t *features); +/** @brief Get supported functions attribute value + * + * Allows if exposed by remote retrieve supported functions attribute. + * + * @param buf Buffer holding original raw record data from remote. + * @param functions On success object to be populated with support functions. + * + * @return 0 on success if feature found and valid, negative in case any error + */ +int bt_sdp_get_functions(const struct net_buf *buf, uint32_t *functions); + +/** @brief Get GOEP L2CAP PSM attribute value + * + * Allows if exposed by remote retrieve supported GOEP L2CAP PSM attribute. + * + * @param buf Buffer holding original raw record data from remote. + * @param psm On success object to be populated with GOEP L2CAP PSM. + * + * @return 0 on success if feature found and valid, negative in case any error + */ +int bt_sdp_get_goep_l2cap_psm(const struct net_buf *buf, uint16_t *psm); + /** @brief Get Vendor ID * * Helper API extracting remote Vendor ID. To get it proper diff --git a/subsys/bluetooth/Kconfig.logging b/subsys/bluetooth/Kconfig.logging index a494dbcee29da..bb0557f4d5427 100644 --- a/subsys/bluetooth/Kconfig.logging +++ b/subsys/bluetooth/Kconfig.logging @@ -409,6 +409,10 @@ module = BT_GOEP module-str = "Bluetooth Generic Object Exchange Profile (GOEP)" source "subsys/logging/Kconfig.template.log_config_inherit" +module = BT_BIP +module-str = "Bluetooth Basic Imaging Profile (BIP)" +source "subsys/logging/Kconfig.template.log_config_inherit" + endmenu # Bluetooth Classic endif # BT_CLASSIC diff --git a/subsys/bluetooth/host/classic/CMakeLists.txt b/subsys/bluetooth/host/classic/CMakeLists.txt index 685c21a08ce0d..ad68c3d7898c4 100644 --- a/subsys/bluetooth/host/classic/CMakeLists.txt +++ b/subsys/bluetooth/host/classic/CMakeLists.txt @@ -10,6 +10,7 @@ zephyr_library_sources_ifdef(CONFIG_BT_AVDTP avdtp.c) zephyr_library_sources_ifdef(CONFIG_BT_AVRCP avrcp.c) zephyr_library_sources_ifdef(CONFIG_BT_AVCTP avctp.c) zephyr_library_sources_ifdef(CONFIG_BT_RFCOMM rfcomm.c) +zephyr_library_sources_ifdef(CONFIG_BT_BIP bip.c) zephyr_library_sources_ifdef( CONFIG_BT_CLASSIC diff --git a/subsys/bluetooth/host/classic/Kconfig b/subsys/bluetooth/host/classic/Kconfig index 60252cbcecc84..df02dc3b39917 100644 --- a/subsys/bluetooth/host/classic/Kconfig +++ b/subsys/bluetooth/host/classic/Kconfig @@ -610,6 +610,13 @@ config BT_OEBX_RSP_CODE_TO_STR endif # BT_GOEP +config BT_BIP + bool "Bluetooth BIP Profile [EXPERIMENTAL]" + select BT_GOEP + select EXPERIMENTAL + help + This option enables the BIP profile + endif # BT_CLASSIC endmenu diff --git a/subsys/bluetooth/host/classic/bip.c b/subsys/bluetooth/host/classic/bip.c new file mode 100644 index 0000000000000..1fafc169a301f --- /dev/null +++ b/subsys/bluetooth/host/classic/bip.c @@ -0,0 +1,2149 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include + +#include "common/assert.h" + +#include +#include +#include + +#include "host/hci_core.h" +#include "host/conn_internal.h" +#include "l2cap_br_internal.h" +#include "obex_internal.h" + +#define LOG_LEVEL CONFIG_BT_BIP_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_bip); + +typedef void (*bt_bip_server_cb_t)(struct bt_bip_server *server, bool final, struct net_buf *buf); +typedef void (*bt_bip_client_cb_t)(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf); + +static bool is_bip_primary_connect(enum bt_bip_conn_type type) +{ + switch (type) { + case BT_BIP_PRIM_CONN_TYPE_IMAGE_PUSH: + case BT_BIP_PRIM_CONN_TYPE_IMAGE_PULL: + case BT_BIP_PRIM_CONN_TYPE_ADVANCED_IMAGE_PRINTING: + case BT_BIP_PRIM_CONN_TYPE_AUTO_ARCHIVE: + case BT_BIP_PRIM_CONN_TYPE_REMOTE_CAMERA: + case BT_BIP_PRIM_CONN_TYPE_REMOTE_DISPLAY: + return true; + default: + break; + } + + return false; +} + +#define BIP_RFDCOMM_SERVER(server) CONTAINER_OF(server, struct bt_bip_rfcomm_server, server); + +static void bip_rfcomm_connected(struct bt_conn *conn, struct bt_goep *goep) +{ + struct bt_bip *bip = CONTAINER_OF(goep, struct bt_bip, goep); + + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_CONNECTED); + + if (bip->ops != NULL && bip->ops->connected != NULL) { + bip->ops->connected(conn, bip); + } +} + +static void bip_rfcomm_disconnected(struct bt_goep *goep) +{ + struct bt_bip *bip = CONTAINER_OF(goep, struct bt_bip, goep); + struct bt_bip_client *client, *cnext; + struct bt_bip_server *server, *snext; + + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_DISCONNECTED); + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bip->_clients, client, cnext, _node) { + if (is_bip_primary_connect(client->_type) && client->_secondary_server != NULL && + client->_secondary_server->_bip != bip) { + bt_bip_rfcomm_disconnect(client->_secondary_server->_bip); + } + atomic_set(&client->_state, BT_BIP_STATE_DISCONNECTED); + } + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bip->_servers, server, snext, _node) { + if (is_bip_primary_connect(server->_type) && server->_secondary_client != NULL && + server->_secondary_client->_bip != bip) { + bt_bip_rfcomm_disconnect(server->_secondary_client->_bip); + } + atomic_set(&server->_state, BT_BIP_STATE_DISCONNECTED); + } + + sys_slist_init(&bip->_clients); + bip->role = BT_BIP_ROLE_UNKNOWN; + bip->_supp_caps = 0; + bip->_supp_feats = 0; + bip->_supp_funcs = 0; + + if (bip->ops != NULL && bip->ops->disconnected != NULL) { + bip->ops->disconnected(bip); + } +} + +static struct bt_goep_transport_ops bip_rfcomm_ops = { + .connected = bip_rfcomm_connected, + .disconnected = bip_rfcomm_disconnected, +}; + +static int bip_rfcomm_accept(struct bt_conn *conn, struct bt_goep_transport_rfcomm_server *server, + struct bt_goep **goep) +{ + struct bt_bip_rfcomm_server *bip_server = BIP_RFDCOMM_SERVER(server); + struct bt_bip *bip; + int err; + + if (bip_server->accept == NULL) { + return -ENOTSUP; + } + + err = bip_server->accept(conn, bip_server, &bip); + if (err != 0) { + return err; + } + *goep = &bip->goep; + bip->role = BT_BIP_ROLE_RESPONDER; + bip->goep.transport_ops = &bip_rfcomm_ops; + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_CONNECTING); + + return 0; +} + +int bt_bip_rfcomm_register(struct bt_bip_rfcomm_server *server) +{ + if (server == NULL || server->accept == NULL) { + return -EINVAL; + } + + server->server.accept = bip_rfcomm_accept; + + return bt_goep_transport_rfcomm_server_register(&server->server); +} + +int bt_bip_rfcomm_connect(struct bt_conn *conn, struct bt_bip *bip, uint8_t channel) +{ + int err; + + if (conn == NULL || bip == NULL || channel == 0) { + return -EINVAL; + } + + if (atomic_get(&bip->_transport_state) != BT_BIP_TRANSPORT_STATE_DISCONNECTED) { + return -EINPROGRESS; + } + + bip->role = BT_BIP_ROLE_INITIATOR; + bip->goep.transport_ops = &bip_rfcomm_ops; + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_CONNECTING); + + err = bt_goep_transport_rfcomm_connect(conn, &bip->goep, channel); + if (err != 0) { + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_DISCONNECTED); + } + + return err; +} + +int bt_bip_rfcomm_disconnect(struct bt_bip *bip) +{ + int err; + + if (bip == NULL) { + return -EINVAL; + } + + if (atomic_get(&bip->_transport_state) != BT_BIP_TRANSPORT_STATE_CONNECTED) { + return -EINPROGRESS; + } + + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_DISCONNECTING); + + err = bt_goep_transport_rfcomm_disconnect(&bip->goep); + if (err != 0) { + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_CONNECTED); + } + + return err; +} + +#define BIP_L2CAP_SERVER(server) CONTAINER_OF(server, struct bt_bip_l2cap_server, server); + +static void bip_l2cap_connected(struct bt_conn *conn, struct bt_goep *goep) +{ + struct bt_bip *bip = CONTAINER_OF(goep, struct bt_bip, goep); + + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_CONNECTED); + + if (bip->ops != NULL && bip->ops->connected != NULL) { + bip->ops->connected(conn, bip); + } +} + +static void bip_l2cap_disconnected(struct bt_goep *goep) +{ + struct bt_bip *bip = CONTAINER_OF(goep, struct bt_bip, goep); + struct bt_bip_client *client, *cnext; + struct bt_bip_server *server, *snext; + + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_DISCONNECTED); + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bip->_clients, client, cnext, _node) { + if (is_bip_primary_connect(client->_type) && client->_secondary_server != NULL && + client->_secondary_server->_bip != bip) { + bt_bip_l2cap_disconnect(client->_secondary_server->_bip); + } + atomic_set(&client->_state, BT_BIP_STATE_DISCONNECTED); + } + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bip->_servers, server, snext, _node) { + if (is_bip_primary_connect(server->_type) && server->_secondary_client != NULL && + server->_secondary_client->_bip != bip) { + bt_bip_l2cap_disconnect(server->_secondary_client->_bip); + } + atomic_set(&server->_state, BT_BIP_STATE_DISCONNECTED); + } + + sys_slist_init(&bip->_clients); + bip->role = BT_BIP_ROLE_UNKNOWN; + bip->_supp_caps = 0; + bip->_supp_feats = 0; + bip->_supp_funcs = 0; + + if (bip->ops != NULL && bip->ops->disconnected != NULL) { + bip->ops->disconnected(bip); + } +} + +static struct bt_goep_transport_ops bip_l2cap_ops = { + .connected = bip_l2cap_connected, + .disconnected = bip_l2cap_disconnected, +}; + +static int bip_l2cap_accept(struct bt_conn *conn, struct bt_goep_transport_l2cap_server *server, + struct bt_goep **goep) +{ + struct bt_bip_l2cap_server *bip_server = BIP_L2CAP_SERVER(server); + struct bt_bip *bip; + int err; + + if (bip_server->accept == NULL) { + return -ENOTSUP; + } + + err = bip_server->accept(conn, bip_server, &bip); + if (err != 0) { + LOG_WRN("Incoming connection rejected"); + return err; + } + + if (bip == NULL || bip->ops == NULL) { + LOG_WRN("Invalid parameter"); + return -EINVAL; + } + + bip->goep.transport_ops = &bip_l2cap_ops; + *goep = &bip->goep; + bip->role = BT_BIP_ROLE_RESPONDER; + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_CONNECTING); + + return 0; +} + +int bt_bip_l2cap_register(struct bt_bip_l2cap_server *server) +{ + if (server == NULL || server->accept == NULL) { + return -EINVAL; + } + + server->server.accept = bip_l2cap_accept; + + return bt_goep_transport_l2cap_server_register(&server->server); +} + +int bt_bip_l2cap_connect(struct bt_conn *conn, struct bt_bip *bip, uint16_t psm) +{ + int err; + + if (conn == NULL || bip == NULL || psm == 0) { + return -EINVAL; + } + + if (atomic_get(&bip->_transport_state) != BT_BIP_TRANSPORT_STATE_DISCONNECTED) { + return -EINPROGRESS; + } + + bip->role = BT_BIP_ROLE_INITIATOR; + bip->goep.transport_ops = &bip_l2cap_ops; + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_CONNECTING); + + err = bt_goep_transport_l2cap_connect(conn, &bip->goep, psm); + if (err != 0) { + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_DISCONNECTED); + } + + return err; +} + +int bt_bip_l2cap_disconnect(struct bt_bip *bip) +{ + int err; + + if (bip == NULL) { + return -EINVAL; + } + + if (atomic_get(&bip->_transport_state) != BT_BIP_TRANSPORT_STATE_CONNECTED) { + return -EINPROGRESS; + } + + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_DISCONNECTING); + + err = bt_goep_transport_l2cap_disconnect(&bip->goep); + if (err != 0) { + atomic_set(&bip->_transport_state, BT_BIP_TRANSPORT_STATE_CONNECTED); + } + + return err; +} + +int bt_bip_set_supported_capabilities(struct bt_bip *bip, uint8_t capabilities) +{ + if (bip == NULL) { + return -EINVAL; + } + + if (bip->role != BT_BIP_ROLE_INITIATOR) { + LOG_ERR("Invalid role %u", bip->role); + return -EINVAL; + } + + bip->_supp_caps = capabilities; + return 0; +} + +int bt_bip_set_supported_features(struct bt_bip *bip, uint16_t features) +{ + if (bip == NULL) { + return -EINVAL; + } + + if (bip->role != BT_BIP_ROLE_INITIATOR) { + LOG_ERR("Invalid role %u", bip->role); + return -EINVAL; + } + + bip->_supp_feats = features; + return 0; +} + +int bt_bip_set_supported_functions(struct bt_bip *bip, uint32_t functions) +{ + if (bip == NULL) { + return -EINVAL; + } + + if (bip->role != BT_BIP_ROLE_INITIATOR) { + LOG_ERR("Invalid role %u", bip->role); + return -EINVAL; + } + + bip->_supp_funcs = functions; + return 0; +} + +static const struct bt_uuid_128 *image_push = BT_BIP_UUID_IMAGE_PUSH; +static const struct bt_uuid_128 *image_pull = BT_BIP_UUID_IMAGE_PULL; +static const struct bt_uuid_128 *image_print = BT_BIP_UUID_IMAGE_PRINT; +static const struct bt_uuid_128 *auto_archive = BT_BIP_UUID_AUTO_ARCHIVE; +static const struct bt_uuid_128 *remote_camera = BT_BIP_UUID_REMOTE_CAMERA; +static const struct bt_uuid_128 *remote_display = BT_BIP_UUID_REMOTE_DISPLAY; +static const struct bt_uuid_128 *referenced_obj = BT_BIP_UUID_REFERENCED_OBJ; +static const struct bt_uuid_128 *archived_obj = BT_BIP_UUID_ARCHIVED_OBJ; + +static const struct bt_uuid_128 *btp_get_uuid_from_type(enum bt_bip_conn_type type) +{ + switch (type) { + case BT_BIP_PRIM_CONN_TYPE_IMAGE_PUSH: + return image_push; + case BT_BIP_PRIM_CONN_TYPE_IMAGE_PULL: + return image_pull; + case BT_BIP_PRIM_CONN_TYPE_ADVANCED_IMAGE_PRINTING: + return image_print; + case BT_BIP_PRIM_CONN_TYPE_AUTO_ARCHIVE: + return auto_archive; + case BT_BIP_PRIM_CONN_TYPE_REMOTE_CAMERA: + return remote_camera; + case BT_BIP_PRIM_CONN_TYPE_REMOTE_DISPLAY: + return remote_display; + case BT_BIP_2ND_CONN_TYPE_REFERENCED_OBJECTS: + return referenced_obj; + case BT_BIP_2ND_CONN_TYPE_ARCHIVED_OBJECTS: + return archived_obj; + } + + return NULL; +} + +static void bip_client_connect(struct bt_obex_client *client, uint8_t rsp_code, uint8_t version, + uint16_t mopl, struct net_buf *buf) +{ + struct bt_bip_client *c = CONTAINER_OF(client, struct bt_bip_client, _client); + + if (rsp_code == BT_OBEX_RSP_CODE_SUCCESS) { + atomic_set(&c->_state, BT_BIP_STATE_CONNECTED); + bt_obex_get_header_conn_id(buf, &c->_conn_id); + } else { + atomic_set(&c->_state, BT_BIP_STATE_DISCONNECTED); + sys_slist_find_and_remove(&c->_bip->_clients, &c->_node); + } + + if (c->_cb->connect != NULL) { + c->_cb->connect(c, rsp_code, version, mopl, buf); + } +} + +static void bip_clinet_clear_pending_request(struct bt_bip_client *client) +{ + client->_rsp_cb = NULL; + client->_req_type = NULL; +} + +static void bip_client_disconnect(struct bt_obex_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + struct bt_bip_client *c = CONTAINER_OF(client, struct bt_bip_client, _client); + + if (rsp_code == BT_OBEX_RSP_CODE_SUCCESS) { + atomic_set(&c->_state, BT_BIP_STATE_DISCONNECTED); + + if (!is_bip_primary_connect(c->_type)) { + if (c->_primary_server != NULL) { + c->_primary_server->_secondary_client = NULL; + } + } else { + if (c->_secondary_server != NULL) { + LOG_WRN("Secondary connect should be disconnected"); + /* Consider removing transport */ + } + } + + sys_slist_find_and_remove(&c->_bip->_clients, &c->_node); + + bip_clinet_clear_pending_request(c); + } else { + atomic_set(&c->_state, BT_BIP_STATE_CONNECTED); + } + + if (c->_cb->disconnect != NULL) { + c->_cb->disconnect(c, rsp_code, buf); + } +} + +static void bip_client_put(struct bt_obex_client *client, uint8_t rsp_code, struct net_buf *buf) +{ + struct bt_bip_client *c = CONTAINER_OF(client, struct bt_bip_client, _client); + + if (c->_rsp_cb != NULL) { + c->_rsp_cb(c, rsp_code, buf); + } else { + LOG_WRN("No cb for rsp"); + } + + if (rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + bip_clinet_clear_pending_request(c); + } +} + +static void bip_client_get(struct bt_obex_client *client, uint8_t rsp_code, struct net_buf *buf) +{ + struct bt_bip_client *c = CONTAINER_OF(client, struct bt_bip_client, _client); + + if (c->_rsp_cb != NULL) { + c->_rsp_cb(c, rsp_code, buf); + } else { + LOG_WRN("No cb for rsp"); + } + + if (rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + bip_clinet_clear_pending_request(c); + } +} + +static void bip_client_abort(struct bt_obex_client *client, uint8_t rsp_code, struct net_buf *buf) +{ + struct bt_bip_client *c = CONTAINER_OF(client, struct bt_bip_client, _client); + int err; + + if (c->_cb->abort != NULL) { + c->_cb->abort(c, rsp_code, buf); + } + + if (rsp_code == BT_OBEX_RSP_CODE_SUCCESS) { + bip_clinet_clear_pending_request(c); + return; + } + + err = bt_bip_disconnect(c, NULL); + if (err != 0) { + LOG_ERR("Failed to send BIP disconnect"); + } +} + +static const struct bt_obex_client_ops bip_client_ops = { + .connect = bip_client_connect, + .disconnect = bip_client_disconnect, + .put = bip_client_put, + .get = bip_client_get, + .abort = bip_client_abort, + .setpath = NULL, + .action = NULL, +}; + +static void bip_server_connect(struct bt_obex_server *server, uint8_t version, uint16_t mopl, + struct net_buf *buf) +{ + struct bt_bip_server *s = CONTAINER_OF(server, struct bt_bip_server, _server); + + if (!is_bip_primary_connect(s->_type) && s->_primary_client != NULL && + atomic_get(&s->_primary_client->_state) != BT_BIP_STATE_CONNECTED) { + LOG_ERR("Primary conn needs to be connected firstly"); + bt_bip_connect_rsp(s, BT_OBEX_RSP_CODE_NOT_ALLOW, NULL); + return; + } + + atomic_set(&s->_state, BT_BIP_STATE_CONNECTING); + + if (s->_cb->connect != NULL) { + s->_cb->connect(s, version, mopl, buf); + } +} + +static void bip_server_disconnect(struct bt_obex_server *server, struct net_buf *buf) +{ + struct bt_bip_server *s = CONTAINER_OF(server, struct bt_bip_server, _server); + + if (is_bip_primary_connect(s->_type)) { + if (s->_secondary_client != NULL) { + bt_bip_disconnect(s->_secondary_client, NULL); + } + } else { + if (s->_primary_client != NULL) { + s->_primary_client->_secondary_server = NULL; + } + } + + atomic_set(&s->_state, BT_BIP_STATE_DISCONNECTING); + + if (s->_cb->disconnect != NULL) { + s->_cb->disconnect(s, buf); + } +} + +struct bip_required_hdr { + uint8_t count; + const uint8_t *hdrs; +}; + +#define BIP_REQUIRED_HDR(_count, _hdrs) \ + { \ + .count = (_count), .hdrs = (const uint8_t *)(_hdrs), \ + } + +#define _BIP_REQUIRED_HDR_LIST(...) \ + BIP_REQUIRED_HDR(sizeof((uint8_t[]){__VA_ARGS__}), ((uint8_t[]){__VA_ARGS__})) + +#define BIP_REQUIRED_HDR_LIST(...) _BIP_REQUIRED_HDR_LIST(__VA_ARGS__) + +struct bip_function { + const char *type; + bool op_get; + uint8_t func_bit; + uint32_t supported_features; + uint32_t required_appl_param_tag_id; + struct bip_required_hdr hdr; + bt_bip_server_cb_t (*get_server_cb)(struct bt_bip_server *server); + bt_bip_client_cb_t (*get_client_cb)(struct bt_bip_client *client); +}; + +#define IMAGE_PUSH BIT(BT_BIP_PRIM_CONN_TYPE_IMAGE_PUSH) +#define IMAGE_PULL BIT(BT_BIP_PRIM_CONN_TYPE_IMAGE_PULL) +#define IMAGE_PRINT BIT(BT_BIP_PRIM_CONN_TYPE_ADVANCED_IMAGE_PRINTING) +#define AUTO_ARCHIVE BIT(BT_BIP_PRIM_CONN_TYPE_AUTO_ARCHIVE) +#define REMOTE_CAMERA BIT(BT_BIP_PRIM_CONN_TYPE_REMOTE_CAMERA) +#define REMOTE_DISPLAY BIT(BT_BIP_PRIM_CONN_TYPE_REMOTE_DISPLAY) +#define REFERENCED_OBJ BIT(BT_BIP_2ND_CONN_TYPE_REFERENCED_OBJECTS) +#define ARCHIVED_OBJ BIT(BT_BIP_2ND_CONN_TYPE_ARCHIVED_OBJECTS) + +#define AP_RETURNED_HANDLES BIT(BT_BIP_APPL_PARAM_TAG_ID_RETURNED_HANDLES) +#define AP_LIST_START_OFFSET BIT(BT_BIP_APPL_PARAM_TAG_ID_LIST_START_OFFSET) +#define AP_LATEST_CAPTURED_IMAGES BIT(BT_BIP_APPL_PARAM_TAG_ID_LATEST_CAPTURED_IMAGES) +#define AP_PARTIAL_FILE_LEN BIT(BT_BIP_APPL_PARAM_TAG_ID_PARTIAL_FILE_LEN) +#define AP_PARTIAL_FILE_START_OFFSET BIT(BT_BIP_APPL_PARAM_TAG_ID_PARTIAL_FILE_START_OFFSET) +#define AP_TOTAL_FILE_SIZE BIT(BT_BIP_APPL_PARAM_TAG_ID_TOTAL_FILE_SIZE) +#define AP_END_FLAG BIT(BT_BIP_APPL_PARAM_TAG_ID_END_FLAG) +#define AP_REMOTE_DISPLAY BIT(BT_BIP_APPL_PARAM_TAG_ID_REMOTE_DISPLAY) +#define AP_SERVICE_ID BIT(BT_BIP_APPL_PARAM_TAG_ID_SERVICE_ID) +#define AP_STORE_FLAG BIT(BT_BIP_APPL_PARAM_TAG_ID_STORE_FLAG) + +#define GET_CAPS_FUNC_BIT BT_BIP_SUPP_FUNC_GET_CAPS +#define GET_CAPS_SUPPORT_FEATS IMAGE_PUSH | IMAGE_PULL | IMAGE_PRINT | REMOTE_DISPLAY | ARCHIVED_OBJ +#define GET_CAPS_REQUIRED_HDR BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE +#define GET_CAPS_AP 0 + +#define GET_IMAGE_LIST_FUNC_BIT BT_BIP_SUPP_FUNC_GET_IMAGE_LIST +#define GET_IMAGE_LIST_SUPPORT_FEATS IMAGE_PULL | REMOTE_DISPLAY | ARCHIVED_OBJ +#define GET_IMAGE_LIST_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_OBEX_HEADER_ID_APP_PARAM, \ + BT_BIP_HEADER_ID_IMG_DESC +#define GET_IMAGE_LIST_AP AP_RETURNED_HANDLES | AP_LIST_START_OFFSET | AP_LATEST_CAPTURED_IMAGES + +#define GET_IMAGE_PROPERTIES_FUNC_BIT BT_BIP_SUPP_FUNC_GET_IMAGE_PROPERTIES +#define GET_IMAGE_PROPERTIES_SUPPORT_FEATS IMAGE_PULL | REMOTE_CAMERA | ARCHIVED_OBJ +#define GET_IMAGE_PROPERTIES_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_BIP_HEADER_ID_IMG_HANDLE +#define GET_IMAGE_PROPERTIES_AP 0 + +#define GET_IMAGE_FUNC_BIT BT_BIP_SUPP_FUNC_GET_IMAGE +#define GET_IMAGE_SUPPORT_FEATS IMAGE_PULL | REMOTE_CAMERA | ARCHIVED_OBJ +#define GET_IMAGE_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_BIP_HEADER_ID_IMG_DESC, \ + BT_BIP_HEADER_ID_IMG_HANDLE +#define GET_IMAGE_AP 0 + +#define GET_LINKED_THUMBNAIL_FUNC_BIT BT_BIP_SUPP_FUNC_GET_LINKED_THUMBNAIL +#define GET_LINKED_THUMBNAIL_SUPPORT_FEATS IMAGE_PULL | REMOTE_CAMERA | ARCHIVED_OBJ +#define GET_LINKED_THUMBNAIL_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_BIP_HEADER_ID_IMG_HANDLE +#define GET_LINKED_THUMBNAIL_AP 0 + +#define GET_LINKED_ATTACHMENT_FUNC_BIT BT_BIP_SUPP_FUNC_GET_LINKED_ATTACHMENT +#define GET_LINKED_ATTACHMENT_SUPPORT_FEATS IMAGE_PULL | ARCHIVED_OBJ +#define GET_LINKED_ATTACHMENT_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_BIP_HEADER_ID_IMG_HANDLE, \ + BT_OBEX_HEADER_ID_NAME +#define GET_LINKED_ATTACHMENT_AP 0 + +#define GET_PARTIAL_IMAGE_FUNC_BIT BT_BIP_SUPP_FUNC_GET_PARTIAL_IMAGE +#define GET_PARTIAL_IMAGE_SUPPORT_FEATS REFERENCED_OBJ +#define GET_PARTIAL_IMAGE_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_OBEX_HEADER_ID_NAME, \ + BT_OBEX_HEADER_ID_APP_PARAM +#define GET_PARTIAL_IMAGE_AP AP_PARTIAL_FILE_LEN | AP_PARTIAL_FILE_START_OFFSET + +#define GET_MONITORING_IMAGE_FUNC_BIT BT_BIP_SUPP_FUNC_GET_MONITORING_IMAGE +#define GET_MONITORING_IMAGE_SUPPORT_FEATS REMOTE_CAMERA +#define GET_MONITORING_IMAGE_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_OBEX_HEADER_ID_APP_PARAM +#define GET_MONITORING_IMAGE_AP AP_STORE_FLAG + +#define GET_STATUS_FUNC_BIT BT_BIP_SUPP_FUNC_GET_STATUS +#define GET_STATUS_SUPPORT_FEATS IMAGE_PRINT | AUTO_ARCHIVE +#define GET_STATUS_REQUIRED_HDR BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE +#define GET_STATUS_AP 0 + +#define PUT_IMAGE_FUNC_BIT BT_BIP_SUPP_FUNC_PUT_IMAGE +#define PUT_IMAGE_SUPPORT_FEATS IMAGE_PUSH | REMOTE_DISPLAY +#define PUT_IMAGE_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_OBEX_HEADER_ID_NAME, \ + BT_BIP_HEADER_ID_IMG_DESC +#define PUT_IMAGE_AP 0 + +#define PUT_LINKED_THUMBNAIL_FUNC_BIT BT_BIP_SUPP_FUNC_PUT_LINKED_THUMBNAIL +#define PUT_LINKED_THUMBNAIL_SUPPORT_FEATS IMAGE_PUSH | REMOTE_DISPLAY +#define PUT_LINKED_THUMBNAIL_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_BIP_HEADER_ID_IMG_HANDLE +#define PUT_LINKED_THUMBNAIL_AP 0 + +#define PUT_LINKED_ATTACHMENT_FUNC_BIT BT_BIP_SUPP_FUNC_PUT_LINKED_ATTACHMENT +#define PUT_LINKED_ATTACHMENT_SUPPORT_FEATS IMAGE_PUSH | REMOTE_DISPLAY +#define PUT_LINKED_ATTACHMENT_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_BIP_HEADER_ID_IMG_HANDLE +#define PUT_LINKED_ATTACHMENT_AP 0 + +#define REMOTE_DISPLAY_FUNC_BIT BT_BIP_SUPP_FUNC_REMOTE_DISPLAY +#define REMOTE_DISPLAY_SUPPORT_FEATS REMOTE_DISPLAY +#define REMOTE_DISPLAY_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_BIP_HEADER_ID_IMG_HANDLE, \ + BT_OBEX_HEADER_ID_APP_PARAM +#define REMOTE_DISPLAY_AP AP_REMOTE_DISPLAY + +#define DELETE_IMAGE_FUNC_BIT BT_BIP_SUPP_FUNC_REMOTE_DISPLAY +#define DELETE_IMAGE_SUPPORT_FEATS IMAGE_PULL | ARCHIVED_OBJ +#define DELETE_IMAGE_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_BIP_HEADER_ID_IMG_HANDLE +#define DELETE_IMAGE_AP 0 + +#define START_PRINT_FUNC_BIT BT_BIP_SUPP_FUNC_START_PRINT +#define START_PRINT_SUPPORT_FEATS IMAGE_PRINT +#define START_PRINT_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_OBEX_HEADER_ID_APP_PARAM +#define START_PRINT_AP AP_SERVICE_ID + +#define START_ARCHIVE_FUNC_BIT BT_BIP_SUPP_FUNC_START_ARCHIVE +#define START_ARCHIVE_SUPPORT_FEATS AUTO_ARCHIVE +#define START_ARCHIVE_REQUIRED_HDR \ + BT_OBEX_HEADER_ID_CONN_ID, BT_OBEX_HEADER_ID_TYPE, BT_OBEX_HEADER_ID_APP_PARAM +#define START_ARCHIVE_AP AP_SERVICE_ID + +static bt_bip_server_cb_t bip_server_get_cb_get_caps(struct bt_bip_server *server) +{ + return server->_cb->get_caps; +} + +static bt_bip_server_cb_t bip_server_get_cb_get_image_list(struct bt_bip_server *server) +{ + return server->_cb->get_image_list; +} + +static bt_bip_server_cb_t bip_server_get_cb_get_image_properties(struct bt_bip_server *server) +{ + return server->_cb->get_image_properties; +} + +static bt_bip_server_cb_t bip_server_get_cb_get_image(struct bt_bip_server *server) +{ + return server->_cb->get_image; +} + +static bt_bip_server_cb_t bip_server_get_cb_get_linked_thumbnail(struct bt_bip_server *server) +{ + return server->_cb->get_linked_thumbnail; +} + +static bt_bip_server_cb_t bip_server_get_cb_get_linked_attachment(struct bt_bip_server *server) +{ + return server->_cb->get_linked_attachment; +} + +static bt_bip_server_cb_t bip_server_get_cb_get_partial_image(struct bt_bip_server *server) +{ + return server->_cb->get_partial_image; +} + +static bt_bip_server_cb_t bip_server_get_cb_get_monitoring_image(struct bt_bip_server *server) +{ + return server->_cb->get_monitoring_image; +} + +static bt_bip_server_cb_t bip_server_get_cb_get_status(struct bt_bip_server *server) +{ + return server->_cb->get_status; +} + +static bt_bip_server_cb_t bip_server_get_cb_put_image(struct bt_bip_server *server) +{ + return server->_cb->put_image; +} + +static bt_bip_server_cb_t bip_server_get_cb_put_linked_thumbnail(struct bt_bip_server *server) +{ + return server->_cb->put_linked_thumbnail; +} + +static bt_bip_server_cb_t bip_server_get_cb_put_linked_attachment(struct bt_bip_server *server) +{ + return server->_cb->put_linked_attachment; +} + +static bt_bip_server_cb_t bip_server_get_cb_remote_display(struct bt_bip_server *server) +{ + return server->_cb->remote_display; +} + +static bt_bip_server_cb_t bip_server_get_cb_delete_image(struct bt_bip_server *server) +{ + return server->_cb->delete_image; +} + +static bt_bip_server_cb_t bip_server_get_cb_start_print(struct bt_bip_server *server) +{ + return server->_cb->start_print; +} + +static bt_bip_server_cb_t bip_server_get_cb_start_archive(struct bt_bip_server *server) +{ + return server->_cb->start_archive; +} + +static bt_bip_client_cb_t bip_client_get_cb_get_caps(struct bt_bip_client *client) +{ + return client->_cb->get_caps; +} + +static bt_bip_client_cb_t bip_client_get_cb_get_image_list(struct bt_bip_client *client) +{ + return client->_cb->get_image_list; +} + +static bt_bip_client_cb_t bip_client_get_cb_get_image_properties(struct bt_bip_client *client) +{ + return client->_cb->get_image_properties; +} + +static bt_bip_client_cb_t bip_client_get_cb_get_image(struct bt_bip_client *client) +{ + return client->_cb->get_image; +} + +static bt_bip_client_cb_t bip_client_get_cb_get_linked_thumbnail(struct bt_bip_client *client) +{ + return client->_cb->get_linked_thumbnail; +} + +static bt_bip_client_cb_t bip_client_get_cb_get_linked_attachment(struct bt_bip_client *client) +{ + return client->_cb->get_linked_attachment; +} + +static bt_bip_client_cb_t bip_client_get_cb_get_partial_image(struct bt_bip_client *client) +{ + return client->_cb->get_partial_image; +} + +static bt_bip_client_cb_t bip_client_get_cb_get_monitoring_image(struct bt_bip_client *client) +{ + return client->_cb->get_monitoring_image; +} + +static bt_bip_client_cb_t bip_client_get_cb_get_status(struct bt_bip_client *client) +{ + return client->_cb->get_status; +} + +static bt_bip_client_cb_t bip_client_get_cb_put_image(struct bt_bip_client *client) +{ + return client->_cb->put_image; +} + +static bt_bip_client_cb_t bip_client_get_cb_put_linked_thumbnail(struct bt_bip_client *client) +{ + return client->_cb->put_linked_thumbnail; +} + +static bt_bip_client_cb_t bip_client_get_cb_put_linked_attachment(struct bt_bip_client *client) +{ + return client->_cb->put_linked_attachment; +} + +static bt_bip_client_cb_t bip_client_get_cb_remote_display(struct bt_bip_client *client) +{ + return client->_cb->remote_display; +} + +static bt_bip_client_cb_t bip_client_get_cb_delete_image(struct bt_bip_client *client) +{ + return client->_cb->delete_image; +} + +static bt_bip_client_cb_t bip_client_get_cb_start_print(struct bt_bip_client *client) +{ + return client->_cb->start_print; +} + +static bt_bip_client_cb_t bip_client_get_cb_start_archive(struct bt_bip_client *client) +{ + return client->_cb->start_archive; +} + +static const struct bip_function bip_functions[] = { + {BT_BIP_HDR_TYPE_GET_CAPS, true, GET_CAPS_FUNC_BIT, GET_CAPS_SUPPORT_FEATS, GET_CAPS_AP, + BIP_REQUIRED_HDR_LIST(GET_CAPS_REQUIRED_HDR), bip_server_get_cb_get_caps, + bip_client_get_cb_get_caps}, + {BT_BIP_HDR_TYPE_GET_IMAGE_LIST, true, GET_IMAGE_LIST_FUNC_BIT, + GET_IMAGE_LIST_SUPPORT_FEATS, GET_IMAGE_LIST_AP, + BIP_REQUIRED_HDR_LIST(GET_IMAGE_LIST_REQUIRED_HDR), bip_server_get_cb_get_image_list, + bip_client_get_cb_get_image_list}, + {BT_BIP_HDR_TYPE_GET_IMAGE_PROPERTIES, true, GET_IMAGE_PROPERTIES_FUNC_BIT, + GET_IMAGE_PROPERTIES_SUPPORT_FEATS, GET_IMAGE_PROPERTIES_AP, + BIP_REQUIRED_HDR_LIST(GET_IMAGE_PROPERTIES_REQUIRED_HDR), + bip_server_get_cb_get_image_properties, bip_client_get_cb_get_image_properties}, + {BT_BIP_HDR_TYPE_GET_IMAGE, true, GET_IMAGE_FUNC_BIT, GET_IMAGE_SUPPORT_FEATS, GET_IMAGE_AP, + BIP_REQUIRED_HDR_LIST(GET_IMAGE_REQUIRED_HDR), bip_server_get_cb_get_image, + bip_client_get_cb_get_image}, + {BT_BIP_HDR_TYPE_GET_LINKED_THUMBNAIL, true, GET_LINKED_THUMBNAIL_FUNC_BIT, + GET_LINKED_THUMBNAIL_SUPPORT_FEATS, GET_LINKED_THUMBNAIL_AP, + BIP_REQUIRED_HDR_LIST(GET_LINKED_THUMBNAIL_REQUIRED_HDR), + bip_server_get_cb_get_linked_thumbnail, bip_client_get_cb_get_linked_thumbnail}, + {BT_BIP_HDR_TYPE_GET_LINKED_ATTACHMENT, true, GET_LINKED_ATTACHMENT_FUNC_BIT, + GET_LINKED_ATTACHMENT_SUPPORT_FEATS, GET_LINKED_ATTACHMENT_AP, + BIP_REQUIRED_HDR_LIST(GET_LINKED_ATTACHMENT_REQUIRED_HDR), + bip_server_get_cb_get_linked_attachment, bip_client_get_cb_get_linked_attachment}, + {BT_BIP_HDR_TYPE_GET_PARTIAL_IMAGE, true, GET_PARTIAL_IMAGE_FUNC_BIT, + GET_PARTIAL_IMAGE_SUPPORT_FEATS, GET_PARTIAL_IMAGE_AP, + BIP_REQUIRED_HDR_LIST(GET_PARTIAL_IMAGE_REQUIRED_HDR), bip_server_get_cb_get_partial_image, + bip_client_get_cb_get_partial_image}, + {BT_BIP_HDR_TYPE_GET_MONITORING_IMAGE, true, GET_MONITORING_IMAGE_FUNC_BIT, + GET_MONITORING_IMAGE_SUPPORT_FEATS, GET_MONITORING_IMAGE_AP, + BIP_REQUIRED_HDR_LIST(GET_MONITORING_IMAGE_REQUIRED_HDR), + bip_server_get_cb_get_monitoring_image, bip_client_get_cb_get_monitoring_image}, + {BT_BIP_HDR_TYPE_GET_STATUS, true, GET_STATUS_FUNC_BIT, GET_STATUS_SUPPORT_FEATS, + GET_STATUS_AP, BIP_REQUIRED_HDR_LIST(GET_STATUS_REQUIRED_HDR), + bip_server_get_cb_get_status, bip_client_get_cb_get_status}, + {BT_BIP_HDR_TYPE_PUT_IMAGE, false, PUT_IMAGE_FUNC_BIT, PUT_IMAGE_SUPPORT_FEATS, + PUT_IMAGE_AP, BIP_REQUIRED_HDR_LIST(PUT_IMAGE_REQUIRED_HDR), bip_server_get_cb_put_image, + bip_client_get_cb_put_image}, + {BT_BIP_HDR_TYPE_PUT_LINKED_THUMBNAIL, false, PUT_LINKED_THUMBNAIL_FUNC_BIT, + PUT_LINKED_THUMBNAIL_SUPPORT_FEATS, PUT_LINKED_THUMBNAIL_AP, + BIP_REQUIRED_HDR_LIST(PUT_LINKED_THUMBNAIL_REQUIRED_HDR), + bip_server_get_cb_put_linked_thumbnail, bip_client_get_cb_put_linked_thumbnail}, + {BT_BIP_HDR_TYPE_PUT_LINKED_ATTACHMENT, false, PUT_LINKED_ATTACHMENT_FUNC_BIT, + PUT_LINKED_ATTACHMENT_SUPPORT_FEATS, PUT_LINKED_ATTACHMENT_AP, + BIP_REQUIRED_HDR_LIST(PUT_LINKED_ATTACHMENT_REQUIRED_HDR), + bip_server_get_cb_put_linked_attachment, bip_client_get_cb_put_linked_attachment}, + {BT_BIP_HDR_TYPE_REMOTE_DISPLAY, false, REMOTE_DISPLAY_FUNC_BIT, + REMOTE_DISPLAY_SUPPORT_FEATS, REMOTE_DISPLAY_AP, + BIP_REQUIRED_HDR_LIST(REMOTE_DISPLAY_REQUIRED_HDR), bip_server_get_cb_remote_display, + bip_client_get_cb_remote_display}, + {BT_BIP_HDR_TYPE_DELETE_IMAGE, false, DELETE_IMAGE_FUNC_BIT, DELETE_IMAGE_SUPPORT_FEATS, + DELETE_IMAGE_AP, BIP_REQUIRED_HDR_LIST(DELETE_IMAGE_REQUIRED_HDR), + bip_server_get_cb_delete_image, bip_client_get_cb_delete_image}, + {BT_BIP_HDR_TYPE_START_PRINT, false, START_PRINT_FUNC_BIT, START_PRINT_SUPPORT_FEATS, + START_PRINT_AP, BIP_REQUIRED_HDR_LIST(START_PRINT_REQUIRED_HDR), + bip_server_get_cb_start_print, bip_client_get_cb_start_print}, + {BT_BIP_HDR_TYPE_START_ARCHIVE, false, START_ARCHIVE_FUNC_BIT, START_ARCHIVE_SUPPORT_FEATS, + START_ARCHIVE_AP, BIP_REQUIRED_HDR_LIST(START_ARCHIVE_REQUIRED_HDR), + bip_server_get_cb_start_archive, bip_client_get_cb_start_archive}, +}; + +static bool has_required_hdrs(struct net_buf *buf, const struct bip_required_hdr *hdr) +{ + for (uint8_t index = 0; index < hdr->count; index++) { + if (!bt_obex_has_header(buf, hdr->hdrs[index])) { + return false; + } + } + return true; +} + +static enum bt_obex_rsp_code bip_server_get_req_cb(struct bt_bip_server *server, + struct net_buf *buf, bool is_get, + bt_bip_server_cb_t *cb) +{ + const uint8_t *type; + uint16_t len; + int err; + + err = bt_obex_get_header_type(buf, &len, &type); + if (err != 0) { + LOG_WRN("Failed to get type header %d", err); + return BT_OBEX_RSP_CODE_BAD_REQ; + } + + if (len <= strlen(type)) { + LOG_WRN("Invalid type string len %u <= %u", len, strlen(type)); + return BT_OBEX_RSP_CODE_BAD_REQ; + } + + *cb = NULL; + ARRAY_FOR_EACH(bip_functions, i) { + if (server->_optype != NULL && bip_functions[i].type != server->_optype) { + continue; + } + + if (strcmp(bip_functions[i].type, type) != 0) { + continue; + } + + if (bip_functions[i].op_get != is_get) { + continue; + } + + if ((bip_functions[i].supported_features & BIT(server->_type)) == 0) { + continue; + } + + if (!has_required_hdrs(buf, &bip_functions[i].hdr)) { + continue; + } + + /* Application parameter tag id is not checked. */ + + if (bip_functions[i].get_server_cb == NULL) { + continue; + } + + *cb = bip_functions[i].get_server_cb(server); + if (*cb == NULL) { + continue; + } + + server->_optype = bip_functions[i].type; + server->_opcode = is_get ? BT_OBEX_OPCODE_GET : BT_OBEX_OPCODE_PUT; + break; + } + + if (*cb == NULL) { + LOG_WRN("Unsupported request"); + return BT_OBEX_RSP_CODE_NOT_IMPL; + } + + return BT_OBEX_RSP_CODE_SUCCESS; +} + +static void bip_server_put(struct bt_obex_server *server, bool final, struct net_buf *buf) +{ + struct bt_bip_server *s = CONTAINER_OF(server, struct bt_bip_server, _server); + int err; + enum bt_obex_rsp_code rsp_code; + + if (s->_optype == NULL || bt_obex_has_header(buf, BT_OBEX_HEADER_ID_TYPE)) { + rsp_code = bip_server_get_req_cb(s, buf, false, &s->_req_cb); + if (rsp_code != BT_OBEX_RSP_CODE_SUCCESS) { + LOG_WRN("Failed to parse req %u", (uint8_t)rsp_code); + goto failed; + } + } + + if (s->_optype == NULL || s->_opcode != BT_OBEX_OPCODE_PUT || s->_req_cb == NULL) { + rsp_code = BT_OBEX_RSP_CODE_NOT_IMPL; + LOG_WRN("Invalid request"); + goto failed; + } + + s->_req_cb(s, final, buf); + + return; + +failed: + s->_optype = NULL; + s->_opcode = 0; + s->_req_cb = NULL; + err = bt_obex_put_rsp(server, rsp_code, NULL); + if (err != 0) { + LOG_ERR("Failed to send put rsp %d", err); + } +} + +static void bip_server_get(struct bt_obex_server *server, bool final, struct net_buf *buf) +{ + struct bt_bip_server *s = CONTAINER_OF(server, struct bt_bip_server, _server); + int err; + enum bt_obex_rsp_code rsp_code; + + if (s->_optype == NULL || bt_obex_has_header(buf, BT_OBEX_HEADER_ID_TYPE)) { + rsp_code = bip_server_get_req_cb(s, buf, true, &s->_req_cb); + if (rsp_code != BT_OBEX_RSP_CODE_SUCCESS) { + LOG_WRN("Failed to parse req %u", (uint8_t)rsp_code); + goto failed; + } + } + + if (s->_optype == NULL || s->_opcode != BT_OBEX_OPCODE_GET || s->_req_cb == NULL) { + rsp_code = BT_OBEX_RSP_CODE_NOT_IMPL; + LOG_WRN("Invalid request"); + goto failed; + } + + s->_req_cb(s, final, buf); + + return; + +failed: + s->_optype = NULL; + s->_opcode = 0; + s->_req_cb = NULL; + err = bt_obex_get_rsp(server, rsp_code, NULL); + if (err != 0) { + LOG_ERR("Failed to send get rsp %d", err); + } +} + +static void bip_server_abort(struct bt_obex_server *server, struct net_buf *buf) +{ + struct bt_bip_server *s = CONTAINER_OF(server, struct bt_bip_server, _server); + int err; + + if (s->_cb->abort != NULL) { + s->_cb->abort(s, buf); + return; + } + + err = bt_obex_abort_rsp(server, BT_OBEX_RSP_CODE_NOT_IMPL, NULL); + if (err != 0) { + LOG_ERR("Failed to send abort rsp %d", err); + } +} + +static const struct bt_obex_server_ops bip_server_ops = { + .connect = bip_server_connect, + .disconnect = bip_server_disconnect, + .put = bip_server_put, + .get = bip_server_get, + .abort = bip_server_abort, + .setpath = NULL, + .action = NULL, +}; + +static uint32_t bip_get_connect_id(void) +{ + static uint32_t connect_id; + + connect_id++; + + return connect_id; +} + +static int bt_bip_server_register(struct bt_bip *bip, struct bt_bip_server *server, + enum bt_bip_conn_type type, const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb, struct bt_bip_client *primary_client) +{ + struct bt_bip_server *s, *next; + int err; + + if (atomic_get(&server->_state) != BT_BIP_STATE_DISCONNECTED) { + LOG_ERR("Invalid state %u", (uint8_t)atomic_get(&server->_state)); + return -EINVAL; + } + + if (is_bip_primary_connect(type)) { + if (bip->role == BT_BIP_ROLE_INITIATOR) { + LOG_ERR("Invalid role initiator"); + return -EINVAL; + } + + if (primary_client != NULL) { + LOG_ERR("primary client should be NULL"); + return -EINVAL; + } + } else { + struct bt_bip *primary_bip; + + if (bip->role == BT_BIP_ROLE_RESPONDER) { + LOG_ERR("Invalid role responder"); + return -EINVAL; + } + + if (primary_client == NULL || primary_client->_bip == NULL) { + LOG_ERR("Invalid primary client"); + return -EINVAL; + } + + primary_bip = primary_client->_bip; + if (!sys_slist_find(&primary_bip->_clients, &primary_client->_node, NULL)) { + LOG_ERR("Primary client %p is not found", primary_client); + return -EINVAL; + } + } + + if (sys_slist_find(&bip->_servers, &server->_node, NULL)) { + LOG_ERR("Server %p has been registered", server); + return -EALREADY; + } + + if (uuid == NULL) { + uuid = btp_get_uuid_from_type(type); + } + + __ASSERT(uuid != NULL, "Invalid UUID"); + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bip->_servers, s, next, _node) { + if (bt_uuid_cmp(&uuid->uuid, &s->_uuid->uuid) == 0) { + LOG_ERR("UUID has been registered"); + return -EALREADY; + } + } + + server->_uuid = uuid; + server->_cb = cb; + server->_bip = bip; + server->_type = type; + server->_server.ops = &bip_server_ops; + server->_server.obex = &bip->goep.obex; + server->_secondary_client = primary_client; + server->_conn_id = bip_get_connect_id(); + err = bt_obex_server_register(&server->_server, server->_uuid); + if (err != 0) { + return err; + } + + sys_slist_append(&bip->_servers, &server->_node); + return 0; +} + +int bt_bip_primary_server_register(struct bt_bip *bip, struct bt_bip_server *server, + enum bt_bip_conn_type type, const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb) +{ + return bt_bip_server_register(bip, server, type, uuid, cb, NULL); +} + +int bt_bip_secondary_server_register(struct bt_bip *bip, struct bt_bip_server *server, + enum bt_bip_conn_type type, const struct bt_uuid_128 *uuid, + struct bt_bip_server_cb *cb, + struct bt_bip_client *primary_client) +{ + return bt_bip_server_register(bip, server, type, uuid, cb, primary_client); +} + +int bt_bip_server_unregister(struct bt_bip_server *server) +{ + int err; + + if (server == NULL || server->_bip == NULL) { + return -EINVAL; + } + + if (atomic_get(&server->_state) != BT_BIP_STATE_DISCONNECTED) { + LOG_ERR("Invalid state %u", (uint8_t)atomic_get(&server->_state)); + return -EINVAL; + } + + if (!sys_slist_find(&server->_bip->_servers, &server->_node, NULL)) { + LOG_ERR("Server %p has not found", server); + return -EALREADY; + } + + err = bt_obex_server_unregister(&server->_server); + if (err != 0) { + LOG_ERR("Failed to unregister %d", err); + return err; + } + + sys_slist_find_and_remove(&server->_bip->_servers, &server->_node); + return 0; +} + +static int bip_check_conn_id(uint32_t id, struct net_buf *buf) +{ + uint32_t conn_id; + int err; + + err = bt_obex_get_header_conn_id(buf, &conn_id); + if (err != 0) { + LOG_ERR("Failed to get conn id %d", err); + return err; + } + + if (conn_id != id) { + LOG_ERR("Conn id is mismatched %u != %u", conn_id, id); + return -EINVAL; + } + + return 0; +} + +static int bip_check_who(const struct bt_uuid *uuid, struct net_buf *buf) +{ + uint16_t len; + const uint8_t *who; + union bt_obex_uuid obex_uuid; + int err; + + err = bt_obex_get_header_who(buf, &len, &who); + if (err != 0) { + LOG_ERR("Failed to get who %d", err); + return err; + } + + err = bt_obex_make_uuid(&obex_uuid, who, len); + if (err != 0) { + LOG_ERR("Invalid UUID of who %d", err); + return err; + } + + if (bt_uuid_cmp(uuid, &obex_uuid.uuid) == 0) { + LOG_ERR("WHO is mismatched"); + return -EINVAL; + } + + return 0; +} + +#define BIP_FEAT_IMAGE_PUSH_BITS \ + (BIT(BT_BIP_SUPP_FEAT_IMAGE_PUSH) | BIT(BT_BIP_SUPP_FEAT_IMAGE_PUSH_STORE) | \ + BIT(BT_BIP_SUPP_FEAT_IMAGE_PUSH_PRINT) | BIT(BT_BIP_SUPP_FEAT_IMAGE_PUSH_DISPLAY)) + +#define BT_BIP_SUPP_FEAT_IMAGE_PRINT BT_BIP_SUPP_FEAT_ADVANCED_IMAGE_PRINT + +#define BIP_FEAT_IMAGE_PUSH(feature) ((feature) & BIP_FEAT_IMAGE_PUSH_BITS) != 0 +#define BIP_FEAT_IMAGE_PULL(feature) ((feature) & BIT(BT_BIP_SUPP_FEAT_IMAGE_PULL)) != 0 +#define BIP_FEAT_IMAGE_PRINT(feature) ((feature) & BIT(BT_BIP_SUPP_FEAT_IMAGE_PRINT)) != 0 +#define BIP_FEAT_AUTO_ARCHIVE(feature) ((feature) & BIT(BT_BIP_SUPP_FEAT_AUTO_ARCHIVE)) != 0 +#define BIP_FEAT_REMOTE_CAMERA(feature) ((feature) & BIT(BT_BIP_SUPP_FEAT_REMOTE_CAMERA)) != 0 +#define BIP_FEAT_REMOTE_DISPLAY(feature) ((feature) & BIT(BT_BIP_SUPP_FEAT_REMOTE_DISPLAY)) != 0 + +static int bip_check_features(struct bt_bip *bip, enum bt_bip_conn_type type) +{ + switch (type) { + case BT_BIP_PRIM_CONN_TYPE_IMAGE_PUSH: + if (BIP_FEAT_IMAGE_PUSH(bip->_supp_feats)) { + return 0; + } + break; + case BT_BIP_PRIM_CONN_TYPE_IMAGE_PULL: + if (BIP_FEAT_IMAGE_PULL(bip->_supp_feats)) { + return 0; + } + break; + case BT_BIP_PRIM_CONN_TYPE_ADVANCED_IMAGE_PRINTING: + if (BIP_FEAT_IMAGE_PRINT(bip->_supp_feats)) { + return 0; + } + break; + case BT_BIP_PRIM_CONN_TYPE_AUTO_ARCHIVE: + if (BIP_FEAT_AUTO_ARCHIVE(bip->_supp_feats)) { + return 0; + } + break; + case BT_BIP_PRIM_CONN_TYPE_REMOTE_CAMERA: + if (BIP_FEAT_REMOTE_CAMERA(bip->_supp_feats)) { + return 0; + } + break; + case BT_BIP_PRIM_CONN_TYPE_REMOTE_DISPLAY: + if (BIP_FEAT_REMOTE_DISPLAY(bip->_supp_feats)) { + return 0; + } + break; + case BT_BIP_2ND_CONN_TYPE_REFERENCED_OBJECTS: + break; + case BT_BIP_2ND_CONN_TYPE_ARCHIVED_OBJECTS: + break; + } + return -ENOTSUP; +} + +static int bt_bip_client_connect(struct bt_bip *bip, struct bt_bip_client *client, + enum bt_bip_conn_type type, struct bt_bip_client_cb *cb, + struct net_buf *buf, struct bt_bip_server *primary_server) +{ + struct bt_bip_client *c, *next; + const struct bt_uuid_128 *uuid; + union bt_obex_uuid obex_uuid; + uint8_t val[BT_UUID_SIZE_128]; + int err; + bool allocated = false; + + if (atomic_get(&client->_state) != BT_BIP_STATE_DISCONNECTED) { + LOG_ERR("Invalid state %u", (uint8_t)atomic_get(&client->_state)); + return -EINVAL; + } + + if (is_bip_primary_connect(type)) { + if (bip->role == BT_BIP_ROLE_RESPONDER) { + LOG_ERR("Invalid role responder"); + return -EINVAL; + } + + if (primary_server != NULL) { + LOG_ERR("primary server should be NULL"); + return -EINVAL; + } + + err = bip_check_features(bip, type); + if (err != 0) { + LOG_ERR("Unsupported features for connection type %d", type); + return err; + } + } else { + struct bt_bip *primary_bip; + + if (bip->role == BT_BIP_ROLE_INITIATOR) { + LOG_ERR("Invalid role initiator"); + return -EINVAL; + } + + if (primary_server == NULL || primary_server->_bip == NULL) { + LOG_ERR("Invalid primary client"); + return -EINVAL; + } + + if (type == BT_BIP_2ND_CONN_TYPE_REFERENCED_OBJECTS && + primary_server->_type != BT_BIP_PRIM_CONN_TYPE_ADVANCED_IMAGE_PRINTING) { + LOG_ERR("Invalid primary connection type for referenced objects"); + return -EINVAL; + } + + if (type == BT_BIP_2ND_CONN_TYPE_ARCHIVED_OBJECTS && + primary_server->_type != BT_BIP_PRIM_CONN_TYPE_AUTO_ARCHIVE) { + LOG_ERR("Invalid primary connection type for referenced objects"); + return -EINVAL; + } + + if (atomic_get(&primary_server->_state) != BT_BIP_STATE_CONNECTED) { + LOG_ERR("Invalid primary server state %u", + (uint8_t)atomic_get(&primary_server->_state)); + return -EINVAL; + } + + primary_bip = primary_server->_bip; + if (!sys_slist_find(&primary_bip->_servers, &primary_server->_node, NULL)) { + LOG_ERR("Primary server %p is not found", primary_server); + return -EINVAL; + } + } + + if (sys_slist_find(&bip->_clients, &client->_node, NULL)) { + LOG_ERR("Client %p is not idle", client); + return -EALREADY; + } + + if (!bt_obex_has_header(buf, BT_OBEX_HEADER_ID_TARGET)) { + uuid = btp_get_uuid_from_type(type); + } else { + uint16_t len; + const uint8_t *target; + + err = bt_obex_get_header_target(buf, &len, &target); + if (err != 0) { + LOG_ERR("Failed to get target %d", err); + return err; + } + + err = bt_obex_make_uuid(&obex_uuid, target, len); + if (err != 0) { + LOG_ERR("Invalid UUID of target %d", err); + return err; + } + uuid = &obex_uuid.u128; + } + + if (uuid == NULL) { + LOG_ERR("Invalid UUID"); + return -EINVAL; + } + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bip->_clients, c, next, _node) { + if (bt_uuid_cmp(&uuid->uuid, &c->_uuid.uuid) == 0) { + LOG_ERR("UUID has been registered"); + return -EALREADY; + } + } + + if (buf == NULL) { + buf = bt_goep_create_pdu(&bip->goep, NULL); + if (buf == NULL) { + LOG_ERR("Failed to allocate buffer"); + return -ENOBUFS; + } + + allocated = true; + } + + memcpy(&client->_uuid, uuid, sizeof(client->_uuid)); + client->_cb = cb; + client->_bip = bip; + client->_type = type; + client->_client.ops = &bip_client_ops; + client->_client.obex = &bip->goep.obex; + client->_primary_server = primary_server; + + if (!bt_obex_has_header(buf, BT_OBEX_HEADER_ID_TARGET)) { + sys_memcpy_swap(val, client->_uuid.val, sizeof(val)); + err = bt_obex_add_header_target(buf, sizeof(val), val); + if (err != 0) { + LOG_ERR("Failed to add header target"); + goto failed; + } + } + + err = bt_obex_connect(&client->_client, bip->goep.obex.rx.mtu, buf); + if (err != 0) { + LOG_ERR("Failed to send conn req"); + goto failed; + } + + atomic_set(&client->_state, BT_BIP_STATE_CONNECTING); + sys_slist_append(&bip->_clients, &client->_node); + return 0; + +failed: + if (allocated) { + net_buf_unref(buf); + } + return err; +} + +int bt_bip_primary_client_connect(struct bt_bip *bip, struct bt_bip_client *client, + enum bt_bip_conn_type type, struct bt_bip_client_cb *cb, + struct net_buf *buf) +{ + return bt_bip_client_connect(bip, client, type, cb, buf, NULL); +} + +int bt_bip_secondary_client_connect(struct bt_bip *bip, struct bt_bip_client *client, + enum bt_bip_conn_type type, struct bt_bip_client_cb *cb, + struct net_buf *buf, struct bt_bip_server *primary_server) +{ + return bt_bip_client_connect(bip, client, type, cb, buf, primary_server); +} + +int bt_bip_connect_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + uint8_t val[BT_UUID_SIZE_128]; + int err; + bool allocated = false; + + if (server == NULL || server->_bip == NULL) { + return -EINVAL; + } + + if (atomic_get(&server->_state) != BT_BIP_STATE_CONNECTING) { + LOG_ERR("Invalid state %u", (uint8_t)atomic_get(&server->_state)); + return -EINVAL; + } + + if (rsp_code == BT_OBEX_RSP_CODE_SUCCESS && !is_bip_primary_connect(server->_type) && + server->_primary_client != NULL && + atomic_get(&server->_primary_client->_state) != BT_BIP_STATE_CONNECTED) { + LOG_ERR("Primary conn needs to be connected firstly"); + return -EINVAL; + } + + if (buf == NULL) { + buf = bt_goep_create_pdu(&server->_bip->goep, NULL); + if (buf == NULL) { + LOG_ERR("Failed to allocate buffer"); + return -ENOBUFS; + } + allocated = true; + } else { + if (bt_obex_has_header(buf, BT_OBEX_HEADER_ID_WHO) && server->_uuid != NULL) { + err = bip_check_who(&server->_uuid->uuid, buf); + if (err != 0) { + return err; + } + } + + if (bt_obex_has_header(buf, BT_OBEX_HEADER_ID_CONN_ID)) { + err = bip_check_conn_id(server->_conn_id, buf); + if (err != 0) { + return err; + } + } + } + + if (rsp_code == BT_OBEX_RSP_CODE_SUCCESS) { + if (!bt_obex_has_header(buf, BT_OBEX_HEADER_ID_WHO)) { + sys_memcpy_swap(val, server->_uuid->val, sizeof(val)); + err = bt_obex_add_header_who(buf, sizeof(val), val); + if (err != 0) { + LOG_ERR("Failed to add header target %d", err); + goto failed; + } + } + + if (!bt_obex_has_header(buf, BT_OBEX_HEADER_ID_CONN_ID)) { + err = bt_obex_add_header_conn_id(buf, server->_conn_id); + if (err != 0) { + LOG_ERR("Failed to add header conn id %d", err); + goto failed; + } + } + } + + err = bt_obex_connect_rsp(&server->_server, rsp_code, server->_bip->goep.obex.rx.mtu, buf); + if (err != 0) { + LOG_ERR("Failed to send conn rsp %d", err); + goto failed; + } + + atomic_set(&server->_state, BT_BIP_STATE_CONNECTED); + return 0; + +failed: + if (allocated) { + net_buf_unref(buf); + } + return err; +} + +int bt_bip_disconnect(struct bt_bip_client *client, struct net_buf *buf) +{ + int err; + bool allocated = false; + + if (client == NULL || client->_bip == NULL) { + return -EINVAL; + } + + if (atomic_get(&client->_state) != BT_BIP_STATE_CONNECTED) { + LOG_ERR("Invalid state %u", (uint8_t)atomic_get(&client->_state)); + return -EINVAL; + } + + if (is_bip_primary_connect(client->_type) && client->_secondary_server != NULL && + atomic_get(&client->_secondary_server->_state) == BT_BIP_STATE_CONNECTED) { + LOG_ERR("Secondary conn needs to be disconnected firstly"); + return -EINVAL; + } + + if (buf == NULL) { + buf = bt_goep_create_pdu(&client->_bip->goep, NULL); + if (buf == NULL) { + LOG_ERR("Failed to allocate buffer"); + return -ENOBUFS; + } + allocated = true; + } + + if (!bt_obex_has_header(buf, BT_OBEX_HEADER_ID_CONN_ID)) { + err = bt_obex_add_header_conn_id(buf, client->_conn_id); + if (err != 0) { + LOG_ERR("Failed to add header conn id %d", err); + goto failed; + } + } else { + err = bip_check_conn_id(client->_conn_id, buf); + if (err != 0) { + goto failed; + } + } + + err = bt_obex_disconnect(&client->_client, buf); + if (err != 0) { + LOG_ERR("Failed to send conn rsp %d", err); + goto failed; + } + + atomic_set(&client->_state, BT_BIP_STATE_DISCONNECTING); + return 0; +failed: + if (allocated) { + net_buf_unref(buf); + } + return err; +} + +int bt_bip_disconnect_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + int err; + + if (server == NULL || server->_bip == NULL) { + return -EINVAL; + } + + if (atomic_get(&server->_state) != BT_BIP_STATE_DISCONNECTING) { + LOG_ERR("Invalid state %u", (uint8_t)atomic_get(&server->_state)); + return -EINVAL; + } + + if (rsp_code == BT_OBEX_RSP_CODE_SUCCESS && is_bip_primary_connect(server->_type) && + server->_secondary_client != NULL && + atomic_get(&server->_secondary_client->_state) == BT_BIP_STATE_CONNECTED) { + LOG_ERR("Secondary conn needs to be disconnected firstly"); + return -EINVAL; + } + + err = bt_obex_disconnect_rsp(&server->_server, rsp_code, NULL); + if (err != 0) { + LOG_ERR("Failed to send conn rsp %d", err); + return err; + } + + atomic_set(&server->_state, BT_BIP_STATE_DISCONNECTED); + return 0; +} + +int bt_bip_abort(struct bt_bip_client *client, struct net_buf *buf) +{ + int err; + bool allocated = false; + + if (client == NULL || client->_bip == NULL) { + return -EINVAL; + } + + if (atomic_get(&client->_state) != BT_BIP_STATE_CONNECTED) { + LOG_ERR("Invalid state %u", (uint8_t)atomic_get(&client->_state)); + return -EINVAL; + } + + if (client->_rsp_cb == NULL) { + LOG_ERR("No operation is ongoing"); + return -EINVAL; + } + + if (buf == NULL) { + buf = bt_goep_create_pdu(&client->_bip->goep, NULL); + if (buf == NULL) { + LOG_ERR("Failed to allocate buffer"); + return -ENOBUFS; + } + allocated = true; + } + + if (bt_obex_has_header(buf, BT_OBEX_HEADER_ID_CONN_ID)) { + err = bip_check_conn_id(client->_conn_id, buf); + if (err != 0) { + goto failed; + } + } else { + err = bt_obex_add_header_conn_id(buf, client->_conn_id); + if (err != 0) { + LOG_ERR("Failed to add header conn id %d", err); + goto failed; + } + } + + err = bt_obex_abort(&client->_client, buf); + if (err != 0) { + LOG_ERR("Failed to send abort request %d", err); + goto failed; + } + + return 0; + +failed: + if (allocated) { + net_buf_unref(buf); + } + return err; +} + +int bt_bip_abort_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + int err; + + if (server == NULL) { + return -EINVAL; + } + + if (atomic_get(&server->_state) != BT_BIP_STATE_CONNECTED) { + LOG_ERR("Invalid state %u", (uint8_t)atomic_get(&server->_state)); + return -EINVAL; + } + + err = bt_obex_abort_rsp(&server->_server, rsp_code, buf); + if (err != 0) { + LOG_ERR("Failed to send abort rsp %d", err); + return err; + } + + if (rsp_code == BT_OBEX_RSP_CODE_SUCCESS) { + server->_opcode = 0; + server->_optype = NULL; + server->_req_cb = NULL; + } + + return 0; +} + +static int bip_client_get_req_cb(struct bt_bip_client *client, const char *type, + struct net_buf *buf, bool is_get, bt_bip_client_cb_t *cb, + const char **req_type) +{ + const uint8_t *type_data; + uint16_t len; + int err; + + err = bt_obex_get_header_type(buf, &len, &type_data); + if (err != 0) { + LOG_WRN("Failed to get type header %d", err); + return -EINVAL; + } + + if (len <= strlen(type_data)) { + LOG_WRN("Invalid type string len %u <= %u", len, strlen(type_data)); + return -EINVAL; + } + + if (client->_bip == NULL) { + LOG_ERR("Invalid BIP client context"); + return -EINVAL; + } + + *cb = NULL; + ARRAY_FOR_EACH(bip_functions, i) { + if (strcmp(bip_functions[i].type, type) != 0) { + continue; + } + + if (strcmp(bip_functions[i].type, type_data) != 0) { + continue; + } + + if (bip_functions[i].op_get != is_get) { + continue; + } + + if ((bip_functions[i].supported_features & BIT(client->_type)) == 0) { + continue; + } + + if ((client->_bip->_supp_funcs & BIT(bip_functions[i].func_bit)) == 0) { + continue; + } + + if (!has_required_hdrs(buf, &bip_functions[i].hdr)) { + continue; + } + + /* Application parameter tag id is not checked. */ + + if (bip_functions[i].get_client_cb == NULL) { + continue; + } + + *cb = bip_functions[i].get_client_cb(client); + if (*cb == NULL) { + continue; + } + + if (req_type != NULL) { + *req_type = bip_functions[i].type; + } + break; + } + + if (*cb == NULL) { + LOG_WRN("Unsupported request"); + return -EINVAL; + } + + return 0; +} + +static int bip_get_or_put(struct bt_bip_client *client, bool is_get, const char *type, bool final, + struct net_buf *buf) +{ + int err; + bt_bip_client_cb_t cb; + const char *req_type; + + if (client == NULL || buf == NULL) { + return -EINVAL; + } + + if (atomic_get(&client->_state) != BT_BIP_STATE_CONNECTED) { + LOG_ERR("Invalid state %u", (uint8_t)atomic_get(&client->_state)); + return -EINVAL; + } + + if (client->_rsp_cb == NULL || bt_obex_has_header(buf, BT_OBEX_HEADER_ID_TYPE)) { + err = bip_client_get_req_cb(client, type, buf, is_get, &cb, &req_type); + if (err != 0) { + LOG_ERR("Invalid request %d", err); + return err; + } + + if (client->_rsp_cb != NULL && cb != client->_rsp_cb) { + LOG_ERR("Previous operation is not completed"); + return -EINVAL; + } + + client->_rsp_cb = cb; + client->_req_type = req_type; + } + + if (client->_req_type != NULL && strcmp(client->_req_type, type) != 0) { + LOG_ERR("Invalid request type %s != %s", client->_req_type, type); + return -EINVAL; + } + + if (bt_obex_has_header(buf, BT_OBEX_HEADER_ID_CONN_ID)) { + err = bip_check_conn_id(client->_conn_id, buf); + if (err != 0) { + return err; + } + } + + if (is_get) { + err = bt_obex_get(&client->_client, final, buf); + } else { + err = bt_obex_put(&client->_client, final, buf); + } + + if (err != 0) { + LOG_ERR("Failed to send get/put req %d", err); + } + + return err; +} + +static int bip_get(struct bt_bip_client *client, const char *type, bool final, struct net_buf *buf) +{ + return bip_get_or_put(client, true, type, final, buf); +} + +static int bip_put(struct bt_bip_client *client, const char *type, bool final, struct net_buf *buf) +{ + return bip_get_or_put(client, false, type, final, buf); +} + +int bt_bip_get_capabilities(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_get(client, BT_BIP_HDR_TYPE_GET_CAPS, final, buf); +} + +static int bip_get_or_put_rsp(struct bt_bip_server *server, bool is_get, const char *type, + uint8_t rsp_code, struct net_buf *buf) +{ + int err; + + if (server == NULL) { + return -EINVAL; + } + + if (atomic_get(&server->_state) != BT_BIP_STATE_CONNECTED) { + LOG_ERR("Invalid state %u", (uint8_t)atomic_get(&server->_state)); + return -EINVAL; + } + + if (server->_optype != NULL && strcmp(server->_optype, type) != 0) { + LOG_ERR("Invalid operation type %s != %s", server->_optype, type); + return -EINVAL; + } + + if (is_get) { + if (server->_opcode != BT_OBEX_OPCODE_GET) { + LOG_ERR("Invalid operation %u", server->_opcode); + return -EINVAL; + } + + err = bt_obex_get_rsp(&server->_server, rsp_code, buf); + } else { + if (server->_opcode != BT_OBEX_OPCODE_PUT) { + LOG_ERR("Invalid operation %u", server->_opcode); + return -EINVAL; + } + + err = bt_obex_put_rsp(&server->_server, rsp_code, buf); + } + + if (err != 0) { + LOG_ERR("Failed to send get/put rsp %d", err); + return err; + } + + if (rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + server->_opcode = 0; + server->_optype = NULL; + server->_req_cb = NULL; + } + + return 0; +} + +int bt_bip_get_capabilities_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, true, BT_BIP_HDR_TYPE_GET_CAPS, rsp_code, buf); +} + +int bt_bip_get_image_list(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_get(client, BT_BIP_HDR_TYPE_GET_IMAGE_LIST, final, buf); +} + +int bt_bip_get_image_list_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, true, BT_BIP_HDR_TYPE_GET_IMAGE_LIST, rsp_code, buf); +} + +int bt_bip_get_image_properties(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_get(client, BT_BIP_HDR_TYPE_GET_IMAGE_PROPERTIES, final, buf); +} + +int bt_bip_get_image_properties_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, true, BT_BIP_HDR_TYPE_GET_IMAGE_PROPERTIES, rsp_code, + buf); +} + +int bt_bip_get_image(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_get(client, BT_BIP_HDR_TYPE_GET_IMAGE, final, buf); +} + +int bt_bip_get_image_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, true, BT_BIP_HDR_TYPE_GET_IMAGE, rsp_code, buf); +} + +int bt_bip_get_linked_thumbnail(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_get(client, BT_BIP_HDR_TYPE_GET_LINKED_THUMBNAIL, final, buf); +} + +int bt_bip_get_linked_thumbnail_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, true, BT_BIP_HDR_TYPE_GET_LINKED_THUMBNAIL, rsp_code, + buf); +} + +int bt_bip_get_linked_attachment(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_get(client, BT_BIP_HDR_TYPE_GET_LINKED_ATTACHMENT, final, buf); +} + +int bt_bip_get_linked_attachment_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, true, BT_BIP_HDR_TYPE_GET_LINKED_ATTACHMENT, rsp_code, + buf); +} + +int bt_bip_get_partial_image(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_get(client, BT_BIP_HDR_TYPE_GET_PARTIAL_IMAGE, final, buf); +} + +int bt_bip_get_partial_image_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, true, BT_BIP_HDR_TYPE_GET_PARTIAL_IMAGE, rsp_code, buf); +} + +int bt_bip_get_monitoring_image(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_get(client, BT_BIP_HDR_TYPE_GET_MONITORING_IMAGE, final, buf); +} + +int bt_bip_get_monitoring_image_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, true, BT_BIP_HDR_TYPE_GET_MONITORING_IMAGE, rsp_code, + buf); +} + +int bt_bip_get_status(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_get(client, BT_BIP_HDR_TYPE_GET_STATUS, final, buf); +} + +int bt_bip_get_status_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, true, BT_BIP_HDR_TYPE_GET_STATUS, rsp_code, buf); +} + +int bt_bip_put_image(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_put(client, BT_BIP_HDR_TYPE_PUT_IMAGE, final, buf); +} + +int bt_bip_put_image_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, false, BT_BIP_HDR_TYPE_PUT_IMAGE, rsp_code, buf); +} + +int bt_bip_put_linked_thumbnail(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_put(client, BT_BIP_HDR_TYPE_PUT_LINKED_THUMBNAIL, final, buf); +} + +int bt_bip_put_linked_thumbnail_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, false, BT_BIP_HDR_TYPE_PUT_LINKED_THUMBNAIL, rsp_code, + buf); +} + +int bt_bip_put_linked_attachment(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_put(client, BT_BIP_HDR_TYPE_PUT_LINKED_ATTACHMENT, final, buf); +} + +int bt_bip_put_linked_attachment_rsp(struct bt_bip_server *server, uint8_t rsp_code, + struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, false, BT_BIP_HDR_TYPE_PUT_LINKED_ATTACHMENT, rsp_code, + buf); +} + +int bt_bip_remote_display(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_put(client, BT_BIP_HDR_TYPE_REMOTE_DISPLAY, final, buf); +} + +int bt_bip_remote_display_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, false, BT_BIP_HDR_TYPE_REMOTE_DISPLAY, rsp_code, buf); +} + +int bt_bip_delete_image(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_put(client, BT_BIP_HDR_TYPE_DELETE_IMAGE, final, buf); +} + +int bt_bip_delete_image_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, false, BT_BIP_HDR_TYPE_DELETE_IMAGE, rsp_code, buf); +} + +int bt_bip_start_print(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_put(client, BT_BIP_HDR_TYPE_START_PRINT, final, buf); +} + +int bt_bip_start_print_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, false, BT_BIP_HDR_TYPE_START_PRINT, rsp_code, buf); +} + +int bt_bip_start_archive(struct bt_bip_client *client, bool final, struct net_buf *buf) +{ + return bip_put(client, BT_BIP_HDR_TYPE_START_ARCHIVE, final, buf); +} + +int bt_bip_start_archive_rsp(struct bt_bip_server *server, uint8_t rsp_code, struct net_buf *buf) +{ + return bip_get_or_put_rsp(server, false, BT_BIP_HDR_TYPE_START_ARCHIVE, rsp_code, buf); +} + +int bt_bip_add_header_image_desc(struct net_buf *buf, uint16_t len, const uint8_t *desc) +{ + size_t total; + + if (buf == NULL || desc == NULL || len == 0) { + LOG_WRN("Invalid parameter"); + return -EINVAL; + } + + total = sizeof(uint8_t) + sizeof(len) + len; + if (net_buf_tailroom(buf) < total) { + return -ENOMEM; + } + + if (!bt_obex_string_is_valid(BT_BIP_HEADER_ID_IMG_DESC, len, desc)) { + LOG_WRN("Invalid string"); + return -EINVAL; + } + + net_buf_add_u8(buf, BT_BIP_HEADER_ID_IMG_DESC); + net_buf_add_be16(buf, (uint16_t)total); + net_buf_add_mem(buf, desc, len); + return 0; +} + +int bt_bip_add_header_image_handle(struct net_buf *buf, uint16_t len, const uint8_t *handle) +{ + size_t total; + + if (buf == NULL || handle == NULL || len == 0) { + LOG_WRN("Invalid parameter"); + return -EINVAL; + } + + total = sizeof(uint8_t) + sizeof(len) + len; + if (net_buf_tailroom(buf) < total) { + return -ENOMEM; + } + + if (!bt_obex_string_is_valid(BT_BIP_HEADER_ID_IMG_HANDLE, len, handle)) { + LOG_WRN("Invalid string"); + return -EINVAL; + } + + net_buf_add_u8(buf, BT_BIP_HEADER_ID_IMG_HANDLE); + net_buf_add_be16(buf, (uint16_t)total); + net_buf_add_mem(buf, handle, len); + return 0; +} diff --git a/subsys/bluetooth/host/classic/obex.c b/subsys/bluetooth/host/classic/obex.c index 2f30cec1d2dc9..1523b50d1f561 100644 --- a/subsys/bluetooth/host/classic/obex.c +++ b/subsys/bluetooth/host/classic/obex.c @@ -97,7 +97,7 @@ static bool bt_obex_has_header_cb(struct bt_obex_hdr *hdr, void *user_data) return true; } -static bool bt_obex_has_header(struct net_buf *buf, uint8_t id) +bool bt_obex_has_header(struct net_buf *buf, uint8_t id) { struct bt_obex_has_header data; int err; @@ -2479,7 +2479,7 @@ static bool bt_obex_unicode_is_valid(uint16_t len, const uint8_t *str) return true; } -static bool bt_obex_string_is_valid(uint8_t id, uint16_t len, const uint8_t *str) +bool bt_obex_string_is_valid(uint8_t id, uint16_t len, const uint8_t *str) { if (BT_OBEX_HEADER_ENCODING(id) == BT_OBEX_HEADER_ENCODING_UNICODE) { return bt_obex_unicode_is_valid(len, str); diff --git a/subsys/bluetooth/host/classic/obex_internal.h b/subsys/bluetooth/host/classic/obex_internal.h index a184aa15c3539..eede693958f9a 100644 --- a/subsys/bluetooth/host/classic/obex_internal.h +++ b/subsys/bluetooth/host/classic/obex_internal.h @@ -8,8 +8,6 @@ #define BT_OBEX_VERSION 0x10 -#define BT_OBEX_MIN_MTU 255 - #define BT_OBEX_OPCODE_CONNECT 0x80 #define BT_OBEX_OPCODE_DISCONN 0x81 #define BT_OBEX_OPCODE_PUT 0x02 diff --git a/subsys/bluetooth/host/classic/sdp.c b/subsys/bluetooth/host/classic/sdp.c index 1ed6e1598e54f..56ba35258c489 100644 --- a/subsys/bluetooth/host/classic/sdp.c +++ b/subsys/bluetooth/host/classic/sdp.c @@ -3537,6 +3537,61 @@ int bt_sdp_get_features(const struct net_buf *buf, uint16_t *features) return sdp_get_u16_data(&attr, features); } +static int sdp_get_u32_data(const struct bt_sdp_attr_item *attr, uint32_t *u32) +{ + const uint8_t *p; + + if (u32 == NULL) { + LOG_ERR("Invalid pointer."); + return -EINVAL; + } + + /* assert 16bit can be read safely */ + if (attr->len != (sizeof(uint8_t) + sizeof(*u32))) { + LOG_ERR("Invalid data length %u", attr->len); + return -EMSGSIZE; + } + + p = attr->val; + __ASSERT(p != NULL, "attr->val cannot be NULL"); + if (p[0] != BT_SDP_UINT32) { + LOG_ERR("Invalid DTD 0x%02x", p[0]); + return -EINVAL; + } + + *u32 = sys_get_be32(++p); + + return 0; +} + +int bt_sdp_get_functions(const struct net_buf *buf, uint32_t *functions) +{ + struct bt_sdp_attr_item attr; + int err; + + err = bt_sdp_get_attr(buf, &attr, BT_SDP_ATTR_SUPPORTED_FUNCTIONS); + if (err < 0) { + LOG_WRN("Attribute 0x%04x not found, err %d", BT_SDP_ATTR_SUPPORTED_FUNCTIONS, err); + return err; + } + + return sdp_get_u32_data(&attr, functions); +} + +int bt_sdp_get_goep_l2cap_psm(const struct net_buf *buf, uint16_t *psm) +{ + struct bt_sdp_attr_item attr; + int err; + + err = bt_sdp_get_attr(buf, &attr, BT_SDP_ATTR_GOEP_L2CAP_PSM); + if (err < 0) { + LOG_WRN("Attribute 0x%04x not found, err %d", BT_SDP_ATTR_GOEP_L2CAP_PSM, err); + return err; + } + + return sdp_get_u16_data(&attr, psm); +} + int bt_sdp_get_vendor_id(const struct net_buf *buf, uint16_t *vendor_id) { struct bt_sdp_attr_item attr; diff --git a/subsys/bluetooth/host/classic/shell/CMakeLists.txt b/subsys/bluetooth/host/classic/shell/CMakeLists.txt index 3ace702c49af6..a373d2cf619b5 100644 --- a/subsys/bluetooth/host/classic/shell/CMakeLists.txt +++ b/subsys/bluetooth/host/classic/shell/CMakeLists.txt @@ -12,3 +12,4 @@ if(CONFIG_BT_HFP_HF OR CONFIG_BT_HFP_AG) endif() zephyr_library_sources_ifdef(CONFIG_BT_GOEP goep.c) +zephyr_library_sources_ifdef(CONFIG_BT_BIP bip.c) diff --git a/subsys/bluetooth/host/classic/shell/bip.c b/subsys/bluetooth/host/classic/shell/bip.c new file mode 100644 index 0000000000000..c4c3e64199fca --- /dev/null +++ b/subsys/bluetooth/host/classic/shell/bip.c @@ -0,0 +1,4528 @@ +/** @file + * @brief Bluetooth BIP shell module + * + * Provide some Bluetooth shell commands that can be useful to applications. + */ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "host/shell/bt.h" +#include "common/bt_shell_private.h" + +#define BIP_MOPL CONFIG_BT_GOEP_RFCOMM_MTU + +NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_MAX_CONN, BT_RFCOMM_BUF_SIZE(BIP_MOPL), + CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); + +static uint8_t add_head_buffer[BIP_MOPL]; + +struct bt_bip_app { + struct bt_bip_client client; + struct bt_bip bip; + struct bt_bip_server server; + struct bt_conn *conn; + struct net_buf *tx_buf; +}; + +static struct bt_bip_app bip_app; + +static struct bt_bip_rfcomm_server rfcomm_server; +static struct bt_bip_l2cap_server l2cap_server; + +#define TLV_COUNT 6 +#define TLV_BUFFER_SIZE 64 + +static struct bt_obex_tlv tlvs[TLV_COUNT]; +static uint8_t tlv_buffers[TLV_COUNT][TLV_BUFFER_SIZE]; +static uint8_t tlv_count; + +static uint8_t bip_supported_caps; +static uint16_t bip_supported_features; +static uint32_t bip_supported_functions; +static uint64_t bip_max_memory_space = 1024; /* 1024 bytes */ + +static struct bt_sdp_attribute bip_responder_attrs[] = { + BT_SDP_NEW_SERVICE, + BT_SDP_LIST( + BT_SDP_ATTR_SVCLASS_ID_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_IMAGING_RESPONDER_SVCLASS) + }, + ) + ), + BT_SDP_LIST( + BT_SDP_ATTR_PROTO_DESC_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 17), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_PROTO_L2CAP) + }, + ) + }, + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 5), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_PROTO_RFCOMM) + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT8), + &rfcomm_server.server.rfcomm.channel + }, + ) + }, + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_PROTO_OBEX) + }, + ) + }, + ) + ), + BT_SDP_SERVICE_NAME("imaging"), + BT_SDP_LIST( + BT_SDP_ATTR_PROFILE_DESC_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 8), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_IMAGING_SVCLASS) + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + BT_SDP_ARRAY_16(0x0102) + }, + ) + }, + ) + ), + { + BT_SDP_ATTR_SUPPORTED_CAPABILITIES, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT8), + &bip_supported_caps, + }, + }, + { + BT_SDP_ATTR_SUPPORTED_FEATURES, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + &bip_supported_features, + }, + }, + { + BT_SDP_ATTR_SUPPORTED_FUNCTIONS, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT32), + &bip_supported_functions, + }, + }, + { + BT_SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT64), + &bip_max_memory_space, + }, + }, + { + BT_SDP_ATTR_GOEP_L2CAP_PSM, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + &l2cap_server.server.l2cap.psm, + }, + }, +}; + +static struct bt_sdp_record bip_responder_rec = BT_SDP_RECORD(bip_responder_attrs); + +static struct bt_bip_app *bip_alloc(struct bt_conn *conn) +{ + if (bip_app.conn != NULL) { + return NULL; + } + + bip_app.conn = conn; + return &bip_app; +} + +static void bip_free(struct bt_bip_app *bip) +{ + bip->conn = NULL; +} + +static void bip_transport_connected(struct bt_conn *conn, struct bt_bip *bip) +{ + bt_shell_print("BIP %p transport connected on %p", bip, conn); +} + +static void bip_transport_disconnected(struct bt_bip *bip) +{ + struct bt_bip_app *g_app = CONTAINER_OF(bip, struct bt_bip_app, bip); + + bip_free(g_app); + bt_shell_print("BIP %p transport disconnected", bip); +} + +static struct bt_bip_transport_ops bip_transport_ops = { + .connected = bip_transport_connected, + .disconnected = bip_transport_disconnected, +}; + +static bool bip_parse_tlvs_cb(struct bt_obex_tlv *tlv, void *user_data) +{ + bt_shell_print("T %02x L %d", tlv->type, tlv->data_len); + bt_shell_hexdump(tlv->data, tlv->data_len); + + return true; +} + +static bool bip_parse_headers_cb(struct bt_obex_hdr *hdr, void *user_data) +{ + bt_shell_print("HI %02x Len %d", hdr->id, hdr->len); + + switch (hdr->id) { + case BT_OBEX_HEADER_ID_APP_PARAM: + case BT_OBEX_HEADER_ID_AUTH_CHALLENGE: + case BT_OBEX_HEADER_ID_AUTH_RSP: { + int err; + + err = bt_obex_tlv_parse(hdr->len, hdr->data, bip_parse_tlvs_cb, NULL); + if (err != 0) { + bt_shell_error("Fail to parse BIP TLV triplet"); + } + } break; + default: + bt_shell_hexdump(hdr->data, hdr->len); + break; + } + + return true; +} + +static int bip_parse_headers(struct net_buf *buf) +{ + int err; + + if (buf == NULL) { + return 0; + } + + err = bt_obex_header_parse(buf, bip_parse_headers_cb, NULL); + if (err != 0) { + bt_shell_print("Fail to parse BIP Headers"); + } + + return err; +} + +static void bip_server_connect(struct bt_bip_server *server, uint8_t version, uint16_t mopl, + struct net_buf *buf) +{ + bt_shell_print("BIP server %p conn req, version %02x, mopl %04x", server, version, mopl); + bip_parse_headers(buf); +} + +static void bip_server_disconnect(struct bt_bip_server *server, struct net_buf *buf) +{ + bt_shell_print("BIP server %p disconn req", server); + bip_parse_headers(buf); +} + +static void bip_server_get_caps(struct bt_bip_server *server, bool final, struct net_buf *buf) +{ + bt_shell_print("BIP server %p get_caps req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static void bip_server_get_image_list(struct bt_bip_server *server, bool final, struct net_buf *buf) +{ + bt_shell_print("BIP server %p get_image_list req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static void bip_server_get_image_properties(struct bt_bip_server *server, bool final, + struct net_buf *buf) +{ + bt_shell_print("BIP server %p get_image_properties req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static void bip_server_get_image(struct bt_bip_server *server, bool final, struct net_buf *buf) +{ + bt_shell_print("BIP server %p get_image req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static void bip_server_get_linked_thumbnail(struct bt_bip_server *server, bool final, + struct net_buf *buf) +{ + bt_shell_print("BIP server %p get_linked_thumbnail req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static void bip_server_get_linked_attachment(struct bt_bip_server *server, bool final, + struct net_buf *buf) +{ + bt_shell_print("BIP server %p get_linked_attachment req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static void bip_server_get_partial_image(struct bt_bip_server *server, bool final, + struct net_buf *buf) +{ + bt_shell_print("BIP server %p get_partial_image req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static void bip_server_get_monitoring_image(struct bt_bip_server *server, bool final, + struct net_buf *buf) +{ + bt_shell_print("BIP server %p get_monitoring_image req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static void bip_server_get_status(struct bt_bip_server *server, bool final, struct net_buf *buf) +{ + bt_shell_print("BIP server %p get_status req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static bool server_put_image_final; +static void bip_server_put_image(struct bt_bip_server *server, bool final, struct net_buf *buf) +{ + server_put_image_final = final; + bt_shell_print("BIP server %p put_image req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static bool server_put_linked_thumbnail_final; +static void bip_server_put_linked_thumbnail(struct bt_bip_server *server, bool final, + struct net_buf *buf) +{ + server_put_linked_thumbnail_final = final; + bt_shell_print("BIP server %p put_linked_thumbnail req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static bool server_put_linked_attachment_final; +static void bip_server_put_linked_attachment(struct bt_bip_server *server, bool final, + struct net_buf *buf) +{ + server_put_linked_attachment_final = final; + bt_shell_print("BIP server %p put_linked_attachment req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static bool server_remote_display_final; +static void bip_server_remote_display(struct bt_bip_server *server, bool final, struct net_buf *buf) +{ + server_remote_display_final = final; + bt_shell_print("BIP server %p remote_display req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static bool server_delete_image_final; +static void bip_server_delete_image(struct bt_bip_server *server, bool final, struct net_buf *buf) +{ + server_delete_image_final = final; + bt_shell_print("BIP server %p delete_image req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static bool server_start_print_final; +static void bip_server_start_print(struct bt_bip_server *server, bool final, struct net_buf *buf) +{ + server_start_print_final = final; + bt_shell_print("BIP server %p start_print req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static bool server_start_archive_final; +static void bip_server_start_archive(struct bt_bip_server *server, bool final, struct net_buf *buf) +{ + server_start_archive_final = final; + bt_shell_print("BIP server %p start_archive req, final %s, data len %u", server, + final ? "true" : "false", buf->len); + bip_parse_headers(buf); +} + +static void bip_server_abort(struct bt_bip_server *server, struct net_buf *buf) +{ + server_put_image_final = false; + server_put_linked_thumbnail_final = false; + server_put_linked_attachment_final = false; + server_remote_display_final = false; + server_delete_image_final = false; + server_start_print_final = false; + server_start_archive_final = false; + bt_shell_print("BIP server %p abort req", server); + bip_parse_headers(buf); +} + +struct bt_bip_server_cb bip_server_cb = { + .connect = bip_server_connect, + .disconnect = bip_server_disconnect, + .abort = bip_server_abort, + .get_caps = bip_server_get_caps, + .get_image_list = bip_server_get_image_list, + .get_image_properties = bip_server_get_image_properties, + .get_image = bip_server_get_image, + .get_linked_thumbnail = bip_server_get_linked_thumbnail, + .get_linked_attachment = bip_server_get_linked_attachment, + .get_partial_image = bip_server_get_partial_image, + .get_monitoring_image = bip_server_get_monitoring_image, + .get_status = bip_server_get_status, + .put_image = bip_server_put_image, + .put_linked_thumbnail = bip_server_put_linked_thumbnail, + .put_linked_attachment = bip_server_put_linked_attachment, + .remote_display = bip_server_remote_display, + .delete_image = bip_server_delete_image, + .start_print = bip_server_start_print, + .start_archive = bip_server_start_archive, +}; + +static void bip_client_connect(struct bt_bip_client *client, uint8_t rsp_code, uint8_t version, + uint16_t mopl, struct net_buf *buf) +{ + bt_shell_print("BIP client %p conn rsp, rsp_code %s, version %02x, mopl %04x", client, + bt_obex_rsp_code_to_str(rsp_code), version, mopl); + bip_parse_headers(buf); +} + +static void bip_client_disconnect(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + bt_shell_print("BIP client %p disconn rsp, rsp_code %s", client, + bt_obex_rsp_code_to_str(rsp_code)); + bip_parse_headers(buf); +} + +static uint8_t client_get_caps_rsp_code; +static void bip_client_get_caps(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf) +{ + client_get_caps_rsp_code = rsp_code; + bt_shell_print("BIP client %p get_caps rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_get_image_list_rsp_code; +static void bip_client_get_image_list(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_get_image_list_rsp_code = rsp_code; + bt_shell_print("BIP client %p get_image_list rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_get_image_properties_rsp_code; +static void bip_client_get_image_properties(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_get_image_properties_rsp_code = rsp_code; + bt_shell_print("BIP client %p get_image_properties rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_get_image_rsp_code; +static void bip_client_get_image(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_get_image_rsp_code = rsp_code; + bt_shell_print("BIP client %p get_image rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_get_linked_thumbnail_rsp_code; +static void bip_client_get_linked_thumbnail(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_get_linked_thumbnail_rsp_code = rsp_code; + bt_shell_print("BIP client %p get_linked_thumbnail rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_get_linked_attachment_rsp_code; +static void bip_client_get_linked_attachment(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_get_linked_attachment_rsp_code = rsp_code; + bt_shell_print("BIP client %p get_linked_attachment rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_get_partial_image_rsp_code; +static void bip_client_get_partial_image(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_get_partial_image_rsp_code = rsp_code; + bt_shell_print("BIP client %p get_partial_image rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_get_monitoring_image_rsp_code; +static void bip_client_get_monitoring_image(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_get_monitoring_image_rsp_code = rsp_code; + bt_shell_print("BIP client %p get_monitoring_image rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_get_status_rsp_code; +static void bip_client_get_status(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_get_status_rsp_code = rsp_code; + bt_shell_print("BIP client %p get_status rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_put_image_rsp_code; +static bool client_put_image_final; +static void bip_client_put_image(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_put_image_rsp_code = rsp_code; + if (client_put_image_rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + client_put_image_final = false; + } + bt_shell_print("BIP client %p put_image rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_put_linked_thumbnail_rsp_code; +static bool client_put_linked_thumbnail_final; +static void bip_client_put_linked_thumbnail(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_put_linked_thumbnail_rsp_code = rsp_code; + if (client_put_linked_thumbnail_rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + client_put_linked_thumbnail_final = false; + } + bt_shell_print("BIP client %p put_linked_thumbnail rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_put_linked_attachment_rsp_code; +static bool client_put_linked_attachment_final; +static void bip_client_put_linked_attachment(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_put_linked_attachment_rsp_code = rsp_code; + if (client_put_linked_attachment_rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + client_put_linked_attachment_final = false; + } + bt_shell_print("BIP client %p put_linked_attachment rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_remote_display_rsp_code; +static bool client_remote_display_final; +static void bip_client_remote_display(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_remote_display_rsp_code = rsp_code; + if (client_remote_display_rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + client_remote_display_final = false; + } + bt_shell_print("BIP client %p remote_display rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_delete_image_rsp_code; +static bool client_delete_image_final; +static void bip_client_delete_image(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_delete_image_rsp_code = rsp_code; + if (client_delete_image_rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + client_delete_image_final = false; + } + bt_shell_print("BIP client %p delete_image rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_start_print_rsp_code; +static bool client_start_print_final; +static void bip_client_start_print(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_start_print_rsp_code = rsp_code; + if (client_start_print_rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + client_start_print_final = false; + } + bt_shell_print("BIP client %p start_print rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static uint8_t client_start_archive_rsp_code; +static bool client_start_archive_final; +static void bip_client_start_archive(struct bt_bip_client *client, uint8_t rsp_code, + struct net_buf *buf) +{ + client_start_archive_rsp_code = rsp_code; + if (client_start_archive_rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + client_start_archive_final = false; + } + bt_shell_print("BIP client %p start_archive rsp, rsp_code %s, data len %u", client, + bt_obex_rsp_code_to_str(rsp_code), buf->len); + bip_parse_headers(buf); +} + +static void bip_client_abort(struct bt_bip_client *client, uint8_t rsp_code, struct net_buf *buf) +{ + client_put_image_final = false; + client_put_linked_thumbnail_final = false; + client_put_linked_attachment_final = false; + client_remote_display_final = false; + client_delete_image_final = false; + client_start_print_final = false; + client_start_archive_final = false; + + client_get_caps_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_get_image_list_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_get_image_properties_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_get_image_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_get_linked_thumbnail_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_get_linked_attachment_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_get_partial_image_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_get_monitoring_image_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_get_status_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_put_image_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_put_linked_thumbnail_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_put_linked_attachment_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_remote_display_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_delete_image_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_start_print_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + client_start_archive_rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + + bt_shell_print("BIP client %p abort rsp, rsp_code %s", client, + bt_obex_rsp_code_to_str(rsp_code)); + bip_parse_headers(buf); +} + +struct bt_bip_client_cb bip_client_cb = { + .connect = bip_client_connect, + .disconnect = bip_client_disconnect, + .abort = bip_client_abort, + .get_caps = bip_client_get_caps, + .get_image_list = bip_client_get_image_list, + .get_image_properties = bip_client_get_image_properties, + .get_image = bip_client_get_image, + .get_linked_thumbnail = bip_client_get_linked_thumbnail, + .get_linked_attachment = bip_client_get_linked_attachment, + .get_partial_image = bip_client_get_partial_image, + .get_monitoring_image = bip_client_get_monitoring_image, + .get_status = bip_client_get_status, + .put_image = bip_client_put_image, + .put_linked_thumbnail = bip_client_put_linked_thumbnail, + .put_linked_attachment = bip_client_put_linked_attachment, + .remote_display = bip_client_remote_display, + .delete_image = bip_client_delete_image, + .start_print = bip_client_start_print, + .start_archive = bip_client_start_archive, +}; + +static int rfcomm_accept(struct bt_conn *conn, struct bt_bip_rfcomm_server *server, + struct bt_bip **bip) +{ + struct bt_bip_app *g_app; + + g_app = bip_alloc(conn); + if (g_app == NULL) { + bt_shell_print("Cannot allocate bip instance"); + return -ENOMEM; + } + + g_app->bip.ops = &bip_transport_ops; + *bip = &g_app->bip; + return 0; +} + +static int cmd_register_rfcomm(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + uint8_t channel; + + if (rfcomm_server.server.rfcomm.channel) { + shell_error(sh, "RFCOMM has been registered"); + return -EBUSY; + } + + channel = (uint8_t)strtoul(argv[1], NULL, 16); + + rfcomm_server.server.rfcomm.channel = channel; + rfcomm_server.accept = rfcomm_accept; + err = bt_bip_rfcomm_register(&rfcomm_server); + if (err != 0) { + shell_error(sh, "Fail to register RFCOMM server (error %d)", err); + rfcomm_server.server.rfcomm.channel = 0; + return -ENOEXEC; + } + shell_print(sh, "RFCOMM server (channel %02x) is registered", + rfcomm_server.server.rfcomm.channel); + return 0; +} + +static int cmd_connect_rfcomm(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + struct bt_bip_app *g_app; + uint8_t channel; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + channel = (uint8_t)strtoul(argv[1], NULL, 16); + if (channel == 0) { + shell_error(sh, "Invalid channel"); + return -ENOEXEC; + } + + g_app = bip_alloc(default_conn); + if (g_app == NULL) { + shell_error(sh, "Cannot allocate bip instance"); + return -ENOMEM; + } + + g_app->bip.ops = &bip_transport_ops; + + err = bt_bip_rfcomm_connect(default_conn, &g_app->bip, channel); + if (err != 0) { + bip_free(g_app); + shell_error(sh, "Fail to connect to channel %d (err %d)", channel, err); + } else { + shell_print(sh, "BIP RFCOMM connection pending"); + } + + return err; +} + +static int cmd_disconnect_rfcomm(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + err = bt_bip_rfcomm_disconnect(&bip_app.bip); + if (err != 0) { + shell_error(sh, "Fail to disconnect to channel (err %d)", err); + } else { + shell_print(sh, "BIP RFCOMM disconnection pending"); + } + return err; +} + +static int l2cap_accept(struct bt_conn *conn, struct bt_bip_l2cap_server *server, + struct bt_bip **bip) +{ + struct bt_bip_app *g_app; + + g_app = bip_alloc(conn); + if (g_app == NULL) { + bt_shell_print("Cannot allocate bip instance"); + return -ENOMEM; + } + + g_app->bip.ops = &bip_transport_ops; + *bip = &g_app->bip; + return 0; +} + +static int cmd_register_l2cap(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + uint16_t psm; + + if (l2cap_server.server.l2cap.psm) { + shell_error(sh, "L2CAP server has been registered"); + return -EBUSY; + } + + psm = (uint16_t)strtoul(argv[1], NULL, 16); + + l2cap_server.server.l2cap.psm = psm; + l2cap_server.accept = l2cap_accept; + err = bt_bip_l2cap_register(&l2cap_server); + if (err != 0) { + shell_error(sh, "Fail to register L2CAP server (error %d)", err); + l2cap_server.server.l2cap.psm = 0; + return -ENOEXEC; + } + shell_print(sh, "L2CAP server (psm %04x) is registered", l2cap_server.server.l2cap.psm); + return 0; +} + +static int cmd_connect_l2cap(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + struct bt_bip_app *g_app; + uint16_t psm; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + psm = (uint16_t)strtoul(argv[1], NULL, 16); + if (psm == 0) { + shell_error(sh, "Invalid psm"); + return -ENOEXEC; + } + + g_app = bip_alloc(default_conn); + if (g_app == NULL) { + shell_error(sh, "Cannot allocate bip instance"); + return -ENOMEM; + } + + g_app->bip.ops = &bip_transport_ops; + + err = bt_bip_l2cap_connect(default_conn, &g_app->bip, psm); + if (err != 0) { + bip_free(g_app); + shell_error(sh, "Fail to connect to PSM %d (err %d)", psm, err); + } else { + shell_print(sh, "BIP L2CAP connection pending"); + } + + return err; +} + +static int cmd_disconnect_l2cap(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + err = bt_bip_l2cap_disconnect(&bip_app.bip); + if (err != 0) { + shell_error(sh, "Fail to disconnect L2CAP conn (err %d)", err); + } else { + shell_print(sh, "BIP L2CAP disconnection pending"); + } + return err; +} + +static int cmd_add_header_count(const struct shell *sh, size_t argc, char *argv[]) +{ + uint32_t count; + int err; + + count = strtoul(argv[1], NULL, 16); + + err = bt_obex_add_header_count(bip_app.tx_buf, count); + if (err != 0) { + shell_error(sh, "Fail to add header count"); + } + return err; +} + +static int cmd_add_header_name(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload; + size_t hex_payload_size; + + if (argc > 1) { + hex_payload = argv[1]; + hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, + sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + } else { + len = 0; + } + + err = bt_obex_add_header_name(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header name"); + } + return err; +} + +static int cmd_add_header_type(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header type"); + } + return err; +} + +static int cmd_add_header_len(const struct shell *sh, size_t argc, char *argv[]) +{ + uint32_t len; + int err; + + len = strtoul(argv[1], NULL, 16); + + err = bt_obex_add_header_len(bip_app.tx_buf, len); + if (err != 0) { + shell_error(sh, "Fail to add header len"); + } + return err; +} + +static int cmd_add_header_time_iso_8601(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_time_iso_8601(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header time_iso_8601"); + } + return err; +} + +static int cmd_add_header_time(const struct shell *sh, size_t argc, char *argv[]) +{ + uint32_t t; + int err; + + t = strtoul(argv[1], NULL, 16); + + err = bt_obex_add_header_time(bip_app.tx_buf, t); + if (err != 0) { + shell_error(sh, "Fail to add header time"); + } + return err; +} + +static int cmd_add_header_description(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_description(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header description"); + } + return err; +} + +static int cmd_add_header_target(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_target(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header target"); + } + return err; +} + +static int cmd_add_header_http(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_http(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header http"); + } + return err; +} + +static int cmd_add_header_body(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_body(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header body"); + } + return err; +} + +static int cmd_add_header_end_body(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_end_body(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header end_body"); + } + return err; +} + +static int cmd_add_header_who(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_who(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header who"); + } + return err; +} + +static int cmd_add_header_conn_id(const struct shell *sh, size_t argc, char *argv[]) +{ + uint32_t conn_id; + int err; + + conn_id = strtoul(argv[1], NULL, 16); + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, conn_id); + if (err != 0) { + shell_error(sh, "Fail to add header conn_id"); + } + return err; +} + +static int cmd_add_header_app_param(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + uint8_t tag; + bool last = false; + + if (tlv_count >= (uint8_t)ARRAY_SIZE(tlvs)) { + shell_warn(sh, "No space of TLV array, add app_param and clear tlvs"); + goto add_header; + } + + len = hex2bin(argv[1], strlen(argv[1]), &tag, sizeof(tag)); + if (len < 1) { + shell_error(sh, "Length should not be zero"); + return -ENOEXEC; + } + + len = hex2bin(argv[2], strlen(argv[2]), &tlv_buffers[tlv_count][0], TLV_BUFFER_SIZE); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + if ((argc > 3) && !strcmp(argv[3], "last")) { + last = true; + } + + tlvs[tlv_count].type = tag; + tlvs[tlv_count].data_len = len; + tlvs[tlv_count].data = &tlv_buffers[tlv_count][0]; + + tlv_count++; + + if (!last) { + return 0; + } + +add_header: + err = bt_obex_add_header_app_param(bip_app.tx_buf, (size_t)tlv_count, tlvs); + if (err != 0) { + shell_error(sh, "Fail to add header app_param"); + } + tlv_count = 0; + return err; +} + +static int cmd_add_header_auth_challenge(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + uint8_t tag; + bool last = false; + + if (tlv_count >= (uint8_t)ARRAY_SIZE(tlvs)) { + shell_warn(sh, "No space of TLV array, add auth_challenge and clear tlvs"); + goto add_header; + } + + len = hex2bin(argv[1], strlen(argv[1]), &tag, sizeof(tag)); + if (len < 1) { + shell_error(sh, "Length should not be zero"); + return -ENOEXEC; + } + + len = hex2bin(argv[2], strlen(argv[2]), &tlv_buffers[tlv_count][0], TLV_BUFFER_SIZE); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + if ((argc > 3) && !strcmp(argv[3], "last")) { + last = true; + } + + tlvs[tlv_count].type = tag; + tlvs[tlv_count].data_len = len; + tlvs[tlv_count].data = &tlv_buffers[tlv_count][0]; + + tlv_count++; + + if (!last) { + return 0; + } + +add_header: + err = bt_obex_add_header_auth_challenge(bip_app.tx_buf, (size_t)tlv_count, tlvs); + if (err != 0) { + shell_error(sh, "Fail to add header auth_challenge"); + } + tlv_count = 0; + return err; +} + +static int cmd_add_header_auth_rsp(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + uint8_t tag; + bool last = false; + + if (tlv_count >= (uint8_t)ARRAY_SIZE(tlvs)) { + shell_warn(sh, "No space of TLV array, add auth_rsp and clear tlvs"); + goto add_header; + } + + len = hex2bin(argv[1], strlen(argv[1]), &tag, sizeof(tag)); + if (len < 1) { + shell_error(sh, "Length should not be zero"); + return -ENOEXEC; + } + + len = hex2bin(argv[2], strlen(argv[2]), &tlv_buffers[tlv_count][0], TLV_BUFFER_SIZE); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + if ((argc > 3) && !strcmp(argv[3], "last")) { + last = true; + } + + tlvs[tlv_count].type = tag; + tlvs[tlv_count].data_len = len; + tlvs[tlv_count].data = &tlv_buffers[tlv_count][0]; + + tlv_count++; + + if (!last) { + return 0; + } + +add_header: + err = bt_obex_add_header_auth_rsp(bip_app.tx_buf, (size_t)tlv_count, tlvs); + if (err != 0) { + shell_error(sh, "Fail to add header auth_rsp"); + } + tlv_count = 0; + return err; +} + +static int cmd_add_header_creator_id(const struct shell *sh, size_t argc, char *argv[]) +{ + uint32_t creator_id; + int err; + + creator_id = strtoul(argv[1], NULL, 16); + + err = bt_obex_add_header_creator_id(bip_app.tx_buf, creator_id); + if (err != 0) { + shell_error(sh, "Fail to add header creator_id"); + } + return err; +} + +static int cmd_add_header_wan_uuid(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_wan_uuid(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header wan_uuid"); + } + return err; +} + +static int cmd_add_header_obj_class(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_obj_class(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header obj_class"); + } + return err; +} + +static int cmd_add_header_session_param(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_session_param(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header session_param"); + } + return err; +} + +static int cmd_add_header_session_seq_number(const struct shell *sh, size_t argc, char *argv[]) +{ + uint32_t session_seq_number; + int err; + + session_seq_number = strtoul(argv[1], NULL, 16); + + err = bt_obex_add_header_session_seq_number(bip_app.tx_buf, session_seq_number); + if (err != 0) { + shell_error(sh, "Fail to add header session_seq_number"); + } + return err; +} + +static int cmd_add_header_action_id(const struct shell *sh, size_t argc, char *argv[]) +{ + uint32_t action_id; + int err; + + action_id = strtoul(argv[1], NULL, 16); + + err = bt_obex_add_header_action_id(bip_app.tx_buf, action_id); + if (err != 0) { + shell_error(sh, "Fail to add header action_id"); + } + return err; +} + +static int cmd_add_header_dest_name(const struct shell *sh, size_t argc, char *argv[]) +{ + size_t len; + int err; + const char *hex_payload = argv[1]; + size_t hex_payload_size = strlen(hex_payload); + + len = hex2bin(hex_payload, hex_payload_size, add_head_buffer, sizeof(add_head_buffer)); + if (len > UINT16_MAX) { + shell_error(sh, "Length exceeds max length (%x > %x)", len, UINT16_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_dest_name(bip_app.tx_buf, (uint16_t)len, add_head_buffer); + if (err != 0) { + shell_error(sh, "Fail to add header dest_name"); + } + return err; +} + +static int cmd_add_header_perm(const struct shell *sh, size_t argc, char *argv[]) +{ + uint32_t perm; + int err; + + perm = strtoul(argv[1], NULL, 16); + + err = bt_obex_add_header_perm(bip_app.tx_buf, perm); + if (err != 0) { + shell_error(sh, "Fail to add header perm"); + } + return err; +} + +static int cmd_add_header_srm(const struct shell *sh, size_t argc, char *argv[]) +{ + uint32_t srm; + int err; + + srm = strtoul(argv[1], NULL, 16); + + if (srm > UINT8_MAX) { + shell_error(sh, "Value exceeds max value (%x > %x)", srm, UINT8_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_srm(bip_app.tx_buf, (uint8_t)srm); + if (err != 0) { + shell_error(sh, "Fail to add header srm"); + } + return err; +} + +static int cmd_add_header_srm_param(const struct shell *sh, size_t argc, char *argv[]) +{ + uint32_t srm_param; + int err; + + srm_param = strtoul(argv[1], NULL, 16); + + if (srm_param > UINT8_MAX) { + shell_error(sh, "Value exceeds max value (%x > %x)", srm_param, UINT8_MAX); + return -ENOEXEC; + } + + err = bt_obex_add_header_srm_param(bip_app.tx_buf, (uint8_t)srm_param); + if (err != 0) { + shell_error(sh, "Fail to add header srm_param"); + } + return err; +} + +static int cmd_bip_client_set_feats_funcs(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + + err = bt_bip_set_supported_features(&bip_app.bip, bip_supported_features); + if (err != 0) { + shell_error(sh, "Failed to set BIP supported features"); + } + + err = bt_bip_set_supported_functions(&bip_app.bip, bip_supported_functions); + if (err != 0) { + shell_error(sh, "Failed to set BIP supported functions"); + } + + return 0; +} + +static int cmd_bip_client_conn(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + uint8_t uuid128[BT_UUID_SIZE_128]; + struct bt_uuid_128 *u = NULL; + uint8_t type; + + static struct bt_uuid_128 uuid; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + err = 0; + type = shell_strtoul(argv[1], 0, &err); + if (err != 0) { + shell_error(sh, "Invalid type %s", argv[1]); + return -ENOEXEC; + } + + if (argc > 2) { + hex2bin(argv[2], strlen(argv[2]), uuid128, sizeof(uuid128)); + uuid.uuid.type = BT_UUID_TYPE_128; + sys_memcpy_swap(uuid.val, uuid128, BT_UUID_SIZE_128); + u = &uuid; + } + + err = bt_bip_primary_client_connect(&bip_app.bip, &bip_app.client, type, &bip_client_cb, + bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send conn req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_client_disconn(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + err = bt_bip_disconnect(&bip_app.client, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send disconn req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_client_abort(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + err = bt_bip_abort(&bip_app.client, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send abort req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_client_get_caps(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_get_caps_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_GET_CAPS), + BT_BIP_HDR_TYPE_GET_CAPS); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + +continue_req: + err = bt_bip_get_capabilities(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get caps req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +#define IMAGE_HANDLES_DESC \ + "" \ + "" \ + "" + +static int cmd_bip_client_get_image_list(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + uint16_t returned_handles = sys_cpu_to_be16(1); + uint16_t list_start_offset = sys_cpu_to_be16(0); + uint8_t latest_captured = 0; + struct bt_obex_tlv appl_params[] = { + {BT_BIP_APPL_PARAM_TAG_ID_RETURNED_HANDLES, sizeof(returned_handles), + (const uint8_t *)&returned_handles}, + {BT_BIP_APPL_PARAM_TAG_ID_LIST_START_OFFSET, sizeof(list_start_offset), + (const uint8_t *)&list_start_offset}, + {BT_BIP_APPL_PARAM_TAG_ID_LATEST_CAPTURED_IMAGES, sizeof(latest_captured), + (const uint8_t *)&latest_captured}, + }; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_get_image_list_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_GET_IMAGE_LIST), + BT_BIP_HDR_TYPE_GET_IMAGE_LIST); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_obex_add_header_app_param(bip_app.tx_buf, ARRAY_SIZE(appl_params), appl_params); + if (err != 0) { + shell_error(sh, "Fail to add appl param header %d", err); + return err; + } + + err = bt_bip_add_header_image_desc(bip_app.tx_buf, sizeof(IMAGE_HANDLES_DESC), + IMAGE_HANDLES_DESC); + if (err != 0) { + shell_error(sh, "Fail to add image desc header %d", err); + return err; + } + +continue_req: + err = bt_bip_get_image_list(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get image list req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +#define IMAGE_HANDLE "\x00\x31\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x31\x00\x00" + +static int cmd_bip_client_get_image_properties(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_get_image_properties_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_GET_IMAGE_PROPERTIES), + BT_BIP_HDR_TYPE_GET_IMAGE_PROPERTIES); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_bip_add_header_image_handle(bip_app.tx_buf, sizeof(IMAGE_HANDLE) - 1, + IMAGE_HANDLE); + if (err != 0) { + shell_error(sh, "Fail to add image handle header %d", err); + return err; + } + +continue_req: + err = bt_bip_get_image_properties(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_image_properties req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +#define IMAGE_DESC \ + "" \ + "" \ + "" + +static int cmd_bip_client_get_image(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_get_image_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_GET_IMAGE), + BT_BIP_HDR_TYPE_GET_IMAGE); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_bip_add_header_image_handle(bip_app.tx_buf, sizeof(IMAGE_HANDLE) - 1, + IMAGE_HANDLE); + if (err != 0) { + shell_error(sh, "Fail to add image handle header %d", err); + return err; + } + + err = bt_bip_add_header_image_desc(bip_app.tx_buf, sizeof(IMAGE_DESC), IMAGE_DESC); + if (err != 0) { + shell_error(sh, "Fail to add image descriptor header %d", err); + return err; + } + +continue_req: + err = bt_bip_get_image(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_image req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_client_get_linked_thumbnail(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_get_linked_thumbnail_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_GET_LINKED_THUMBNAIL), + BT_BIP_HDR_TYPE_GET_LINKED_THUMBNAIL); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_bip_add_header_image_handle(bip_app.tx_buf, sizeof(IMAGE_HANDLE) - 1, + IMAGE_HANDLE); + if (err != 0) { + shell_error(sh, "Fail to add image handle header %d", err); + return err; + } + +continue_req: + err = bt_bip_get_linked_thumbnail(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_linked_thumbnail req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +#define IMAGE_ATTCHMENT_FILE_NAME "\x00\x31\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x31\x00\x00" + +static int cmd_bip_client_get_linked_attachment(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_get_linked_attachment_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_GET_LINKED_ATTACHMENT), + BT_BIP_HDR_TYPE_GET_LINKED_ATTACHMENT); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_bip_add_header_image_handle(bip_app.tx_buf, sizeof(IMAGE_HANDLE) - 1, + IMAGE_HANDLE); + if (err != 0) { + shell_error(sh, "Fail to add image handle header %d", err); + return err; + } + + err = bt_obex_add_header_name(bip_app.tx_buf, sizeof(IMAGE_ATTCHMENT_FILE_NAME) - 1, + IMAGE_ATTCHMENT_FILE_NAME); + if (err != 0) { + shell_error(sh, "Fail to add image name header %d", err); + return err; + } + +continue_req: + err = bt_bip_get_linked_attachment(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_linked_attachment req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +#define IMAGE_PARTIAL_FILE_NAME "\x00\x31\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x31\x00\x00" + +static int cmd_bip_client_get_partial_image(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + uint32_t partial_file_len = 0xffffffff; + uint32_t partial_file_start_offset = 0; + struct bt_obex_tlv appl_params[] = { + {BT_BIP_APPL_PARAM_TAG_ID_PARTIAL_FILE_LEN, sizeof(partial_file_len), + (const uint8_t *)&partial_file_len}, + {BT_BIP_APPL_PARAM_TAG_ID_PARTIAL_FILE_START_OFFSET, + sizeof(partial_file_start_offset), (const uint8_t *)&partial_file_start_offset}, + }; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_GET_PARTIAL_IMAGE), + BT_BIP_HDR_TYPE_GET_PARTIAL_IMAGE); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_obex_add_header_name(bip_app.tx_buf, sizeof(IMAGE_PARTIAL_FILE_NAME) - 1, + IMAGE_PARTIAL_FILE_NAME); + if (err != 0) { + shell_error(sh, "Fail to add image name header %d", err); + return err; + } + + err = bt_obex_add_header_app_param(bip_app.tx_buf, ARRAY_SIZE(appl_params), appl_params); + if (err != 0) { + shell_error(sh, "Fail to add app param header %d", err); + return err; + } + + if (client_get_partial_image_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + +continue_req: + err = bt_bip_get_partial_image(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_partial_image req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_client_get_monitoring_image(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + uint8_t store_flag = 0; + struct bt_obex_tlv appl_params[] = { + {BT_BIP_APPL_PARAM_TAG_ID_STORE_FLAG, sizeof(store_flag), + (const uint8_t *)&store_flag}, + }; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_get_monitoring_image_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_GET_MONITORING_IMAGE), + BT_BIP_HDR_TYPE_GET_MONITORING_IMAGE); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_obex_add_header_app_param(bip_app.tx_buf, ARRAY_SIZE(appl_params), appl_params); + if (err != 0) { + shell_error(sh, "Fail to add app param header %d", err); + return err; + } + +continue_req: + err = bt_bip_get_monitoring_image(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_monitoring_image req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_client_get_status(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_GET_STATUS), + BT_BIP_HDR_TYPE_GET_STATUS); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + if (client_get_status_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + +continue_req: + err = bt_bip_get_status(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_status req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +#define IMAGE_PUT_FILE_NAME "\x00\x31\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x31\x00\x00" + +/* Minimal 1x1 pixel white JPEG image data */ +static const uint8_t jpeg_1000001[] = { + /* SOI (Start of Image) */ + 0xFF, 0xD8, + + /* APP0 segment */ + 0xFF, 0xE0, /* APP0 marker */ + 0x00, 0x10, /* Length (16 bytes) */ + 0x4A, 0x46, 0x49, 0x46, 0x00, /* "JFIF\0" */ + 0x01, 0x01, /* Version 1.1 */ + 0x01, /* Density units (1 = pixels per inch) */ + 0x00, 0x48, /* X density (72) */ + 0x00, 0x48, /* Y density (72) */ + 0x00, 0x00, /* Thumbnail width and height (0,0 = no thumbnail) */ + + /* DQT (Define Quantization Table) - Luminance quantization table */ + 0xFF, 0xDB, /* DQT marker */ + 0x00, 0x43, /* Length (67 bytes) */ + 0x00, /* Table ID (0) and precision (8bit) */ + /* 8x8 quantization table (simplified version) */ + 0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E, 0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, + 0x28, 0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, + 0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, + 0x6D, 0x51, 0x57, 0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79, 0x70, 0x64, 0x78, + 0x5C, 0x65, 0x67, 0x63, + + /* SOF0 (Start of Frame - Baseline DCT) */ + 0xFF, 0xC0, /* SOF0 marker */ + 0x00, 0x11, /* Length (17 bytes) */ + 0x08, /* Data precision (8 bits) */ + 0x00, 0x01, /* Image height (1) */ + 0x00, 0x01, /* Image width (1) */ + 0x01, /* Number of color components (1 = grayscale) */ + 0x01, /* Component ID (1) */ + 0x11, /* Sampling factors (1x1) */ + 0x00, /* Quantization table selector (0) */ + + /* DHT (Define Huffman Table) - DC table */ + 0xFF, 0xC4, /* DHT marker */ + 0x00, 0x1F, /* Length (31 bytes) */ + 0x00, /* Table class (0=DC) and table ID (0) */ + /* Symbol length counts (16 bytes) */ + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* Symbol values (12 bytes) */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + + /* DHT (Define Huffman Table) - AC table */ + 0xFF, 0xC4, /* DHT marker */ + 0x00, 0xB5, /* Length (181 bytes) */ + 0x10, /* Table class (1=AC) and table ID (0) */ + /* AC Huffman table data (simplified version) */ + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, + 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, + 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, + 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, + 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, + 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, + 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, + + /* SOS (Start of Scan) */ + 0xFF, 0xDA, /* SOS marker */ + 0x00, 0x08, /* Length (8 bytes) */ + 0x01, /* Number of components (1) */ + 0x01, /* Component ID (1) */ + 0x00, /* DC and AC Huffman table selectors */ + 0x00, /* Spectral selection start */ + 0x3F, /* Spectral selection end */ + 0x00, /* Successive approximation bit position */ + + /* Image data (encoded data for 1 white pixel) */ + 0xFF, 0x00, /* Stuffed DC coefficient (white) */ + + /* EOI (End of Image) */ + 0xFF, 0xD9 +}; + +static int cmd_bip_client_put_image(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + uint16_t len = 0; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_put_image_final) { + goto continue_req; + } + + if (client_put_image_rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + offset = 0; + } + + if (offset == 0) { + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_PUT_IMAGE), + BT_BIP_HDR_TYPE_PUT_IMAGE); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_obex_add_header_name(bip_app.tx_buf, sizeof(IMAGE_PUT_FILE_NAME) - 1, + IMAGE_PUT_FILE_NAME); + if (err != 0) { + shell_error(sh, "Fail to add name header %d", err); + return err; + } + + err = bt_bip_add_header_image_desc(bip_app.tx_buf, sizeof(IMAGE_DESC), IMAGE_DESC); + if (err != 0) { + shell_error(sh, "Fail to add image desc header %d", err); + return err; + } + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.client._client.tx.mopl, + sizeof(jpeg_1000001) - offset, + jpeg_1000001 + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + client_put_image_final = true; + } + +continue_req: + err = bt_bip_put_image(&bip_app.client, client_put_image_final, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send put_image req %d", err); + } else { + bip_app.tx_buf = NULL; + if (!client_put_image_final) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +static int cmd_bip_client_put_linked_thumbnail(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + uint16_t len = 0; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_put_linked_thumbnail_final) { + goto continue_req; + } + + if (client_put_linked_thumbnail_rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + offset = 0; + } + + if (offset == 0) { + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, + sizeof(BT_BIP_HDR_TYPE_PUT_LINKED_THUMBNAIL), + BT_BIP_HDR_TYPE_PUT_LINKED_THUMBNAIL); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_bip_add_header_image_handle(bip_app.tx_buf, sizeof(IMAGE_HANDLE) - 1, + IMAGE_HANDLE); + if (err != 0) { + shell_error(sh, "Fail to add image handle header %d", err); + return err; + } + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.client._client.tx.mopl, + sizeof(jpeg_1000001) - offset, + jpeg_1000001 + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + client_put_linked_thumbnail_final = true; + } + +continue_req: + err = bt_bip_put_linked_thumbnail(&bip_app.client, client_put_linked_thumbnail_final, + bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send put_linked_thumbnail req %d", err); + } else { + bip_app.tx_buf = NULL; + if (!client_put_linked_thumbnail_final) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +static int cmd_bip_client_put_linked_attachment(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + uint16_t len = 0; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_put_linked_attachment_final) { + goto continue_req; + } + + if (client_put_linked_attachment_rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + offset = 0; + } + + if (offset == 0) { + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, + sizeof(BT_BIP_HDR_TYPE_PUT_LINKED_ATTACHMENT), + BT_BIP_HDR_TYPE_PUT_LINKED_ATTACHMENT); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_bip_add_header_image_handle(bip_app.tx_buf, sizeof(IMAGE_HANDLE) - 1, + IMAGE_HANDLE); + if (err != 0) { + shell_error(sh, "Fail to add image handle header %d", err); + return err; + } + + err = bt_bip_add_header_image_desc(bip_app.tx_buf, sizeof(IMAGE_DESC), IMAGE_DESC); + if (err != 0) { + shell_error(sh, "Fail to add image descriptor header %d", err); + return err; + } + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.client._client.tx.mopl, + sizeof(jpeg_1000001) - offset, + jpeg_1000001 + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + client_put_linked_attachment_final = true; + } + +continue_req: + err = bt_bip_put_linked_attachment(&bip_app.client, client_put_linked_attachment_final, + bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send put_linked_attachment req %d", err); + } else { + bip_app.tx_buf = NULL; + if (!client_put_linked_attachment_final) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +static int cmd_bip_client_remote_display(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + uint8_t remote_display_id = 0x04; + + struct bt_obex_tlv appl_params[] = { + {BT_BIP_APPL_PARAM_TAG_ID_REMOTE_DISPLAY, sizeof(remote_display_id), + (const uint8_t *)&remote_display_id}, + }; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_remote_display_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_REMOTE_DISPLAY), + BT_BIP_HDR_TYPE_REMOTE_DISPLAY); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_bip_add_header_image_handle(bip_app.tx_buf, sizeof(IMAGE_HANDLE) - 1, + IMAGE_HANDLE); + if (err != 0) { + shell_error(sh, "Fail to add image handle header %d", err); + return err; + } + + err = bt_obex_add_header_app_param(bip_app.tx_buf, ARRAY_SIZE(appl_params), appl_params); + if (err != 0) { + shell_error(sh, "Fail to add app param header %d", err); + return err; + } + +continue_req: + err = bt_bip_remote_display(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send remote_display req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_client_delete_image(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_delete_image_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_DELETE_IMAGE), + BT_BIP_HDR_TYPE_DELETE_IMAGE); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_bip_add_header_image_handle(bip_app.tx_buf, sizeof(IMAGE_HANDLE) - 1, + IMAGE_HANDLE); + if (err != 0) { + shell_error(sh, "Fail to add image handle header %d", err); + return err; + } + +continue_req: + err = bt_bip_delete_image(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send delete_image req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +#define PRINT_CONTROL_OBJECT \ + "[HDR]" \ + "GEN REV = 01.10" \ + "GEN CRT = \"Bluetooth camera\" -01.00" \ + "GEN DTM = 2001:01:01:12:00:00" \ + "[JOB]" \ + "PRT PID = 001" \ + "PRT TYP = STD" \ + "PRT QTY = 001" \ + "IMG FMT = EXIF2 -J" \ + "" \ + "CFG DSC = \"100-0001\" -ATR FID" \ + "[JOB]" \ + "PRT PID = 002" \ + "PRT TYP = STD" \ + "PRT QTY = 002" \ + "IMG FMT = EXIF2 -J" \ + "" \ + "CFG DSC = \"2000.12.24\" -ATR DTM" \ + "CFG DSC = \"100-0002\" -ATR FID" \ + "[JOB]" \ + "PRT PID = 003" \ + "PRT TYP = STD" \ + "PRT QTY = 001" \ + "IMG FMT = EXIF2 -J" \ + "" \ + "CFG DSC = \"2000.12.25\" -ATR DTM" \ + "CFG DSC = \"100-0003\" -ATR FID" \ + "[JOB]" \ + "PRT PID = 004" \ + "PRT TYP = STD" \ + "PRT QTY = 003" \ + "IMG FMT = EXIF2 -J" \ + "" \ + "CFG DSC = \"102-0001\" -ATR FID" \ + "[JOB]" \ + "PRT PID = 100" \ + "PRT TYP = IDX" \ + "PRT QTY = 001" \ + "IMG FMT = EXIF2 -J" \ + "IMG SRC = \"../DCIM/100ABCDE/ABCD0001.JPG\"" \ + "IMG SRC = \"../DCIM/100ABCDE/ABCD0002.JPG\"" \ + "IMG SRC = \"../DCIM/100ABCDE/ABCD0003.JPG\"" \ + "IMG SRC = \"../DCIM/102_BLUE/BLUE0001.JPG\"" + +static int cmd_bip_client_start_print(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + uint8_t uuid_128[] = { + BT_UUID_128_ENCODE(0x8E61F95D, 0x1A79, 0x11D4, 0x8EA4, 0x00805F9B9834)}; + struct bt_obex_tlv appl_params[] = { + {BT_BIP_APPL_PARAM_TAG_ID_STORE_FLAG, sizeof(uuid_128), (const uint8_t *)uuid_128}, + }; + uint16_t len = 0; + const uint8_t *print_control_obj = (const uint8_t *)PRINT_CONTROL_OBJECT; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_start_print_final) { + goto continue_req; + } + + if (client_start_print_rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { + offset = 0; + } + + if (offset == 0) { + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_START_PRINT), + BT_BIP_HDR_TYPE_START_PRINT); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_obex_add_header_app_param(bip_app.tx_buf, ARRAY_SIZE(appl_params), + appl_params); + if (err != 0) { + shell_error(sh, "Fail to add app param header %d", err); + return err; + } + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.client._client.tx.mopl, + sizeof(PRINT_CONTROL_OBJECT) - offset, + print_control_obj + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + client_start_print_final = true; + } + +continue_req: + err = bt_bip_start_print(&bip_app.client, client_start_print_final, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send start_print req %d", err); + } else { + bip_app.tx_buf = NULL; + if (!client_start_print_final) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +static int cmd_bip_client_start_archive(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + uint8_t uuid_128[] = { + BT_UUID_128_ENCODE(0x8E61F95E, 0x1A79, 0x11D4, 0x8EA4, 0x00805F9B9834)}; + struct bt_obex_tlv appl_params[] = { + {BT_BIP_APPL_PARAM_TAG_ID_STORE_FLAG, sizeof(uuid_128), (const uint8_t *)uuid_128}, + }; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (client_start_archive_rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + goto continue_req; + } + + err = bt_obex_add_header_conn_id(bip_app.tx_buf, bip_app.client._conn_id); + if (err != 0) { + shell_error(sh, "Fail to add conn id header %d", err); + return err; + } + + err = bt_obex_add_header_type(bip_app.tx_buf, sizeof(BT_BIP_HDR_TYPE_START_ARCHIVE), + BT_BIP_HDR_TYPE_START_ARCHIVE); + if (err != 0) { + shell_error(sh, "Fail to add type header %d", err); + return err; + } + + err = bt_obex_add_header_app_param(bip_app.tx_buf, ARRAY_SIZE(appl_params), appl_params); + if (err != 0) { + shell_error(sh, "Fail to add app param header %d", err); + return err; + } + +continue_req: + err = bt_bip_start_archive(&bip_app.client, true, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send start_archive req %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_server_reg(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + uint8_t uuid128[BT_UUID_SIZE_128]; + struct bt_uuid_128 *u = NULL; + uint8_t type; + + static struct bt_uuid_128 uuid; + + err = 0; + type = shell_strtoul(argv[1], 0, &err); + if (err != 0) { + shell_error(sh, "Invalid type %s", argv[1]); + return -ENOEXEC; + } + + if (argc > 2) { + hex2bin(argv[2], strlen(argv[2]), uuid128, sizeof(uuid128)); + uuid.uuid.type = BT_UUID_TYPE_128; + sys_memcpy_swap(uuid.val, uuid128, BT_UUID_SIZE_128); + u = &uuid; + } + + err = bt_bip_primary_server_register(&bip_app.bip, &bip_app.server, type, u, + &bip_server_cb); + if (err != 0) { + shell_error(sh, "Fail to register server %d", err); + } + + return err; +} + +static int cmd_bip_server_unreg(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + + err = bt_bip_server_unregister(&bip_app.server); + if (err != 0) { + shell_error(sh, "Fail to unregister obex server %d", err); + } + + return err; +} + +static int cmd_bip_server_conn(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "continue")) { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } else if (!strcmp(rsp, "success")) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + } else { + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + + err = bt_bip_connect_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send conn rsp %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_server_disconn(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "continue")) { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } else if (!strcmp(rsp, "success")) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + } else { + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + + err = bt_bip_disconnect_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send disconn rsp %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_server_abort(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "continue")) { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } else if (!strcmp(rsp, "success")) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + } else { + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + + err = bt_bip_abort_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send abort rsp %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +#define IMAGING_CAPS_BODY \ + "" \ + "" \ + "" \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + "" + +static int cmd_bip_server_get_caps(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + uint16_t len = 0; + const uint8_t *caps_body = (const uint8_t *)IMAGING_CAPS_BODY; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.server._server.tx.mopl, + sizeof(IMAGING_CAPS_BODY) - offset, + caps_body + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } + +error_rsp: + err = bt_bip_get_capabilities_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_capabilities rsp %d", err); + } else { + bip_app.tx_buf = NULL; + if (rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +#define IMAGE_LIST_BODY \ + "" \ + "" \ + "" \ + "" \ + "" + +static int cmd_bip_server_get_image_list(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + uint16_t len = 0; + const uint8_t *image_list_body = (const uint8_t *)IMAGE_LIST_BODY; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (offset == 0) { + uint16_t returned_handles = sys_cpu_to_be16(1); + struct bt_obex_tlv appl_params[] = { + {BT_BIP_APPL_PARAM_TAG_ID_RETURNED_HANDLES, sizeof(returned_handles), + (const uint8_t *)&returned_handles}, + }; + + err = bt_obex_add_header_app_param(bip_app.tx_buf, ARRAY_SIZE(appl_params), + appl_params); + if (err != 0) { + shell_error(sh, "Fail to add appl param header %d", err); + return err; + } + + err = bt_bip_add_header_image_desc(bip_app.tx_buf, sizeof(IMAGE_HANDLES_DESC), + IMAGE_HANDLES_DESC); + if (err != 0) { + shell_error(sh, "Fail to add image desc header %d", err); + return err; + } + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.server._server.tx.mopl, + sizeof(IMAGE_LIST_BODY) - offset, + image_list_body + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } + +error_rsp: + err = bt_bip_get_image_list_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_image_list rsp %d", err); + } else { + bip_app.tx_buf = NULL; + if (rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +#define IMAGE_PROPERTIES_BODY \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" + +static int cmd_bip_server_get_image_properties(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + uint16_t len = 0; + const uint8_t *image_properties_body = (const uint8_t *)IMAGE_PROPERTIES_BODY; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.server._server.tx.mopl, + sizeof(IMAGE_PROPERTIES_BODY) - offset, + image_properties_body + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } + +error_rsp: + err = bt_bip_get_image_properties_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_image_properties rsp %d", err); + } else { + bip_app.tx_buf = NULL; + if (rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +static int cmd_bip_server_get_image(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + uint16_t len = 0; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (offset == 0) { + err = bt_obex_add_header_len(bip_app.tx_buf, sizeof(jpeg_1000001)); + if (err != 0) { + shell_error(sh, "Fail to add len header %d", err); + return err; + } + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.server._server.tx.mopl, + sizeof(jpeg_1000001) - offset, + jpeg_1000001 + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } + +error_rsp: + err = bt_bip_get_image_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_image rsp %d", err); + } else { + bip_app.tx_buf = NULL; + if (rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +static int cmd_bip_server_get_linked_thumbnail(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + uint16_t len = 0; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.server._server.tx.mopl, + sizeof(jpeg_1000001) - offset, + jpeg_1000001 + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } + +error_rsp: + err = bt_bip_get_linked_thumbnail_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_linked_thumbnail rsp %d", err); + } else { + bip_app.tx_buf = NULL; + if (rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +static int cmd_bip_server_get_linked_attachment(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + uint16_t len = 0; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.server._server.tx.mopl, + sizeof(jpeg_1000001) - offset, + jpeg_1000001 + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } + +error_rsp: + err = bt_bip_get_linked_attachment_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_linked_attachment rsp %d", err); + } else { + bip_app.tx_buf = NULL; + if (rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +static int cmd_bip_server_get_partial_image(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + uint16_t len = 0; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (offset == 0) { + uint32_t total_file_size = sys_cpu_to_be32(sizeof(jpeg_1000001)); + uint8_t end_flag = 0x01; + struct bt_obex_tlv appl_params[] = { + {BT_BIP_APPL_PARAM_TAG_ID_TOTAL_FILE_SIZE, sizeof(total_file_size), + (const uint8_t *)&total_file_size}, + {BT_BIP_APPL_PARAM_TAG_ID_END_FLAG, sizeof(end_flag), + (const uint8_t *)&end_flag}, + }; + + err = bt_obex_add_header_len(bip_app.tx_buf, sizeof(jpeg_1000001)); + if (err != 0) { + shell_error(sh, "Fail to add appl param header %d", err); + return err; + } + + err = bt_obex_add_header_app_param(bip_app.tx_buf, ARRAY_SIZE(appl_params), + appl_params); + if (err != 0) { + shell_error(sh, "Fail to add appl param header %d", err); + return err; + } + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.server._server.tx.mopl, + sizeof(jpeg_1000001) - offset, + jpeg_1000001 + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } + +error_rsp: + err = bt_bip_get_partial_image_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_partial_image rsp %d", err); + } else { + bip_app.tx_buf = NULL; + if (rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +static int cmd_bip_server_get_monitoring_image(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + uint16_t len = 0; + + static uint16_t offset; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (offset == 0) { + err = bt_bip_add_header_image_handle(bip_app.tx_buf, sizeof(IMAGE_HANDLE) - 1, + IMAGE_HANDLE); + if (err != 0) { + shell_error(sh, "Fail to add image handle header %d", err); + return err; + } + } + + err = bt_obex_add_header_body_or_end_body(bip_app.tx_buf, bip_app.server._server.tx.mopl, + sizeof(jpeg_1000001) - offset, + jpeg_1000001 + offset, &len); + if (err != 0) { + shell_error(sh, "Fail to add body"); + return -ENOEXEC; + } + + if (bt_obex_has_header(bip_app.tx_buf, BT_OBEX_HEADER_ID_END_BODY)) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } + +error_rsp: + err = bt_bip_get_monitoring_image_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_monitoring_image rsp %d", err); + } else { + bip_app.tx_buf = NULL; + if (rsp_code == BT_OBEX_RSP_CODE_CONTINUE) { + offset += len; + } else { + offset = 0; + } + } + return err; +} + +static int cmd_bip_server_get_status(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "continue")) { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } else if (!strcmp(rsp, "success")) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + } else { + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + err = bt_bip_get_status_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send get_status rsp %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_server_put_image(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (server_put_image_final) { + err = bt_bip_add_header_image_handle(bip_app.tx_buf, sizeof(IMAGE_HANDLE) - 1, + IMAGE_HANDLE); + if (err != 0) { + shell_error(sh, "Fail to add image handle header %d", err); + return err; + } + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } + +error_rsp: + err = bt_bip_put_image_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send put_image rsp %d", err); + } else { + bip_app.tx_buf = NULL; + server_put_image_final = false; + } + return err; +} + +static int cmd_bip_server_put_linked_thumbnail(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (server_put_linked_thumbnail_final) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } + +error_rsp: + err = bt_bip_put_linked_thumbnail_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send put_linked_thumbnail rsp %d", err); + } else { + bip_app.tx_buf = NULL; + server_put_linked_thumbnail_final = false; + } + return err; +} + +static int cmd_bip_server_put_linked_attachment(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (server_put_linked_attachment_final) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } + +error_rsp: + err = bt_bip_put_linked_attachment_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send put_linked_attachment rsp %d", err); + } else { + bip_app.tx_buf = NULL; + server_put_linked_attachment_final = false; + } + return err; +} + +static int cmd_bip_server_remote_display(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + + err = bt_bip_add_header_image_handle(bip_app.tx_buf, sizeof(IMAGE_HANDLE) - 1, + IMAGE_HANDLE); + if (err != 0) { + shell_error(sh, "Fail to add image handle header %d", err); + return err; + } + +error_rsp: + err = bt_bip_remote_display_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send remote_display rsp %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_server_delete_image(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + +error_rsp: + err = bt_bip_delete_image_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send delete_image rsp %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_server_start_print(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + if (server_start_print_final) { + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + } else { + rsp_code = BT_OBEX_RSP_CODE_CONTINUE; + } +error_rsp: + err = bt_bip_start_print_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send start_print rsp %d", err); + } else { + bip_app.tx_buf = NULL; + server_start_print_final = false; + } + return err; +} + +static int cmd_bip_server_start_archive(const struct shell *sh, size_t argc, char *argv[]) +{ + uint8_t rsp_code; + const char *rsp; + int err; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + if (bip_app.conn == NULL) { + shell_error(sh, "No bip transport connection"); + return -ENOEXEC; + } + + rsp = argv[1]; + if (!strcmp(rsp, "error")) { + if (argc < 3) { + shell_error(sh, "[rsp_code] is needed if the rsp is %s", rsp); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + rsp_code = (uint8_t)strtoul(argv[2], NULL, 16); + + goto error_rsp; + } + + if (bip_app.tx_buf != NULL) { + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + } + + if (bip_app.tx_buf == NULL) { + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + } + + if (bip_app.tx_buf == NULL) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + rsp_code = BT_OBEX_RSP_CODE_SUCCESS; + +error_rsp: + err = bt_bip_start_archive_rsp(&bip_app.server, rsp_code, bip_app.tx_buf); + if (err != 0) { + shell_error(sh, "Fail to send start_archive rsp %d", err); + } else { + bip_app.tx_buf = NULL; + } + return err; +} + +static int cmd_bip_sdp_reg(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + + static bool registered; + + if (registered) { + shell_error(sh, "SDP record has been registered"); + return -EINVAL; + } + + err = bt_sdp_register_service(&bip_responder_rec); + if (err != 0) { + shell_error(sh, "Failed to register SDP record"); + return err; + } + + return err; +} + +static int cmd_sdp_set_caps(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + bip_supported_caps = shell_strtol(argv[1], 0, &err); + if (err != 0) { + shell_error(sh, "Invalid capabilities %d", err); + return err; + } + + return 0; +} + +static int cmd_sdp_set_features(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + bip_supported_features = shell_strtol(argv[1], 0, &err); + if (err != 0) { + shell_error(sh, "Invalid features %d", err); + return err; + } + + return 0; +} + +static int cmd_sdp_set_functions(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + bip_supported_functions = shell_strtol(argv[1], 0, &err); + if (err != 0) { + shell_error(sh, "Invalid functions %d", err); + return err; + } + + return 0; +} + +#define BIP_SDP_DISCOVER_BUF_LEN 512 +NET_BUF_POOL_FIXED_DEFINE(sdp_client_pool, CONFIG_BT_MAX_CONN, BIP_SDP_DISCOVER_BUF_LEN, + CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); + +static uint8_t bip_discover_func(struct bt_conn *conn, struct bt_sdp_client_result *result, + const struct bt_sdp_discover_params *params) +{ + int err; + uint16_t features = 0; + uint32_t functions = 0; + uint16_t rfcomm_channel = 0; + uint16_t l2cap_psm = 0; + + if (result == NULL || result->resp_buf == NULL || conn == NULL || params == NULL) { + bt_shell_info("No record found"); + return BT_SDP_DISCOVER_UUID_CONTINUE; + } + + err = bt_sdp_get_proto_param(result->resp_buf, BT_SDP_PROTO_RFCOMM, &rfcomm_channel); + if (err != 0) { + bt_shell_error("Failed to get RFCOMM channel: %d", err); + } else { + bt_shell_info("Found RFCOMM channel %u", rfcomm_channel); + } + + err = bt_sdp_get_goep_l2cap_psm(result->resp_buf, &l2cap_psm); + if (err != 0) { + bt_shell_error("Failed to get GOEP L2CAP PSM: %d", err); + } else { + bt_shell_info("Found GOEP L2CAP PSM %u", l2cap_psm); + } + + err = bt_sdp_get_features(result->resp_buf, &features); + if (err != 0) { + bt_shell_error("Failed to get BIP features: %d", err); + } else { + bt_shell_info("Found BIP features 0x%08x", features); + bip_supported_features = features; + } + + err = bt_sdp_get_functions(result->resp_buf, &functions); + if (err != 0) { + bt_shell_error("Failed to get BIP functions: %d", err); + } else { + bt_shell_info("Found BIP functions 0x%08x", functions); + bip_supported_functions = functions; + } + + return BT_SDP_DISCOVER_UUID_CONTINUE; +} + +static int cmd_sdp_discover(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + static struct bt_sdp_discover_params sdp_bip_params; + static struct bt_uuid_16 uuid; + + if (default_conn == NULL) { + shell_error(sh, "Not connected"); + return -ENOEXEC; + } + + uuid.uuid.type = BT_UUID_TYPE_16; + uuid.val = BT_SDP_IMAGING_SVCLASS; + sdp_bip_params.uuid = &uuid.uuid; + sdp_bip_params.func = bip_discover_func; + sdp_bip_params.pool = &sdp_client_pool; + sdp_bip_params.type = BT_SDP_DISCOVER_SERVICE_SEARCH_ATTR; + + err = bt_sdp_discover(default_conn, &sdp_bip_params); + if (err != 0) { + shell_error(sh, "Failed to start SDP discovery %d", err); + return err; + } + + return 0; +} + +#define HELP_NONE "" + +SHELL_STATIC_SUBCMD_SET_CREATE( + obex_add_header_cmds, + SHELL_CMD_ARG(count, NULL, "", cmd_add_header_count, 2, + 0), + SHELL_CMD_ARG(name, NULL, "[name of the object (often a file name)]", cmd_add_header_name, + 1, 1), + SHELL_CMD_ARG(type, NULL, + "", + cmd_add_header_type, 2, 0), + SHELL_CMD_ARG(len, NULL, "", cmd_add_header_len, 2, 0), + SHELL_CMD_ARG(time_iso_8601, NULL, "", + cmd_add_header_time_iso_8601, 2, 0), + SHELL_CMD_ARG(time, NULL, "", + cmd_add_header_time, 2, 0), + SHELL_CMD_ARG(description, NULL, "", + cmd_add_header_description, 2, 0), + SHELL_CMD_ARG(target, NULL, "", + cmd_add_header_target, 2, 0), + SHELL_CMD_ARG(http, NULL, "", cmd_add_header_http, 2, 0), + SHELL_CMD_ARG(body, NULL, "", cmd_add_header_body, 2, 0), + SHELL_CMD_ARG(end_body, NULL, "", + cmd_add_header_end_body, 2, 0), + SHELL_CMD_ARG(who, NULL, + "", + cmd_add_header_who, 2, 0), + SHELL_CMD_ARG(conn_id, NULL, "", + cmd_add_header_conn_id, 2, 0), + SHELL_CMD_ARG(app_param, NULL, "application parameter: [last]", + cmd_add_header_app_param, 3, 1), + SHELL_CMD_ARG(auth_challenge, NULL, "authentication digest-challenge: [last]", + cmd_add_header_auth_challenge, 3, 1), + SHELL_CMD_ARG(auth_rsp, NULL, "authentication digest-response: [last]", + cmd_add_header_auth_rsp, 3, 1), + SHELL_CMD_ARG(creator_id, NULL, "", + cmd_add_header_creator_id, 2, 0), + SHELL_CMD_ARG(wan_uuid, NULL, "", + cmd_add_header_wan_uuid, 2, 0), + SHELL_CMD_ARG(obj_class, NULL, "", cmd_add_header_obj_class, 2, + 0), + SHELL_CMD_ARG(session_param, NULL, "", + cmd_add_header_session_param, 2, 0), + SHELL_CMD_ARG(session_seq_number, NULL, + "", + cmd_add_header_session_seq_number, 2, 0), + SHELL_CMD_ARG(action_id, NULL, + "", + cmd_add_header_action_id, 2, 0), + SHELL_CMD_ARG(dest_name, NULL, + "", + cmd_add_header_dest_name, 2, 0), + SHELL_CMD_ARG(perm, NULL, "<4-byte bit mask for setting permissions>", cmd_add_header_perm, + 2, 0), + SHELL_CMD_ARG(srm, NULL, "<1-byte value to setup Single Response Mode (SRM)>", + cmd_add_header_srm, 2, 0), + SHELL_CMD_ARG(srm_param, NULL, "", + cmd_add_header_srm_param, 2, 0), + SHELL_SUBCMD_SET_END); + +SHELL_STATIC_SUBCMD_SET_CREATE( + obex_client_cmds, + SHELL_CMD_ARG(set_feats_funcs, NULL, HELP_NONE, cmd_bip_client_set_feats_funcs, 1, 0), + SHELL_CMD_ARG(conn, NULL, " [UUID 128]", cmd_bip_client_conn, 2, 1), + SHELL_CMD_ARG(disconn, NULL, HELP_NONE, cmd_bip_client_disconn, 1, 0), + SHELL_CMD_ARG(abort, NULL, HELP_NONE, cmd_bip_client_abort, 1, 0), + SHELL_CMD_ARG(get_caps, NULL, HELP_NONE, cmd_bip_client_get_caps, 1, 0), + SHELL_CMD_ARG(get_image_list, NULL, HELP_NONE, cmd_bip_client_get_image_list, 1, 0), + SHELL_CMD_ARG(get_image_properties, NULL, HELP_NONE, cmd_bip_client_get_image_properties, 1, + 0), + SHELL_CMD_ARG(get_image, NULL, HELP_NONE, cmd_bip_client_get_image, 1, 0), + SHELL_CMD_ARG(get_linked_thumbnail, NULL, HELP_NONE, cmd_bip_client_get_linked_thumbnail, 1, + 0), + SHELL_CMD_ARG(get_linked_attachment, NULL, HELP_NONE, cmd_bip_client_get_linked_attachment, + 1, 0), + SHELL_CMD_ARG(get_partial_image, NULL, HELP_NONE, cmd_bip_client_get_partial_image, 1, 0), + SHELL_CMD_ARG(get_monitoring_image, NULL, HELP_NONE, cmd_bip_client_get_monitoring_image, 1, + 0), + SHELL_CMD_ARG(get_status, NULL, HELP_NONE, cmd_bip_client_get_status, 1, 0), + SHELL_CMD_ARG(put_image, NULL, HELP_NONE, cmd_bip_client_put_image, 1, 0), + SHELL_CMD_ARG(put_linked_thumbnail, NULL, HELP_NONE, cmd_bip_client_put_linked_thumbnail, 1, + 0), + SHELL_CMD_ARG(put_linked_attachment, NULL, HELP_NONE, cmd_bip_client_put_linked_attachment, + 1, 0), + SHELL_CMD_ARG(remote_display, NULL, HELP_NONE, cmd_bip_client_remote_display, 1, 0), + SHELL_CMD_ARG(delete_image, NULL, HELP_NONE, cmd_bip_client_delete_image, 1, 0), + SHELL_CMD_ARG(start_print, NULL, HELP_NONE, cmd_bip_client_start_print, 1, 0), + SHELL_CMD_ARG(start_archive, NULL, HELP_NONE, cmd_bip_client_start_archive, 1, 0), + SHELL_SUBCMD_SET_END); + +SHELL_STATIC_SUBCMD_SET_CREATE( + obex_server_cmds, SHELL_CMD_ARG(reg, NULL, " [UUID 128]", cmd_bip_server_reg, 2, 1), + SHELL_CMD_ARG(unreg, NULL, HELP_NONE, cmd_bip_server_unreg, 1, 0), + SHELL_CMD_ARG(conn, NULL, " [rsp_code]", cmd_bip_server_conn, + 2, 1), + SHELL_CMD_ARG(disconn, NULL, " [rsp_code]", + cmd_bip_server_disconn, 2, 1), + SHELL_CMD_ARG(abort, NULL, " [rsp_code]", + cmd_bip_server_abort, 2, 1), + SHELL_CMD_ARG(get_caps, NULL, " [rsp_code]", cmd_bip_server_get_caps, + 2, 1), + SHELL_CMD_ARG(get_image_list, NULL, " [rsp_code]", + cmd_bip_server_get_image_list, 2, 1), + SHELL_CMD_ARG(get_image_properties, NULL, " [rsp_code]", + cmd_bip_server_get_image_properties, 2, 1), + SHELL_CMD_ARG(get_image, NULL, " [rsp_code]", cmd_bip_server_get_image, + 2, 1), + SHELL_CMD_ARG(get_linked_thumbnail, NULL, " [rsp_code]", + cmd_bip_server_get_linked_thumbnail, 2, 1), + SHELL_CMD_ARG(get_linked_attachment, NULL, " [rsp_code]", + cmd_bip_server_get_linked_attachment, 2, 1), + SHELL_CMD_ARG(get_partial_image, NULL, " [rsp_code]", + cmd_bip_server_get_partial_image, 2, 1), + SHELL_CMD_ARG(get_monitoring_image, NULL, " [rsp_code]", + cmd_bip_server_get_monitoring_image, 2, 1), + SHELL_CMD_ARG(get_status, NULL, " [rsp_code]", + cmd_bip_server_get_status, 2, 1), + SHELL_CMD_ARG(put_image, NULL, " [rsp_code]", cmd_bip_server_put_image, + 2, 1), + SHELL_CMD_ARG(put_linked_thumbnail, NULL, " [rsp_code]", + cmd_bip_server_put_linked_thumbnail, 2, 1), + SHELL_CMD_ARG(put_linked_attachment, NULL, " [rsp_code]", + cmd_bip_server_put_linked_attachment, 2, 1), + SHELL_CMD_ARG(remote_display, NULL, " [rsp_code]", + cmd_bip_server_remote_display, 2, 1), + SHELL_CMD_ARG(delete_image, NULL, " [rsp_code]", + cmd_bip_server_delete_image, 2, 1), + SHELL_CMD_ARG(start_print, NULL, " [rsp_code]", + cmd_bip_server_start_print, 2, 1), + SHELL_CMD_ARG(start_archive, NULL, " [rsp_code]", + cmd_bip_server_start_archive, 2, 1), + SHELL_SUBCMD_SET_END); + +SHELL_STATIC_SUBCMD_SET_CREATE( + bip_sdp_cmds, SHELL_CMD_ARG(reg, NULL, HELP_NONE, cmd_bip_sdp_reg, 1, 0), + SHELL_CMD_ARG(set_caps, NULL, "", cmd_sdp_set_caps, 2, 0), + SHELL_CMD_ARG(set_features, NULL, "", cmd_sdp_set_features, 2, 0), + SHELL_CMD_ARG(set_functions, NULL, "", cmd_sdp_set_functions, 2, 0), + SHELL_CMD_ARG(discover, NULL, HELP_NONE, cmd_sdp_discover, 1, 0), + SHELL_SUBCMD_SET_END); + +static int cmd_alloc_buf(const struct shell *sh, size_t argc, char **argv) +{ + if (bip_app.tx_buf) { + shell_error(sh, "Buf %p is using", bip_app.tx_buf); + return -EBUSY; + } + + bip_app.tx_buf = bt_goep_create_pdu(&bip_app.bip.goep, &tx_pool); + if (!bip_app.tx_buf) { + shell_error(sh, "Fail to allocate tx buffer"); + return -ENOBUFS; + } + + return 0; +} + +static int cmd_release_buf(const struct shell *sh, size_t argc, char **argv) +{ + if (!bip_app.tx_buf) { + shell_error(sh, "No buf is using"); + return -EINVAL; + } + + net_buf_unref(bip_app.tx_buf); + bip_app.tx_buf = NULL; + + return 0; +} + +static int cmd_common(const struct shell *sh, size_t argc, char **argv) +{ + if (argc == 1) { + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + + shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]); + + return -ENOEXEC; +} + +SHELL_STATIC_SUBCMD_SET_CREATE( + bip_cmds, SHELL_CMD_ARG(register-rfcomm, NULL, "", cmd_register_rfcomm, 2, 0), + SHELL_CMD_ARG(connect-rfcomm, NULL, "", cmd_connect_rfcomm, 2, 0), + SHELL_CMD_ARG(disconnect-rfcomm, NULL, HELP_NONE, cmd_disconnect_rfcomm, 1, 0), + SHELL_CMD_ARG(register-l2cap, NULL, "", cmd_register_l2cap, 2, 0), + SHELL_CMD_ARG(connect-l2cap, NULL, "", cmd_connect_l2cap, 2, 0), + SHELL_CMD_ARG(disconnect-l2cap, NULL, HELP_NONE, cmd_disconnect_l2cap, 1, 0), + SHELL_CMD_ARG(alloc-buf, NULL, "Alloc tx buffer", cmd_alloc_buf, 1, 0), + SHELL_CMD_ARG(release-buf, NULL, "Free allocated tx buffer", cmd_release_buf, 1, 0), + SHELL_CMD_ARG(add-header, &obex_add_header_cmds, "Adding header sets", cmd_common, 1, 0), + SHELL_CMD_ARG(client, &obex_client_cmds, "Client sets", cmd_common, 1, 0), + SHELL_CMD_ARG(server, &obex_server_cmds, "Server sets", cmd_common, 1, 0), + SHELL_CMD_ARG(sdp, &bip_sdp_cmds, "SDP sets", cmd_common, 1, 0), + SHELL_SUBCMD_SET_END); + +SHELL_CMD_ARG_REGISTER(bip, &bip_cmds, "Bluetooth BIP shell commands", cmd_common, 1, 1); diff --git a/tests/bluetooth/shell/prj_br.conf b/tests/bluetooth/shell/prj_br.conf index 36e831627024e..f9938e278701b 100644 --- a/tests/bluetooth/shell/prj_br.conf +++ b/tests/bluetooth/shell/prj_br.conf @@ -37,3 +37,5 @@ CONFIG_BT_AVRCP=y CONFIG_BT_AVRCP_TARGET=y CONFIG_BT_AVRCP_CONTROLLER=y CONFIG_BT_AVRCP_BROWSING=y + +CONFIG_BT_BIP=y